2

So im working on an crackme and came across a couple of FPU loads and pops that confused me.

Address main 0040169E pops 80 bit value of 00000000_FED63690h from the FPU stack (ST0=4275451536.0000000000) to the CPU stack

but when put on the CPU stack its value is changed to

CPU Stack
Locked    Value      ASCII Comments
0028FB38  |D2000000     Ò
0028FB3C  |41EFDAC6  ÆÚïA

Why?

Here is the code with some comments:

main    00401696    PUSH EAX                                EAX=FED63690
main    00401697    FILD QWORD PTR SS:[LOCAL.268]           Loads the FED63690 as a 64 bit value so -> 00000000_FED63690
main    0040169A    LEA ESP,[LOCAL.266]                     ESP=0028FB20 (loads address of string on stack), ST0=4275451536.0000000000 (which euals above number)
main    0040169E    FSTP QWORD PTR SS:[LOCAL.260]           POPS 80 bit value of ST0 onto program stack as 64 bit   

        LOCAL.260 is address 0028FB38

        Stack now looks like:

        CPU Stack
        Locked    Value      ASCII Comments
        0028FB38  |D2000000     Ò
        0028FB3C  |41EFDAC6  ÆÚïA


main    004016A4    FLD QWORD PTR SS:[LOCAL.260]            Loads value onto FPU Stack so -> ST0=4275451536.0000000000 (Same as before)
main    004016AA    FSTP QWORD PTR SS:[LOCAL.264]           <%i> = -771751936.       POPS 80 bit value of ST0 onto program stack as 64 bit

        LOCAL.260 is address 0028FB28

        Stack now looks like:

        CPU Stack
        Locked    Value      ASCII Comments
        0028FB28  |D2000000     Ò
        0028FB2C  |41EFDAC6  ÆÚïA


main    004016AE    MOV DWORD PTR SS:[LOCAL.265],00401469   Format => "%i"
main    004016B6    LEA EAX,[LOCAL.194]             
main    004016BC    MOV DWORD PTR SS:[LOCAL.266],EAX        s => OFFSET LOCAL.194
main    004016BF    CALL <JMP.&msvcrt.sprintf>          

        Result is:

        s="-771751936"
4

4275451536 is greater than 2^31 (2147483648) but less than 2 ^32 (4294967296)
so it is represented as 2^31 + ( 2 ^31 * ((4275451536 - 2 ^31 ) / 2^31)
ie 2^31 * 1.990912266075611114501953125

exponent is always written with bias (1023 for 64 bit precision) added to it so 1054 = 0x41e

fractional part can be written as 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + 1/64 ..... 1/2 ^n

0.984375     < 0.990912266075611114501953125 < 0.9921875

(1/2+...+1/64) <   -------                     < (1/2+....+ 1/128)
 111111

mantissa is approximated upto 52 bits explicitly and 1 or 0 is added implicitly

a c src that shows the conversion of your specific decimal is shown below

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BIAS 1023
int main(void) {
    char binform[100] = {0};
    unsigned long a = 4275451536;
    _ultoa_s(a%2,&binform[0],4,10);
    a = a/2;
    int i = 1;
    while (a>2) {
        a = a/2;
        _ultoa_s(a%2,&binform[i++],4,10);
    }
    char paddedstr[100] = {0};
    sprintf_s(paddedstr,100,"%s0000000000000000000000",_strrev(binform));
    printf("%x-%I64x\n",i+BIAS,_strtoui64(&paddedstr[1],0,2));
}

on execution the results are

>F2H.exe
41e-fdac6d2000000
  • In short: The original number is not normalized. Loading it into the FPU normalizes it, so storing it back changes the bits, but not the value. – Guntram Blohm May 20 '15 at 16:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.