Related
My program is composed of two files: main.c and core.s and runs on a 32 bit virtual machine of lubuntu linux.
Main.c takes in an integer and passes it to the assembly function void printFunc(int x). The assembly function in turn calls to a C function to check the parity of x. If x is even the function will print 4x and if x is odd it will print 8x. The print call must be done within the assembly function.
section .text
global printFunc
extern c_checkValidity
extern printf
section .data ; data section
fmt: db "%d", 10, 0 ; The printf format, "\n", '0'
printFunc:
push ebp ; code for handling stack I've seen from
mov ebp, esp ; other examples online
pushad
mov ebx, eax ; copy and input value
push eax ; Move input onto stack
call c_checkValidity ; Call C function, return value is in eax
cmp eax, 1 ; Check result, 1 indicates even
je multby4 ; If even, do mult by 4
jmp multby8 ; Otherwise odd, do mult by 8
multby4: ;INPUT WAS EVEN
sal ebx, 2 ; left shift by 2 is equivalent to multiplying by 4
jmp exitcode ; print and exit code
multby8: ;INPUT WAS ODD
sal ebx, 3 ; left shift by 3 is equivalent to multiplying by 8
jmp exitcode ; print and exit code
exitcode:
mov eax,ebx ; move value to eax to keep as default return value of func
push ebx ; Push final answer to the stack
push dword fmt ; Push print format to the stack
call printf ; Print answer
mov eax, ebx ; Copy final answer as return value
popad
mov esp, ebp ; return stack pointer to what it was before operation
pop ebp ; get rid of saved pointer
ret ; return state to caller
The integer input is received, parity tested, and printed to stdout correctly. A segfault occurs somewhere after call printf has successfully executed. When I use gdb to try and backtrace the segfault the report says "0x0804a0f in exitcode ()". Presumably this is the address of the code during operation that causes the segfault?
It is clear to me that I have failed to properly handle the stack pointer register (esp?) in some way. I've tried searching this site and others for examples of how to properly address the stack before returning control to the caller but to no avail. Certainly I would love to make the code work and any advice on how to fix the code is appreciated but I am primarily asking for an explanation on what I should be tracking and maintaining in general to return from an assembly function to a caller (extra appreciation if that explanation includes how to pass back values to the caller).
I'm trying to understand what this test does exactly. This toy code
int _tmain(int argc, _TCHAR* argv[])
{
int i;
printf("%d", i);
return 0;
}
Compiles into this:
int _tmain(int argc, _TCHAR* argv[])
{
012C2DF0 push ebp
012C2DF1 mov ebp,esp
012C2DF3 sub esp,0D8h
012C2DF9 push ebx
012C2DFA push esi
012C2DFB push edi
012C2DFC lea edi,[ebp-0D8h]
012C2E02 mov ecx,36h
012C2E07 mov eax,0CCCCCCCCh
012C2E0C rep stos dword ptr es:[edi]
012C2E0E mov byte ptr [ebp-0D1h],0
int i;
printf("%d", i);
012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain+3Bh (012C2E2Bh)
012C2E1E push 12C2E5Ch
012C2E23 call __RTC_UninitUse (012C10B9h)
012C2E28 add esp,4
012C2E2B mov esi,esp
012C2E2D mov eax,dword ptr [i]
012C2E30 push eax
012C2E31 push 12C5858h
012C2E36 call dword ptr ds:[12C9114h]
012C2E3C add esp,8
012C2E3F cmp esi,esp
012C2E41 call __RTC_CheckEsp (012C1140h)
return 0;
012C2E46 xor eax,eax
}
012C2E48 pop edi
012C2E49 pop esi
012C2E4A pop ebx
012C2E4B add esp,0D8h
012C2E51 cmp ebp,esp
012C2E53 call __RTC_CheckEsp (012C1140h)
012C2E58 mov esp,ebp
012C2E5A pop ebp
012C2E5B ret
The 5 lines emphasized are the only ones removed by properly initializing the variable i. The lines 'push 12C2E5Ch, call __RTC_UninitUse' call the function that display the error box, with a pointer to a string containing the variable name ("i") as an argument.
What I can't understand are the 3 lines that perform the actual test:
012C2E0E mov byte ptr [ebp-0D1h],0
012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain+3Bh (012C2E2Bh)
It would have seemed the compiler is probing the stack area of i (setting a byte to zero and immediately testing whether it's zero), just to be sure it isn't initialized somewhere it couldn't see during build. However, the probed address, ebp-0D1h, has little to do with the actual address of i.
Even worse, it seems if there were such an external (other thread?) initialization that did initialize the probed address but to zero, this test would still shout about the variable being uninitialized.
What's going on? Maybe the probe is meant for something entirely different, say to test if a certain byte is writable?
[ebp-0D1h] is a temporary variable used by the compiler to track "initialized" status of variables. If we modify the source a bit, it will be more clear:
int _tmain(int argc, _TCHAR* argv[])
{
int i, j;
printf("%d %d", i, j);
i = 1;
printf("%d %d", i, j);
j = 2;
return 0;
}
Produces the following (irrelevant parts skipped):
mov DWORD PTR [ebp-12], -858993460 ; ccccccccH
mov DWORD PTR [ebp-8], -858993460 ; ccccccccH
mov DWORD PTR [ebp-4], -858993460 ; ccccccccH
mov BYTE PTR $T4694[ebp], 0
mov BYTE PTR $T4693[ebp], 0
In prolog, variables are filled with 0xCC, and two tracking variables (one for i and one for j) are set to 0.
; 7 : printf("%d %d", i, j);
cmp BYTE PTR $T4693[ebp], 0
jne SHORT $LN3#main
push OFFSET $LN4#main
call __RTC_UninitUse
add esp, 4
$LN3#main:
cmp BYTE PTR $T4694[ebp], 0
jne SHORT $LN5#main
push OFFSET $LN6#main
call __RTC_UninitUse
add esp, 4
$LN5#main:
mov eax, DWORD PTR _j$[ebp]
push eax
mov ecx, DWORD PTR _i$[ebp]
push ecx
push OFFSET $SG4678
call _printf
add esp, 12 ; 0000000cH
This corresponds roughly to:
if ( $T4693 == 0 )
_RTC_UninitUse("j");
if ( $T4694 == 0 )
_RTC_UninitUse("j");
printf("%d %d", i, j);
Next part:
; 8 : i = 1;
mov BYTE PTR $T4694[ebp], 1
mov DWORD PTR _i$[ebp], 1
So, once i is intialized, the tracking variable is set to 1.
; 10 : j = 2;
mov BYTE PTR $T4693[ebp], 1
mov DWORD PTR _j$[ebp], 2
Here, the same is happening for j.
Here is my guess: the compiler probably allocates flags in memory showing the initialization status of variables. In your case for variable i this is a single byte at [ebp-0D1h]. The zeroing of this byte means i is not initialized. I assume if you initialize i this byte will be set to non-zero. Try something run-time like this: if (argc > 1) i = 1; This should generate code instead of omitting the whole check. You can also add another variable, and see if you get two different flags.
The zeroing of the flag and the testing just happen to be consecutive in this case, but that might not always be the case.
C7060F000055 mov dword ptr [esi],5500000Fh
C746048BEC5151 mov dword ptr [esi+0004],5151EC8Bh
b. And one of its later generations:
BF0F000055 mov edi,5500000Fh
893E mov [esi],edi
5F pop edi
52 push edx
B640 mov dh,40
BA8BEC5151 mov edx,5151EC8Bh
53 push ebx
8BDA mov ebx,edx
895E04 mov [esi+0004],ebx
c. And yet another generation with recalculated ("encrypted") "constant" data:
BB0F000055 mov ebx,5500000Fh
891E mov [esi],ebx
5B pop ebx
51 push ecx
B9CB00C05F mov ecx,5FC000CBh
81C1C0EB91F1 add ecx,F191EBC0h ; ecx=5151EC8Bh
I need to make a program that outputs a text file with an extension of .dna, I don't know if I can really do that, and if the text file will even be compatible with what I need to compare it afterwards. Anyway, I'm not really sure how to do this. I tried to look for some examples for NASM, but I didn't find much. I have an idea of what I'd need to do, but I just don't know what to call to generate a file.
Afterwards I'd need to write stuff into it, I'm not really sure on how to go on about that. Could anyone point me to some examples or something? I just need to see what is required to write my own thing.
Here's an example using system calls. Basically, you just open the file, write some data to it, then close and exit:
; nasm -f elf file.asm
; ld -m elf_i386 file.o
BITS 32
section .data
; don't forget the 0 terminator if it akes a C string!
filename: db 'test.txt', 0
; an error message to be printed with write(). The function doesn't
; use a C string so no need for a 0 here, but we do need length.
error_message: db 'Something went wrong.', 10 ; 10 == \n
; this next line means current location minus the error_message location
; which works out the message length.
; many of the system calls use pointer+length pairs instead of
; 0 terminated strings.
error_message_length: equ $ - error_message
; a message we'll write to our file, same as the error message
hello: db 'Hello, file!', 10 ; the 10 is a newline at the end
hello_length: equ $ - hello
fd: dd 0 ; this is like a global int variable in C
; global variables are generally a bad idea and there's other
; ways to do it, but for simplicity I'm using one here as the
; other ways are a bit more work in asm
section .text
global _start
_start:
; first, open or create the file. in C it would be:
; // $ man 2 creat
; int fd = creat("file.txt", 0644); // the second argument is permission
; we get the syscall numbers from /usr/include/asm/unistd_32.h
mov eax, 8 ; creat
mov ebx, filename ; first argument
mov ecx, 644O ; the suffix O means Octal in nasm, like the leading 0 in C. see: http://www.nasm.us/doc/nasmdoc3.html
int 80h ; calls the kernel
cmp eax, -1 ; creat returns -1 on error
je error
mov [fd], eax ; the return value is in eax - the file descriptor
; now, we'll write something to the file
; // man 2 write
; write(fd, hello_pointer, hello_length)
mov eax, 4 ; write
mov ebx, [fd],
mov ecx, hello
mov edx, hello_length
int 80h
cmp eax, -1
; it should also close the file in a normal program upon write error
; since it is open, but meh, since we just terminate the kernel
; will clean up after us
je error
; and now we close the file
; // man 2 close
; close(fd);
mov eax, 6 ; close
mov ebx, [fd]
int 80h
; and now close the program by calling exit(0);
mov eax, 1 ; exit
mov ebx, 0 ; return value
int 80h
error:
mov eax, 4 ; write
mov ebx, 1 ; write to stdout - file #1
mov ecx, error_message ; pointer to the string
mov edx, error_message_length ; length of the string
int 80h ; print it
mov eax, 1 ; exit
mov ebx, 1 ; return value
int 80h
The file will be called a.out if you copied my link command above. The -o option to ld changes that.
We can also call C functions, which helps if you need to write out things like numbers.
; nasm -f elf file.asm
; gcc -m32 file.o -nostdlib -lc # notice that we're using gcc to link, makes things a bit easier
; # the options are: -m32, 32 bit, -nostdlib, don't try to use the C lib cuz it will look for main()
; # and finally, -lc to add back some of the C standard library we want
BITS 32
; docs here: http://www.nasm.us/doc/nasmdoc6.html
; we declare the C functions as external symbols. the leading underscore is a C thing.
extern fopen
extern fprintf
extern fclose
section .data
; don't forget the 0 terminator if it akes a C string!
filename: db 'test.txt', 0
filemode: db 'wt', 0 ; the mode for fopen in C
format_string: db 'Hello with a number! %d is it.', 10, 0 ; new line and 0 terminator
; an error message to be printed with write(). The function doesn't
; use a C string so no need for a 0 here, but we do need length.
error_message: db 'Something went wrong.', 10 ; 10 == \n
; this next line means current location minus the error_message location
; which works out the message length.
; many of the system calls use pointer+length pairs instead of
; 0 terminated strings.
error_message_length: equ $ - error_message
fp: dd 0 ; this is like a global int variable in C
; global variables are generally a bad idea and there's other
; ways to do it, but for simplicity I'm using one here as the
; other ways are a bit more work in asm
section .text
global _start
_start:
; first, open or create the file. in C it would be:
; FILE* fp = fopen("text.txt", "wt");
; arguments for C functions are pushed on to the stack, right from left.
push filemode ; "wt"
push filename ; "text.txt"
call fopen
add esp, 8 ; we need to clean up our own stack. Since we pushed two four-byte items, we need to pop the 8 bytes back off. Alternatively, we could have called pop twice, but a single add instruction keeps our registers cleaner.
; the return value is in eax, store it in our fp variable after checking for errors
; in C: if(fp == NULL) goto error;
cmp eax, 0 ; check for null
je error
mov [fp], eax;
; call fprintf(fp, "format string with %d", 55);
; the 55 is just a random number to print
mov eax, 55
push eax ; all arguments are pushed, right to left. We want a 4 byte int equal to 55, so eax is it
push format_string
mov eax, [fp] ; again using eax as an intermediate to store our 4 bytes as we push to the stack
push eax
call fprintf
add esp, 12 ; 3 words this time to clean up
; fclose(fp);
mov eax, [fp] ; again using eax as an intermediate to store our 4 bytes as we push to the stack
push eax
call fclose
; the rest is unchanged from the above example
; and now close the program by calling exit(0);
mov eax, 1 ; exit
mov ebx, 0 ; return value
int 80h
error:
mov eax, 4 ; write
mov ebx, 1 ; write to stdout - file #1
mov ecx, error_message ; pointer to the string
mov edx, error_message_length ; length of the string
int 80h ; print it
mov eax, 1 ; exit
mov ebx, 1 ; return value
int 80h
There's a lot more that can be done here, like a few techniques to eliminate those global variables, or better error checking, or even writing a C style main() in assembly. But this should get you started in writing out a text file. Tip: Files are the same as writing to the screen, you just need to open/create them first!
BTW don't mix the system calls and the C library functions at the same time. The C library (fprintf etc) buffers data, the system calls don't. If you mix them, the data might end up written to the file in a surprising order.
The code is similar, but slightly different in 64 bit.
Finally, this same pattern can be used to translate almost any C code to asm - the C calling convention is the same with different functions, and the linux system call convention with the argument placement etc. follows a consistent pattern too.
Further reading:
http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl on the C calling convention
http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html on linux system calls
What is the purpose of EBP in the following code? is another SO answer I wrote up a while ago about local variables in asm - this will have hints as to one way to get rid of that global and describes how the C compile does it. (the other way to get rid of that global is to either keep the fd/fp in a register and push and pop it onto the stack when you need to free up the register for something else)
And the man pages referenced in the code for each function. From your linux prompt, do things like man 2 write or man 3 fprintf to see more. (System calls are in manual section 2 and C functions are in manual section 3).
how in c++ visual can i set labels for when i need to use inline assembly, so it would look like something like this for example...
__asm
{
PUSH EAX
PUSH VAR1
MOV ECX,DWORD PTR DS:[VAR2]
CALL DWORD PTR DS:[VAR3]
JMP VAR4
}
where the VAR varables link to a value or address?
i have tried the following
DWORD VAR2 = 0x991770; //0x991770 is the location of the function
__asm
{
..code
MOV ECX,DWORD PTR DS:[VAR2]
..code
}
but then the app crashes, how is this done?
Use offset variableName to access variables from inline assembly. See reference here.
Example:
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "world";
int main( void )
{
__asm
{
mov eax, offset world
push eax
mov eax, offset hello
push eax
mov eax, offset format
push eax
call printf
//clean up the stack so that main can exit cleanly
//use the unused register ebx to do the cleanup
pop ebx
pop ebx
pop ebx
}
}
C variable names are visible in inline assembly. So if you need data access, just write the var name:
int var2 = 3;
__asm
{
mov ecx, var2
That will compile to the appropriate memory access statement.
For code labels - you just declare them, like in real assembly:
Label1:
mov ecx, 0
jmp Label1
External functions are seen as labels, too. Name mangling applies, though.
If you need the numeric value of the current IP as a general purpose register, there's no direct command, but a very simple workaround is available:
call Next
Next:
pop eax ; eax now is the value of IP at the current point
Oh, and forget about the ds: stuff. You're in Flatland now - check your segment registers at the door.
I try to understand the way gcc x64 organize the stack, a small program generate this asm
(gdb) disassemble *main
Dump of assembler code for function main:
0x0000000000400534 <main+0>: push rbp
0x0000000000400535 <main+1>: mov rbp,rsp
0x0000000000400538 <main+4>: sub rsp,0x30
0x000000000040053c <main+8>: mov DWORD PTR [rbp-0x14],edi
0x000000000040053f <main+11>: mov QWORD PTR [rbp-0x20],rsi
0x0000000000400543 <main+15>: mov DWORD PTR [rsp],0x7
0x000000000040054a <main+22>: mov r9d,0x6
0x0000000000400550 <main+28>: mov r8d,0x5
0x0000000000400556 <main+34>: mov ecx,0x4
0x000000000040055b <main+39>: mov edx,0x3
0x0000000000400560 <main+44>: mov esi,0x2
0x0000000000400565 <main+49>: mov edi,0x1
0x000000000040056a <main+54>: call 0x4004c7 <addAll>
0x000000000040056f <main+59>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400572 <main+62>: mov esi,DWORD PTR [rbp-0x4]
0x0000000000400575 <main+65>: mov edi,0x400688
0x000000000040057a <main+70>: mov eax,0x0
0x000000000040057f <main+75>: call 0x400398 <printf#plt>
0x0000000000400584 <main+80>: mov eax,0x0
0x0000000000400589 <main+85>: leave
0x000000000040058a <main+86>: ret
Why it reserve up to 0x30 bytes just to save edi and rsi
I don't see any where restore values of edi and rsi as required by ABI
edi and rsi save at position that has delta 0x20 - 0x14 = 0xC, not a continuous region, does it make sense?
follow is source code
int mix(int a,int b,int c,int d,int e,int f, int g){
return a | b | c | d | e | f |g;
}
int addAll(int a,int b,int c,int d,int e,int f, int g){
return a+b+c+d+e+f+g+mix(a,b,c,d,e,f,g);
}
int main(int argc,char **argv){
int total;
total = addAll(1,2,3,4,5,6,7);
printf("result is %d\n",total);
return 0;
}
Edit
It's seem that stack has stored esi,rdi, 7th parameter call to addAll and total , it should take 4x8 = 32 (0x20) bytes, it round up to 0x30 for some reasons.
I dont know your original code, but locals are also stored on the stack, and when you have some local variables that space is also "allocated". Also for alignment reason it can be, that he "rounded" to the next multiple of 16. I would guess you have a local for passing the result from your addAll to the printf, and that is stored at rbp-04.
I just had look in your linked ABI - where does it say that the callee has to restore rdi and rsi? It says already on page 15, footnote:
Note that in contrast to the Intel386 ABI, %rdi, and %rsi belong to the called function, not
the caller.
Afaik they are used for passing the first arguments to the callee.
0xC are 12. This comes also from alignment, as you can see, he just needs to store edi not rdi, for alignment purpose I assume that he aligns it on a 4 byte border, while si is rsi, which is 64 bit and aligned on 8 byte border.
2: The ABI explicitly says that rdi/rsi need NOT be saved by the called function; see page 15 and footnote 5.
1 and 3: unsure; probably stack alignment issues.