What's the best way to parse a packet data in c++? - c++

I'm transcoding a Delphi App to C++ and got a problem to get the correct way to do the cases of packet headers in C++.
The Headers Comes as "Dword" in a UCHAR buf and i dont want make like: if (packet[0] == 0xFF && packet[1] == 0xFF...);
Ex: Delphi Working with switch case:
case PDword(#Packet[6])^ of
$68FB0200:
begin
//Dword match correctly
End;
end;
is there a way to do the same in C++ like the Delphi Example?
Already checked for some methds but ever fails the comparsion.
UCHAR *Packet ;
Packet = ParsePacket(buf->Buf, buf->Size);
// The ParsePacket Returns FB 00 00 00 78 00 01 F3 02 FD
DWORD Op1 = 0x01F302FD;
DWORD *p_dw = (DWORD*) Packet[6];
if (p_dw == Op1)
{
//Dword never match...
}

Yes, the portable C (and C++) way is:
DWORD const Op1 = 0x01F302FD;
if (0 == memcmp(Packet + 6, &Op1, sizeof Op1)) { ... }
Note that we haven't tried to access the bytes at Packet+6 as a 32-bit quantity... they're likely to be misaligned for that. We're just asking whether the same sequence of 4 bytes exists there as exist in Op1.

The obvious issue with DWORD *p_dw = (DWORD*) Packet[6] is that you take a value as an address. You probably meant DWORD *p_dw = (DWORD*)&(Packet[6]).
Note, however, that you get additional troubles for two reasons:
First, alignment could be wrong such that the pointer received is not a valid address to be taken for dereferencing a DWORD. This is undefined behaviour then.
Second, you depend on a specific endianess.
To overcome the first issue, you could use memcpy:
DWORD Op2 = 0;
memcpy(&Op2,Packet+6,sizeof(Op2));
To overcome both the first and the second issue, you'll need to sum the bytes on your own, e.g.:
DWORD Op2 = Packet[6]<<24 | Packet[7]<<16 | Packet[8]<<8 | Packet[9]

Related

Access voilation exception when trying to rewrite jmp

I have a game, I disasembled it and located a jump which I want to rewrite,
but whenever I try to write to the address I get a access voilation exception, even when I use VirtualProtect and set the READWRITE permission.
the instruction on 0x0042BD5F is this:
0x0046AACF E9 FF FF 89 FC | jmp some address here
Now, when I try to write to 0x0042BD5F, to change the relative jump address, I get an access voilation exception.
How do I change the jump on that address?
Code was requested, so here it is:
#define AddVar(Type,Name,Address) Type& Name = *reinterpret_cast<Type*>(Address)
/*
Hooker
1b 0x0042BD5F == E9 <relative jmp>
4b 0x0042BD60 - relative jump offset (always the value 0xFFFF89FC)
*/
AddVar(uqbyte, jump_hook_bytes, 0x0042BD60);
//the user tick function
void(*tick)(void);
void SetTick(void(*passed)(void))
{
tick = passed;
}
void Ticker();
void OnDLLLoad(void(*passed)(void) = nullptr)
{
tick = passed;
//point the game loop end to Ticker()
//replace the jump address
//jmp (DESTINATION_RVA - CURRENT_RVA - 5 [sizeof(E9 xx xx xx xx)])
DWORD old;
VirtualProtect(
(LPVOID)0x0042BD5F,
0x05,
PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE,
&old
);
jump_hook_bytes = (((uqbyte)((uqbyte*)&Ticker) - (uqbyte)0x0042BD5F) - (uqbyte)0x0000005);
}
void Ticker()
{
if (tick != nullptr)
{
tick();
}
__asm
{
MOV EAX, 0x0042B9EA;//old address
JMP EAX;
}
}
uqbyte is an unsigned long.
When calling getlasterror the code seems to return the decimal error 87 (INVALID_PARAMETERS).
The documentation for the memory protection constants says:
The following are the memory-protection options; you must specify one of the following values when allocating or protecting a page in memory.
It then lists a number of values, including the three that you combined together. When the documentation says "specify one of the following values" it means exactly one. You must not combine them.
You need to use PAGE_EXECUTE_READWRITE on its own.
I recommend that you add error checking around all your API calls. I also think that you could avoid hard coding the addresses.
You need to use a debugger or a program that enables the process token privilege for debugger mode (like a trainer). I assume this isn't being used for an online cheat (offline shouldn't matter).
Passing PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE is wrong, you need to pass PAGE_EXECUTE_READWRITE only to Virtual Protect. Now it works.

Calculating offset for hotpatching/inline function hooking

From http://lastfrag.com/hotpatching-and-inline-hooking-explained/,
Q1) Does code proceed from high memory to low memory or vice versa?
Q2) More importantly, during the calculation of the replacement offset, why is it that you have to minus the function preamble? Is it because the offset starts from the end of the instruction and not the beginning?
DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;
Full Code:
void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
HMODULE hModule = LoadLibrary(Module);
DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);
DWORD ReplacementAddress = (DWORD)Function;
DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;
LPBYTE pOriginalAddress = (LPBYTE)OriginalAddress;
LPBYTE pReplacementAddressOffset = (LPBYTE)(&ReplacementAddressOffset);
DWORD OldProtect = 0;
DWORD NewProtect = PAGE_EXECUTE_READWRITE;
VirtualProtect((PVOID)OriginalAddress, 5, NewProtect, &OldProtect);
for (int i = 0; i < 5; i++)
Store[i] = pOriginalAddress[i];
pOriginalAddress[0] = (BYTE)0xE9;
for (int i = 0; i < 4; i++)
pOriginalAddress[i + 1] = pReplacementAddressOffset[i];
VirtualProtect((PVOID)OriginalAddress, 5, OldProtect, &NewProtect);
FlushInstructionCache(GetCurrentProcess(), NULL, NULL);
FreeLibrary(hModule);
}
Q3) In this code, the relative address of a jmp instruction is being replaced; relAddrSet is a pointer to the original destination; to is a pointer to the new destination. I don't understand the calculation of the to address, why is it that you have to add the original destination to the functionForHook + opcodeOffset?
DWORD *relAddrSet = (DWORD *)(currentOpcode + 1);
DWORD_PTR to = (*relAddrSet) + ((DWORD_PTR)functionForHook + opcodeOffset);
*relAddrSet = (DWORD)(to - ((DWORD_PTR)originalFunction + opcodeOffset));
Yes the relative address is the the offset after the instructions, that's why you have to substract 5.
But, in my opinion, you should just forget the idea of the relative jump and try absolute jump.
Why ? Because it is a lot easier and x86-64 compatible (relative jumps are limited to +/-2GB).
The absolute jump is (x64) :
48 b8 ef cd ab 89 67 45 23 01 mov rax, 0x0123456789abcdef
ff e0 jmp rax
And for x86 :
b8 67 45 23 01 mov eax, 0x01234567
ff e0 jmp eax
Here is the modified code (the loader is now 7 bytes instead of 5):
void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
HMODULE hModule = LoadLibrary(Module);
DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);
DWORD OldProtect = 0;
DWORD NewProtect = PAGE_EXECUTE_READWRITE;
VirtualProtect((PVOID)OriginalAddress, 7, NewProtect, &OldProtect);
memcpy(Store, OriginalAddress, 7);
memcpy(OriginalAddress, "\xb8\x00\x00\x00\x00\xff\xe0", 7);
memcpy(OriginalAddress+1, &ReplacementAddress, sizeof(void*));
VirtualProtect((PVOID)OriginalAddress, 7, OldProtect, &NewProtect);
FlushInstructionCache(GetCurrentProcess(), NULL, NULL);
FreeLibrary(hModule);
}
The code is the same for x64 but you have to add 2 nops (90) at the beginning or the end in order match the size of the following instructions, so the loader is "\x48\xb8<8-bytes addr>\xff\xe0\x90\x90" (14 bytes)
Q1) The program runs from lower to highest addresses (i.e. the program counter gets increased by the size of each instruction, unless in case of jumps, calls or ret). But I am probably missing the point of the question.
Q2) Yes, on x86 the jumps are executed after the program counter has been increased by the size of the jump instruction (5 bytes); when the CPU adds the jump offset to the program counter to calculate the target address, the program counter has already been increased of 5.
Q3) This code is quite weird, but it may work. I suppose that *relAddrset initially contains a jump offset to originalFunction (i.e. *relAddSet==originalFunction-relativeOffset). If this is true, the final result is that *reladdrSet contains a jump offset to functionFoHook. Indeed the last instruction becomes:
*relAddrSet=(originalFunction-relativeOffset)+functionForHook-originalFunction
== functionForHook-relativeOffset
Yes, code runs "forward" if I understand this question correctly. One instruction is executed after another if it is not branching.
An instruction that does a relative jump (JMP, CALL) does the jump relative to the start of the next instruction. That's why you have to subtract the length of the instruction (here: 5) from the difference.
I can't answer your third question. Please give some context and what the code is supposed to do.

What's the reason behind applying two explicit type casts in a row?

What's the reason behind applying two explicit type casts as below?
if (unlikely(val != (long)(char)val)) {
Code taken from lxml.etree.c source file from lxml's source package.
That's a cheap way to check to see if there's any junk in the high bits. The char cast chops of the upper 8, 24 or 56 bits (depending on sizeof(val)) and then promotes it back. If char is signed, it will sign extend as well.
A better test might be:
if (unlikely(val & ~0xff)) {
or
if (unlikely(val & ~0x7f)) {
depending on whether this test cares about bit 7.
Just for grins and completeness, I wrote the following test code:
void RegularTest(long val)
{
if (val != ((int)(char)val)) {
printf("Regular = not equal.");
}
else {
printf("Regular = equal.");
}
}
void MaskTest(long val)
{
if (val & ~0xff) {
printf("Mask = not equal.");
}
else {
printf("Mask = equal.");
}
}
And here's what the cast code turns into in debug in visual studio 2010:
movsx eax, BYTE PTR _val$[ebp]
cmp DWORD PTR _val$[ebp], eax
je SHORT $LN2#RegularTes
this is the mask code:
mov eax, DWORD PTR _val$[ebp]
and eax, -256 ; ffffff00H
je SHORT $LN2#MaskTest
In release, I get this for the cast code:
movsx ecx, al
cmp eax, ecx
je SHORT $LN2#RegularTes
In release, I get this for the mask code:
test DWORD PTR _val$[ebp], -256 ; ffffff00H
je SHORT $LN2#MaskTest
So what's going on? In the cast case it's doing a byte mov with sign extension (ha! bug - the code is not the same because chars are signed) and then a compare and to be totally sneaky, the compiler/linker has also made this function use register passing for the argument. In the mask code in release, it has folded everything up into a single test instruction.
Which is faster? Beats me - and frankly unless you're running this kind of test on a VERY slow CPU or are running it several billion times, it won't matter. Not in the least.
So the answer in this case, is to write code that is clear about its intent. I would expect a C/C++ jockey to look at the mask code and understand its intent, but if you don't like that, you should opt for something like this instead:
#define BitsAbove8AreSet(x) ((x) & ~0xff)
#define BitsAbove7AreSet(x) ((x) & ~0x7f)
or:
inline bool BitsAbove8AreSet(long t) { return (t & ~0xff) != 0; } // make it a bool to be nice
inline bool BitsAbove7AreSet(long t) { return (t & ~0x7f) != 0; }
And use the predicates instead of the actual code.
In general, I think "is it cheap?" is not a particularly good question to ask about this unless you're working in some very specific problem domains. For example, I work in image processing and when I have some kind of operation going from one image to another, I often have code that looks like this:
BYTE *srcPixel = PixelOffset(src, x, y, srcrowstride, srcdepth);
int srcAdvance = PixelAdvance(srcrowstride, right, srcdepth);
BYTE *dstPixel = PixelOffset(dst, x, y, dstrowstride, dstdepth);
int dstAdvance = PixelAdvance(dstrowstride, right, dstdepth);
for (y = top; y < bottom; y++) {
for (x=left; x < right; x++) {
ProcessOnePixel(srcPixel, srcdepth, dstPixel, dstdepth);
srcPixel += srcdepth;
dstPixel += dstdepth;
}
srcPixel += srcAdvance;
dstPixel += dstAdvance;
}
And in this case, assume that ProcessOnePixel() is actually a chunk of inline code that will be executed billions and billions of times. In this case, I care a whole lot about not doing function calls, not doing redundant work, not rechecking values, ensuring that the computational flow will translate into something that will use registers wisely, etc. But my actual primary concern is that the code can be read by the next poor schmuck (probably me) who has to look at it.
And in our current coding world, it is FAR FAR CHEAPER for nearly every problem domain to spend a little time up front ensuring that your code is easy to read and maintain than it is to worry about performance out of the gate.
Speculations:
cast to char: to mask the 8 low bits,
cast to long: to bring the value back to signed (if char is unsigned).
If val is a long then the (char) will strip off all but the bottom 8 bits. The (long) casts it back for the comparison.

Why are my TCP transfers corrupted on cygwin?

I am trying to debug why my TCP transfers are corrupted when sent from Cygwin. I see that only the first 24 bytes of each structure are showing up in my server program running on Centos. The 25th through 28th bytes are scrambled and all others after that are zeroed out. Going in the other direction, receiving from Centos on Cygwin, again only the first 24 bytes of each block are showing up in my server program running on Cygwin. The 25th through 40th bytes are scrambled and all others after that are zeroed out. I also see the issue when sending or receiving to/from localhost on Cygwin. For localhost, the first 34 bytes are correct and all after that are zeroed out.
The application I am working on work fine on Centos4 talking to Centos and I am trying to port it to Cygwin. Valgrind reports no issues on Centos, I do not have Valgrind running on Cygwin. Both platforms are little-endian x86.
I've run Wireshark on the host Windows XP system under which Cygwin is running. When I sniff the packets with Wireshark they look perfect, for both sent packets from Cygwin and received packets to Cygwin.
Somehow, the data is corrupted between the level Wireshark looks at and the program itself.
The C++ code uses ::write(fd, buffer, size) and ::read(fd, buffer, size) to write and read the TCP packets where fd is a file descriptor for the socket that is opened between the client and server. This code works perfectly on Centos4 talking to Centos.
The strangest thing to me is that the packet sniffer shows the correct complete packet for all cases, yet the cygwin application never reads the complete packet or in the other direction, the Centos application never reads the complete packet.
Can anyone suggest how I might go about debugging this?
Here is some requested code:
size_t
read_buf(int fd, char *buf, size_t count, bool &eof, bool immediate)
{
if (count > SSIZE_MAX) {
throw;
}
size_t want = count;
size_t got = 0;
fd_set readFdSet;
int fdMaxPlus1 = fd + 1;
FD_ZERO(&readFdSet);
FD_SET(fd, &readFdSet);
while (got < want) {
errno = 0;
struct timeval timeVal;
const int timeoutSeconds = 60;
timeVal.tv_usec = 0;
timeVal.tv_sec = immediate ? 0 : timeoutSeconds;
int selectReturn = ::select(fdMaxPlus1, &readFdSet, NULL, NULL, &timeVal);
if (selectReturn < 0) {
throw;
}
if (selectReturn == 0 || !FD_ISSET(fd, &readFdSet)) {
throw;
}
errno = 0;
// Read buffer of length count.
ssize_t result = ::read(fd, buf, want - got);
if (result < 0) {
throw;
} else {
if (result != 0) {
// Not an error, increment the byte counter 'got' & the read pointer,
// buf.
got += result;
buf += result;
} else { // EOF because zero result from read.
eof = true;
break;
}
}
}
return got;
}
I've discovered more about this failure. The C++ class where the packet is being read into is laid out like this:
unsigned char _array[28];
long long _sequence;
unsigned char _type;
unsigned char _num;
short _size;
Apparently, the long long is getting scrambled with the four bytes that follow.
The C++ memory sent by Centos application, starting with _sequence, in hex, looks like this going to write():
_sequence: 45 44 35 44 33 34 43 45
_type: 05
_num: 33
_size: 02 71
Wireshark shows the memory laid out in network big-endian format like this in the packet:
_sequence: 45 43 34 33 44 35 44 45
_type: 05
_num: 33
_size: 71 02
But, after read() in the C++ cygwin little-endian application, it looks like this:
_sequence: 02 71 33 05 45 44 35 44
_type: 00
_num: 00
_size: 00 00
I'm stumped as to how this is occurring. It seems to be an issue with big-endian and little-endian, but the two platforms are both little-endian.
Here _array is 7 ints instead of 28 chars.
Complete memory dump at sender:
_array[0]: 70 a2 b7 cf
_array[1]: 9b 89 41 2c
_array[2]: aa e9 15 76
_array[3]: 9e 09 b6 e2
_array[4]: 85 49 08 81
_array[5]: bd d7 9b 1e
_array[6]: f2 52 df db
_sequence: 41 41 31 35 32 43 38 45
_type: 05
_num: 45
_size: 02 71
And at receipt:
_array[0]: 70 a2 b7 cf
_array[1]: 9b 89 41 2c
_array[2]: aa e9 15 76
_array[3]: 9e 09 b6 e2
_array[4]: 85 49 08 81
_array[5]: bd d7 9b 1e
_array[6]: f2 52 df db
_sequence: 02 71 45 05 41 41 31 35
_type: 0
_num: 0
_size: 0
Cygwin test result:
4
8
48
0x22be08
0x22be28
0x22be31
0x22be32
0x22be38
Centos test result:
4
8
40
0xbfffe010
0xbfffe02c
0xbfffe035
0xbfffe036
0xbfffe038
Now that you've shown the data, your problem is clear. You're not controlling the alignment of your struct, so the compiler is automatically putting the 8 byte field (the long long) on an 8 byte boundary (offset 32) from the start of the struct, leaving 4 bytes of padding.
Change the alignment to 1 byte and everything should resolve. Here's the snippet you need:
__attribute__ ((aligned (1))) __attribute ((packed))
I also suggest that you use the fixed-size types for structures being blitted across the network, e.g. uint8_t, uint32_t, uint64_t
Previous thoughts:
With TCP, you don't read and write packets. You read and write from a stream of bytes. Packets are used to carry these bytes, but boundaries are not preserved.
Your code looks like it deals with this reasonably well, you might want to update the wording of your question.
Hopefully final update :-)
Based on your latest update, Centos is packing your structures at the byte level whilst CygWin is not. This causes alignment problems. I'm not sure why the CygWin-to-CygWin case is having problems since the padding should be identical there but I can tell you how to fix the other case.
Using the code I gave earlier:
#include <stdio.h>
typedef struct {
unsigned char _array[28];
long long _sequence;
unsigned char _type;
unsigned char _num;
short _size;
} tType;
int main (void) {
tType t[2];
printf ("%d\n", sizeof(long));
printf ("%d\n", sizeof(long long));
printf ("%d\n", sizeof(tType));
printf ("%p\n", &(t[0]._array));
printf ("%p\n", &(t[0]._sequence));
printf ("%p\n", &(t[0]._num));
printf ("%p\n", &(t[0]._size));
printf ("%p\n", &(t[1]));
return 0;
}
If you don't want any padding, you have two choices. The first is to re-organise your structure to put the more restrictive types up front:
typedef struct {
long long _sequence;
short _size;
unsigned char _array[28];
unsigned char _type;
unsigned char _num;
} tType;
which gives you:
4
8
40
0x22cd42
0x22cd38
0x22cd5f
0x22cd40
0x22cd60
In other words, each structure is exactly 40 bytes (8 for sequence, 2 for size, 28 for array and 1 each for type and num). But this may not be possible if you want it in a specific order.
In that case, you can force the alignments to be on a byte level with:
typedef struct {
unsigned char _array[28];
long long _sequence;
unsigned char _type;
unsigned char _num;
short _size;
} __attribute__ ((aligned(1),packed)) tType;
The aligned(1) sets it to byte alignment but that won't affect much since objects don't like having their alignments reduced. To force that, you need to use packed as well.
Doing that gives you:
4
8
40
0x22cd3c
0x22cd58
0x22cd61
0x22cd62
0x22cd64
Earlier history for prosperity:
Well, since I wget and ftp huge files just fine from CygWin, my psychic debugging skills tell me it's more likely to be a problem with your code rather than the CygWin software.
In other words, regarding the sentence "the packets are corrupted between the level Wireshark looks at and the program itself", I'd be seriously looking towards the upper end of that scale rather than the lower end :-)
Usually, it's the case that you've assumed a read will get the whole packet that was sent rather than bits at a time but, without seeing the code in question, that's a pretty wild guess.
Make sure you're checking the return value from read to see how many bytes are actually being received. Beyond that, post the code responsible for the read so we can give a more in-depth analysis.
Based on your posted code, it looks okay. The only thing I can suggest is that you check that the buffers you're passing in are big enough and, even if they are, make sure you print them immediately after return in case some other piece of code is corrupting the data.
In fact, in re-reading your question more closely, I'm a little confused. You state you have the same problem with your server code on both Linux and CygWin yet say it's working on Centos.
My only advice at this point is to put debugging printf statements in that function you've shown, such as after the select and read calls to output the relevant variables, including got and buf after changing them, and also in every code path so you can see what it's doing. And also dump the entire structure byte-for-byte at the sending end.
This will hopefully show you immediately where the problem lies, especially since you seem to have data showing up in the wrong place.
And make sure your types are compatible at both ends. By that, I mean if long long is different sizes on the two platforms, your data will be misaligned.
Okay, checking alignments at both ends, compile and run this program on both systems:
#include <stdio.h>
typedef struct {
unsigned char _array[28];
long long _sequence;
unsigned char _type;
unsigned char _num;
short _size;
} tType;
int main (void) {
tType t[2];
printf ("%d\n", sizeof(long));
printf ("%d\n", sizeof(long long));
printf ("%d\n", sizeof(tType));
printf ("%p\n", &(t[0]._array));
printf ("%p\n", &(t[0]._sequence));
printf ("%p\n", &(t[0]._num));
printf ("%p\n", &(t[0]._size));
printf ("%p\n", &(t[1]));
return 0;
}
On my CygWin, I get:
4 long size
8 long long size
48 structure size
0x22cd30 _array start (size = 28, padded to 32)
0x22cd50 _sequence start (size = 8, padded to 9???)
0x22cd59 _type start (size = 1)
0x22cd5a _size start (size = 2, padded to 6 for long long alignment).
0x22cd60 next array element.
The only odd bit there is the padding before _type but that's certainly valid though unexpected.
Check the output from Centos to see if it's incompatible. However, your statement that CygWin-to-CygWin doesn't work is incongruous with that possibility since the alinments and sizes would be compatible (unless your sending and receiving code is compiled differently).

memcpy() crashes randomly

I am using memcpy in my application. memcpy crashes randomely and below is the logs i got in Dr.Watson files.
100181b5 8bd1 mov edx,ecx
100181b7 c1e902 shr ecx,0x2
100181ba 8d7c030c lea edi,[ebx+eax+0xc]
100181be f3a5 rep movsd
100181c0 8bca mov ecx,edx
100181c2 83e103 and ecx,0x3
FAULT ->100181c5 f3a4 rep movsb ds:02a3b000=?? es:01b14e64=00
100181c7 ff1508450210 call dword ptr [Debug (10024508)]
100181cd 83c424 add esp,0x24
100181d0 6854580210 push 0x10025854
100181d5 ff1508450210 call dword ptr [Debug (10024508)]
100181db 83c404 add esp,0x4
Below is the code
memcpy((char *)dep + (int)sizeof(EntryRec) + (int)adp->fileHdr.keySize, data, dataSize );
Where:
dep is a structure
EntryRec is a charecter pointer
adp is a structure
data is not NULL in this case
Has anyone faced this issue and can help me?
I have tried to debug the prog,
then i got the following error
Unhandled exception in Prog.exe(MSVCRTD.DLL):0xC0000005: Access voilation
Data is passed argument for this program and this is void*
Further Info:
I have tried to Debug the code adapter is crashing in the following area this function is present in OUTPUT.c (I think this is a library function)
#else /* _UNICODE */
if (flags & (FL_LONG|FL_WIDECHAR)) {
if (text.wz == NULL) /* NULL passed, use special string */
text.wz = __wnullstring;
bufferiswide = 1;
pwch = text.wz;
while ( i-- && *pwch )
++pwch;
textlen = pwch - text.wz;
/* textlen now contains length in wide chars */
} else {
if (text.sz == NULL) /* NULL passed, use special string */
text.sz = __nullstring;
p = text.sz;
while (i-- && *p) //Crash points here
++p;
textlen = p - text.sz; /* length of the string */
}
Value for variables:
p= ""(not initialised)
i= 2147483598
There are two very likely explanations:
You are using memcpy across overlapping addresses -- the behavior of this situation is undefined. If you require the ability to handle overlapping addresses, std::memmove is the "equivalent" tool.
You are using memcpy to copy to/from memory that is inaccessible to your program.
From the code you've shown, it looks like (2) is the more likely scenario. Since you are able to debug the source, try setting a breakpoint before the memcpy occurs, and verify that the arguments to memcpy all match up (i.e. source + num < dest or source > dest + num).
From the disassembled code it appears that the source pointer is not in your address space.
rep movsb copies from ds:si to es:di. The ?? indicates that the memory at ds:si could not be read.
Is the data pointed to by (char *)dep + (int)sizeof(EntryRec) + (int)adp->fileHdr.keySize always at least dataSize long?
I have come across similar crashes where variable length strings are later treated like fixed with strings.
eg
char * ptr = strdup("some string");
// ...
memcpy(ptr, dest, fixedLength);
Where fixedLength is greater than 10. Obviously these were in different functions so the length issue was not noticed. Most of the time this will work, dest will contain "some string" and after the null will be random garbage. In this case if you treat dest as a null terminated string you will never notice, as you don't see the garbage after the null.
However if ptr is allocated at the end of a page of memory, you can only read to the end of the allocated memory and no further. As soon as you read past the end of the page the operating system will rightly crash your program.
It looks like you've run over the end of a buffer and generated an access violation.
Edit: There still is not enough information. We cannot spot a bug without knowing much more about how the buffer you are trying to copy to is allocated whether it has enough space (I suspect it does not) and whether dataSize is valid.
If memcpy crashes the usual reason is, that you passed illegal arguments.
Note that with memcpy source and destination may not overlap.
In such a case use memmove.
from your code "memcpy((char *)dep + (int)sizeof(EntryRec) + (int)adp->fileHdr.keySize, data, dataSize)" and the debug infomation, the "data" looks like a local variable (on-stack variable), you'd do "data = malloc(DATA_SIZE)" instead of "char data[DATA_SIZE]" etc; otherwise, at your current code line, the "data" was popped already, so may cause memory accessing fault randomly.
I'd suggest using memmove as this handles overlapping strings, when using memcpy in this situation the result is unpredictable.