📚 OpenRCE is preserved as a read-only archive. Launched at RECon Montreal in 2005. Registration and posting are disabled.








Flag: Tornado! Hurricane!

 Forums >>  Target Specific - General  >>  Memcpy/pointers help needed!

Topic created on: March 10, 2008 22:20 CDT by z3usone .

I am currently reversing a piece of malware but have become stuck. So far I have uncovered SSDT hooking and MajorFunction[] hooks, but the part I am stuck on is a unpacker for code that attaches itself onto services.exe. The unpacking is very straightforward (simple xoring) and the allocation of the virtual memory in services.exe is simple, but I am having trouble interpreting how memcpy translates to assembly. Also, I do not see how the ida hex-rays plugin generates some of the decimal indexes. I will post the full code below. The following is some of the questions I have:

In the assembly we have the following code:
    .text:00010BF4                 mov     ebx, data_0E0h
    ...
    .text:00010BFC                 lea     ebx, Start_of_unpacking_address[ebx] ; ebx contains Start_of_unpacking_address[E0h]
    ...
    .text:00010C28                 movzx   eax, word ptr [ebx+14h]
    .text:00010C2C                 lea     edx, [eax+ebx+18h]
    .text:00010C30                 movzx   eax, word ptr [ebx+6]
    .text:00010C34                 and     [ebp+services_exe_handle], 0 ; clear the handle to services.exe
    .text:00010C38                 lea     ecx, [eax+eax*4]
    .text:00010C3B                 shl     ecx, 3
    .text:00010C3E                 mov     eax, offset Start_of_unpacking_address
    .text:00010C43                 sub     ecx, eax
    .text:00010C45                 add     ecx, edx

Hex-rays reports that this translates to the following:

       v6 = (int)&v4[*((_WORD *)v4 + 10) + 24];                    // pointer to 159C3
                                                                    // Note that v4 + 10 = 1122A and the value at this location
                                                                    // is 478B after unpacking. The dereferencing * uses this value.
                                                                    // We then add 24 to it. This value is equal to 47A3. We then
                                                                    // cast the location (&) of v4[0x47A3] to an int which is equal
                                                                    // to (int) loc_159C3
                                                                    //
        v7 = *((_WORD *)v4 + 3);                                    // equals 4C00h


I have been staring at the assembly for hours and still do not see how hex-rays gets this! Any explanation would be greatly appreciated!

Also, if you look at the assembly vs pseudo-c below, how does the memcpy reported in hex-rays correspond to the assembly? Again, help is greatly appreciated!

The following is the assembly:

    .text:00010BCA ; int __stdcall unpack_and_put_in_memory(HANDLE services_exe_handle)
    .text:00010BCA unpack_and_put_in_memory proc near      ; CODE XREF: sub_10CDC+8Bp
    .text:00010BCA
    .text:00010BCA AllocationSize  = dword ptr -0Ch
    .text:00010BCA var_8           = dword ptr -8
    .text:00010BCA BaseAddress     = dword ptr -4
    .text:00010BCA services_exe_handle= dword ptr  8
    .text:00010BCA
    .text:00010BCA                 mov     edi, edi
    .text:00010BCC                 push    ebp
    .text:00010BCD                 mov     ebp, esp
    .text:00010BCF                 sub     esp, 0Ch
    .text:00010BD2                 mov     ecx, data_1E000 ; off_1113c is an offset to 1E000
    .text:00010BD2                                         ; ie we will unpack until eax = 1E000
    .text:00010BD8                 and     [ebp+BaseAddress], 0
    .text:00010BDC                 xor     eax, eax
    .text:00010BDE                 test    ecx, ecx
    .text:00010BE0                 jbe     short loc_10BF3
    .text:00010BE2
    .text:00010BE2 UNPACKING_FUNCTION:                     ; CODE XREF: unpack_and_put_in_memory+27j
    .text:00010BE2                 mov     dl, Unpack_47h  ; This is our unpacker!
    .text:00010BE8                 xor     byte ptr Start_of_unpacking_address[eax], dl
    .text:00010BEE                 inc     eax
    .text:00010BEF                 cmp     eax, ecx
    .text:00010BF1                 jb      short UNPACKING_FUNCTION
    .text:00010BF3
    .text:00010BF3 loc_10BF3:                              ; CODE XREF: unpack_and_put_in_memory+16j
    .text:00010BF3                 push    ebx
    .text:00010BF4                 mov     ebx, data_0E0h
    .text:00010BFA                 push    40h             ; Protect
    .text:00010BFC                 lea     ebx, Start_of_unpacking_address[ebx] ; ebx contains Start_of_unpacking_address[E0h]
    .text:00010C02                 mov     eax, [ebx+50h]
    .text:00010C05                 push    3000h           ; AllocationType
    .text:00010C0A                 mov     [ebp+AllocationSize], eax ; this will be 21000h that is loaded into AllocationSize
    .text:00010C0D                 lea     eax, [ebp+AllocationSize]
    .text:00010C10                 push    eax             ; AllocationSize
    .text:00010C11                 push    0               ; ZeroBits
    .text:00010C13                 lea     eax, [ebp+BaseAddress]
    .text:00010C16                 push    eax             ; BaseAddress
    .text:00010C17                 push    [ebp+services_exe_handle] ; ProcessHandle
    .text:00010C1A                 call    ds:ZwAllocateVirtualMemory ; We are calling this with a protect of PAGE_EXECUTE_READWRITE
    .text:00010C1A                                         ; A AllocationType of MEM_RESERVE | MEM_COMMIT which will reserve
    .text:00010C1A                                         ; the memory space, and make it accessible to the process.
    .text:00010C1A                                         ; It is 21000h in size.
    .text:00010C1A                                         ;
    .text:00010C20                 test    eax, eax        ; check if there is an error from the above call.
    .text:00010C22                 jl      exit
    .text:00010C28                 movzx   eax, word ptr [ebx+14h]
    .text:00010C2C                 lea     edx, [eax+ebx+18h]
    .text:00010C30                 movzx   eax, word ptr [ebx+6]
    .text:00010C34                 and     [ebp+services_exe_handle], 0 ; clear the handle to services.exe
    .text:00010C38                 lea     ecx, [eax+eax*4]
    .text:00010C3B                 shl     ecx, 3
    .text:00010C3E                 mov     eax, offset Start_of_unpacking_address
    .text:00010C43                 sub     ecx, eax
    .text:00010C45                 add     ecx, edx
    .text:00010C47                 push    esi
    .text:00010C48                 push    edi
    .text:00010C49                 mov     edi, [ebp+BaseAddress]
    .text:00010C4C                 mov     [ebp+var_8], edx
    .text:00010C4F                 mov     edx, ecx
    .text:00010C51                 shr     ecx, 2
    .text:00010C54                 mov     esi, eax
    .text:00010C56                 rep movsd
    .text:00010C58                 mov     ecx, edx
    .text:00010C5A                 and     ecx, 3
    .text:00010C5D                 rep movsb
    .text:00010C5F                 cmp     word ptr [ebx+6], 0
    .text:00010C64                 jbe     short loc_10C9F
    .text:00010C66                 mov     edx, [ebp+var_8]
    .text:00010C69                 add     edx, 14h
    .text:00010C6C                 mov     [ebp+var_8], edx
    .text:00010C6F
    .text:00010C6F loc_10C6F:                              ; CODE XREF: unpack_and_put_in_memory+D3j
    .text:00010C6F                 mov     ecx, [edx-4]
    .text:00010C72                 mov     esi, [edx]
    .text:00010C74                 mov     edi, [edx-8]
    .text:00010C77                 add     edi, [ebp+BaseAddress]
    .text:00010C7A                 mov     edx, ecx
    .text:00010C7C                 shr     ecx, 2
    .text:00010C7F                 add     esi, eax
    .text:00010C81                 rep movsd
    .text:00010C83                 mov     ecx, edx
    .text:00010C85                 mov     edx, [ebp+var_8]
    .text:00010C88                 and     ecx, 3
    .text:00010C8B                 inc     [ebp+services_exe_handle]
    .text:00010C8E                 rep movsb
    .text:00010C90                 movzx   ecx, word ptr [ebx+6]
    .text:00010C94                 add     edx, 28h
    .text:00010C97                 cmp     [ebp+services_exe_handle], ecx
    .text:00010C9A                 mov     [ebp+var_8], edx
    .text:00010C9D                 jb      short loc_10C6F
    .text:00010C9F
    .text:00010C9F loc_10C9F:                              ; CODE XREF: unpack_and_put_in_memory+9Aj
    .text:00010C9F                 mov     ecx, [ebx+28h]
    .text:00010CA2                 mov     eax, [ebp+BaseAddress]
    .text:00010CA5                 add     ecx, eax
    .text:00010CA7                 pop     edi
    .text:00010CA8                 mov     Services_exe_our_base_memAddress, eax
    .text:00010CAD                 mov     Services_exe_our_base_someOffset, ecx
    .text:00010CB3                 xor     eax, eax
    .text:00010CB5                 pop     esi
    .text:00010CB6
    .text:00010CB6 exit:                                   ; CODE XREF: unpack_and_put_in_memory+58j
    .text:00010CB6                 pop     ebx
    .text:00010CB7                 leave
    .text:00010CB8                 retn    4
    .text:00010CB8 unpack_and_put_in_memory endp

And the hex-rays pseudo-c output:

    NTSTATUS __stdcall unpack_and_put_in_memory(HANDLE services_exe_handle)
    {
      unsigned int v1; // eax@1
      void *v2; // ecx@1
      NTSTATUS result; // eax@3
      char *v4; // ebx@3
      int v5; // edx@5
      int v6; // edx@4
      int v7; // eax@4
      unsigned int v8; // ecx@6
      int v9; // esi@6
      void *v10; // edi@6
      int v11; // edx@6
      const void *v12; // esi@6
      const void *v13; // esi@6
      void *v14; // edi@6
      int v15; // edx@6
      unsigned int v16; // ecx@6
      int v17; // ecx@7
      PVOID BaseAddress; // [sp+8h] [bp-4h]@1
      ULONG AllocationSize; // [sp+0h] [bp-Ch]@3
      int v20; // [sp+4h] [bp-8h]@4

      v2 = data_1E000;
      BaseAddress = 0;
      v1 = 0;
      if ( data_1E000 )
      {
        do
        {
          Start_of_unpacking_address[v1] = Unpack_47h ^ (unsigned __int8)Start_of_unpacking_address[v1];
          ++v1;
        }
        while ( v1 < (unsigned int)v2 );
      }
      v4 = &Start_of_unpacking_address[data_0E0h];
      AllocationSize = *(_DWORD *)&Start_of_unpacking_address[data_0E0h + 80];// AllocationSize is 21000h
      result = ZwAllocateVirtualMemory(services_exe_handle, &BaseAddress, 0, &AllocationSize, 0x3000u, 0x40u);
      if ( result >= 0 )
      {
        v6 = (int)&v4[*((_WORD *)v4 + 10) + 24];                    // pointer to 159C3
                                                                    // Note that v4 + 10 = 1122A and the value at this location
                                                                    // is 478B after unpacking. The dereferencing * uses this value.
                                                                    // We then add 24 to it. This value is equal to 47A3. We then
                                                                    // cast the location (&) of v4[0x47A3] to an int which is equal
                                                                    // to (int) loc_159C3
                                                                    //
        v7 = *((_WORD *)v4 + 3);                                    // equals 4C00h
        services_exe_handle = 0;
        v20 = v6;
        memcpy(BaseAddress, Start_of_unpacking_address, v6 + 40 * v7 - (_DWORD)Start_of_unpacking_address);
        if ( *((_WORD *)v4 + 3) )
        {
          v5 = v20 + 20;
          v20 += 20;
          do
          {
            v8 = *(_DWORD *)(v5 - 4);
            v9 = *(_DWORD *)v5;
            v10 = (char *)BaseAddress + *(_DWORD *)(v5 - 8);
            v11 = *(_DWORD *)(v5 - 4);
            v8 >>= 2;
            v12 = &Start_of_unpacking_address[v9];
            memcpy(v10, v12, 4 * v8);
            v13 = (char *)v12 + 4 * v8;
            v14 = (char *)v10 + 4 * v8;
            LOBYTE(v8) = v11;
            v15 = v20;
            ++services_exe_handle;
            memcpy(v14, v13, v8 & 3);
            v16 = *((_WORD *)v4 + 3);
            v5 = v15 + 40;
            v20 = v5;
          }
          while ( (unsigned int)services_exe_handle < v16 );
        }
        v17 = (int)((char *)BaseAddress + *((_DWORD *)v4 + 10));
        dword_2F194 = (int)BaseAddress;
        dword_2F190 = v17;
        result = 0;
      }
      return result;
    }

  cseagle     March 10, 2008 23:50.50 CDT
> z3usone:
>
>     .text:00010C28                 movzx   eax, word ptr [ebx+14h]
>     .text:00010C2C                 lea     edx, [eax+ebx+18h]
>     .text:00010C30                 movzx   eax, word ptr [ebx+6]

> Hex-rays reports that this translates to the following:
>
>        v6 = (int)&v4[*((_WORD *)v4 + 10) + 24];                    // pointer to 159C3
>         v7 = *((_WORD *)v4 + 3);                                    // equals 4C00h
>
>
> I have been staring at the assembly for hours and still do not see how hex-rays gets this! Any explanation would be greatly appreciated!
>

Yep, that's correct.  But it only accounts for the three disassembly lines above.  In the first line, a word is being read, so Hex-Rays sees ebx as a word pointer and 14h(20) as an index into an array of shorts.  Since shorts are 2 bytes, this is index 10 into the array.
The second line, ebx is treated as a char* and the address of ebx + the word from the first line + 24 is taken.
In the third line, ebx is again treated as a short pointer and 6/2 = 3.  Alternatives to the above might be:

eax = *((_WORD *)v4 + 10);   //or eax = *(word*)(v4 + 20) note v4 == ebx
v6 = &v4[eax + 24];   //or simply  v6 = v4 + eax + 24;
v7 = *((_WORD *)v4 + 3);  //or v7 = *(word*)(v4 + 6)

You can have any number of interpretations depending on how you place the parenthesis.  Another interpretation entirely would be that ebx points to a struct and you could do the following:

eax = ebx->field_14;  //word sized field
v6 = &ebx->field_18[eax];  //field_18 is array of char
v7 = ebx->field_6;   //word sized field

> Also, if you look at the assembly vs pseudo-c below, how does the memcpy reported in hex-rays correspond to the assembly? Again, help is greatly appreciated!
>
>     .text:00010C49                 mov     edi, [ebp+BaseAddress]  //1  edi gets base address
>     .text:00010C4C                 mov     [ebp+var_8], edx        //2
>     .text:00010C4F                 mov     edx, ecx                //3  ecx holds # bytes to move
>     .text:00010C51                 shr     ecx, 2                  //4  length / 4
>     .text:00010C54                 mov     esi, eax                //5  eax holds source address
>     .text:00010C56                 rep movsd                       //6  move dwords
>     .text:00010C58                 mov     ecx, edx                //7  get saved length
>     .text:00010C5A                 and     ecx, 3                  //8  length % 4
>     .text:00010C5D                 rep movsb                       //9  move bytes

I trimmed to the relevent portion which is a fairly standard implementation of memcpy, other than the second line which simply saves the current value of edx.
The first line sets the destination, line 3 saves the length in edx, line 4 divides the length by 4 to compute the number of dwords to be moved, line 5 sets the destination, line 6 moves the number of dwords computed at 4, line 7 and 8 compute the number of bytes remaining, and line 9 moves the remaining bytes.

Hope that helps,

Chris

  z3usone     March 11, 2008 00:21.32 CDT
Thanks for the quick reply Chris. You were able to clear up the confusion I had :).

  abuse007     March 12, 2008 01:01.13 CDT
I personally prefer the structure representation.

Ilfak posted on his blog creating structures from the Hex-Rays window.

Note: Registration is required to post to the forums.

There are 31,328 total registered users.


Recently Created Topics
[help] Unpacking VMP...
Mar/12
Reverse Engineering ...
Jul/06
let 'IDAPython' impo...
Sep/24
set 'IDAPython' as t...
Sep/24
GuessType return une...
Sep/20
About retrieving the...
Sep/07
How to find specific...
Aug/15
How to get data depe...
Jul/07
Identify RVA data in...
May/06
Question about memor...
Dec/12


Recent Forum Posts
Finding the procedur...
rolEYder
Question about debbu...
rolEYder
Identify RVA data in...
sohlow
let 'IDAPython' impo...
sohlow
How to find specific...
hackgreti
Problem with ollydbg
sh3dow
How can I write olly...
sh3dow
New LoadMAP plugin v...
mefisto...
Intel pin in loaded ...
djnemo
OOP_RE tool available?
Bl4ckm4n


Recent Blog Entries
halsten
Mar/14
Breaking IonCUBE VM

oleavr
Oct/24
Anatomy of a code tracer

hasherezade
Sep/24
IAT Patcher - new tool for ...

oleavr
Aug/27
CryptoShark: code tracer ba...

oleavr
Jun/25
Build a debugger in 5 minutes

More ...


Recent Blog Comments
nieo on:
Mar/22
IAT Patcher - new tool for ...

djnemo on:
Nov/17
Kernel debugger vs user mod...

acel on:
Nov/14
Kernel debugger vs user mod...

pedram on:
Dec/21
frida.github.io: scriptable...

capadleman on:
Jun/19
Using NtCreateThreadEx for ...

More ...


Imagery
SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit