Understanding the decompilation of an object to source code - c

First of all, I am a student, I do not yet have extensive knowledge about C, C ++ and assembler, so I am making a extreme effort to understand it.
I have this piece of assembly code from an Intel x86-32 bit processor.
My goal is to transform it to source code.
0x80483dc <main>: push ebp
0x80483dd <main+1>: mov ebp,esp
0x80483df <main+3>: sub esp,0x10
0x80483e2 <main+6>: mov DWORD PTR [ebp-0x8],0x80484d0
0x80483e9 <main+13>: lea eax,[ebp-0x8]
0x80483ec <main+16>: mov DWORD PTR [ebp-0x4],eax
0x80483ef <main+19>: mov eax,DWORD PTR [ebp-0x4]
0x80483f2 <main+22>: mov edx,DWORD PTR [eax+0xc]
0x80483f5 <main+25>: mov eax,DWORD PTR [ebp-0x4]
0x80483f8 <main+28>: movzx eax,WORD PTR [eax+0x10]
0x80483fc <main+32>: cwde
0x80483fd <main+33>: add edx, eax
0x80483ff <main+35>: mov eax,DWORD PTR [ebp-0x4]
0x8048402 <main+38>: mov DWORD PTR [eax+0xc],edx
0x8048405 <main+41>: mov eax,DWORD PTR [ebp-0x4]
0x8048408 <main+44>: movzx eax,BYTE PTR [eax]
0x804840b <main+47>: cmp al,0x4f
0x804840d <main+49>: jne 0x8048419 <main+61>
0x804840f <main+51>: mov eax,DWORD PTR [ebp-0x4]
0x8048412 <main+54>: movzx eax,BYTE PTR [eax]
0x8048415 <main+57>: cmp al,0x4b
0x8048417 <main+59>: je 0x804842d <main+81>
0x8048419 <main+61>: mov eax,DWORD PTR [ebp-0x4]
0x804841c <main+64>: mov eax,DWORD PTR [eax+0xc]
0x804841f <main+67>: mov edx, eax
0x8048421 <main+69>: and edx,0xf0f0f0f
0x8048427 <main+75>: mov eax,DWORD PTR [ebp-0x4]
0x804842a <main+78>: mov DWORD PTR [eax+0x4],edx
0x804842d <main+81>: mov eax,0x0
0x8048432 <main+86>: leave
0x8048433 <main+87>: ret
This is what I understand from the code:
There are 4 variables:
a = [ebp-0x8] ebp
b = [ebp-0x4] eax
c = [eax + 0xc] edx
d = [eax + 0x10] eax
Values:
0x4 = 4
0x8 = 8
0xc = 12
0x10 = 16
0x4b = 75
0x4f = 79
Types:
char (8 bits) = 1 BYTE
short (16 bits) = WORD
int (32 bit) = DWORD
long (32 bits) = DWORD
long long (32 bit) = DWORD
This is what I was able to create:
#include <stdio.h>
int main (void)
{
   int a = 0x80484d0;
   int b
   short c;
   int d;
   c + b?
if (79 <= al) {
instructions
} else {
instructions
}
   return 0
}
But I'm stuck. Nor can I understand what the sentence "cmp al .." compares to, what is "al"?
How do these instructions work?
EDIT1:
That said, as you comment the assembly seems to be wrong or as someone comments say, it is insane!
The code and the exercise are from the following book called: "Reversing, Reverse Engineering" on page 140 (3.8 Proposed Exercises). It would never have occurred to me that it was wrong, if so, this clearly makes it difficult for me to learn ...
So it is not possible to do a reversing to get the source code because it is not a good assembly? Maybe I am not oppressed? Is it possible to optimize it?
EDIT2:
Hi!
I did ask and finally she says this should be the c code:
inf foo(void){
char *string;//ebp-0x8
unsigned int *pointerstring//[ebp-0x4]
unsigned int *position;
*position = *(pointerstring+0xc);
unsigned char character;
character=(unsigned char) string[*position];
if ((character != 0x4)||(character != 0x4b))
{
*(position+0x4)=(unsigned int)(*position & 0x0f0f0f0f);
}
return(0);
}
Does it have any sense at all for you?, could someone please explain this to me?
Does anyone really program like this?
Thanks very much!

Your assembly is completely insane. This is roughly equivalent C:
int main() {
int i = 0x80484d0; // in ebp-8
int *p = &i; // in ebp-4
p[3] += (short)p[4]; // add argc to the return address(!)
if((char)*p != 0x4f || (char)*p != 0x4b) // always true because of || instead of &&
p[1] = p[3] & 0xf0f0f0f; // note that p[1] is p
return 0;
}
It should be immediately obvious that this is horrifically bad code that almost certainly won't do what the programmer intended.

The x86 assembly language follows a long legacy and has mostly kept compatibility. We need to go back to the 8086/8088 chip where that story starts. These were 16 bit processors, which means that their register had a word size of 16 bits. The general purpose registers were named AX, BX, CX and DX. The 8086 had instructions to manipulate the upper and lower 8-bit parts of these registers that were then named AH, AL, BH, BL, CH, CL, DH and DL. This Wikipedia page describes this, please take a look.
The 32 bit versions of these registers have an E in front: EAX, EBX, ECX, etc.
The particular instruction you mention, e.g, cmp al,0x4f is comparing the lower byte of the AX register with 0x4f. The comparison is effectively the same as a subtraction, but does not save the result, only sets the flags.
For the 8086 instruction set, there is a nice reference here. Your program is 32 bit code, so you will need at least a 80386 instruction reference.

You have analyzed variables, and that's a good place to start. You should try to add type annotations to them, size, as you started, and, when used as pointers (like b), pointers to what kind/size.
I might update your variable chart as follows, knowing that [ebp-4] is b:
c = [b + 0xc]
d = [b + 0x10]
e = [b + 0], size = byte
Another thing to analyze is the control flow. For most instructions control flow is sequential, but certain instructions purposefully alter it. Broadly speaking, when the pc is moved forward, it skips some code and when the pc is moved backward it repeats some code it already ran. Skipping code is used to construct if-then, if-then-else, and statements that break out of loops. Jumping back is used to continue looping.
Some instructions, called conditional branches, on some dynamic condition being true: skip forward (or backwards) and on being false do the simple sequential advancement to the next instruction (sometimes called conditional branch fall through).
The control sequences here:
...
0x8048405 <main+41>: mov eax,DWORD PTR [ebp-0x4] b
0x8048408 <main+44>: movzx eax,BYTE PTR [eax] b->e
0x804840b <main+47>: cmp al,0x4f b->e <=> 'O'
0x804840d <main+49>: jne 0x8048419 <main+61> b->e != 'O' skip to 61
** we know that the letter, a->e, must be 'O' here
0x804840f <main+51>: mov eax,DWORD PTR [ebp-0x4] b
0x8048412 <main+54>: movzx eax,BYTE PTR [eax] b->e
0x8048415 <main+57>: cmp al,0x4b b->e <=> 'K'
0x8048417 <main+59>: je 0x804842d <main+81> b->e == 'K' skip to 81
** we know that the letter, a->e must not be 'K' here if we fall thru the above je
** this line can be reached by taken branch jne or by fall thru je
0x8048419 <main+61>: mov eax,DWORD PTR [ebp-0x4] ******
...
The flow of control reaches this last line tagged we know that either the letter is either not 'O' or it is not 'K'.
The construct where the jne instruction is used to skip another test is a short-circuit || operator. Thus the control construct is:
if ( a->e != 'O' || a->e != 'K' ) {
then-part
}
As that these two conditional branches are the only flow control modifications in the function, there is no else part of the if, and there are no loops or other if's.
This code appears to have a slight problem.
If the value is not 'O', the then-part will fire from the first test. However, if we reach the 2nd test, we already know the letter is 'O', so testing it for 'K' is silly and will be true ('O' is not 'K').
Thus, this if-then will always fire.
It is either very inefficient, or, there is a bug that perhaps it is the next letter along in the (presumably) string should be tested for 'K' not the same exact letter.

Related

What is the decompiled (C) code construct of this assembly x86 code?

This code compares every char of a string (located at ebp+arg_0) with different constants (ASCII chars) like 'I', 'o' and 'S'. I guess, based on other code parts, this code is originally written in C.
This compare-code-part looks very inefficient to. My question, how do you think this code will look in C? What code construct was used originally? my thoughts so far
It's not a for loop. Because i don't see any upward jump and stop-condition.
It's not a while/case/switch code construct
My best guess is that this are a lot of consecutive if/else statements.
Can you help?
Yes it's part of a challenge, i already have the flag/solution, no worries about that. Just trying to better understand the code.
It's not a for loop. Because i don't see any upward jump and stop-condition.
Correct.
It's not a while/case/switch code constuct
It can't be, it compares different indicies of the array.
My best guess is that this are a lot of consecutive if/elses. Can you help?
Looks like it could be this code:
void f(const char* arg_0) {
if(arg_0[4] == 'I' && arg_0[5] == 'o' && arg_0[6] == 'S') {
printf("Gratz man :)");
exit(0); //noreturn, hence your control flow ends here in the assembly
}
puts("Wrong password"); // Or `printf("Wrong password\n");` which gets optimized to `puts`
// leave, retn
}
This is how gcc compiles it without optimizations:
.LC0:
.string "Gratz man :)"
.LC1:
.string "Wrong password"
f(char const*):
push ebp
mov ebp, esp
sub esp, 8
mov eax, DWORD PTR [ebp+8]
add eax, 4
movzx eax, BYTE PTR [eax]
cmp al, 73
jne .L2
mov eax, DWORD PTR [ebp+8]
add eax, 5
movzx eax, BYTE PTR [eax]
cmp al, 111
jne .L2
mov eax, DWORD PTR [ebp+8]
add eax, 6
movzx eax, BYTE PTR [eax]
cmp al, 83
jne .L2
sub esp, 12
push OFFSET FLAT:.LC0
call printf
add esp, 16
sub esp, 12
push 0
call exit
.L2:
sub esp, 12
push OFFSET FLAT:.LC1
call puts
add esp, 16
nop
leave
ret
Looks very similar to your disassembled code.
This compare-code-part looks very inefficient
Looks like it was compiled without optimizations. With optimizations enabled, gcc compiled the code to:
.LC0:
.string "Gratz man :)"
.LC1:
.string "Wrong password"
f(char const*):
sub esp, 12
mov eax, DWORD PTR [esp+16]
cmp BYTE PTR [eax+4], 73
jne .L2
cmp BYTE PTR [eax+5], 111
je .L5
.L2:
mov DWORD PTR [esp+16], OFFSET FLAT:.LC1
add esp, 12
jmp puts
.L5:
cmp BYTE PTR [eax+6], 83
jne .L2
sub esp, 12
push OFFSET FLAT:.LC0
call printf
mov DWORD PTR [esp], 0
call exit
Not sure why gcc decided to jump down and back up again instead of a straight line of jnes. Also, the ret is gone, your printf got tail-call-optimized, i.e. a jmp printf istead of a call printf followed by a ret.
The first argument (arg_0) is a pointer to the given password string, e.g., const char *arg_0. This pointer (the address of the first character) is loaded into the eax register (mov eax, [ebp+arg_0]), and then the index of the current character is added to advance it to that index (add eax, 4 etc.). The single byte at that address is then loaded into eax (movzx eax, byte ptr [eax]).
Then that byte/character is compared against the correct one (cmp eax, 'I', etc). If the result is not zero (i.e., if they are not equal), the program jumps to the "Wrong password" branch (jnz - jump if not zero), otherwise it continues on to the next comparison (and ultimately success).
The nearest direct C equivalent would therefore be something like:
void check(const char *arg_0) {
// presumably comparisons 0-3 are omitted
if (arg_0[4] != 'I') goto fail;
if (arg_0[5] != 'o') goto fail;
if (arg_0[6] != 'S') goto fail;
printf("Gratz man :)");
exit(0);
fail:
puts("Wrong password");
}
(Of course the actual C code is unlikely to have looked like this, since the goto fail arrangement is not typical.)

F# Breakable Array Iteration With Bounds Checking Elided?

I am interested in trying F# in a high-performance application. I do not want to have a large array's bounds checked during iteration and the lack of break/return statements is concerning.
This is a contrived example that will break upon finding a value, but can someone tell me if bounds checking is also elided?
let innerExists (item: Char) (items: Char array): bool =
let mutable state = false
let mutable i = 0
while not state && i < items.Length do
state <- item = items.[i]
i <- i + 1
state
let exists (input: Char array)(illegalChars: Char array): bool =
let mutable state = false
let mutable i = 0
while not state && i < input.Length do
state <- innerExists input.[i] illegalChars
i <- i + 1
state
exists [|'A'..'z'|] [|'.';',';';'|]
Here is the relevant disassembly:
while not state && i < input.Length do
000007FE6EB4237A cmp dword ptr [rbp-14h],0
000007FE6EB4237E jne 000007FE6EB42383
000007FE6EB42380 nop
000007FE6EB42381 jmp 000007FE6EB42386
000007FE6EB42383 nop
000007FE6EB42384 jmp 000007FE6EB423A9
000007FE6EB42386 nop
000007FE6EB42387 mov r8d,dword ptr [rbp-18h]
000007FE6EB4238B mov rdx,qword ptr [rbp+18h]
000007FE6EB4238F cmp r8d,dword ptr [rdx+8]
000007FE6EB42393 setl r8b
000007FE6EB42397 movzx r8d,r8b
000007FE6EB4239B mov dword ptr [rbp-24h],r8d
000007FE6EB4239F mov r8d,dword ptr [rbp-24h]
000007FE6EB423A3 mov dword ptr [rbp-1Ch],r8d
000007FE6EB423A7 jmp 000007FE6EB423B1
000007FE6EB423A9 nop
000007FE6EB423AA xor r8d,r8d
000007FE6EB423AD mov dword ptr [rbp-1Ch],r8d
000007FE6EB423B1 cmp dword ptr [rbp-1Ch],0
000007FE6EB423B5 je 000007FE6EB42409
state <- innerExists input.[i] illegalChars
000007FE6EB423B7 mov r8d,dword ptr [rbp-18h]
000007FE6EB423BB mov rdx,qword ptr [rbp+18h]
000007FE6EB423BF cmp r8,qword ptr [rdx+8]
000007FE6EB423C3 jb 000007FE6EB423CA
000007FE6EB423C5 call 000007FECD796850
000007FE6EB423CA lea rdx,[rdx+r8*2+10h]
000007FE6EB423CF movzx r8d,word ptr [rdx]
000007FE6EB423D3 mov rdx,qword ptr [rbp+10h]
000007FE6EB423D7 mov rdx,qword ptr [rdx+8]
000007FE6EB423DB mov r9,qword ptr [rbp+20h]
000007FE6EB423DF mov rcx,7FE6EEE0640h
000007FE6EB423E9 call 000007FE6EB41E40
000007FE6EB423EE mov dword ptr [rbp-20h],eax
000007FE6EB423F1 mov eax,dword ptr [rbp-20h]
000007FE6EB423F4 movzx eax,al
000007FE6EB423F7 mov dword ptr [rbp-14h],eax
i <- i + 1
000007FE6EB423FA mov eax,dword ptr [rbp-18h]
Others pointed out to use existing function FSharp.Core to achieve the same result but I think that OP asks if in loops like the boundary check of arrays are elided (as it is checked in the loop condition).
For simple code like above the jitter should be able to elide the checks. To see this it is correct to check the assembly code but it is important to not run with the VS debugger attached as the jitter don't optimize the code then. The reason that it can be impossible to show correct values in the debugger.
First let's look at exists optimized x64:
; not state?
00007ff9`1cd37551 85c0 test eax,eax
; if state is true then exit the loop
00007ff9`1cd37553 7521 jne 00007ff9`1cd37576
; i < input.Length?
00007ff9`1cd37555 395e08 cmp dword ptr [rsi+8],ebx
; Seems overly complex but perhaps this is as good as it gets?
00007ff9`1cd37558 0f9fc1 setg cl
00007ff9`1cd3755b 0fb6c9 movzx ecx,cl
00007ff9`1cd3755e 85c9 test ecx,ecx
; if we have reached end of the array then exit
00007ff9`1cd37560 7414 je 00007ff9`1cd37576
; mov i in ebx to rcx, unnecessary but moves like these are very cheap
00007ff9`1cd37562 4863cb movsxd rcx,ebx
; input.[i] (note we don't check the boundary again)
00007ff9`1cd37565 0fb74c4e10 movzx ecx,word ptr [rsi+rcx*2+10h]
; move illegalChars pointer to rdx
00007ff9`1cd3756a 488bd7 mov rdx,rdi
; call innerExists
00007ff9`1cd3756d e8ee9affff call 00007ff9`1cd31060
; i <- i + 1
00007ff9`1cd37572 ffc3 inc ebx
; Jump top of loop
00007ff9`1cd37574 ebdb jmp 00007ff9`1cd37551
; We are done!
00007ff9`1cd37576
So the code looks a bit too complex for what it should need to be but it seems it only checks the array condition once.
Now let's look at innerExists optimized x64:
# let mutable state = false
00007ff9`1cd375a0 33c0 xor eax,eax
# let mutable i = 0
00007ff9`1cd375a2 4533c0 xor r8d,r8d
; not state?
00007ff9`1cd375a5 85c0 test eax,eax
; if state is true then exit the loop
00007ff9`1cd375a7 752b jne 00007ff9`1cd375d4
; i < items.Length
00007ff9`1cd375a9 44394208 cmp dword ptr [rdx+8],r8d
; Seems overly complex but perhaps this is as good as it gets?
00007ff9`1cd375ad 410f9fc1 setg r9b
00007ff9`1cd375b1 450fb6c9 movzx r9d,r9b
00007ff9`1cd375b5 4585c9 test r9d,r9d
; if we have reached end of the array then exit
00007ff9`1cd375b8 741a je 00007ff9`1cd375d4
; mov i in r8d to rax, unnecessary but moves like these are very cheap
00007ff9`1cd375ba 4963c0 movsxd rax,r8d
; items.[i] (note we don't check the boundary again)
00007ff9`1cd375bd 0fb7444210 movzx eax,word ptr [rdx+rax*2+10h]
; mov item in cx to r9d, unnecessary but moves like these are very cheap
00007ff9`1cd375c2 440fb7c9 movzx r9d,cx
; item = items.[i]?
00007ff9`1cd375c6 413bc1 cmp eax,r9d
00007ff9`1cd375c9 0f94c0 sete al
; state <- ?
00007ff9`1cd375cc 0fb6c0 movzx eax,al
; i <- i + 1
00007ff9`1cd375cf 41ffc0 inc r8d
; Jump top of loop
00007ff9`1cd375d2 ebd1 jmp 00007ff9`1cd375a5
; We are done!
00007ff9`1cd375d4 c3 ret
So looks overly complex for what it should be but at least it looks like it only checks the array condition once.
So finally, it looks like the jitter eliminates the array boundary checks because it can prove this has already been checked successfully in the loop condition which I believe is what the OP wondered.
The x64 code doesn't look as clean as it could but from my experimentation x64 code that is cleaned up doesn't perform that much better, I suspect the CPU vendors optimize the CPU for the crappy code jitters produce.
An interesting exercise would be to code up an equivalent program in C++ and run it through https://godbolt.org/, choose x86-64 gcc (trunk) (gcc seems to do best right now) and specify the options -O3 -march=native and see the resulting x64 code.
Update
The code rewritten in https://godbolt.org/ to allow us seeing the assembly code generated by a c++ compiler:
template<int N>
bool innerExists(char item, char const (&items)[N]) {
for (auto i = 0; i < N; ++i) {
if (item == items[i]) return true;
}
return false;
}
template<int N1, int N2>
bool exists(char const (&input)[N1], char const (&illegalCharacters)[N2]) {
for (auto i = 0; i < N1; ++i) {
if (innerExists(input[i], illegalCharacters)) return true;
}
return false;
}
char const separators[] = { '.', ',', ';' };
char const str[58] = { };
bool test() {
return exists(str, separators);
}
x86-64 gcc (trunk) with options -O3 -march=native the following code is generated
; Load the string to test into edx
mov edx, OFFSET FLAT:str+1
.L2:
; Have we reached the end?
cmp rdx, OFFSET FLAT:str+58
; If yes, then jump to the end
je .L7
; Load a character
movzx ecx, BYTE PTR [rdx]
; Comparing the 3 separators are encoded in the assembler
; because the compiler detected the array is always the same
mov eax, ecx
and eax, -3
cmp al, 44
sete al
cmp cl, 59
sete cl
; increase outer i
inc rdx
; Did we find a match?
or al, cl
; If no then loop to .L2
je .L2
; We are done!
ret
.L7:
; No match found, clear result
xor eax, eax
; We are done!
ret
Looks pretty good but what I missing in the code above is using AVX to test multiple characters at once.
Bound check are eliminated by the JIT compiler, so it works the same for F# as for C#. You can expect elimination for code as in your example, as well as for
for i = 0 to data.Lenght - 1 do
...
and also for tail recursive functions, which compiles down to loops.
The built in Array.contains and Array.exists (source code) are written so that the JIT compiler can eliminate bounds checks.
What's wrong with the Array.contains and Array.exists functions ?
let exists input illegalChars =
input |> Array.exists (fun c -> illegalChars |> Array.contains c)

C pointers and references

I would like to know what's really happening calling & and * in C.
Is that it costs a lot of resources? Should I call & each time I wanna get an adress of a same given variable or keep it in memory i.e in a cache variable. Same for * i.e when I wanna get a pointer value ?
Example
void bar(char *str)
{
check_one(*str)
check_two(*str)
//... Could be replaced by
char c = *str;
check_one(c);
check_two(c);
}
I would like to know what's really happening calling & and * in C.
There's no such thing as "calling" & or *. They are the address operator, or the dereference operator, and instruct the compiler to work with the address of an object, or with the object that a pointer points to, respectively.
And C is not C++, so there's no references; I think you just misused that word in your question's title.
In most cases, that's basically two ways to look at the same thing.
Usually, you'll use & when you actually want the address of an object. Since the compiler needs to handle objects in memory with their address anyway, there's no overhead.
For the specific implications of using the operators, you'll have to look at the assembler your compiler generates.
Example: consider this trivial code, disassembled via godbolt.org:
#include <stdio.h>
#include <stdlib.h>
void check_one(char c)
{
if(c == 'x')
exit(0);
}
void check_two(char c)
{
if(c == 'X')
exit(1);
}
void foo(char *str)
{
check_one(*str);
check_two(*str);
}
void bar(char *str)
{
char c = *str;
check_one(c);
check_two(c);
}
int main()
{
char msg[] = "something";
foo(msg);
bar(msg);
}
The compiler output can far wildly depending on the vendor and optimization settings.
clang 3.8 using -O2
check_one(char): # #check_one(char)
movzx eax, dil
cmp eax, 120
je .LBB0_2
ret
.LBB0_2:
push rax
xor edi, edi
call exit
check_two(char): # #check_two(char)
movzx eax, dil
cmp eax, 88
je .LBB1_2
ret
.LBB1_2:
push rax
mov edi, 1
call exit
foo(char*): # #foo(char*)
push rax
movzx eax, byte ptr [rdi]
cmp eax, 88
je .LBB2_3
movzx eax, al
cmp eax, 120
je .LBB2_2
pop rax
ret
.LBB2_3:
mov edi, 1
call exit
.LBB2_2:
xor edi, edi
call exit
bar(char*): # #bar(char*)
push rax
movzx eax, byte ptr [rdi]
cmp eax, 88
je .LBB3_3
movzx eax, al
cmp eax, 120
je .LBB3_2
pop rax
ret
.LBB3_3:
mov edi, 1
call exit
.LBB3_2:
xor edi, edi
call exit
main: # #main
xor eax, eax
ret
Notice that foo and bar are identical. Do other compilers do something similar? Well...
gcc x64 5.4 using -O2
check_one(char):
cmp dil, 120
je .L6
rep ret
.L6:
push rax
xor edi, edi
call exit
check_two(char):
cmp dil, 88
je .L11
rep ret
.L11:
push rax
mov edi, 1
call exit
bar(char*):
sub rsp, 8
movzx eax, BYTE PTR [rdi]
cmp al, 120
je .L16
cmp al, 88
je .L17
add rsp, 8
ret
.L16:
xor edi, edi
call exit
.L17:
mov edi, 1
call exit
foo(char*):
jmp bar(char*)
main:
sub rsp, 24
movabs rax, 7956005065853857651
mov QWORD PTR [rsp], rax
mov rdi, rsp
mov eax, 103
mov WORD PTR [rsp+8], ax
call bar(char*)
mov rdi, rsp
call bar(char*)
xor eax, eax
add rsp, 24
ret
Well, if there were any doubt foo and bar are equivalent, a least by the compiler, I think this:
foo(char*):
jmp bar(char*)
is a strong argument they indeed are.
In C, there's no runtime cost associated with either the unary & or * operators; both are evaluated at compile time. So there's no difference in runtime between
check_one(*str)
check_two(*str)
and
char c = *str;
check_one( c );
check_two( c );
ignoring the overhead of the assignment.
That's not necessarily true in C++, since you can overload those operators.
tldr;
If you are programming in C, then the & operator is used to obtain the address of a variable and * is used to get the value of that variable, given it's address.
This is also the reason why in C, when you pass a string to a function, you must state the length of the string otherwise, if someone unfamiliar with your logic sees the function signature, they could not tell if the function is called as bar(&some_char) or bar(some_cstr).
To conclude, if you have a variable x of type someType, then &x will result in someType* addressOfX and *addressOfX will result in giving the value of x. Functions in C only take pointers as parameters, i.e. you cannot create a function where the parameter type is &x or &&x
Also your examples can be rewritten as:
check_one(str[0])
check_two(str[0])
AFAIK, in x86 and x64 your variables are stored in memory (if not stated with register keyword) and accessed by pointers.
const int foo = 5 equal to foo dd 5 and check_one(*foo) equal to push dword [foo]; call check_one.
If you create additional variable c, then it looks like:
c resd 1
...
mov eax, [foo]
mov dword [c], eax ; Variable foo just copied to c
push dword [c]
call check_one
And nothing changed, except additional copying and memory allocation.
I think that compiler's optimizer deals with it and makes both cases as fast as it is possible. So you can use more readable variant.

A few assembly instructions

Could you please help me understand the purpose of the two assembly instructions in below ? (for more context, assembly + C code at the end). Thanks !
movzx edx,BYTE PTR [edx+0xa]
mov BYTE PTR [eax+0xa],dl
===================================
Assembly code below:
push ebp
mov ebp,esp
and esp,0xfffffff0
sub esp,0x70
mov eax,gs:0x14
mov DWORD PTR [esp+0x6c],eax
xor eax,eax
mov edx,0x8048520
lea eax,[esp+0x8]
mov ecx,DWORD PTR [edx]
mov DWORD PTR [eax],ecx
mov ecx,DWORD PTR [edx+0x4]
mov DWORD PTR [eax+0x4],ecx
movzx ecx,WORD PTR [edx+0x8]
mov WORD PTR [eax+0x8],cx
movzx edx,BYTE PTR [edx+0xa] ; instruction 1
mov BYTE PTR [eax+0xa],dl ; instruction 2
mov edx,DWORD PTR [esp+0x6c]
xor edx,DWORD PTR gs:0x14
je 804844d <main+0x49>
call 8048320 <__stack_chk_fail#plt>
leave
ret
===================================
C source code below (without libraries inclusion):
int main() {
char str_a[100];
strcpy(str_a, "eeeeefffff");
}
It inlined the strcpy() call, the code generator can tell that 11 bytes need to be copied. The string literal "eeeeefffff" has 10 characters, one extra for the zero terminator.
The code optimizer unrolled the copy loop to 4 moves, moving 4 + 4 + 2 + 1 bytes. It needs to be done this way because there is no processor instruction that moves 3 bytes. The instructions you are asking about copy the 11th byte. Using movzx is a bit overkill but it is probably faster than loading the DL register.
Observe the changes in the generated code when you alter the string. Adding an extra letter should unroll to 3 moves, 4 + 4 + 4. When the string gets too long you ought to see it fall back to something like memmove.

X86 inline assembly, writing into C array

Assembly info: Using Visual Studio 2010 to write inline assembly embedded into C
Hello,
I am trying to write into an array of chars in C and trying to mimic the action of this C code:
resNum[posNum3] = currNum3 + '0';
currently this is what i have:
mov ebx, posNum3;
mov resNum[ebx], edx; //edx is currNum3
add resNum[ebx], 48; // add 48 because thats the value of the char '0'
I also tried doing this:
mov ebx, posNum3;
mov eax, resNum[ebx] ;// eax points to the beggining of the string
mov eax, edx; // = currnum3
add eax, 48; // + '0'
No luck with any of this, help is more than appreciated!
The problem is the instruction
mov resNum[ebx], edx
moves 4 bytes (an entire dword) into the destination, not a single byte. You probably want
mov byte ptr resNum[ebx], dl
instead. While the assembler will allow you to leave off the 'size ptr' prefix on the address, you probably don't want to, as getting it wrong leads to hard to see bugs.
My X86 asm is rusty, but...
If you're using characters (8 bits) you need to first, before you start a loop, zero out EAX and then move the char into AH or AL, something like:
; Before you start your loop
xor EAX, EAX ; If you're sure you won't overflow an 8 bit number by adding 48, this can go outside the loop
; ... code here to setup loop
mov EBX, posNum3
mov AL, resNum[EBX]
add AL, 48
; ... rest of loop
Note that the compiler will do a better job of this than you will... Unless you're Mike Abrash or someone like him :)
Avoid using expressions like
mov resNum[ebx], edx;
because you never know what is resNum. It could be an expression like esp + 4, and there is no opcode for mov [esp + ebx + 4], edx, so use small steps instead.
Also, ebx is a register that have to be preserved across calls. See http://msdn.microsoft.com/en-us/library/k1a8ss06%28v=VS.71%29.aspx for details and learn about calling conventions.
Most of inline assemblers allows using name instead of size ptr [name], so you can just write
mov al, currNum3
add al, 0x30 //'0'
mov edx, posNum3
mov ecx, resNum
mov byte ptr [edx+ecx], al
if resNum is a global array, not an function argument or local variable, you can write shorter code:
mov al, currNum3
add al, 0x30 //'0'
mov edx, posNum3
mov byte ptr [resNum+ecx], al

Resources