Table of Contents

Question

This function uses a combination SCAS and STOS to do its work. First, explain what is the type of the [EBP+8] and [EBP+C] in line 1 and 8, respectively. Next, explain what this snippet does.

01: mov edi, [ebp + 8]
02: mov edx, edi
03: xor eax, eax
04: or ecx, 0FFFFFFFFh
05: repne scasb
06: add ecx, 2
07: neg ecx
08: mov al, [ebp + 0Ch]
09: mov edi, edx
10: rep stosb
11: mov eax, edx

Answer

[EBP + 8h] appears to be a char buffer pointer/PCHAR(size = 4 bytes) since it is loaded into EDI register which is then implicitly used by scasb instruction with repne prefix as the memory operand address to compare for a particular byte value specified by AL register.

[EBP + 0Ch] appears to be a CHAR(size = 1 byte) since it is loaded into AL register which is then implicitly used by stosb instruction with rep prefix as the byte value to store into the destination operand given by EDI register.

Line 1 sets EDI to the value at address (EBP+8h) which is probably the first argument(PCHAR) passed to this function following the __cdecl/__stdcall calling convention.

Line 2 sets EDX with EDI, essentially saving a copy of the first parameter in EDX register. This is necessary because in Line 5 when scasb is used, it automatically increments/decrements EDI based on EFLAGS.DF, therefore, trashing the original value.

Line 3 performs Bitwise Exclusive OR operation on EAX with itself, thereby clearing it to 0.

Line 4 performs Bitwise Inclusive OR operation on ECX with 0xFFFFFFFF, thereby setting it to 0xFFFFFFFF.

Line 5 uses the scasb instruction to scan the string(pointed to by EDI) for the NULL byte terminator(given by AL) one byte at a time. repne prefix(which uses ECX as an unsigned counter) is used to indicate to keep scanning until it finds the NULL byte. This will also decrement ECX for each byte scanned until the NULL byte is encountered.

Line 6 adds 2 to ECX to compensate for counting from -1 instead of 0 and including the NULL byte.

Line 7 replaces ECX with its 2’s complement, thereby turning it into a positive value. Now, ECX contains the length of the string excluding the NULL byte.

Line 8 sets AL to the value at address (EBP+0xC) which is probably the second argument(CHAR) passed to this function following the __cdecl/__stdcall calling convention.

Line 9 sets EDI with EDX to prime it with the destination operand address for the next instruction.

Line 10 uses the stosb instruction to store the byte(given by AL) into the string(pointed to by EDI) one byte at a time. rep prefix is used to indicate to keep setting until ECX decrements to 0(i.e. the entire length of the string).

Line 11 sets EAX with EDX now pointing to the overwritten string. EAX register holds the return value when returning from a procedure.

Based on all of the above, we can construct a decompilation of the assembly snippet as follows:

extern "C" __declspec(noinline) PCHAR __cdecl asm_func(
  _In_ PCHAR pchBuffer,
  _In_ CHAR  chValue
) {
  return (PCHAR)memset(pchBuffer, chValue, strlen(pchBuffer));
}