2009-03-07

Internal binary representation of FGInt

From FGInt.pas:

// TFGInt = Record
// Sign : TSign;
// Number : Array Of LongWord;

In a crackme, the MD5 of "myname" == ABB45C192F0818FF22B7DDDA8566DAC3 is made into an FGInt via Base256StringToFGInt(). Looking at the memory where this FGInt is stored:

0014fc04 00000001 00862354 0014fc34 0001d4c7

Where 000000001 is the Sign member and 00862354 is the Number member.

00862354 00000009 44414333 706a6c6c 11111104
00862364 119211ba 13846463 48c60706 50cc4e4c
00862374 21211a1a 00000041 00860a3c 0001f604

Where 00000009 is the amount of dwords in the array, and the 44414333 706a6c6c 11111104... are the members of the array.

Strange, but the only quantities contributing to the FGInt are the 31 lower bits of the 9 dwords.

Here is some C where a 64-bit value is used as kind of a bit buffer to shift in the 31-bit chunks. When more than 8 are collected, a byte can be consumed. If you know a better way, please tell me.

typedef struct {
ULONG nLongWords;
ULONG longWords[1];
} DELPHI6ARRAYLONGWORD, *PDELPHI6ARRAYLONGWORD;

// assume DELPHI6ARRAYLONGWORD variable darray and byte buffer bytes[]

for(int i=0; i<darray.nLongWords; ++i)
OutputRaw("digit %d: %08X\n", i, (ULONG)digits[i]);

ULONGLONG ullBuff=0;
UINT bytei=0;

UINT nBitsInBuff=0;

for(int i=0; i<darray.nLongWords; ++i)
{
ullBuff |= (digits[i] << nBitsInBuff);

nBitsInBuff += 31;

while(nBitsInBuff >= 8)
{
bytes[bytei++] = ullBuff & 0xFF;
ullBuff >>= 8;
nBitsInBuff -= 8;
}
}

if(nBitsInBuff)
bytes[bytei++] = ullBuff & 0xFF;

for(int i=bytei-1; i>=0; i--)
OutputRaw("%02X", (ULONG)bytes[i]);

The output is good:

digit 0: 44414333
digit 1: 706A6C6C
digit 2: 11111104
digit 3: 119211BA
digit 4: 13846463
digit 5: 48C60706
digit 6: 50CC4E4C
digit 7: 21211A1A
digit 8: 00000041
0000004142423434433139304630383038464630324237404444410035363644414333
The bytes of the FGInt (within the 31-bit chunks) are stored little-endian. The 414242... start the "ABB..." from the hash.

Feb 02, 2010 EDIT: Numernia informed me that 31-bits is chosen to easily propagate carry bits across addition (carry flag and adc instruction are not accessible from delphi)... a closer look at FGInt source verifies this:

Trest := FGInt1.Number[i] + FGInt2.Number[i] + rest;
Sum.Number[i] := Trest And 2147483647;
rest := Trest Shr 31;

Switch "rest" with "carry" in your mind. 2147483647 is 0x7FFFFFFF.

No comments:

Post a Comment

thanks for commenting!