Fork me on GitHub

First steps in malware reversing

This article will describe the next steps after the unpacking stuff from the first article. In the last article i explained what happened in the call ebp part. The next step is to jump to the unpacked code located at 0x401018.

0x00413bf2   call ebp
0x00413bf4   mov edi, edi
0x00413bf6   jmp 0x401018
  • First of all [fs:0x18] returns the thread information block (TIB), +0x30 defines the position of the process environment block (PEB) address in the TIB struct so [eax+0x30] returns this address. We read the address of _PEB_LDR_DATA at +0xC and add 0x1c to get the LIST_ENTRY for InInitializationOrderModuleList. A LIST_ENTRY is a struct that contains two pointers to another LIST_ENTRY named Blink and Flink. So after dereferencing this pointer at 0x00401029 eax will contain the Flink ptr of this LIST_ENTRY.
typedef struct _PEB_LDR_DATA {
    ULONG Length; // 0x00
    UCHAR Initialized; // 0x04
    PVOID SsHandle; // 0x08
    LIST_ENTRY InLoadOrderModuleList;   // 0x0C
    LIST_ENTRY InMemoryOrderModuleList; // 0x14
    LIST_ENTRY InInitializationOrderModuleList; // 0x1C
    PVOID EntryInProgress; // 0x21
} PEB_LDR_DATA, *PPEB_LDR_DATA;
0x00401018   mov eax, [fs:0x18]
0x0040101e   mov eax, [eax+0x30]
0x00401021   mov ecx, [eax+0xc]
0x00401024   push ebx
0x00401025   push esi
0x00401026   add ecx, 0x1c
0x00401029   mov eax, [ecx]
0x0040102b   push edi
0x0040102c   jmp 0x401052
  • This block decides if we already checked all loaded modules in the list. Otherwise we continue at 0x40102e.
0x00401052   cmp ecx, eax
0x00401054   jne 0x40102e
0x00401056   xor eax, eax
0x00401058   pop edi
0x00401059   pop esi
0x0040105a   pop ebx
0x0040105b   ret
  • The LDR_MODULE struct looks like this:
typedef struct _LDR_MODULE {
  LIST_ENTRY              InLoadOrderModuleList;                        
  LIST_ENTRY              InMemoryOrderModuleList;                  
  LIST_ENTRY              InInitializationOrderModuleList; // 0x00
  PVOID                   BaseAddress; // 0x08
  PVOID                   EntryPoint;   // 0x0C
  ULONG                   SizeOfImage; // 0x10
  UNICODE_STRING          FullDllName; // 0x14
  UNICODE_STRING          BaseDllName; // 0x1C
  ULONG                   Flags;
  SHORT                   LoadCount;
  SHORT                   TlsIndex;
  LIST_ENTRY              HashTableEntry;
  ULONG                   TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;

We walk through the ldr list in 'InInitializationOrderModuleList' order so we start at the position of this LIST_ENTRY in the LDR_MODULE. A LIST_ENTRY is 8 bytes in size, a PVOID and ULONG counts 4 bytes. A UNICODE_STRING is defined like the following struct:

typedef struct _UNICODE_STRING {
    USHORT  Length;  
    USHORT  MaximumLength;  
    PWSTR   Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

So that this struct counts 8 bytes. If we add everything together till we reach the offset 0x20 we hit the PWSRT Buffer member of the BaseDllName struct. Finally the algorithm multiplies the start value 0x3567de8a with 0x21, and xor`s the first character with the result. We continue this character by character till we reach the end of the BaseDllName. This will be continued module by module until we get the final result 0xbd3cda7e.

0x0040102e   mov edx, [eax+0x20]
0x00401031   mov edi, 0x3567de8a ;  0x3567de8a 
0x00401036   movzx esi, word [edx]
0x00401039   imul edi, edi, 0x21
0x0040103c   movzx ebx, si
0x0040103f   xor edi, ebx
0x00401041   inc edx
0x00401042   inc edx
0x00401043   test si, si
0x00401046   jne 0x401036
0x00401048   cmp edi, 0xbd3cda7e
0x0040104e   je 0x40105c
0x00401050   mov eax, [eax]
  • Just for fun i wrote a little program that takes the start, end and factor as argument, reads a string from stdin and tests if the algorithm creates the correct result.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    unsigned int result = strtol(argv[1], 0, 16);
    unsigned int start = strtol(argv[2], 0, 16); 
    unsigned int factor = strtol(argv[3], 0, 16); 
    unsigned int tmp = start;
    char buffer[512];
    int c, i;
    int size = 0;

    while (1) {
        while (1) {
            c = fgetc(stdin);
            if (c == EOF) break;
            if (c == '\n') break;
            buffer[size++] = c;
        }
        buffer[size++] = '.';
        buffer[size++] = 'd';
        buffer[size++] = 'l';
        buffer[size++] = 'l';
        buffer[size] = '\0';

        for (i = 0; i <= size; i++) {
            tmp *= factor;
            tmp ^= buffer[i];
        }
        if (tmp == result) break;
        size = 0;
        tmp = start;
    }
    printf("%s\n", buffer);
    return 0;
}

Now we just have to call john the ripper for example to get the name of the dll that we are searching for:

john  --incremental --stdout | ./module_find 0x3567de8a 0xbd3cda7e 0x21
ntdll.dll

links

social