Single Function and Multiple DLLs

While looking at the payload of a BlackHole Exploit Kit, I noticed something that I haven't seen before. It's kind of unique so I thought I'd describe it. The malware is installed in the System32 directory with a name of nvdia32.exe. The file description says the file is a "video driver" with a company name "nVidia software". If we were to view the strings of the executable we would notice it's a NSIS Installer. If executed nvidia32.exe would write six files to the %TEMP% directory. Rather than execute it, we can up 7zip to extract out the embedded files.

NSIS Executable & Extracted Content
This is where things get kind of interesting. Each one of these DLLs contains a function that is imported by Sohozovi.exe. Importing DLLs is not uncommon but each DLL contains one function. Let's check out WinMain of Sohozovi.exe.

.text:004010E0 _WinMain@16 proc near ; CODE XREF: start+12Fp .text:004010E0 .text:004010E0 var_4 = dword ptr -4 .text:004010E0 hInstance = dword ptr 4 .text:004010E0 hPrevInstance = dword ptr 8 .text:004010E0 lpCmdLine = dword ptr 0Ch .text:004010E0 nShowCmd = dword ptr 10h .text:004010E0 .text:004010E0 push ecx .text:004010E1 push offset OutputString ; "Jahuzejijas" .text:004010E6 call ds:OutputDebugStringA .text:004010EC push offset aWokynrxqmt ; "wokynrxqmt" .text:004010F1 push offset unk_40316C .text:004010F6 call Cejarinam ; calculate Logical Shift Key .text:004010F6 ; Imports from Cawizuhova.dll .text:004010F6 ; returns 1888 or 31 38 38 38 .text:004010FB push offset unk_403064 .text:00401100 call sub_401000 .text:00401105 push 4E00h ; unsigned int .text:0040110A call ??2@YAPAXI@Z ; operator new(uint) .text:0040110F add esp, 8 .text:00401112 mov dword_403168, eax .text:00401117 push offset String2 ; "fovuqonayura.mop" .text:0040111C push offset dword_403168 .text:00401121 push offset String2 ; "fovuqonayura.mop" .text:00401126 call Xugegisedumu .text:0040112B add esp, 8 .text:0040112E push eax ; lpString1 .text:0040112F call ds:lstrcmpiA .text:00401135 push 64h ; dwMilliseconds .text:00401137 mov dword_403054, eax .text:0040113C call ds:Sleep .text:00401142 call ds:GetCurrentThread .text:00401148 push eax .text:00401149 mov eax, _size ; 0x4E00 .text:0040114E push offset unk_40316C .text:00401153 push eax .text:00401154 push offset dword_403168 .text:00401159 call Tikeyicenapu ; Deobfuscated .text:0040115E mov ecx, hDlg .text:00401164 lea edx, [esp+4+var_4] .text:00401168 push ecx ; hDlg .text:00401169 push edx ; int .text:0040116A mov [esp+0Ch+var_4], 4

 The code basically calls a function called Cejarinam imported from Cawizhhova.dll. This function/DLL is responsible for calculating the XOR key. Once that is completed a function named Xugegisedumu imported from Necacaza.dll opens and reads into a buffer named fovuqonayura.mop. The buffer is decoded by a function named Tikeyicenapu imported from Cawizuhova.dll. Below is the decoder function in Cawizuhova.dll.

.text:10001000 Tikeyicenapu proc near ; DATA XREF: .rdata:off_10002128o .text:10001000 .text:10001000 arg_0 = dword ptr 4 .text:10001000 arg_4_size = dword ptr 8 .text:10001000 arg_8 = dword ptr 0Ch .text:10001000 .text:10001000 xor ecx, ecx .text:10001002 cmp [esp+arg_4_size], ecx .text:10001006 jbe short loc_10001027 .text:10001008 push esi ; save off esi .text:10001009 .text:10001009 _loop: ; CODE XREF: Tikeyicenapu+24j .text:10001009 mov eax, [esp+4+arg_0] ; static .text:1000100D mov esi, [esp+4+arg_8] ; key = 1888 or 31 38 38 38 .text:10001011 mov edx, ecx ; edx = count .text:10001013 mov eax, [eax] ; address of data/buffer .text:10001015 and edx, 3 ; edx = count & 3 .text:10001018 add eax, ecx ; address/buffer[index] .text:1000101A mov dl, [edx+esi] .text:1000101D xor [eax], dl .text:1000101F inc ecx ; inc count/index .text:10001020 cmp ecx, [esp+4+arg_4_size] .text:10001024 jb short _loop .text:10001026 pop esi .text:10001027 .text:10001027 loc_10001027: ; CODE XREF: Tikeyicenapu+6j .text:10001027 push 5Ah ; index .text:10001029 push 0 ; hWnd .text:1000102B call ds:GetDC .text:10001031 push eax ; hdc .text:10001032 call ds:GetDeviceCaps .text:10001038 retn 10h .text:10001038 Tikeyicenapu endp

If we were to look at fovuqonayura.mop in a hex editor we would see obfuscated data. The recurring patterns is a sign that XOR was likely used.

Encoded Data
The following Python code can be used to decode fovuqonayura.mop.

key = (0x31, 0x38, 0x38, 0x38)
f = open('fovuqonayura.mop', 'rb')
byte = f.read(1)
count = 0
data = ''
while byte != '':
    data = data + chr(ord(byte)^key[count & 3])
    byte = f.read(1)
    count += 1
o = open( 'out.bin', 'wb')
o.write(data)
o.close()

Below is the decoded file.
Decoded Data
Besides the MZ header, notice anything interesting about the APIs? These are the APIs that are needed to hollow out and inject into a process. Please see the following link for more information on debugging hollow processes (Summary of debugging hollow processes: BP SetThreadContext, Run, Read value at [[esp+4]+0xb0], located at address in hollow process, int3 address, Ollydbg JIT, and Run). Carving out the executable would be a logical solution but the executable wouldn't load in Ollydbg. If we were to follow the code we would find that WinMain calls another function named zaxiwezu which is imported from Jarexenihuz.dll. This function is responsible for getting the addresses of the functions needed for process injection and to inject into the process. Below we can see the usual APIs needed for importing.

.text:10001000 zaxiwezu proc near ; DATA XREF: .rdata:off_10002118o .text:10001000 .text:10001000 var_3D0 = dword ptr -3D0h .text:10001000 var_32C = dword ptr -32Ch .text:10001000 var_320 = dword ptr -320h .text:10001000 var_104 = byte ptr -104h .text:10001000 var_103 = byte ptr -103h .text:10001000 arg_4 = dword ptr 0Ch .text:10001000 arg_8 = dword ptr 10h .text:10001000 .text:10001000 push ebp .text:10001001 mov ebp, esp .text:10001003 sub esp, 3D0h .text:10001009 push ebx .text:1000100A push esi .text:1000100B mov esi, [ebp+arg_8] .text:1000100E push edi .text:1000100F mov edi, ds:LoadLibraryA .text:10001015 lea eax, [esi+0E9h] .text:1000101B push eax ; lpLibFileName .text:1000101C call edi ; LoadLibraryA .text:1000101E lea ecx, [esi+106h] .text:10001024 mov [ebp+arg_4], eax .text:10001027 push ecx ; lpProcName .text:10001028 push eax ; hModule .text:10001029 call ds:GetProcAddress .text:1000102F mov ebx, eax .text:10001031 lea eax, [esi+0CCh] .text:10001037 push eax .text:10001038 push [ebp+arg_4] .text:1000103B call ebx

Details around the process injection will be excluded from this post.  I was more interested in the single function multiple DLL technique. In summary we have an NSIS installer executable, that writes six files to the %TEMP% dir, the installer executes Sohozovi.exe, which import four functions from four separate DLLs, these DLLs are used to decode a data file, then from the DLL process space hollow out the executable process and then write an executable from the decoded data file to the executable process memory space. Interesting obfuscation technique.

Virustotal Results

Note:
If we were to follow out the process in debugging hollow processes  we would have set a breakpoint at the entrypoint of the newly started thread in the hollow process. During my debugging attempts the address contained zeroed out memory. The error looks to be caused by not writing the decoded executable to the address space of the parent process Sohozovi.exe. This error could be caused by anti-debugging or something else that I missed.

2 comments:

  1. I don't understand, why the NSIS installer and the multiple DLLs? The technique is not new at all but it's done in a complicated and inefficient way...

    Maybe it's on purpose, to defeat antivirus heuristics? Or maybe the programmer just didn't know what the hell he/she was doing :)

    ReplyDelete
    Replies
    1. "complicated and inefficient way" is the key there. The more complicated it is the less likely it will be unpacked or emulated.

      Delete