I am reversing some x86 from an old CTF from 2014 and am trying to understand the below code (it has been shortened drastically). I believe it is performing some sort of while or for loop through a string x number of times where x is the length of the string.

Some Basic Info:

  • Intel Syntax
  • Function Prototype Given:
    • int main(char*);

My primary confusion lies within the end of .L3 and .L2:

I believe the end of .L3 is storing the register arithmetic in [ebp-12] and then increasing the value of [ebp-8] (I believe this is a pointer to a copy of the char* arg)

    push    ebp    
    mov     ebp, esp
    push    ebx 
    sub     esp, 20 
    mov     DWORD PTR [ebp-12], 0
    mov     eax, DWORD PTR [ebp+8]
    mov     DWORD PTR [ebp-8], eax
    jmp     .L2
    // Bunch of annoying arithmetic w/ registers
    mov     DWORD PTR [ebp-12], eax     
    add     DWORD PTR [ebp-8], 1        
    mov     eax, DWORD PTR [ebp-8]      
    movzx   eax, BYTE PTR [eax]         
    test    al, al                      
    jne     .L3                             
    add     esp, 20
    pop     ebx
    pop     ebp

My loose conversion of this (disregarding much of .L3) is as follows in c:

int main(char* arg)
    int loc1 = 0;
    char* str = arg;
    for(i = 0; str[i] != '\0'; i++) {         //  <=== Pretty Sure
         // .L3 Stuff w/ Assignment to loc1   //  This is incorrect
    return loc1;

Would someone be willing to explain the incrementation of the [ebp-8], first four lines of .L2, and confirm/deny that during the return the value of eax or loc1 as I called it will return based on the end of .L2?

  • Note that movzx eax, BYTE PTR [eax] will load one byte into al, and set the rest of eax to zero. If that byte is != 0, the loop continues; so once the loop exits, eax is 0 so the function returns 0. Apr 8, 2015 at 7:01

2 Answers 2


Let's annotate the assembly.

    push    ebp       // standard function prologue
    mov     ebp, esp
    push    ebx       // saved register
    sub     esp, 20   // room for locals
    mov     DWORD PTR [ebp-12], 0    // x = 0 (x = var@-12)
    mov     eax, DWORD PTR [ebp+8]   // s = arg1 (s = var@-8)
    mov     DWORD PTR [ebp-8], eax
    jmp     .L2
    // Bunch of annoying arithmetic w/ registers
    mov     DWORD PTR [ebp-12], eax     // x = eax (whatever you computed)
    add     DWORD PTR [ebp-8], 1        // s++
    mov     eax, DWORD PTR [ebp-8]      // eax = s
    movzx   eax, BYTE PTR [eax]         // eax = *s
    test    al, al                      // if(*s != 0) goto L3
    jne     .L3                         
    add     esp, 20                     // cleanup and return
    pop     ebx
    pop     ebp

Your reconstruction was pretty accurate. I'd change it as follows:

void func(char* arg) {
    int x = 0; // @-12
    char* s; // @-8
    for(s = arg; *s != '\0'; s++) {
         // do stuff and assign stuff to x
    // no return: eax at function end is always 0 and seems to be just a temporary

this construct imho is a do while loop the telltale jmp at start and the conditional jump at end indicates that construct

this seems to be a strlen kind of function takes one char * input and finds the null at the end of the input

code pasted below generates an equivalent assembly in vc2kten express

  1. the cdb commands are set symbol path to local directory to avoid hitting internet with .sympath

  2. disassemble the function of interest completely with uf Funct

  3. execute until the start of desired function with g funct
  4. dump value of edx , stepover till the first return with pt and step out with p
  5. redump value of edx and eax and quit

:dir /b


code for demo

:type x86while.cpp
void funct (unsigned char *input ) {
        unsigned char *a;
        int b,c,d,e,g,h,f=0;
        a = input;
        do {
unsigned char buff[] = {"hello my dear world"};
void main (void) {

compiled linked and executed with

:type compile.bat
del x86while.exe
if "%vcinstalldir%" == ""  ( @call "C:\Program Files\Microsoft Visual Studio 10.
0\VC\vcvarsall.bat" x86 )
cl /Zi /EHsc  /nologo /W4 /analyze *.cpp /link /RELEASE /ENTRY:main /FIXED
cdb -c ".sympath \"f:\symbols\";uf funct;g funct;r @edx;pt;p;r @edx;r eax;q" x86

results of execution generic spew removed only disassembly kept

00401000 55              push    ebp
00401001 8bec            mov     ebp,esp
00401003 83ec20          sub     esp,20h
00401006 c745f400000000  mov     dword ptr [ebp-0Ch],0
0040100d 8b4508          mov     eax,dword ptr [ebp+8]
00401010 8945f8          mov     dword ptr [ebp-8],eax

00401013 8b4df4          mov     ecx,dword ptr [ebp-0Ch]
00401016 83c101          add     ecx,1
00401019 894df4          mov     dword ptr [ebp-0Ch],ecx
0040101c 8b55f8          mov     edx,dword ptr [ebp-8]
0040101f 0355f4          add     edx,dword ptr [ebp-0Ch]
00401022 0fb602          movzx   eax,byte ptr [edx]
00401025 85c0            test    eax,eax
00401027 75ea            jne     x86while!funct+0x13 (00401013)

00401029 8be5            mov     esp,ebp
0040102b 5d              pop     ebp
0040102c c3              ret

edx=7c90e514 when entering function
edx=00403013 on returning from function
0:000> ?? buff
unsigned char [20] 0x00403000
0x68 'h'
0:000> db buff @edx
00403000  68 65 6c 6c 6f 20 6d 79-20 64 65 61 72 20 77 6f  hello my dear wo
00403010  72 6c 64 00                                      rld.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.