I recently came across this programming assignment that someone posted on the Internet. The task was to convert the following x86 assembly language into C source code.
proc near
push sPassword
call _strlen
pop ecx
mov esi, eax
mov ebx, offset sMyPassword
push ebx
call _strlen
pop ecx
cmp esi, eax
jz short loc_4012B2
xor eax, eax
jmp short end_proc
loc_4012B2:
push esi
push ebx
push sPassword
call _strcmp
add esp, 8
test eax, eax
jnz short loc_4012CC
mov eax, 1
jmp short end_proc
loc_4012CC:
xor eax, eax
end_proc:
pop esi
pop ebx
pop ebp
retn
endp
Well, tasks and assignments like these often get asked on online programming forums, so I googled "loc_4012B2". Sure enough, it was on
stackoverflow and several other websites. The stackoverflow link is now dead though, and those sites don't really show you the process, only the result. So here's my take on this problem:
; eax = retval
; esi = a
; assume we start with an empty stack
proc near ; void checkPassword() // start of function, let's give it a meaningful name
push sPassword ; // stack: sPassword
call _strlen ; int retval = strlen(sPassword);
pop ecx ; // stack:
mov esi, eax ; int a = retval;
mov ebx, offset sMyPassword ; // because we cannot push a value from memory to the stack
push ebx ; // stack: sMyPassword
call _strlen ; retval = strlen(sMyPassword);
pop ecx ; // stack:
cmp esi, eax ; if (a == retval)
jz short loc_4012B2 ; goto loc_4012B2;
xor eax, eax ; retval = 0;
jmp short end_proc ; goto end_proc;
loc_4012B2:
push esi ; // stack: esi
push ebx ; // stack: sMyPassword
push sPassword ; // stack: sPassword, sMyPassword, esi
call _strcmp ; retval = strcmp(sPassword, sMyPassword);
add esp, 8 ; // stack: esi
test eax, eax ; if (retval != 0)
jnz short loc_4012CC ; goto loc_4012CC;
mov eax, 1 ; retval = 1;
jmp short end_proc ; goto end_proc;
loc_4012CC:
xor eax, eax ; retval = 0;
end_proc:
pop esi ; // stack:
pop ebx ; // must have been pushed to the stack before
pop ebp ; // and this one too
retn ; return retval;
endp ; // end of function
If we remove all the comments, we get:
void checkPassword() {
int retval = strlen(sPassword);
int a = retval;
retval = strlen(sMyPassword);
if (a == retval)
goto loc_4012B2;
retval = 0;
goto end_proc;
loc_4012B2:
retval = strcmp(sPassword, sMyPassword);
if (retval != 0)
goto loc_4012CC;
retval = 1;
goto end_proc;
loc_4012CC:
retval = 0;
return retval;
}
We can simplify the code like this:
void checkPassword() {
int retval;
int a = strlen(sPassword);
if (a == strlen(sMyPassword))
goto loc_4012B2;
return 0;
loc_4012B2:
if (strcmp(sPassword, sMyPassword) != 0)
goto loc_4012CC;
return 1;
loc_4012CC:
return 0;
}
We can actually insert
else statements to make the code easier to refactor:
void checkPassword() {
if (strlen(sPassword) == strlen(sMyPassword))
goto loc_4012B2;
else
return 0;
loc_4012B2:
if (strcmp(sPassword, sMyPassword) != 0)
goto loc_4012CC;
else
return 1;
loc_4012CC:
return 0;
}
Now we can remove all the
goto statements and simplify a bit more:
void checkPassword() {
if (strlen(sPassword) == strlen(sMyPassword))
if (strcmp(sPassword, sMyPassword))
return 0;
else
return 1;
else
return 0;
}
It looks like a jumbled-up mess, though. Remember the
else statements we added? We can do this instead:
void checkPassword() {
if (strlen(sPassword) != strlen(sMyPassword))
return 0;
if (strcmp(sPassword, sMyPassword))
return 0;
return 1;
}
It turned out to be a simple function. This reminds me why I love C: it allows us to do anything we want, and at the same time it doesn't make the code overly complicated like assembly.
No comments:
Post a Comment