2

I'm trying to understand how to locate local variables in memory. I have disassembled a c code and I have the address of the instructions. I want the memory location where that variable is stored. I tried to access the instruction address of i in c code but the variable is not located there. I think it is located at a "offset" from the base pointer. But I don't know about the concept of offset. Any help will be appreciated.

C code:

int main()
{
    int i = 10;
    int j = -1;
    int k = 4;
    int l = 9;
    int m;

    m = (i-j) + (k-l);
}

Disassembled code

int main()
{
003E13A0  push        ebp  
003E13A1  mov         ebp,esp  
003E13A3  sub         esp,0FCh  
003E13A9  push        ebx  
003E13AA  push        esi  
003E13AB  push        edi  
003E13AC  lea         edi,[ebp-0FCh]  
003E13B2  mov         ecx,3Fh  
003E13B7  mov         eax,0CCCCCCCCh  
003E13BC  rep stos    dword ptr es:[edi]  
    int i = 10;
003E13BE  mov         dword ptr [i],0Ah  
    int j = -1;
003E13C5  mov         dword ptr [j],0FFFFFFFFh  
    int k = 4;
003E13CC  mov         dword ptr [k],4  
    int l = 9;
003E13D3  mov         dword ptr [l],9  
    int m;

    m = (i-j) + (k-l);
003E13DA  mov         eax,dword ptr [i]  
003E13DD  sub         eax,dword ptr [j]  
003E13E0  mov         ecx,dword ptr [k]  
003E13E3  sub         ecx,dword ptr [l]  
003E13E6  add         eax,ecx  
003E13E8  mov         dword ptr [m],eax  
}
003E13EB  xor         eax,eax  
003E13ED  pop         edi  
003E13EE  pop         esi  
003E13EF  pop         ebx  
003E13F0  mov         esp,ebp  
003E13F2  pop         ebp  
003E13F3  ret

Registers

EAX = 001A9478 EBX = 7EFDE000 ECX = 001A8868 EDX = 00000001 ESI = 00000000 EDI = 00000000 EIP = 00BE13A0 ESP = 003BF990 EBP = 003BF9DC EFL = 00000214 
1
  • For the record, it would be *incredibly) helpful in the future for you to provide the exact compiler, OS, and compile options you used, as well as providing the precise commands you used to create your disassembly.
    – avgvstvs
    Commented Mar 19, 2014 at 2:36

3 Answers 3

2

I'm not sure why this question was down-voted. I'm going to answer it from a generic standpoint as it might be a bit off-topic for the RE group.

Anyway, you need to spend some time understand assembly instruction format for your processor architecture. From looking at the disassembly above, you have the information you need but you'll (1) need to understand the format of each instruction and (2) need a debugger. A debugger works with a processor to extract run-time information about the register set. In addition to understanding assembly instruction format, you will need to review the processor registers. You'll want to set breakpoints (see debugger instructions) and check the Program Counter often.

If you try to add C instructions to read out memory values, you will change the entire assembly offset! In other words, by adding an instruction, you may change the location of the value.

One helpful piece of info: when working with C (and other languages) there is typically several instructions used to setup the stack and for arguments passed in to the function. Once you get an understanding of what each instruction is you'll begin to see the pattern emerge.

You can also put some known instructions in your code to help you trace through it. For example, maybe do some printfs and see what those look like. Or add instructions to add 0xBE to 0xAD and look for those values in the produced code.

2

While you could accomplish a similar result by using gdb's "disassemble main" command, I chose to write your example (with one minor alteration), compile it with mingw (no flags/options) and disassemble it with the trial version of IDA Pro:

.text:00401334 ; =============== S U B R O U T I N E =======================================
.text:00401334
.text:00401334 ; Attributes: bp-based frame
.text:00401334
.text:00401334 ; void main()
.text:00401334                 public _main
.text:00401334 _main           proc near               ; CODE XREF: ___mingw_CRTStartup+F8p
.text:00401334
.text:00401334 var_4           = dword ptr -4
.text:00401334
.text:00401334                 push    ebp
.text:00401335                 mov     ebp, esp
.text:00401337                 push    ebx
.text:00401338                 and     esp, 0FFFFFFF0h ; Logical AND
.text:0040133B                 sub     esp, 20h        ; Integer Subtraction
.text:0040133E                 call    ___main         ; Call Procedure
.text:00401343                 mov     dword ptr [esp+1Ch], 0Ah
.text:0040134B                 mov     dword ptr [esp+18h], 0FFFFFFFFh
.text:00401353                 mov     dword ptr [esp+14h], 4
.text:0040135B                 mov     dword ptr [esp+10h], 9
.text:00401363                 mov     eax, [esp+18h]
.text:00401367                 mov     edx, [esp+1Ch]
.text:0040136B                 mov     ecx, edx
.text:0040136D                 sub     ecx, eax        ; Integer Subtraction
.text:0040136F                 mov     eax, [esp+10h]
.text:00401373                 mov     edx, [esp+14h]
.text:00401377                 mov     ebx, edx
.text:00401379                 sub     ebx, eax        ; Integer Subtraction
.text:0040137B                 mov     eax, ebx
.text:0040137D                 add     eax, ecx        ; Add
.text:0040137F                 mov     [esp+0Ch], eax
.text:00401383                 mov     ebx, [ebp+var_4]
.text:00401386                 leave                   ; High Level Procedure Exit
.text:00401387                 retn                    ; Return Near from Procedure
.text:00401387 _main           endp

The key point here in understanding offsets is that its all based on the program's base address, which can be different depending on your platform. Understanding base address is a processor, language, and OS-specific problem. For windows binaries, do some googling on PE Headers. (That will explain the .text.) Generally speaking offsets mean "how many bytes from the beginning of the program/function call." Convention holds that an "int" (in C languages" is 4 bytes (32b) and that's why you usually see variables like yours placed 4B apart from each other. Notice the offsets by your variable declarations:

.text:00401343 mov dword ptr [esp+1Ch], 0Ah .text:0040134B mov dword ptr [esp+18h], 0FFFFFFFFh .text:00401353 mov dword ptr [esp+14h], 4 .text:0040135B mov dword ptr [esp+10h], 9

Each variable is 4B off from each other. Do you get the hint about offsets yet?

Offsets have alot to do with "esp," traditionally the "stack pointer."

So when you see the line:

.text:00401343                 mov     dword ptr [esp+1Ch], 0Ah

You're looking at the data that's at the address that is "1Ch" away from "esp." (Get familiar with HEX arithmetic. Google.)

The ".text" that you see is telling you what the instructions are at that particular offset in the ".text" portion of the program. (Google. PE headers again.)

I know that I haven't completely answered your question, but if you're doing binary analysis, you really DO need to get used to wide swaths of ambiguity... reverse engineering isn't like programming in a high-level language at all. There's no concept of a data type, only a data type inferred by looking at the surrounding instructions. Its ugly, and its beautiful at the same time.

[EDIT]

I altered your program thusly:

void main()
{
    int i = 10;
    int j = -1;
    int k = 4;
    int l = 9;
    int m;

    m = (i-j) + (k-l);

}

If you start a C program with "int main(){..}" you really must at least have return 0;.

Since you didn't have that, I altered the program to return void.

2

in the code you posted all the variables i,j,k,l,m are local variables

they will all be located at an address below base pointer ie from [ebp-4] onwards

use .cpp instead of c and make use of some advanatges of c++ in a c program like 
not having to make declaration of variables at the start 

if you need to access them in your own program you can use the following helper code
this prints

all the registers  5 dwords above base pointer namely arguments to the    
function and 5 dwords below the base pointer namely Locals   

this is a naked function so it uses the ebp of the function from which it is called in your case the ebp constructed by main

#include <stdio.h>

    char regs_5args_and_5locs[] = 
    { 
        "==========Registers===================\n"
        "eax = %08x\n"
        "ebx = %08x\n"
        "ecx = %08x\n"
        "edx = %08x\n"
        "esi = %08x\n"
        "edi = %08x\n"
        "esp = %08x\n"
        "ebp = %08x\n"
        "==========Arguments===================\n"
        "1rg = %08x\n"
        "2rg = %08x\n"
        "3rg = %08x\n"
        "4rg = %08x\n"
        "5rg = %08x\n"
        "==========Locals===================\n"
        "1lo = %08x\n"
        "2lo = %08x\n"
        "3lo = %08x\n"
        "4lo = %08x\n"
        "5lo = %08x\n"
        "===================================\n"
    };
 __declspec (naked) void regsargsnlocs(void)
{
    __asm
    {
        pushad
            pushfd
            push [ebp-0x14]
        push [ebp-0x10]
        push [ebp-0x0c]
        push [ebp-0x08]
        push [ebp-0x04] 
        push [ebp+0x14]
        push [ebp+0x10] 
        push [ebp+0x0c]
        push [ebp+0x08]
        push [ebp+0x04]
        push ebp
            push esp
            push edi
            push esi
            push edx
            push ecx
            push ebx
            push eax
            lea eax, regs_5args_and_5locs
            push eax        
            call printf
            add esp,0x4c
            popfd
            popad
            retn
    }   
}


void main()
{
    regsargsnlocs();    
    int i = 10;
    int j = -1;
    int k = 4;
    int l = 9;
    int m;
    regsargsnlocs();
    m = (i-j) + (k-l);
   regsargsnlocs();
}

the result as follows you can notice i= 0xa ,j= -0x1,k= 0x4,l=0x9 ,m = 0x6 in the locals on the 2nd and 3rd invocation respectively

\locvar>locvar.exe
==========Registers===================
eax = 000339e8
ebx = 7ffd4000
ecx = 00000001
edx = 0041dcf0
esi = 00000000
edi = 2d6648b4
esp = 0013ff10
ebp = 0013ff78
==========Arguments===================
1rg = 004013ca
2rg = 00000001
3rg = 000339c8
4rg = 000339e8
5rg = 4b9239ba
==========Locals===================
the locals have not been assigned yet we printed prior to them 
notice this is a cpp program not c which wont allow you a function 
before declaration of variables
1lo = 004039a1 
2lo = 00402d5b 
3lo = 0013ff80 
4lo = 004039a1
5lo = 004086a8
===================================
==========Registers===================
eax = 000339e8
ebx = 7ffd4000
ecx = 00000001
edx = 0041dcf0
esi = 00000000
edi = 2d6648b4
esp = 0013ff10
ebp = 0013ff78
==========Arguments===================
1rg = 004013ca
2rg = 00000001
3rg = 000339c8
4rg = 000339e8
5rg = 4b9239ba
==========Locals===================
1lo = 0000000a   i
2lo = 00000004   k
3lo = 00000009   l
4lo = 004039a1    m not calculated yet
5lo = ffffffff   j
===================================
==========Registers===================
eax = 00000006
ebx = 7ffd4000
ecx = fffffffb
edx = 0041dcf0
esi = 00000000
edi = 2d6648b4
esp = 0013ff10
ebp = 0013ff78
==========Arguments===================
1rg = 004013ca
2rg = 00000001
3rg = 000339c8
4rg = 000339e8
5rg = 4b9239ba
==========Locals===================
1lo = 0000000a
2lo = 00000004
3lo = 00000009
4lo = 00000006   m calculated here 3rd invocation
5lo = ffffffff
===================================

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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