I've just started to dip into Assembly for CTF reversing challenges, and am having a great time.

A loop structure in the current challenge I'm working on has me stumped, however - hoping someone can help with a few basic Assembly questions - or point me to good resources.

I ran the binary provided for the challenge through Binary Ninja and identified the key function - tracing the logic within a loop is giving me problems.

For the program to return the flag, we need this check function - which looks at a user-entered string - to return 1. For the check function to return a 1, we need this loop to set EAX to 0x1.

The loop starts off fairly simply:

080486d5  sub     dword [ebp-0x10 {var_14_1} {var_14}], 0x1 
080486d9  mov     dword [ebp-0xc {var_10_1}], 0x0
080486e0  jmp     0x804870b

var_14 is the string length of the user input. By this point in the function, we know that the string length has to be at least 20

So this seems to simply set var_14_1 to var_14-1 and var_10_1 to 0.

Then we enter the loop.

The first block of the loop reads:

0804870b  mov     eax, dword [ebp-0xc {var_10_1}]
0804870e  cmp     eax, dword [ebp-0x10 {var_14_1}]
08048711  jbe     0x80486e2

Which seems to say that if var_10_1 is less than var_14_1 continue with the loop. This next block of the loop is where I think I'm not reading the code correctly:

080486e2  mov     edx, dword [ebp+0x8 {arg1}]
080486e5  mov     eax, dword [ebp-0xc {var_10_1}]
080486e8  add     eax, edx                  
080486ea  movzx   edx, byte [eax]               
080486ed  mov     ecx, dword [ebp+0x8 {arg1}]
080486f0  mov     eax, dword [ebp-0x10 {var_14_1}]
080486f3  add     eax, ecx
080486f5  movzx   eax, byte [eax]
080486f8  cmp     dl, al                    
080486fa  je      0x8048703

arg1 is the user input - at this point all the know is that it has to be at least 20 characters long, and the first 4 characters are "auqa"

We need this cmp to succeed (dl == al) for the loop to continue. Otherwise, the code exits the loop and returns EAX to 0x0 (failure). Having said that - if we know that var_10_1 is 0 and var_14_1 is at least 19 at this first pass in the loop, and we add each to arg1 - then how can DL and AL be equal? Am I misunderstanding how add eax, edx and add eax, ecx work?

I'm not sure where my understanding of the code is incorrect - very much appreciate any tips or pointers. Apologize if this covers basic knowledge - I'm working through these on my own.

Thank you!

  • 1
    show the full disassembly for the function. – julian Apr 25 '18 at 21:14

the code possibly checks for a palindrome

the query doesnt contain full disassembly for example what it does when it is equal

edx, arg1 == edx = *input eax in first iteration is 0 so eax will be equal to edx == first letter of the input string for the first 4 instructions in the loop you provide

next 4 instructions possibly take the last character

and compares both

so a sequence like abcdefghijjihgfedcba will pass the first iteration

here is a src that uses your snippet and stepping through it in windbg

0:000> t
>    3: int foo (char *a) {
012b6831 8bec            mov     ebp,esp
0:000> t
>    3: int foo (char *a) {
012b6833 83ec10          sub     esp,10h
0:000> t
>    4:     int h = 0;
012b6836 c745fc00000000  mov     dword ptr [ebp-4],0  ss:0023:0013f9f8=00000000
0:000> t
>    5:     int j = 0;
012b683d c745f800000000  mov     dword ptr [ebp-8],0  ss:0023:0013f9f4=012c4a9d
0:000> t
>    6:     int k = 0;
012b6844 c745f400000000  mov     dword ptr [ebp-0Ch],0 ss:0023:0013f9f0=00000000
0:000> t
>    7:     int l = 19;  <<<<<<<<< simulating here sub instruction reduced 20 to 19 
012b684b c745f013000000  mov     dword ptr [ebp-10h],13h ss:0023:0013f9ec=012b69a9
0:000> t
>   10: mov     edx, dword ptr ds:[ebp+0x8]
012b6852 3e8b5508        mov     edx,dword ptr ds:[ebp+8] ds:0023:0013fa04=01308e50
0:000> t
>   11: mov     eax, dword ptr ds:[ebp-0xc]
012b6856 3e8b45f4        mov     eax,dword ptr ds:[ebp-0Ch] ds:0023:0013f9f0=00000000
0:000> t
>   12: add     eax, edx                  
012b685a 03c2            add     eax,edx
0:000> t
>   13: movzx   edx, byte ptr ds:[eax]               
012b685c 3e0fb610        movzx   edx,byte ptr ds:[eax]      ds:0023:01308e50=61
0:000> da eax
01308e50  "abcdefghijjihgfedcba"
0:000> t
>   14: mov     ecx, dword ptr ds:[ebp+0x8 ]
012b6860 3e8b4d08        mov     ecx,dword ptr ds:[ebp+8] ds:0023:0013fa04=01308e50
0:000> ?? char @dl
Unexpected token '@dl'
0:000> ?? (char) @dl
char 0n97 'a'
0:000> t
>   15: mov     eax, dword ptr ds:[ebp-0x10 ]
012b6864 3e8b45f0        mov     eax,dword ptr ds:[ebp-10h] ds:0023:0013f9ec=00000013
0:000> t
>   16: add     eax, ecx
012b6868 03c1            add     eax,ecx
0:000> t
>   17: movzx   eax, byte ptr ds:[eax]
012b686a 3e0fb600        movzx   eax,byte ptr ds:[eax]      ds:0023:01308e63=61
0:000> da eax
01308e63  "a"
0:000> ? eax
Evaluate expression: 19959395 = 01308e63
0:000> t
>   18: cmp     dl, al 
012b686e 3ad0            cmp     dl,al
0:000> ?? (char) @eax
char 0n97 'a'
0:000> ?? (char) @al
char 0n97 'a'
0:000> ? eax
Evaluate expression: 97 = 00000061
0:000> ?? @dl == @al
bool true

the src code that uses the snippet in your post

int foo(char *a) {
    int h = 0;
    int j = 0;
    int k = 0;
    int l = 19;
    __asm {
        mov     edx, dword ptr ds : [ebp + 0x8]
        mov     eax, dword ptr ds : [ebp - 0xc]
        add     eax, edx
        movzx   edx, byte ptr ds : [eax]
        mov     ecx, dword ptr ds : [ebp + 0x8]
        mov     eax, dword ptr ds : [ebp - 0x10]
        add     eax, ecx
        movzx   eax, byte ptr ds : [eax]
        cmp     dl, al
        je done
        undone :
        jmp undone
            done :
        jmp done
    return h + j + k + l;
char *blah = "abcdefghijjihgfedcba";
int main(void) {
  • Wonderful! And thank you for running through it in windbg. Much appreciated. – BronzeOtter Apr 26 '18 at 15:29

I cannot post comments because of low reputation.

What I am noticing here are the square brackets in the two movzx instructions.

These instructions compute an address:

080486e2  mov     edx, dword [ebp+0x8 {arg1}]
080486e5  mov     eax, dword [ebp-0xc {var_10_1}]
080486e8  add     eax, edx      

And this instruction gets the value at that address and stores it in dl because of the movzx (zero-extending):

080486ea  movzx   edx, byte [eax]   

Your question:

We need this cmp to succeed (dl == al) for the loop to continue. Otherwise, the code exits the loop and returns EAX to 0x0 (failure). Having said that - if we know that var_10_1 is 0 and var_14_1 is at least 19 at this first pass in the loop, and we add each to arg1 - then how can DL and AL be equal?

The answer is simple: that is not what cmp is checking.

As I see it (I'm also just getting into reverse engineering) the bytes stored at these addresses must be equal for je to be executed:

dword [ebp+0x8 {arg1}] + dword [ebp-0xc {var_10_1}]
dword [ebp+0x8 {arg1}] + dword [ebp-0x10 {var_14_1}]

This makes it quite a difficult problem because you now have to find out which addresses are pointing to the correct bytes.


This seems to be a search in your string for the first character matching the starting character

080486e2 mov edx, dword [ebp+0x8 {arg1}] ;arg1 -> edx, Pointer to string 080486e5 mov eax, dword [ebp-0xc {var_10_1}] ;0-> eax 080486e8 add eax, edx ;eax = eax + edx, i.e. eax = Pointer to string 080486ea movzx edx, byte [eax] ; 1st character -> edx (zero extend)
080486ed mov ecx, dword [ebp+0x8 {arg1}] ;arg1-> ecx, Pointer to string 080486f0 mov eax, dword [ebp-0x10 {var_14_1}] ; var_14_1 -> eax 080486f3 add eax, ecx ;eax = eax + ecx; Ptr at pos. var_14_1 080486f5 movzx eax, byte [eax] ; content of that ptr -> eax 080486f8 cmp dl, al ; are both characters equal?
080486fa je 0x8048703 ; yes (i.e. ZF = 1) jmp to 0x848703 ; no: continue here

  • Thank you! This is a great explanation. I see my mistake - that I didn't recognize that this code was actually setting up pointers to specific characters in the input string. I should have recognized it, given that a similar operation happens earlier in the code. Basic question - how did you recognize that the loop was dealing with a pointer to a specific character in the string, specifically the line add eax, edx - instances like that are hard for me to recognize coming from higher level languages with more defined data types. Thanks again! – BronzeOtter Apr 26 '18 at 15:27
  • 1
    You are welcome! The key to recognize the pointer is at address 80486ea. There you load the content of the value stored at eax into edx. Thus, eax must be an address. The fact that the code deals with characters can be guessed from the fact that dl and al are compared. Characters usually have one-byte length (at least ASCII characters). – josh Apr 26 '18 at 20:10
  • Ah, I see. I was wondering what the significance of the many one-byte references was. Thank you! – BronzeOtter Apr 30 '18 at 1:40

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.