Thursday, February 25, 2021

Password Checking Function in x86 Assembly

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: