1

I know that every x86 instruction has an opcode for that, but I don't know how data types and variables are converted to machine code. So I tried to figure out. I wrote a simple C program and converted it to assembly:

#include<stdio.h>

int variable1=1;
int variable2=1;

char variable3='A';
char variable4='A';

char name[20]="naveen prakash";


int main(int argc, char const *argv[])
{

char g[4]="hell";
int local_variable1=1;
int local_variable2=2;
int local_variable3;

variable1=0;
variable2=1;

 variable3='B';
 variable4='A';

 printf("HeLLO wORLD\n");
 getch();

}

Assembly for the above program (Compiled using gcc -S hello.c):

.file   "hello.c"
.globl _variable1
    .data
    .align 4
_variable1:
    .long   1
.globl _variable2
    .align 4
_variable2:
    .long   1
.globl _variable3
_variable3:
    .byte   65
.globl _variable4
_variable4:
    .byte   65
.globl _name
_name:
    .ascii "naveen prakash\0"
    .space 5
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC1:
    .ascii "HeLLO wORLD\0"
LC0:
    .ascii "hell\0"
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    call    ___main
    movl    LC0, %eax
    movl    %eax, 16(%esp)
    movl    $1, 20(%esp)
    movl    $2, 24(%esp)
    movl    $0, _variable1
    movl    $1, _variable2
    movb    $66, _variable3
    movb    $65, _variable4
    movl    $LC1, (%esp)
    call    _puts
    call    _getch
    leave
    ret
    .def    _puts;  .scl    2;  .type   32; .endef
    .def    _getch; .scl    2;  .type   32; .endef

I could figure out only global variables and cannot find local variables. Then I created executable file of the above program and used IDA, ollyDBG, PE Explorer and tried to find where the variables are, but I could not find.

What happens to the variables and data type declarations when it converted into machine language?

I read that .data segment contains the initialized data, then I tried to find my initialized data, but I couldn't find anything. I have a text "naveen prakash" but I can't find it anywhere in the executable file. I used Hex editor to view the binary file.

So my question is, How variables and data types are represented in machine code and how do we find it using debuggers?

  • The local variables are located in the stack of every function. You can easily find local ones in the definition of a function in Idea for example. It shows the locals with negative pointer values on the top of the function definition. – EWD-0- Nov 22 '17 at 9:19
  • 1
    There are no variables in assembler, as they are a high level concept. There are bitfields of different lengths, which are interpreted depending on the operation. These can reside on the stack (locals) or at a certain memory location (globals). – Nordwald Nov 22 '17 at 11:52
  • your name char array has been optimised away by the compiler since you are not using it try a printf("%s", name ) and i will remain – blabb Nov 22 '17 at 17:45
2

This is an expansion of Nordwald's comment:

There are no variables in assembler, as they are a high level concept. There are bitfields of different lengths, which are interpreted depending on the operation. These can reside on the stack (locals) or at a certain memory location (globals).

HLLs vs. Machine Code

What happens to the variables and data type declarations when it converted into machine language?

Variable and data type declaration information is lost when code written in a high-level language are translated into machine language via compilation, since notions of variables and data types do not exist in machine language. This is part of the reason why decompilation is challenging.

High-level programming languages have data abstractions such as buffers, structures, and local variables that all help programmers and program analyses reason about programs in a scalable manner. During compilation, these abstractions are removed as code is translated down to operations on registers and one globally addressed memory region.1

High-level languages are machine-independent. In order for code written in a HLL to be executed on a machine by its CPU, it needs to be translated into a form that is compatible with the CPU instruction set architecture of the target machine. Examples of instruction sets are x86, MIPS and ARM. We can think of instruction sets as representations of processor-specific machine language. Machine language deals only with binary values that represent information such as constants, memory locations, CPU registers and instructions.

Executable Binary Organization

It is not enough to simply perform HLL to object code translation, however. Information must be organized in a file in such a manner that it can be mapped into memory by the program loader and become a running process. This is where binary formats and system application binary interfaces (ABIs) enter the picture.

When it comes to binary analysis, several things need to be taken into consideration:

  • compiler toolchain
  • system architecture
  • runtime environment
  • binary format

I believe gcc will compose ELF binaries by default. ELF binaries are structured such that memory is allocated for statically-allocated variables such as global variables in the .data section. String literals such as "hello world" are stored in the .rodata section and are accessed via pointers.

Of course, if the binary you are analyzing is a PE rather than an ELF binary, this is not relevant. Either way, you should consult the ABI documentation appropriate for your system.

Regardless of this, space for variables local to functions will be allocated on the runtime stack in virtual memory, not within the binary. This is the difference between static memory allocation and stack-based allocation:

Each instance of a subroutine at run time has its own frame (also called an activation record) on the stack, containing arguments and return values, local variables, temporaries, and bookkeeping information. Arguments to be passed to subsequent routines lie at the top of the frame, where the callee can easily find them. The organization of the remaining information is implementation-dependent: it varies from one language, machine, and compiler to another.2

This will be reflected in the code generated by the compiler, since it is the compiler that is responsible for generating the code that manages the runtime stack.

See also:

How to figure out method argument sizes and types in elf32-i386 disasembly?


1. TIE: Principled Reverse Engineering of Types in Binary Programs

2. Scott, Michael L. Programming Language Pragmatics. 3rd ed. Page 117

0

Assembly represents variables using pointers in memory. The better question is "How are variables represented in memory?"

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.