本帖最后由 itholl 于 2016-12-16 00:32 编辑
Unlocker工具是一款用于解锁被占用文件或模块的工具。比如说某个文件被其它进程打开,而造成无法删除或修改,某DLL被某进程载入而占用,造成文件无法删除、修改或更新。使用这个工具可以解锁该占用文件。通常这种情况都是由于,进程或者线程僵死,或者文件句柄未能及时关闭造成的。这个工具是装机的时候附带的,版本1.8.5, 上官网查了查,其最新版本1.9.1,新版本的实现原理和方式和1.8.5基本一致,只不过新版本增加了对文件的处理权限的操作(为所需要操作的文件对象获取administrator权限),主要是为了针对Vista和WIN7上的权限检查。本文的分析大都来自于1.8.5版本。我们将分析该软件的工作原理和功能实现方式,软件运行环境是Windows XP SP3。 该软件表面上只有一个文件,就是Unlocker1.8.5.EXE,
1.8.5 工作原理分析" style="border: none;"> 其实这是个RAR自解压包,运行之后会出来一个CMD窗口,可以选择安装和删除
1.8.5 工作原理分析" style="border: none;"> 我们可以把这个自解压包用rar打开,
1.8.5 工作原理分析" style="border: none;"> 发现文件其实是解压到C:\Program Files\Unlocker\下,并运行setup.bat脚本来安装的。 其中UnlockerCOM.dll用于注册右键功能的,这里不作研究; Unlocker.exe是主程序,核心功能在这里完成。 UnlockerDriver5.SYS是辅助内核程序,完成的功能只有一个,就是从文件句柄得到全局的一致文件名。 UnlockerHook.dll是一个钩子DLL,由于我这里安装的是绿色版,少了一个Unlockerasistent.EXE,所以这个DLL没有起作用。Unlockerasistent.EXE是一个托盘应用程序,负责加载UnlockerHook.dll,并按照用户的需求来加载和卸载钩子,当用户手动操作一个文件失败后(当然这种失败用户是感知不到的),比如删除一个文件等,此时Unlocker的界面就会跳出来提示用户是否需要解锁等操作。后面我们还会分析UnlockerHook.dll的功能。 好了,文件就这么多,我们分析的技术点包括如下内容: - UnlockerHook.dll的工作目的和原理,也就是它干了什么和怎么干的。
- UnlockerDriver5.SYS的工作原理和目的,这个比较简单。
- Unlocker.exe的工作原理,包括它是如何把文件名对应到锁定进程的,以及如何解锁的等。
好了,先看UnlockerHook.dll,再看Unlocker.exe,最后说说UnlockerDriver5.SYS。 - UnlockerHook.dll 工作原理
UnlockerHook.dll导出两个函数, HookInstall 以及HookUninstall,如下所示。
1.8.5 工作原理分析" style="border: none;">
但是从代码分析可以看出,这两个钩子并没有起什么作用,
.text:10001256 public HookInstall .text:10001256 HookInstall proc near .text:10001256 push 0 ; dwThreadId .text:10001258 push hModule ; hmod .text:1000125E push offset fn ; lpfn .text:10001263 push 5 ; idHook .text:10001265 call ds:SetWindowsHookExA .text:1000126B mov hhk, eax .text:10001270 retn .text:10001270 .text:10001270 HookInstall endp .text:10001270 .text:10001271 ; Exported entry 2. HookUninstall .text:10001271 public HookUninstall .text:10001271 HookUninstall proc near .text:10001271 push hhk ; hhk .text:10001277 call ds:UnhookWindowsHookEx .text:1000127D retn .text:1000127D .text:1000127D HookUninstall endp 只是负责安装和卸载WH_CBT钩子,对应ID号是5,hook函数的工作也很简单: .text:1000123B ; LRESULT __stdcall fn(int,WPARAM,LPARAM) .text:1000123B fn proc near ; DATA XREF: HookInstall+8o .text:1000123B nCode = dword ptr 4 .text:1000123B wParam = dword ptr 8 .text:1000123B lParam = dword ptr 0Ch .text:1000123B .text:1000123B push [esp+lParam] ; lParam .text:1000123F push [esp+4+wParam] ; wParam .text:10001243 push [esp+8+nCode] ; nCode .text:10001247 push hhk ; hhk .text:1000124D call ds:CallNextHookEx ; Pass the hook information to the .text:1000124D ; next hook procedure .text:10001253 retn 0Ch .text:10001253 fn endp 看到了吧,它什么也不做,只不过向下传递这个钩子信息而以。 那么核心工作在哪里呢?答案是在初始化的地方,钩子只不过是保证DLL能够每进程注入,关键是为了保证能注入explorer.exe。 .text:10001509 cmp [ebp+fdwReason], 1, 这个是指的进程ATTATCH的时候 .text:1000150D jnz short loc_10001514 …………. .text:10001515 push [ebp+lpReserved] ; int .text:10001518 push [ebp+fdwReason] ; int .text:1000151B push [ebp+hObject] ; hObject .text:1000151E call sub_100012E6 看看这个函数 代码比较长我们拣重点的说 .text:100012FB mov eax, [ebp+hObject] .text:100012FE push eax ; hLibModule .text:100012FF mov hModule, eax .text:10001304 call ds:DisableThreadLibraryCalls,THREADATTACH的信息不再处理,可以节省代码空间,提高效率,后面不说了。 .text:1000130A push offset word_10001100 ; lpString2 .text:1000130F lea eax, [ebp+String1] .text:10001315 push eax ; lpString1 .text:10001316 call ds:lstrcpyW .text:1000131C push 400h ; nSize .text:10001321 lea eax, [ebp+String1] .text:10001327 push eax ; lpFilename .text:10001328 xor ebx, ebx .text:1000132A push ebx ; hModule .text:1000132B call ds:GetModuleFileNameW .text:10001331 lea eax, [ebp+String1] .text:10001337 push eax ; lpString .text:10001338 call ds:lstrlenW .text:1000133E test eax, eax .text:10001340 jz loc_100014FD 得到包含本DLL的进程的文件名 .text:10001346 lea eax, [ebp+String1] .text:1000134C push eax ; pszPath .text:1000134D call ds:PathStripPathW .text:10001353 push offset s_Explorer_exe ; "explorer.exe" .text:10001358 lea eax, [ebp+String1] .text:1000135E push eax ; lpString1 .text:1000135F call ds:lstrcmpiW 看进程的文件名是否是explorer.exe,也就是桌面浏览器 .text:10001365 test eax, eax .text:10001367 jnz loc_100014FD 如果是浏览器就进行下面的操作: text:1000136D push offset ModuleName ; "Shell32.dll" .text:10001372 call ds:GetModuleHandleA .text:10001378 cmp eax, ebx .text:1000137A mov [ebp+hObject], eax .text:1000137D jz loc_100014FD .text:10001383 push offset ProcName ; "SHFileOperationW" .text:10001388 push eax ; hModule .text:10001389 call ds:GetProcAddress .text:1000138F cmp eax, ebx .text:10001391 mov lpBaseAddress, eax .text:10001396 jz loc_100014A9 得到explorer.exe进程空间里Shell32.dll里面SHFileOperationW函数的地址,这个函数是用来处理文件的删除,复制,剪贴工作等,具体可参考MSDN .text:1000139C lea eax, [ebp+flOldProtect] .text:1000139F push eax ; lpflOldProtect .text:100013A0 push 40h ; flNewProtect .text:100013A2 push 0Bh ; dwSize .text:100013A4 mov esi, offset unk_10002408 .text:100013A9 push esi ; lpAddress .text:100013AA call ds:VirtualProtect .text:100013B0 test eax, eax .text:100013B2 jz loc_100014A9 下面的工作就是要 inlinehook这个函数,具体的过程比较长,方法就是修改函数的前5个字节(内部还要判断,因为不同版本的shell32.DLL的这个函数的实现代码还有差异),然后该成跳转到UnlockerHook.DLL内部的一个函数,这个函数接管这个调用,并判断调用返回值,如果成功就不做处理并直接返回,否则通过ShellExecuteEx函数来启动Unlocker.exe,并把原调用里的文件名作为参数传递给它。这也就是为什么,有时用户手动操作文件时会弹出Unlocker的运行界面的原因。 这里面有一些inlinehook的技术细节在这里提一下,改写前,SHFileOperationW的前6字节是这样:
1.8.5 工作原理分析" style="border: none;"> 这6字节被读取出来并缓存到10002408地址处,后面还要用:
1.8.5 工作原理分析" style="border: none;"> 下面就是判断版本并做相应修改的过程,依据上面的数据,我把程序的路径用红色标出: 100013D2 |. A0 0B240010 mov al, [1000240B] 100013D7 |. 3C 83 cmp al, 83 100013D9 |. 75 31 jnz short 1000140C 100013DB |. 803D 0C240010>cmp byte ptr [1000240C], 0EC 100013E2 |. 0F85 CC000000 jnz 100014B4 100013E8 |. A1 20240010 mov eax, [10002420] 100013ED |. 2D 13240010 sub eax, 10002413 100013F2 |. 83C0 06 add eax, 6 100013F5 |. 8945 0C mov [ebp+C], eax 100013F8 |. 6A 04 push 4 100013FA |. 8D45 0C lea eax, [ebp+C] 100013FD |. 50 push eax 100013FE |. C605 0E240010>mov byte ptr [1000240E], 0E9 10001405 |. 68 0F240010 push 1000240F 1000140A |. EB 42 jmp short 1000144E 1000140C |> 3C 8B cmp al, 8B 1000140E |. 8A0D 0C240010 mov cl, [1000240C] 10001414 |. 75 05 jnz short 1000141B 10001416 |. 80F9 EC cmp cl, 0EC 10001419 |. 74 11 je short 1000142C 1000141B |> 3C 53 cmp al, 53 1000141D |. 0F85 91000000 jnz 100014B4 10001423 |. 80F9 55 cmp cl, 55 10001426 |. 0F85 88000000 jnz 100014B4 1000142C |> A1 20240010 mov eax, [10002420];这里存放的是SHFileOperationW的地址 10001431 |. 2D 12240010 sub eax, 10002412 10001436 |. 83C0 05 add eax, 5; 这两步是用来计算jmp的偏移量,加5是因为我们要跳过前面已经覆盖掉的5字节 10001439 |. 8945 0C mov [ebp+C], eax 1000143C |. 6A 04 push 4 1000143E |. 8D45 0C lea eax, [ebp+C] 10001441 |. 50 push eax //上面计算的偏移压栈 10001442 |. C605 0D240010>mov byte ptr [1000240D], 0E9; 这个就是jmp指令的字节码 10001449 |. 68 0E240010 push 1000240E刚好是要填写jmp指令后面那个地址的位置 1000144E |> E8 2BFEFFFF call 1000127E 这个函数就是把上面的4字节填入jmp指令后面,修改后的字节码是:
1.8.5 工作原理分析" style="border: none;"> 也就是:
1.8.5 工作原理分析" style="border: none;"> 目的地址刚好就是原函数中5字节后的那条指令(可参看前面的截图)。 紧接着下面: 10001453 |. 83C4 0C add esp, 0C 10001456 |. 6A 0B push 0B ; /RegionSize = B 10001458 |. 56 push esi ; |RegionBase 10001459 |.8B35 1C100010 mov esi, [<&KERNEL32.FlushInstructionCa>; |kernel32.FlushInstructionCache 1000145F |. 57 push edi ; |hProcess 10001460 |. FFD6 call esi ; \FlushInstructionCache 因为前面修改了内存字节的内容,所以这里清除指令缓存以防万一 10001462 |. B8 02110010 mov eax, 10001102 10001467 |. 2B05 20240010 sub eax, [10002420] ; SHELL32.SHFileOperationW 1000146D |. 6A 04 push 4 1000146F |. 83E8 05 sub eax, 5 这几句是为了计算从SHFileOperationW跳转到unlockerhook.DLL内部函数的偏移 10001472 |. 8945 0C mov [ebp+C], eax 10001475 |. 8D45 0C lea eax, [ebp+C] 10001478 |. 50 push eax 10001479 |. 8D45 F9 lea eax, [ebp-7] 1000147C |. 50 push eax 1000147D |. C645 F8 E9 mov byte ptr [ebp-8], 0E9 10001481 |. E8 F8FDFFFF call 1000127E 同样的,上面的代码也是些跳转指令的字节码,同样的函数call 1000127E。 也就是说SHFileOperationW调用被修改后,将会先跳转到10001102这个地方来执行。 10001489 |. 53 push ebx ; /pBytesWritten 1000148A |. 6A 05 push 5 ; |BytesToWrite = 5 1000148C |. 8D45 F8 lea eax, [ebp-8] ; | 1000148F |. 50 push eax ; |Buffer = 0006F678 10001490 |. FF35 20240010 push dword ptr [10002420] ; |Address = 7D640924 10001496 |. 57 push edi ; |hProcess 10001497 |. FF15 18100010 call [<&KERNEL32.WriteProcessMemory>] ; \WriteProcessMemory 通过WriteProcessMemory来改写SHFileOperationW函数的前5字节,7D640924就是函数的首地址。 修改后的代码变成了:
1.8.5 工作原理分析" style="border: none;"> 1000149D |. 6A 05 push 5 1000149F |. FF35 20240010 push dword ptr [10002420] SHELL32.SHFileOperationW 100014A5 |. 57 push edi 100014A6 |. FFD6 call esi kernel32.FlushInstructionCache 接下来这几句是必须的,因为SHFileOperationW已经改变,要刷新指令缓存,长度5字节。 好了,10001102处的代码到底是什么呢? .text:10001102 sub_10001102 proc near ; DATA XREF: sub_100012E6+17Co .text:10001102 var_183C = word ptr -183Ch .text:10001102 var_103C = word ptr -103Ch .text:10001102 pszPath = word ptr -83Ch .text:10001102 ExecInfo = _SHELLEXECUTEINFOW ptr -3Ch .text:10001102 arg_0 = dword ptr 8 .text:10001102 push ebp .text:10001103 mov ebp, esp .text:10001105 sub esp, 183Ch .text:1000110B push ebx .text:1000110C mov ebx, [ebp+arg_0] .text:1000110F push ebx .text:10001110 mov eax, offset unk_10002408 .text:10001115 call eax ; unk_10002408 请回看10002408处的代码,作用就是建立栈帧并跳转到原SHFileOperationW的内部 7D640929的位置,紧接着那条插入的jmp指令。也就是说我们在10001102处转了个弯又回到了原函数内部。注意这里用的是call指令,就是说原函数执行完后还回到这里,方便我们判断结果,同时由于我们保留了原函数的代码,所以栈帧的一致性是有保证,这一点尤其重要。 .text:10001117 cmp eax, 5, 判断原函数返回值 .text:1000111A mov [ebp+arg_0], eax .text:1000111D jz short loc_10001128 .text:1000111F cmp eax, 20h,返回值是20h的时候才忽略,不用触发 .text:10001122 jnz loc_100011E8 .text:10001122 .text:10001128 下面就是通过不同的错误值来判断是否需要触发 .text:10001128 loc_10001128: ; CODE XREF: sub_10001102+1Bj .text:10001128 call ds:GetLastError .text:1000112E mov ecx, [ebx+4] .text:10001131 cmp ecx, 1 .text:10001134 jz short loc_10001148 .text:10001134 .text:10001136 cmp ecx, 2 .text:10001139 jbe loc_100011E8 .text:10001139 .text:1000113F cmp ecx, 4 .text:10001142 ja loc_100011E8 .text:10001148 .text:10001148 loc_10001148: ; CODE XREF: sub_10001102+32j .text:10001148 cmp eax, 6 .text:1000114B jz short loc_10001155 .text:1000114B .text:1000114D test eax, eax .text:1000114F jnz loc_100011E8 .text:10001155 好了下面就是触发代码 .text:10001155 loc_10001155: ; CODE XREF: sub_10001102+49j .text:10001155 push esi .text:10001156 push edi .text:10001157 push 0Eh .text:10001159 pop ecx .text:1000115A xor eax, eax .text:1000115C lea edi, [ebp+ExecInfo.fMask] .text:1000115F rep stosd .text:10001161 push 400h ; nSize .text:10001166 lea eax, [ebp+pszPath] .text:1000116C push eax ; lpFilename .text:1000116D push hModule ; hModule .text:10001173 mov [ebp+ExecInfo.cbSize], 3Ch .text:1000117A mov [ebp+ExecInfo.lpVerb], offset s_Open ; "open" .text:10001181 call ds:GetModuleFileNameW .text:10001187 lea eax, [ebp+pszPath] .text:1000118D push eax ; pszPath .text:1000118E call ds:PathRemoveFileSpecW .text:10001194 mov esi, ds:wsprintfW .text:1000119A lea eax, [ebp+pszPath] .text:100011A0 push eax .text:100011A1 lea eax, [ebp+var_103C] .text:100011A7 push offset s_SUnlocker_exe ; ""%s\\Unlocker.exe"" .text:100011AC push eax ; LPWSTR .text:100011AD call esi ; wsprintfW .text:100011AF push dword ptr [ebx+8] .text:100011B2 lea eax, [ebp+var_103C] .text:100011B8 mov [ebp+ExecInfo.lpFile], eax .text:100011BB lea eax, [ebp+var_183C] .text:100011C1 push offset s_S ; ""%s"" .text:100011C6 push eax ; LPWSTR .text:100011C7 call esi ; wsprintfW .text:100011C9 lea eax, [ebp+var_183C] .text:100011CF mov [ebp+ExecInfo.lpParameters], eax,把目标文件作为参数传入 .text:100011D2 add esp, 18h .text:100011D5 lea eax, [ebp+ExecInfo] .text:100011D8 push eax ; lpExecInfo .text:100011D9 mov [ebp+ExecInfo.nShow], 1 .text:100011E0 call ds:ShellExecuteExW 通过这个来触发unlocker.EXE .text:100011E6 pop edi .text:100011E7 pop esi .text:100011E8 .text:100011E8 loc_100011E8: ; CODE XREF: sub_10001102+20j .text:100011E8 mov eax, [ebp+arg_0] .text:100011EB pop ebx .text:100011EC leave .text:100011ED retn 4 .text:100011ED sub_10001102 endp 这只是钩子加载的时候的代码,当钩子卸载的时候(UnhookWindowsHookEx在HookUninstall里被调用的时候,这个调用由用户指示unlockerasistent.exe托盘程序触发),DLL会被卸载,此时还必须还原被修改的函数代码,否则就会出错,因为我们的DLL已经卸载了,那个插入的跳转指令会要了explorer的命。 卸载的代码在这里: text:100014C2 loc_100014C2: ; CODE XREF: sub_100012E6+Fj .text:100014C2 xor ebx, ebx .text:100014C4 cmp [ebp+arg_4], ebx .text:100014C7 jnz short loc_100014FD .text:100014C7 .text:100014C9 cmp lpBaseAddress, ebx .text:100014CF jz short loc_100014FD 因为每个进程都会卸载自己的那份DLL,而只有explorer里的那份要特别处理,所以这里要判断一下 .text:100014D1 call ds:GetCurrentProcess .text:100014D7 push ebx ; lpNumberOfBytesWritten .text:100014D8 push 5 ; nSize .text:100014DA push offset unk_10002408 ; lpBuffer .text:100014DF push lpBaseAddress ; lpBaseAddress .text:100014E5 mov esi, eax .text:100014E7 push esi ; hProcess .text:100014E8 call ds:WriteProcessMemory .text:100014EE push 5 ; dwSize .text:100014F0 push lpBaseAddress ; lpBaseAddress .text:100014F6 push esi ; hProcess .text:100014F7 call ds:FlushInstructionCache 我们只需要把10002408处的5字节写回去就可以了,因为这里的5字节就是原先读出来的原数据。 卸载代码包含在同一个函数sub_100012E6里,通过外部传入的参数fdwReason来判断是加载还是卸载。 好了UnlockerHook.dll的代码就分析到这里,为了把问题说清楚还是贴了不少的汇编代码,真是占篇幅啊。总结一下UnlockerHook.dll的功能就是通过CBT钩子来植入每个进程的方式来打入exploer.exe,通过inlinehook SHFileOperationW的方式来挂钩,只要该函数执行返回错误,通过识别不同的错误码来判断是否需要调用unlocker程序,并通过ShellExecuteExW的方式来触发unlocker程序。 说了半天我们还是没有触及unlocker的核心功能,下面我们就来分析。 对于该程序的工作原理,这里先概述一下,首先通过ZwQuerySystemInformation函数得到系统内所有的句柄信息以及其所在的进程信息,然后通过NtLoaddriver来加载驱动(UnlokerDriver5.sys),并写入注册表信息等,使用驱动在内核层得到句柄的名字,并和目标文件命核对,最终定位到占有该句柄,也就是占有该文件的进程;而进程的名字是通过toolhelp类函数,并辅以EnumProcessModules来得到(枚举的第一个模块句柄就是进程自身,再通过GetModuleFileNameEx来得到全路径名)。 好了,只要找到了占用文件的进程,事情就好办了。对于"过程结束"命令,就是使用TerminateProcess来干掉进程。对于解锁,分为两类,如果是DLL文件,则通过远程线程注入来FreeLibrary而解锁文件;否则更简单,通过Duplicatehandle,使用参数DUPLICATE_CLOSE_SOURCE来关闭目标进程里的句柄,这样文件就解锁了。对于拷贝的操作,则是通过远程线程注入,在目标进程里读写文件来完成的。值得一提的是,这种方式是有问题的,因为远程线程和本地线程使用同一个文件句柄操作文件,这样文件指针就会乱掉,虽然远程线程使用OVERLAPPED结构来指定文件偏移避免了拷贝数据乱的情况,从而保证了自己的拷贝工作能顺利完成,但是本地线程的指针是肯定要乱掉的,这对本地线程是致命的!! 移动文件是通过MoveFileEx来实现,如果不能成功则会提示是否在下次启动系统后移动,这是通过MOVEFILE_DELAY_UNTIL_REBOOT标志来实现的,其它文件操作则是通过SHFileOperationW来实现的。 下面来看具体的代码: 0040F94B |. 68 48CB4000 push 0040CB48 ; /FileName = "ntdll.dll" 0040F950 |. 8BF1 mov esi, ecx ; | 0040F952 |. FF15 74104000 call [<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA 0040F958 |. 8BD8 mov ebx, eax 0040F95A |. 85DB test ebx, ebx 0040F95C |. 74 59 je short 0040F9B7 0040F95E |. 57 push edi 0040F95F |. 8B3D 70104000 mov edi, [<&KERNEL32.GetProcAddress>] ; kernel32.GetProcAddress 0040F965 |. 68 2CCB4000 push 0040CB2C ; /ProcNameOrOrdinal = "ZwQuerySystemInformation" 0040F96A |. 53 push ebx ; |hModule 0040F96B |. FFD7 call edi ; \GetProcAddress 0040F96D |. 68 1CCB4000 push 0040CB1C ; /ProcNameOrOrdinal = "ZwQueryObject" 0040F972 |. 53 push ebx ; |hModule 0040F973 |. 8906 mov [esi], eax ; | 0040F975 |. FFD7 call edi ; \GetProcAddress 0040F977 |. 68 0CCB4000 push 0040CB0C ; /ProcNameOrOrdinal = "ZwDeleteFile" 0040F97C |. 53 push ebx ; |hModule 0040F97D |. 8946 04 mov [esi+4], eax ; | 0040F980 |. FFD7 call edi ; \GetProcAddress 0040F982 |. 68 F4CA4000 push 0040CAF4 ; /ProcNameOrOrdinal = "RtlInitUnicodeString" 0040F987 |. 53 push ebx ; |hModule 0040F988 |. 8946 08 mov [esi+8], eax ; | 0040F98B |. FFD7 call edi ; \GetProcAddress 0040F98D |. 68 E0CA4000 push 0040CAE0 ; /ProcNameOrOrdinal = "RtlAdjustPrivilege" 0040F992 |. 53 push ebx ; |hModule 0040F993 |. 8946 0C mov [esi+C], eax ; | 0040F996 |. FFD7 call edi ; \GetProcAddress 0040F998 |. 68 D0CA4000 push 0040CAD0 ; /ProcNameOrOrdinal = "NtLoadDriver" 0040F99D |. 53 push ebx ; |hModule 0040F99E |. 8946 10 mov [esi+10], eax ; | 0040F9A1 |. FFD7 call edi ; \GetProcAddress 0040F9A3 |. 68 C0CA4000 push 0040CAC0 ; /ProcNameOrOrdinal = "NtUnloadDriver" 0040F9A8 |. 53 push ebx ; |hModule 0040F9A9 |. 8946 14 mov [esi+14], eax ; | 0040F9AC |. FFD7 call edi ; \GetProcAddress 首先通过动态载入ntdll来得到需要使用的函数的地址,这些函数都是未公开的。我们拣重要的说。 然后通过RtlAdjustPrivilege来调整特权并通过NtLoadDriver来加载驱动,具体代码就不列举了。 004131CD |. 56 push esi ; /hTemplateFile 004131CE |. BF 00000002 mov edi, 2000000 ; | 004131D3 |. 57 push edi ; |Attributes => BACKUP_SEMANTICS 004131D4 |. 6A 03 push 3 ; |Mode = OPEN_EXISTING 004131D6 |. 56 push esi ; |pSecurity 004131D7 |. 8B35 0C114000 mov esi, [<&KERNEL32.CreateFileA>] ; |kernel32.CreateFileA 004131DD |. 6A 03 push 3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE 004131DF |. 68 000000C0 push C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE 004131E4 |. 8D85 E4FEFFFF lea eax, [ebp-11C] ; | 004131EA |. 50 push eax ; |FileName "\\.\C:" 004131EB |. FFD6 call esi ; \CreateFileA 打开C盘,并通过ZwQueryObject来得到其全局一致名字: 00413214 |. 8D4D E4 lea ecx, [ebp-1C] 00413217 |. 51 push ecx 00413218 |. 68 00040000 push 400 0041321D |. 8D8D E4EAFFFF lea ecx, [ebp-151C] 00413223 |. 51 push ecx 00413224 |. 6A 01 push 1 //这个表示ObjectNameInformation 00413226 |. FF75 F8 push dword ptr [ebp-8] 00413229 |. FF50 04 call [eax+4] ; ntdll.ZwQueryObject 返回值是一个UNICODE_STRING Name,可以从中得到比如 "\Device\HarddiskVolume1"。 00411948 |. 8945 E4 mov [ebp-1C], eax 0041194B |. E8 6CE0FFFF call 0040F9BC 00411950 |. 8D4D D8 lea ecx, [ebp-28] 00411953 |. 51 push ecx 00411954 |. 8B0D 24504100 mov ecx, [415024] 0041195A |. C1E1 02 shl ecx, 2 0041195D |. 51 push ecx 0041195E |. FF75 E4 push dword ptr [ebp-1C] 00411961 |. 6A 10 push 10 ;这个表示句柄信息 00411963 |. FF10 call [eax] ; ntdll.ZwQuerySystemInformation 通过ZwQuerySystemInformation来得到系统内全部的句柄信息。 004119B8 |. 53 push ebx ; /ProcessID = 0 004119B9 |. 83C0 04 add eax, 4 ; | 004119BC |. 6A 02 push 2 ; |Flags = TH32CS_SNAPPROCESS 004119BE |. 8945 F0 mov [ebp-10], eax ; | 004119C1 |. 891D E4A54100 mov [41A5E4], ebx ; | 004119C7 |. 891D 04A64100 mov [41A604], ebx ; | 004119CD |. E8 68230000 call <jmp.&KERNEL32.CreateToolhelp32Snapshot> ; 通过CreateToolhelp32Snapshot来准备枚举系统内的所有进程。 00411BED |. FFB5 70F9FFFF |push dword ptr [ebp-690] ; /ProcessID = 0 00411BF3 |. 6A 08 |push 8 ; |Flags = TH32CS_SNAPMODULE 00411BF5 |. E8 40210000 |call <jmp.&KERNEL32.CreateToolhelp32Snapshot> ; 对枚举到的每个进程使用CreateToolhelp32Snapshot来准备枚举每个模块(EXE,DLL等). 00411C1A |. E8 0F210000 |call <jmp.&KERNEL32.Module32FirstW> 00411C1F |. 85C0 |test eax, eax 00411C21 |. 75 06 |jnz short 00411C29 00411C23 |. 56 |push esi 00411C24 |. E9 C0010000 |jmp 00411DE9 00411C29 |> 8B35 78104000 |/mov esi, [<&KERNEL32.lstrcpyW>] ; kernel32.lstrcpyW 00411C2F |. 8D85 B4FDFFFF ||lea eax, [ebp-24C] 00411C35 |. 50 ||push eax ; /String2 00411C36 |. 8D85 68E9FFFF ||lea eax, [ebp-1698] ; | 00411C3C |. 50 ||push eax ; |String1 00411C3D |. FFD6 ||call esi ; \lstrcpyW 00411C3F |. 8D85 68E9FFFF ||lea eax, [ebp-1698] 00411C45 |. 50 ||push eax ; /StringOrChar 00411C46 |. FF15 9C114000 ||call [<&USER32.CharUpperW>] ; \CharUpperW 00411C4C |. 8D85 B4FDFFFF ||lea eax, [ebp-24C] 00411C52 |. 68 88CD4000 ||push 0040CD88 ; UNICODE ".DLL" 00411C57 |. 50 ||push eax 00411C58 |. E8 9ADEFFFF ||call 0040FAF7 对每个模块,确定其是不是DLL,对应DLL要单独进行标注。 00411E0F |. E8 FCE5FFFF call 00410410,该函数通过判断OS系统版本来确定文件句柄的类型代码 代码中红色的部分就是具体的句柄类型代码 .text:00410410 sub_410410 proc near ; CODE XREF: sub_411929+4E6p .text:00410410 VersionInformation= _OSVERSIONINFOA ptr -94h .text:00410410 .text:00410410 push ebp .text:00410411 lea ebp, [esp-78h] .text:00410415 sub esp, 94h .text:0041041B and [ebp+78h+VersionInformation.dwMajorVersion], 0 .text:0041041F push esi .text:00410420 push edi .text:00410421 push 23h .text:00410423 pop ecx .text:00410424 xor eax, eax .text:00410426 mov [ebp+78h+VersionInformation.dwOSVersionInfoSize], 94h .text:0041042D lea edi, [ebp+78h+VersionInformation.dwMinorVersion] .text:00410430 rep stosd .text:00410432 lea eax, [ebp+78h+VersionInformation] .text:00410435 push eax ; lpVersionInformation .text:00410436 or esi, 0FFFFFFFFh .text:00410439 call ds:GetVersionExA ; Get extended information about the .text:00410439 ; version of the operating system .text:0041043F test eax, eax .text:00410441 jz short loc_410478 .text:00410441 .text:00410443 movzx eax, word ptr [ebp+78h+VersionInformation.dwMinorVersion] .text:00410447 movzx ecx, word ptr [ebp+78h+VersionInformation.dwMajorVersion] .text:0041044B shl eax, 10h .text:0041044E or eax, ecx .text:00410450 sub eax, 4 .text:00410453 jz short loc_410475 .text:00410455 dec eax .text:00410456 jz short loc_410471 .text:00410458 dec eax .text:00410459 jz short loc_41046D .text:0041045B sub eax, 0FFFFh .text:00410460 jz short loc_410469 .text:00410462 sub eax, 10000h .text:00410467 jnz short loc_410478 .text:00410469 .text:00410469 loc_410469: ; CODE XREF: sub_410410+50j .text:00410469 push 1Ch .text:0041046B jmp short loc_410477 .text:0041046D loc_41046D: ; CODE XREF: sub_410410+49j .text:0041046D push 24h .text:0041046F jmp short loc_410477 .text:00410471 loc_410471: ; CODE XREF: sub_410410+46j .text:00410471 push 1Ah .text:00410473 jmp short loc_410477 .text:00410475 loc_410475: ; CODE XREF: sub_410410+43j .text:00410475 push 17h .text:00410477 pop esi .text:00410478 pop edi .text:00410479 mov eax, esi .text:0041047B pop esi .text:0041047C add ebp, 78h .text:0041047F leave .text:00410480 retn 下面的代码就是从目标进程复制每个文件句柄,然后送入驱动进行名字解析。 00411E23 |> /8B75 F0 /mov esi, [ebp-10] 00411E26 |. |0FB646 04 |movzx eax, byte ptr [esi+4],这个就是句柄类型代码 00411E2A |. |3B45 C8 |cmp eax, [ebp-38],比较是不是文件句柄 00411E2D |. |0F85 D2030000 |jnz 00412205 00411E33 |. |FF36 |push dword ptr [esi] ; /ProcessId 00411E35 |. |8B3D F4104000 |mov edi, [<&KERNEL32.OpenProcess>] ; |kernel32.OpenProcess 00411E3B |. |53 |push ebx ; |Inheritable 00411E3C |. |68 50040000 |push 450 ; |Access = VM_READ|DUP_HANDLE|QUERY_INFORMATION,这个权限比较多,如果不成功下面还有一次机会 00411E41 |. |FFD7 |call edi ; \OpenProcess 00411E43 |. |3BC3 |cmp eax, ebx 00411E45 |. |8945 FC |mov [ebp-4], eax 00411E48 |. |75 12 |jnz short 00411E5C 00411E4A |. |FF36 |push dword ptr [esi] ; /ProcessId 00411E4C |. |53 |push ebx ; |Inheritable 00411E4D |. |6A 40 |push 40 ; |Access = DUP_HANDLE,这个权限少 00411E4F |. |FFD7 |call edi ; \OpenProcess 00411E51 |. |3BC3 |cmp eax, ebx 00411E53 |. |8945 FC |mov [ebp-4], eax 00411E56 |. |0F84 A9030000 |je 00412205 00411E5C |> |53 |push ebx ; /Options 00411E5D |. |53 |push ebx ; |Inheritable 00411E5E |. |53 |push ebx ; |Access 00411E5F |. |8D45 EC |lea eax, [ebp-14] ; | 00411E62 |. |50 |push eax ; |phTarget 00411E63 |. |FF15 6C104000 |call [<&KERNEL32.GetCurrentProcess>] ; |[GetCurrentProcess 00411E69 |. |50 |push eax ; |hTargetProcess 00411E6A |. |0FB746 06 |movzx eax, word ptr [esi+6] ; | 00411E6E |. |50 |push eax ; |hSource 00411E6F |. |FF75 FC |push dword ptr [ebp-4] ; |hSourceProcess 00411E72 |. |FF15 F8104000 |call [<&KERNEL32.DuplicateHandle>] ; \DuplicateHandle, 复制句柄 00411E78 |. |85C0 |test eax, eax 00411E7A |. |0F84 7C030000 |je 004121FC 00411E80 |. |53 |push ebx ; /hTemplateFile 00411E81 |. |53 |push ebx ; |Attributes 00411E82 |. |6A 03 |push 3 ; |Mode = OPEN_EXISTING 00411E84 |. |53 |push ebx ; |pSecurity 00411E85 |. |53 |push ebx ; |ShareMode 00411E86 |. |68 000000C0 |push C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE 00411E8B |. |68 F4CD4000 |push 0040CDF4 ; |FileName = "\\?\UnlockerDriver5" 00411E90 |. |FF15 84104000 |call [<&KERNEL32.CreateFileW>] ; \CreateFileW 打开驱动,并写入将要解析的句柄 00411E96 |. |3BC3 |cmp eax, ebx 00411E98 |. |8945 D0 |mov [ebp-30], eax 00411E9B |. |0F84 52030000 |je 004121F3 00411EA1 |. |8B4D EC |mov ecx, [ebp-14] 00411EA4 |. |894D C0 |mov [ebp-40], ecx 00411EA7 |. |8B4E 08 |mov ecx, [esi+8] 00411EAA |. |53 |push ebx ; /pOverlapped 00411EAB |. |894D C4 |mov [ebp-3C], ecx ; | 00411EAE |. |8D4D CC |lea ecx, [ebp-34] ; | 00411EB1 |. |51 |push ecx ; |pBytesWritten 00411EB2 |. |6A 08 |push 8 ; |nBytesToWrite = 8 00411EB4 |. |8D4D C0 |lea ecx, [ebp-40] ; | 00411EB7 |. |51 |push ecx ; |Buffer 00411EB8 |. |50 |push eax ; |hFile 00411EB9 |. |FF15 88104000 |call [<&KERNEL32.WriteFile>] ; \WriteFile 00411EBF |. |33C0 |xor eax, eax 00411EC1 |. |889D BCFBFFFF |mov [ebp-444], bl 00411EC7 |. |B9 FF000000 |mov ecx, 0FF 00411ECC |. |8DBD BDFBFFFF |lea edi, [ebp-443] 00411ED2 |. |F3:AB |rep stos dword ptr es:[edi] 00411ED4 |. |66:AB |stos word ptr es:[edi] 00411ED6 |. |53 |push ebx ; /pOverlapped 00411ED7 |. |AA |stos byte ptr es:[edi] ; | 00411ED8 |. |8D45 CC |lea eax, [ebp-34] ; | 00411EDB |. |50 |push eax ; |pBytesRead 00411EDC |. |68 00040000 |push 400 ; |BytesToRead = 400 (1024.) 00411EE1 |. |8D85 BCFBFFFF |lea eax, [ebp-444] ; | 00411EE7 |. |50 |push eax ; |Buffer 00411EE8 |. |FF75 D0 |push dword ptr [ebp-30] ; |hFile 00411EEB |. |FF15 80104000 |call [<&KERNEL32.ReadFile>] ; \ReadFile 送入的部分包括文件句柄,以及对应的object指针,送入指针的目的是为了在驱动内部进行核对。 读出时,就是句柄对应文件的全局一致名称。 00411F17 |> \FF75 0C |push dword ptr [ebp+C] ; /Pattern = "\Device\HarddiskVolume1" 00411F1A |. 66:899D 68F1F>|mov [ebp-E98], bx ; | 00411F21 |. FFB5 C0FBFFFF |push dword ptr [ebp-440] ; |String 00411F27 |. 33C0 |xor eax, eax ; | 00411F29 |. B9 FF010000 |mov ecx, 1FF ; | 00411F2E |. 8DBD 6AF1FFFF |lea edi, [ebp-E96] ; | 00411F34 |. F3:AB |rep stos dword ptr es:[edi] ; | 00411F36 |. 66:AB |stos word ptr es:[edi] ; | 00411F38 |. FF15 58114000 |call [<&SHLWAPI.StrStrW>] ; \StrStrW 通过模式查询来对比文件名称,然后再转换成通常所见的"C:\..."的模式,并最终确定文件名的一致性。找到之后通过如下代码确定进程的名字。 0041206B |. 8D45 BC ||lea eax, [ebp-44] 0041206E |. 50 ||push eax 0041206F |. 6A 04 ||push 4 00412071 |. 8D45 D4 ||lea eax, [ebp-2C] ,模块句柄列表首地址 00412074 |. 50 ||push eax 00412075 |. FF75 FC ||push dword ptr [ebp-4] 00412078 |. E8 751C0000 ||call <jmp.&PSAPI.EnumProcessModules> 0041207D |. 85C0 ||test eax, eax 0041207F |. 74 2D ||je short 004120AE 00412081 |. BE 00040000 ||mov esi, 400 00412086 |. 56 ||push esi 00412087 |. 8D85 54B9FFFF ||lea eax, [ebp+FFFFB954] 0041208D |. 50 ||push eax 0041208E |. FF75 D4 ||push dword ptr [ebp-2C],模块句柄列表的第一个 00412091 |. FF75 FC ||push dword ptr [ebp-4] 00412094 |. E8 531C0000 ||call <jmp.&PSAPI.GetModuleBaseNameW> 00412099 |. 56 ||push esi 0041209A |. 8D85 68E9FFFF ||lea eax, [ebp-1698] 004120A0 |. 50 ||push eax 004120A1 |. FF75 D4 ||push dword ptr [ebp-2C] 004120A4 |. FF75 FC ||push dword ptr [ebp-4] 004120A7 |. E8 3A1C0000 ||call <jmp.&PSAPI.GetModuleFileNameExW> 004120AC |. EB 57 ||jmp short 00412105 得到进程全路径名之后,就是出对话框了, 00413455 |. 53 |push ebx 00413456 |. 68 48234100 |push 00412348 0041345B |. 53 |push ebx 0041345C |. 6A 65 |push 65 0041345E |. 53 |push ebx 0041345F |. C645 F6 01 |mov byte ptr [ebp-A], 1 00413463 |. FFD7 |call edi 00413465 |. 50 |push eax 00413466 |. FFD6 |call esi ; USER32.DialogBoxParamA
1.8.5 工作原理分析" style="border: none;"> 下面看解锁的代码: 004118D2 |. FF36 |push dword ptr [esi] ; /ProcessId 004118D4 |. 53 |push ebx ; |Inheritable 004118D5 |. 6A 40 |push 40 ; |Access = DUP_HANDLE 004118D7 |. FF15 F4104000 |call [<&KERNEL32.OpenProcess>] ; \OpenProcess 004118DD |. 8BF8 |mov edi, eax 004118DF |. 3BFB |cmp edi, ebx 004118E1 |. 74 2F |je short 00411912 004118E3 |. 6A 01 |push 1 ; /Options = DUPLICATE_CLOSE_SOURCE 004118E5 |. 53 |push ebx ; |Inheritable 004118E6 |. 53 |push ebx ; |Access 004118E7 |. 8D45 E8 |lea eax, [ebp-18] ; | 004118EA |. 50 |push eax ; |phTarget 004118EB |. FF15 6C104000 |call [<&KERNEL32.GetCurrentProcess>; |[GetCurrentProcess 004118F1 |. 50 |push eax ; |hTargetProcess 004118F2 |. 0FB746 06 |movzx eax, word ptr [esi+6] ; | 004118F6 |. 50 |push eax ; |hSource 004118F7 |. 57 |push edi ; |hSourceProcess 004118F8 |. FF15 F8104000 |call [<&KERNEL32.DuplicateHandle>] ; \DuplicateHandle 正如前面说到的,就是通过DuplicateHandle来解锁。 终止进程的代码: 004116C4 |. FF7408 04 |push dword ptr [eax+ecx+4] 004116C8 |> 53 |push ebx ; |Inheritable 004116C9 |. 6A 01 |push 1 ; |Access = TERMINATE 004116CB |. FF15 F4104000 |call [<&KERNEL32.OpenProcess>] ; \OpenProcess 004116D1 |. 8BF0 |mov esi, eax 004116D3 |. 3BF3 |cmp esi, ebx 004116D5 |. 0F84 37020000 |je 00411912 004116DB |. 53 |push ebx ; /ExitCode 004116DC |. 56 |push esi ; |hProcess 004116DD |. FF15 FC104000 |call [<&KERNEL32.TerminateProcess>] ; \TerminateProcess 004116E3 |. 56 |push esi 正是使用TerminateProcess. 解锁DLL的代码: 00411758 |. 6A 0E |push 0E ; | 0041175A |. 59 |pop ecx ; | 0041175B |. 33C0 |xor eax, eax ; | 0041175D |. 8D7D B0 |lea edi, [ebp-50] ; | 00411760 |. F3:AB |rep stos dword ptr es:[edi] ; | 00411762 |. 8D85 A0EFFFFF |lea eax, [ebp-1060] ; | 00411768 |. 50 |push eax ; |</s> 00411769 |. 8D85 A0E7FFFF |lea eax, [ebp-1860] ; | 0041176F |. 68 70CD4000 |push 0040CD70 ; |Format = "/s /u ""%s""" 00411774 |. 50 |push eax ; |s 00411775 |. C745 AC 3C000>|mov dword ptr [ebp-54], 3C ; | 0041177C |. C745 B0 00000>|mov dword ptr [ebp-50], 2000000 ; | 00411783 |. C745 B8 64CD4>|mov dword ptr [ebp-48], 0040CD6>; |UNICODE "open" 0041178A |. C745 BC 48CD4>|mov dword ptr [ebp-44], 0040CD4>; |UNICODE "regsvr32.exe" 00411791 |. FF15 08124000 |call [<&USER32.wsprintfW>] ; \wsprintfW 00411797 |. 8D85 A0E7FFFF |lea eax, [ebp-1860] 0041179D |. 8945 C0 |mov [ebp-40], eax 004117A0 |. 83C4 0C |add esp, 0C 004117A3 |. 8D45 AC |lea eax, [ebp-54] 004117A6 |. 50 |push eax 004117A7 |. 895D C8 |mov [ebp-38], ebx 004117AA |. FF15 40114000 |call [<&SHELL32.ShellExecuteExW>>; SHELL32.ShellExecuteExW 004117B0 |. 8B3D F0104000 |mov edi, [<&KERNEL32.VirtualAll>; kernel32.VirtualAllocEx 首先通过regsvr32.exe来取消该DLL的服务。然后通过远程线程来关闭DLL。 004117B0 |. 8B3D F0104000 |mov edi, [<&KERNEL32.VirtualAllocEx>] ; 004117B6 |. 6A 40 |push 40 004117B8 |. 68 00100000 |push 1000 004117BD |. BE BB044100 |mov esi, 004104BB 004117C2 |. 81EE 81044100 |sub esi, 00410481 004117C8 |. 56 |push esi 004117C9 |. 53 |push ebx 004117CA |. FF75 FC |push dword ptr [ebp-4] 004117CD |. FFD7 |call edi ; <&KERNEL32.VirtualAllocEx> 004117CF |. 3BC3 |cmp eax, ebx 004117D1 |. 8945 F0 |mov [ebp-10], eax 004117D4 |. 0F84 E9000000 |je 004118C3 004117DA |. 8D4D EC |lea ecx, [ebp-14] 004117DD |. 51 |push ecx ; /pBytesWritten 004117DE |. 56 |push esi ; |BytesToWrite 004117DF |. 8B35 EC104000 |mov esi, [<&KERNEL32.WriteProcessMemory>] ; 004117E5 |. 68 81044100 |push 00410481 ; |Buffer = Unlocker.00410481,线程函数地址 004117EA |. 50 |push eax ; |Address 004117EB |. FF75 FC |push dword ptr [ebp-4] ; |hProcess 004117EE |. 895D EC |mov [ebp-14], ebx ; | 004117F1 |. FFD6 |call esi ; \WriteProcessMemory 004117F3 |. 6A 40 |push 40 004117F5 |. 68 00100000 |push 1000 004117FA |. 68 0C080000 |push 80C 004117FF |. 53 |push ebx 00411800 |. FF75 FC |push dword ptr [ebp-4] 00411803 |. FFD7 |call edi ; <&KERNEL32.VirtualAllocEx> 再分配,用来放线程参数 00411805 |. 8BF8 |mov edi, eax 00411807 |. 3BFB |cmp edi, ebx 00411809 |. 0F84 A2000000 |je 004118B1 0041180F |. 395D F8 |cmp [ebp-8], ebx 00411812 |. 74 3C |je short 00411850 00411814 |. 68 3CCD4000 |push 0040CD3C ; /ProcNameOrOrdinal = "FreeLibrary" 00411819 |. FF75 F8 |push dword ptr [ebp-8] ; |hModule 0041181C |. FF15 70104000 |call [<&KERNEL32.GetProcAddress>] ; \GetProcAddress 00411822 |. 68 28CD4000 |push 0040CD28 ; /ProcNameOrOrdinal = "GetModuleHandleW" 00411827 |. FF75 F8 |push dword ptr [ebp-8] ; |hModule 0041182A |. 8985 A0F7FFFF |mov [ebp-860], eax ; | 00411830 |. FF15 70104000 |call [<&KERNEL32.GetProcAddress>] ; \GetProcAddress 00411836 |. 68 DCCC4000 |push 0040CCDC ; /ProcNameOrOrdinal = "CloseHandle" 0041183B |. FF75 F8 |push dword ptr [ebp-8] ; |hModule 0041183E |. 8985 A4F7FFFF |mov [ebp-85C], eax ; | 00411844 |. FF15 70104000 |call [<&KERNEL32.GetProcAddress>] ; \GetProcAddress 0041184A |. 8985 A8F7FFFF |mov [ebp-858], eax 00411850 |> 8D85 A0EFFFFF |lea eax, [ebp-1060] 00411856 |. 50 |push eax ; /String2 00411857 |. 8D85 ACF7FFFF |lea eax, [ebp-854] ; | 0041185D |. 50 |push eax ; |String1 0041185E |. FF15 78104000 |call [<&KERNEL32.lstrcpyW>] ; \lstrcpyW 00411864 |. 8D45 EC |lea eax, [ebp-14] 00411867 |. 50 |push eax 00411868 |. 68 0C080000 |push 80C 0041186D |. 8D85 A0F7FFFF |lea eax, [ebp-860] 00411873 |. 50 |push eax 00411874 |. 57 |push edi 00411875 |. FF75 FC |push dword ptr [ebp-4] 00411878 |. FFD6 |call esi ; \WriteProcessMemory,远程线程需要调的函数地址传过去,同时传过去的还有目标文件名,因为GetModuleHandle需要使用 0041187A |. 53 |push ebx 0041187B |. 53 |push ebx 0041187C |. 57 |push edi 0041187D |. FF75 F0 |push dword ptr [ebp-10] 00411880 |. 53 |push ebx 00411881 |. 53 |push ebx 00411882 |. FF75 FC |push dword ptr [ebp-4] 00411885 |. FF15 E8104000 |call [<&KERNEL32.CreateRemoteThread>] ; 00411891 |. 6A FF |push -1 ; /Timeout = INFINITE 00411893 |. 56 |push esi ; |hObject 00411894 |. FF15 E4104000 |call [<&KERNEL32.WaitForSingleObject>] ; \WaitForSingleObject Unlocker.EXE将等待远程线程结束才返回。 文件的移动就不列举了,代码很简单,就是API的直接调用。 文件拷贝的代码如下: 0041147E /$ 55 push ebp 0041147F |. 8BEC mov ebp, esp 00411481 |. 81EC 44080000 sub esp, 844 00411487 |. 68 1CCD4000 push 0040CD1C ; /pModule = "kernel32" 0041148C |. FF15 8C104000 call [<&KERNEL32.GetModuleHandleA>] ; 00411492 |. 85C0 test eax, eax 00411494 |. 8945 FC mov [ebp-4], eax 00411497 |. 0F84 C0010000 je 0041165D 0041149D |. FF75 0C push dword ptr [ebp+C] ; /ProcessId 004114A0 |. 6A 00 push 0 ; |Inheritable = FALSE 004114A2 |. 68 3A040000 push 43A ; |Access = CREATE_THREAD|VM_OPERATION|VM_READ|VM_WRITE|QUERY_INFORMATION 004114A7 |. FF15 F4104000 call [<&KERNEL32.OpenProcess>] ; \OpenProcess 004114AD |. 85C0 test eax, eax 004114AF |. 8945 F8 mov [ebp-8], eax 004114B2 |. 0F84 A5010000 je 0041165D 004114B8 |. 53 push ebx 004114B9 |. 56 push esi 004114BA |. 8B35 F0104000 mov esi, [<&KERNEL32.VirtualAllocEx>] ; 004114C0 |. 57 push edi 004114C1 |. 6A 40 push 40 004114C3 |. B8 8D054100 mov eax, 0041058D 004114C8 |. BF BC044100 mov edi, 004104BC 004114CD |. 2BC7 sub eax, edi 004114CF |. BB 00100000 mov ebx, 1000 004114D4 |. 53 push ebx 004114D5 |. 50 push eax 004114D6 |. 6A 00 push 0 004114D8 |. FF75 F8 push dword ptr [ebp-8] 004114DB |. 8945 F4 mov [ebp-C], eax 004114DE |. FFD6 call esi ; <&KERNEL32.VirtualAllocEx> 004114E0 |. 85C0 test eax, eax 004114E2 |. 8945 F0 mov [ebp-10], eax 004114E5 |. 0F84 66010000 je 00411651 004114EB |. 8365 EC 00 and dword ptr [ebp-14], 0 004114EF |. 8D4D EC lea ecx, [ebp-14] 004114F2 |. 51 push ecx ; /pBytesWritten 004114F3 |. FF75 F4 push dword ptr [ebp-C] ; |BytesToWrite 004114F6 |. 57 push edi ; |Buffer 004114F7 |. 8B3D EC104000 mov edi, [<&KERNEL32.WriteProcessMemory>] ; 004114FD |. 50 push eax ; |Address 004114FE |. FF75 F8 push dword ptr [ebp-8] ; |hProcess 00411501 |. FFD7 call edi ; \WriteProcessMemory 00411503 |. 6A 40 push 40 00411505 |. 53 push ebx 00411506 |. BB 30080000 mov ebx, 830 0041150B |. 53 push ebx 0041150C |. 6A 00 push 0 0041150E |. FF75 F8 push dword ptr [ebp-8] 00411511 |. FFD6 call esi 00411513 |. 85C0 test eax, eax 00411515 |. 8945 F4 mov [ebp-C], eax 00411518 |. 0F84 20010000 je 0041163E 0041151E |. FF75 08 push dword ptr [ebp+8] ; /String2 00411521 |. 8D85 ECF7FFFF lea eax, [ebp-814] ; | 00411527 |. 50 push eax ; |String1 00411528 |. FF15 78104000 call [<&KERNEL32.lstrcpyW>] ; \lstrcpyW 0041152E |. 8B45 10 mov eax, [ebp+10] 00411531 |. 8B35 70104000 mov esi, [<&KERNEL32.GetProcAddress>] ; 00411537 |. 68 10CD4000 push 0040CD10 ; /ProcNameOrOrdinal = "CreateFileW" 0041153C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041153F |. 8985 E8F7FFFF mov [ebp-818], eax ; | 00411545 |. FFD6 call esi ; \GetProcAddress 00411547 |. 68 00CD4000 push 0040CD00 ; /ProcNameOrOrdinal = "SetFilePointer" 0041154C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041154F |. 8985 BCF7FFFF mov [ebp-844], eax ; | 00411555 |. FFD6 call esi ; \GetProcAddress 00411557 |. 68 F4CC4000 push 0040CCF4 ; /ProcNameOrOrdinal = "ReadFile" 0041155C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041155F |. 8985 C0F7FFFF mov [ebp-840], eax ; | 00411565 |. FFD6 call esi ; \GetProcAddress 00411567 |. 68 E8CC4000 push 0040CCE8 ; /ProcNameOrOrdinal = "WriteFile" 0041156C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041156F |. 8985 C4F7FFFF mov [ebp-83C], eax ; | 00411575 |. FFD6 call esi ; \GetProcAddress 00411577 |. 68 DCCC4000 push 0040CCDC ; /ProcNameOrOrdinal = "CloseHandle" 0041157C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041157F |. 8985 C8F7FFFF mov [ebp-838], eax ; | 00411585 |. FFD6 call esi ; \GetProcAddress 00411587 |. 68 D0CC4000 push 0040CCD0 ; /ProcNameOrOrdinal = "GlobalAlloc" 0041158C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041158F |. 8985 CCF7FFFF mov [ebp-834], eax ; | 00411595 |. FFD6 call esi ; \GetProcAddress 00411597 |. 68 C4CC4000 push 0040CCC4 ; /ProcNameOrOrdinal = "GlobalFree" 0041159C |. FF75 FC push dword ptr [ebp-4] ; |hModule 0041159F |. 8985 D0F7FFFF mov [ebp-830], eax ; | 004115A5 |. FFD6 call esi ; \GetProcAddress 004115A7 |. 68 B8CC4000 push 0040CCB8 ; /ProcNameOrOrdinal = "GetFileSize" 004115AC |. FF75 FC push dword ptr [ebp-4] ; |hModule 004115AF |. 8985 D4F7FFFF mov [ebp-82C], eax ; | 004115B5 |. FFD6 call esi ; \GetProcAddress 004115B7 |. 68 B0CC4000 push 0040CCB0 ; /ProcNameOrOrdinal = "Sleep" 004115BC |. FF75 FC push dword ptr [ebp-4] ; |hModule 004115BF |. 8985 D8F7FFFF mov [ebp-828], eax ; | 004115C5 |. FFD6 call esi ; \GetProcAddress 004115C7 |. 68 9CCC4000 push 0040CC9C ; /ProcNameOrOrdinal = "GetOverlappedResult" 004115CC |. FF75 FC push dword ptr [ebp-4] ; |hModule 004115CF |. 8985 DCF7FFFF mov [ebp-824], eax ; | 004115D5 |. FFD6 call esi ; \GetProcAddress 004115D7 |. 68 8CCC4000 push 0040CC8C ; /ProcNameOrOrdinal = "GetLastError" 004115DC |. FF75 FC push dword ptr [ebp-4] ; |hModule 004115DF |. 8985 E0F7FFFF mov [ebp-820], eax ; | 004115E5 |. FFD6 call esi ; \GetProcAddress 004115E7 |. 8985 E4F7FFFF mov [ebp-81C], eax 004115ED |. 8D45 EC lea eax, [ebp-14] 004115F0 |. 50 push eax 004115F1 |. 53 push ebx 004115F2 |. 8D85 BCF7FFFF lea eax, [ebp-844] 004115F8 |. 50 push eax 004115F9 |. FF75 F4 push dword ptr [ebp-C] 004115FC |. FF75 F8 push dword ptr [ebp-8] 004115FF |. FFD7 call edi 00411601 |. 33F6 xor esi, esi 00411603 |. 56 push esi 00411604 |. 56 push esi 00411605 |. FF75 F4 push dword ptr [ebp-C] 00411608 |. FF75 F0 push dword ptr [ebp-10] 0041160B |. 56 push esi 0041160C |. 56 push esi 0041160D |. FF75 F8 push dword ptr [ebp-8] 00411610 |. FF15 E8104000 call [<&KERNEL32.CreateRemoteThread>] ; 00411616 |. 8BF8 mov edi, eax 00411618 |. 3BFE cmp edi, esi 0041161A |. 74 09 je short 00411625 0041161C |. 6A FF push -1 ; /Timeout = INFINITE 0041161E |. 57 push edi ; |hObject 0041161F |. FF15 E4104000 call [<&KERNEL32.WaitForSingleObject>] ; \WaitForSingleObject 过程是类似的,只不过导入的函数地址多了些而以,这次的函数地址是004104bc。 上面的两个具体的远程线程代码就不分析了。 至此,Unlocker.EXE的功能原理就分析完了。 该驱动很简单,就调用两个函数,ObReferenceObjectByHandle得到内核jcect指针,然后通过ObQueryNameString来得到UNICODE模式的名字字符串。由于是通过读写接口,也就是readfile和writefile函数接口,传入的IRP里面是mdl,需要重新映射,因此需要使用MmMapLockedPagesSpecifyCache来映射内存。再就是输入的时候,传入了句柄和object地址,共8字节,objcet地址应用层是通过ZwQuerySystemInformation得到的,内核层会通过传入的handle再获取一遍并核对,如果一致则继续操作,否则直接返回。 处理写入的代码: GE:00401044 loc_401044: ; CODE XREF: PAGE:0040103Dj PAGE:00401044 push 10h PAGE:00401046 push 0 PAGE:00401048 push 0 PAGE:0040104A push 1 PAGE:0040104C push 0 PAGE:0040104E push eax PAGE:0040104F call ds:MmMapLockedPagesSpecifyCache PAGE:00401055 PAGE:00401055 loc_401055: ; CODE XREF: PAGE:00401042j PAGE:00401055 mov ecx, [eax] PAGE:00401057 mov Handle, ecx PAGE:0040105D mov edx, [eax+4] PAGE:00401060 mov dword_404004, edx,这里存放object指针 PAGE:00401066 xor esi, esi PAGE:00401068 mov ecx, 8 PAGE:0040106D loc_40106D: ; CODE XREF: PAGE:00401030j PAGE:0040106D ; PAGE:00401037j PAGE:0040106D mov [edi+1Ch], ecx PAGE:00401070 xor dl, dl PAGE:00401072 mov ecx, edi PAGE:00401074 mov [edi+18h], esi PAGE:00401077 call ds:IofCompleteRequest PAGE:0040107D pop edi PAGE:0040107E mov eax, esi PAGE:00401080 pop esi PAGE:00401081 retn 8 处理读的代码: PAGE:004010D0 loc_4010D0: ; CODE XREF: sub_401090+39j PAGE:004010D0 push 10h ; Priority PAGE:004010D2 push 0 ; BugCheckOnFailure PAGE:004010D4 push 0 ; BaseAddress PAGE:004010D6 push 1 ; CacheType PAGE:004010D8 push 0 ; AccessMode PAGE:004010DA push eax ; MemoryDescriptorList PAGE:004010DB call ds:MmMapLockedPagesSpecifyCache PAGE:004010E1 mov ebp, eax PAGE:004010E1 PAGE:004010E3 PAGE:004010E3 loc_4010E3: ; CODE XREF: sub_401090+3Ej PAGE:004010E3 mov ecx, Handle PAGE:004010E9 push 0 ; HandleInformation PAGE:004010EB lea eax, [esp+24h+Object] PAGE:004010EF push eax ; Object PAGE:004010F0 push 0 ; AccessMode PAGE:004010F2 push 0 ; ObjectType PAGE:004010F4 push 80000000h ; DesiredAccess PAGE:004010F9 push ecx ; Handle PAGE:004010FA call ds:ObReferenceObjectByHandle PAGE:00401100 test eax, eax PAGE:00401102 jnz loc_40121E 失败返回 PAGE:00401102 PAGE:00401108 mov ecx, [esp+20h+Object] ; Object PAGE:0040110C test ecx, ecx PAGE:0040110E jz loc_401214 PAGE:0040110E PAGE:00401114 cmp ecx, dword_404004 核对一致性 PAGE:0040111A jnz loc_401214 。。。。。。。。。。。。。 PAGE:0040114A mov eax, [esi] PAGE:0040114C lea edx, [esp+24h+var_10] PAGE:00401150 push edx PAGE:00401151 push 400h PAGE:00401156 push ebx PAGE:00401157 push eax PAGE:00401158 call ds:ObQueryNameString PAGE:0040115E test eax, eax PAGE:00401160 jnz loc_401204 。。。。。。。。。。。。。 PAGE:00401214 call ds:ObfDereferenceObject 释放reference PAGE:0040121A mov esi, [esp+30h+var_1C] PAGE:0040121E pop ebp 。。。。。。。。。。。。。 PAGE:0040122D call ds:IofCompleteRequest PAGE:00401233 mov eax, esi PAGE:00401235 pop esi PAGE:00401236 pop ebx PAGE:00401237 add esp, 14h PAGE:0040123A retn 8 驱动的初始化代码这里就不列举了。(完)
|