38

How could this 32-bit x86 assembly be written in C?

loc_536FB0:
mov cl, [eax]
cmp cl, ' '
jb short loc_536FBC
cmp cl, ','
jnz short loc_536FBF

loc_536FBC:
mov byte ptr [eax], ' '

loc_536FBF
mov cl, [eax+1]
inc eax
test cl, cl
jnz short loc_536FB0

I have already figured out that it is a for loop that loops 23 times before exiting.

124

Such small snippets are not too hard to decompile manually. Let's try it.

You have already figured out that cl holds a character, this means that eax where it's read from is a pointer to a character array. Let's call it p. Now, let's do a dumb translation for every assembly statement to C:

l1:            ; l1:
mov cl, [eax]  ;   cl = *p;
cmp cl, ' '    ;   if ( cl < ' ' )
jb short l2    ;     goto l2
cmp cl, ','    ;   if ( cl != ',' )
jnz short l3   ;     goto l3

l2:                      ; l2:
mov byte ptr [eax], ' '  ;   *p = ' '

l3:                  ; l3:
mov cl, [eax+1]      ; cl = *(p+1)
inc eax              ; p = p + 1
test cl, cl          ; if ( cl != 0 )
jnz short l1         ;   goto l1

And cleaned up:

l1:               
  cl = *p;
  if ( cl < ' ' ) 
    goto l2;
  if ( cl != ',' )
    goto l3;       
l2:       
  *p = ' ';
l3:           
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

Now, let's have a look at the second if. It has the following form:

if ( condition )
  goto end_of_if;
  <if body>
end_of_if:

And here's how we can get rid of the goto:

if ( !condition )
{
  <if body>
}

Applying it to our snippet:

l1:               
  cl = *p;
  if ( cl < ' ' ) 
    goto l2;
  if ( cl == ',' )  {
l2:       
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

Now, how we can get rid of goto l2? If you look at it carefully, you can see that the body at l2 will get executed if either cl < ' ' or cl == ','. So we can just combine the two conditions with a logical OR (||):

l1:               
  cl = *p;
  if ( cl < ' ' || cl == ',' ) {
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

Now we have one goto left. We have: 1) label at the beginning of a statement block 2) check at the end of the block and 3) goto to the start of the block if the check succeeded. This is a typical pattern of a do-while loop, and we can easily convert it:

do {
  cl = *p;
  if ( cl < ' ' || cl == ',' ) {
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
} while ( cl != 0 )

Now the code is almost nice and pretty, but we can compress it a bit more by substituting equivalent statements:

do {
  if ( *p < ' ' || *p == ',' )
    *p = ' ';
  cl = *++p;
} while ( cl != 0 )

And, finally, the last assignment can be moved into the condition:

do {
  if ( *p < ' ' || *p == ',' )
    *p = ' ';
} while ( *++p != 0 )

Now it's obvious what the code is doing: it's going through the string, and replacing all special characters (those with codes less than 0x20 aka space) and commas with the spaces.

  • 4
    Great answer, especially since you are explaining each step. – Remko May 27 '13 at 13:02
  • 1
    Fantastic answer. – Lightness Races with Monica May 30 '13 at 11:31
  • Couldn't of done it better myself – 06needhamt Nov 1 '13 at 14:06
  • 7
    reads answer "Wow, that's really detailed an informative, I wonder who..." realises it's the guy who reverse engineered Intel IME "Ahhhhh..." – Polynomial Mar 23 '15 at 21:03
9

Well, especially for that, Hex-Rays Decompiler was invented. It will decompile ASM code into pseudo-C, and from there You may write C-based logic of assembly code You have.

  • solved this now – user1365830 May 25 '13 at 19:58
  • 10
    not everybody have access to hex-rays tho... Igor's answer is far more thorough. – 0xea May 26 '13 at 11:12
  • 1
    Well, not everybody have small snippet of assembly, so answer should be wider than one specific case. And indeed thx Igor for spending his precious time for this specific piece of code. – Denis Laskov May 26 '13 at 11:40
  • 8
    Nah, you didn't answer the question. If you want to link to a tool that makes the question obsolete, then do so in a comment on the question. – Lightness Races with Monica May 30 '13 at 11:28
  • oh, my sorry, masa :) – Denis Laskov May 30 '13 at 15:40
5

Here's what it would have looked like in the source. Fastcall being a replacement for the custom leaf convention the compiler used when it was optimized.

void __fastcall __forceinline RemoveControlChars(char* szInput) {
    int i;
    for (i = 0; i < 23 && *szInput; ++i, ++szInput) {
        if (*szInput < ' ' || *szInput == ',')
            *szInput = ' ';
    }
}
  • 5
    There is nothing that I can see in the original code to correspond to your i < 24 continuation condition. Starting at i=0 and looping while i<24 will also execute the loop 24 times, not 23 as stated by the OP. – a CVn May 26 '13 at 13:31
  • 1
    fastcall and forceinline together don't make any sense. If it's inlined, there is no formal calling convention. – Jonathon Reinhart Jun 15 '13 at 8:52
  • 1
    __fastcall simply implies that args will be passed in registers instead of on the stack, which is assumed if inline, however the compiler may still push args onto the stack if there are not enough registers, making it sort of a hackish anti-optimization. According to msdn: Even with __forceinline, the compiler cannot inline code in all circumstances. – Tox1k Jun 29 '13 at 9:17

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.