Unlocker1.8.5工作原理分析 三级管工作原理图分析

Unlocker工具是一款用于解锁被占用文件或模块的工具。比如说某个文件被其它进程打开,而造成无法删除或修改,某DLL被某进程载入而占用,造成文件无法删除、修改或更新。使用这个工具可以解锁该占用文件。通常这种情况都是由于,进程或者线程僵死,或者文件句柄未能及时关闭造成的。这个工具是装机的时候附带的,版本1.8.5,上官网查了查,其最新版本1.9.1,新版本的实现原理和方式和1.8.5基本一致,只不过新版本增加了对文件的处理权限的操作(为所需要操作的文件对象获取administrator权限),主要是为了针对Vista和WIN7上的权限检查。本文的分析大都来自于1.8.5版本。我们将分析该软件的工作原理和功能实现方式,软件运行环境是WindowsXP SP3。

该软件表面上只有一个文件,就是Unlocker1.8.5.EXE,

其实这是个RAR自解压包,运行之后会出来一个CMD窗口,可以选择安装和删除

我们可以把这个自解压包用rar打开,

发现文件其实是解压到C:ProgramFilesUnlocker下,并运行setup.bat脚本来安装的。

其中UnlockerCOM.dll用于注册右键功能的,这里不作研究;

Unlocker.exe是主程序,核心功能在这里完成。

UnlockerDriver5.SYS是辅助内核程序,完成的功能只有一个,就是从文件句柄得到全局的一致文件名。

UnlockerHook.dll是一个钩子DLL,由于我这里安装的是绿色版,少了一个Unlockerasistent.EXE,所以这个DLL没有起作用。Unlockerasistent.EXE是一个托盘应用程序,负责加载UnlockerHook.dll,并按照用户的需求来加载和卸载钩子,当用户手动操作一个文件失败后(当然这种失败用户是感知不到的),比如删除一个文件等,此时Unlocker的界面就会跳出来提示用户是否需要解锁等操作。后面我们还会分析UnlockerHook.dll的功能。

好了,文件就这么多,我们分析的技术点包括如下内容:

  1. UnlockerHook.dll的工作目的和原理,也就是它干了什么和怎么干的。
  2. UnlockerDriver5.SYS的工作原理和目的,这个比较简单。
  3. Unlocker.exe的工作原理,包括它是如何把文件名对应到锁定进程的,以及如何解锁的等。

好了,先看UnlockerHook.dll,再看Unlocker.exe,最后说说UnlockerDriver5.SYS。

  1. UnlockerHook.dll工作原理

    UnlockerHook.dll导出两个函数, HookInstall 以及HookUninstall,如下所示。

    但是从代码分析可以看出,这两个钩子并没有起什么作用,

.text:10001256 publicHookInstall

.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 callds:UnhookWindowsHookEx

.text:1000127D retn

.text:1000127D

.text:1000127D HookUninstall endp

只是负责安装和卸载WH_CBT钩子,对应ID号是5,hook函数的工作也很简单:

.text:1000123B ; LRESULT __stdcallfn(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 thehook 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 callds: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 callds: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, offsetunk_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字节是这样:

这6字节被读取出来并缓存到10002408地址处,后面还要用:

下面就是判断版本并做相应修改的过程,依据上面的数据,我把程序的路径用红色标出:

100013D2 |. A0 0B240010mov al, [1000240B]

100013D7 |. 3C 83 cmp al,83

100013D9 |. 75 31 jnzshort 1000140C

100013DB |. 803D0C240010>cmp byte ptr [1000240C], 0EC

100013E2 |. 0F85 CC000000 jnz100014B4

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 |. C6050E240010>mov byte ptr [1000240E], 0E9

10001405 |. 68 0F240010 push1000240F

1000140A |. EB 42 jmp short1000144E

1000140C|> 3C 8B cmp al, 8B

1000140E |. 8A0D 0C240010mov cl, [1000240C]

10001414 |. 75 05 jnzshort 1000141B

10001416 |. 80F9 EC cmpcl, 0EC

10001419 |. 74 11 jeshort 1000142C

1000141B |> 3C 53 cmpal, 53

1000141D |. 0F85 91000000 jnz100014B4

10001423 |. 80F9 55 cmp cl,55

10001426 |. 0F85 88000000 jnz100014B4

1000142C|> A1 20240010 mov eax,[10002420];这里存放的是SHFileOperationW的地址

10001431 |. 2D 12240010sub eax, 10002412

10001436 |. 83C0 05 addeax, 5; 这两步是用来计算jmp的偏移量,加5是因为我们要跳过前面已经覆盖掉的5字节

10001439 |. 8945 0C mov[ebp+C], eax

1000143C |. 6A 04 push4

1000143E |. 8D45 0C leaeax, [ebp+C]

10001441 |. 50 push eax//上面计算的偏移压栈

10001442 |. C6050D240010>mov byte ptr [1000240D], 0E9;这个就是jmp指令的字节码

10001449 |. 68 0E240010push 1000240E刚好是要填写jmp指令后面那个地址的位置

1000144E|> E8 2BFEFFFF call 1000127E

这个函数就是把上面的4字节填入jmp指令后面,修改后的字节码是:

也就是:

目的地址刚好就是原函数中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 02110010mov eax, 10001102

10001467 |. 2B05 20240010sub eax, [10002420] ; SHELL32.SHFileOperationW

1000146D |. 6A 04 push4

1000146F |. 83E8 05 subeax, 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 call1000127E

同样的,上面的代码也是些跳转指令的字节码,同样的函数call1000127E。

也就是说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 dwordptr [10002420] ; |Address = 7D640924

10001496 |. 57 push edi ;|hProcess

10001497 |. FF15 18100010 call[<&KERNEL32.WriteProcessMemory>]; WriteProcessMemory

通过WriteProcessMemory来改写SHFileOperationW函数的前5字节,7D640924就是函数的首地址。

修改后的代码变成了:

1000149D |. 6A 05 push 5

1000149F |. FF35 20240010 push dwordptr [10002420] SHELL32.SHFileOperationW

100014A5 |. 57 push edi

100014A6 |. FFD6 call esikernel32.FlushInstructionCache

接下来这几句是必须的,因为SHFileOperationW已经改变,要刷新指令缓存,长度5字节。

好了,10001102处的代码到底是什么呢?

.text:10001102 sub_10001102 procnear ; 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 ptr8

.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 shortloc_10001128

.text:1000111F cmp eax,20h,返回值是20h的时候才忽略,不用触发

.text:10001122 jnzloc_100011E8

.text:10001122

.text:10001128下面就是通过不同的错误值来判断是否需要触发

.text:10001128 loc_10001128: ; CODEXREF: sub_10001102+1Bj

.text:10001128 callds:GetLastError

.text:1000112E mov ecx,[ebx+4]

.text:10001131 cmp ecx, 1

.text:10001134 jz shortloc_10001148

.text:10001134

.text:10001136 cmp ecx, 2

.text:10001139 jbeloc_100011E8

.text:10001139

.text:1000113F cmp ecx, 4

.text:10001142 jaloc_100011E8

.text:10001148

.text:10001148 loc_10001148: ; CODEXREF: sub_10001102+32j

.text:10001148 cmp eax, 6

.text:1000114B jz shortloc_10001155

.text:1000114B

.text:1000114D test eax,eax

.text:1000114F jnzloc_100011E8

.text:10001155好了下面就是触发代码

.text:10001155 loc_10001155: ; CODEXREF: 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 callds:GetModuleFileNameW

.text:10001187 lea eax,[ebp+pszPath]

.text:1000118D push eax ;pszPath

.text:1000118E callds: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 pushoffset 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 callds:ShellExecuteExW 通过这个来触发unlocker.EXE

.text:100011E6 pop edi

.text:100011E7 pop esi

.text:100011E8

.text:100011E8 loc_100011E8: ; CODEXREF: sub_10001102+20j

.text:100011E8 mov eax,[ebp+arg_0]

.text:100011EB pop ebx

.text:100011EC leave

.text:100011ED retn 4

.text:100011ED sub_10001102endp

这只是钩子加载的时候的代码,当钩子卸载的时候(UnhookWindowsHookEx在HookUninstall里被调用的时候,这个调用由用户指示unlockerasistent.exe托盘程序触发),DLL会被卸载,此时还必须还原被修改的函数代码,否则就会出错,因为我们的DLL已经卸载了,那个插入的跳转指令会要了explorer的命。

卸载的代码在这里:

text:100014C2 loc_100014C2: ; CODEXREF: sub_100012E6+Fj

.text:100014C2 xor ebx,ebx

.text:100014C4 cmp [ebp+arg_4],ebx

.text:100014C7 jnz shortloc_100014FD

.text:100014C7

.text:100014C9 cmp lpBaseAddress,ebx

.text:100014CF jz shortloc_100014FD

因为每个进程都会卸载自己的那份DLL,而只有explorer里的那份要特别处理,所以这里要判断一下

.text:100014D1 callds:GetCurrentProcess

.text:100014D7 push ebx ;lpNumberOfBytesWritten

.text:100014D8 push 5 ;nSize

.text:100014DA push offsetunk_10002408 ; lpBuffer

.text:100014DF push lpBaseAddress ;lpBaseAddress

.text:100014E5 mov esi,eax

.text:100014E7 push esi ;hProcess

.text:100014E8 callds:WriteProcessMemory

.text:100014EE push 5 ;dwSize

.text:100014F0 push lpBaseAddress ;lpBaseAddress

.text:100014F6 push esi ;hProcess

.text:100014F7 callds:FlushInstructionCache

我们只需要把10002408处的5字节写回去就可以了,因为这里的5字节就是原先读出来的原数据。

卸载代码包含在同一个函数sub_100012E6里,通过外部传入的参数fdwReason来判断是加载还是卸载。

好了UnlockerHook.dll的代码就分析到这里,为了把问题说清楚还是贴了不少的汇编代码,真是占篇幅啊。总结一下UnlockerHook.dll的功能就是通过CBT钩子来植入每个进程的方式来打入exploer.exe,通过inlinehookSHFileOperationW的方式来挂钩,只要该函数执行返回错误,通过识别不同的错误码来判断是否需要调用unlocker程序,并通过ShellExecuteExW的方式来触发unlocker程序。

说了半天我们还是没有触及unlocker的核心功能,下面我们就来分析。

  1. Unlocker.EXE的工作原理

对于该程序的工作原理,这里先概述一下,首先通过ZwQuerySystemInformation函数得到系统内所有的句柄信息以及其所在的进程信息,然后通过NtLoaddriver来加载驱动(UnlokerDriver5.sys),并写入注册表信息等,使用驱动在内核层得到句柄的名字,并和目标文件命核对,最终定位到占有该句柄,也就是占有该文件的进程;而进程的名字是通过toolhelp类函数,并辅以EnumProcessModules来得到(枚举的第一个模块句柄就是进程自身,再通过GetModuleFileNameEx来得到全路径名)。

好了,只要找到了占用文件的进程,事情就好办了。对于"过程结束"命令,就是使用TerminateProcess来干掉进程。对于解锁,分为两类,如果是DLL文件,则通过远程线程注入来FreeLibrary而解锁文件;否则更简单,通过Duplicatehandle,使用参数DUPLICATE_CLOSE_SOURCE来关闭目标进程里的句柄,这样文件就解锁了。对于拷贝的操作,则是通过远程线程注入,在目标进程里读写文件来完成的。值得一提的是,这种方式是有问题的,因为远程线程和本地线程使用同一个文件句柄操作文件,这样文件指针就会乱掉,虽然远程线程使用OVERLAPPED结构来指定文件偏移避免了拷贝数据乱的情况,从而保证了自己的拷贝工作能顺利完成,但是本地线程的指针是肯定要乱掉的,这对本地线程是致命的!!

移动文件是通过MoveFileEx来实现,如果不能成功则会提示是否在下次启动系统后移动,这是通过MOVEFILE_DELAY_UNTIL_REBOOT标志来实现的,其它文件操作则是通过SHFileOperationW来实现的。

下面来看具体的代码:

0040F94B |. 68 48CB4000 push0040CB48 ; /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 short0040F9B7

0040F95E |. 57 push edi

Unlocker1.8.5工作原理分析 三级管工作原理图分析

0040F95F |. 8B3D 70104000 mov edi,[<&KERNEL32.GetProcAddress>]; kernel32.GetProcAddress

0040F965 |. 68 2CCB4000 push0040CB2C ; /ProcNameOrOrdinal ="ZwQuerySystemInformation"

0040F96A |. 53 push ebx ;|hModule

0040F96B |. FFD7 call edi ;GetProcAddress

0040F96D |. 68 1CCB4000 push0040CB1C ; /ProcNameOrOrdinal = "ZwQueryObject"

0040F972 |. 53 push ebx ;|hModule

0040F973 |. 8906 mov [esi], eax ;|

0040F975 |. FFD7 call edi ;GetProcAddress

0040F977 |. 68 0CCB4000 push0040CB0C ; /ProcNameOrOrdinal = "ZwDeleteFile"

0040F97C |. 53 push ebx ;|hModule

0040F97D |. 8946 04 mov [esi+4], eax; |

0040F980 |. FFD7 call edi ;GetProcAddress

0040F982 |. 68 F4CA4000 push0040CAF4 ; /ProcNameOrOrdinal = "RtlInitUnicodeString"

0040F987 |. 53 push ebx ;|hModule

0040F988 |. 8946 08 mov [esi+8], eax; |

0040F98B |. FFD7 call edi ;GetProcAddress

0040F98D |. 68 E0CA4000 push0040CAE0 ; /ProcNameOrOrdinal = "RtlAdjustPrivilege"

0040F992 |. 53 push ebx ;|hModule

0040F993 |. 8946 0C mov [esi+C], eax; |

0040F996 |. FFD7 call edi ;GetProcAddress

0040F998 |. 68 D0CA4000 push0040CAD0 ; /ProcNameOrOrdinal = "NtLoadDriver"

0040F99D |. 53 push ebx ;|hModule

0040F99E |. 8946 10 mov [esi+10],eax ; |

0040F9A1 |. FFD7 call edi ;GetProcAddress

0040F9A3 |. 68 C0CA4000 push0040CAC0 ; /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 pushC0000000 ; |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 push400

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,可以从中得到比如"DeviceHarddiskVolume1"。

00411948 |. 8945 E4 mov [ebp-1C],eax

0041194B |. E8 6CE0FFFF call0040F9BC

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 |pushdword 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 short00411C29

00411C23 |. 56 |push esi

00411C24 |. E9 C0010000 |jmp00411DE9

00411C29 |> 8B3578104000 |/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 ||push0040CD88 ; UNICODE ".DLL"

00411C57 |. 50 ||push eax

00411C58 |. E8 9ADEFFFF ||call0040FAF7

对每个模块,确定其是不是DLL,对应DLL要单独进行标注。

00411E0F |. E8 FCE5FFFFcall 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 callds:GetVersionExA ; Get extended information about the

.text:00410439 ; version of theoperating system

.text:0041043F test eax,eax

.text:00410441 jz shortloc_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 shortloc_410475

.text:00410455 dec eax

.text:00410456 jz shortloc_410471

.text:00410458 dec eax

.text:00410459 jz shortloc_41046D

.text:0041045B sub eax,0FFFFh

.text:00410460 jz shortloc_410469

.text:00410462 sub eax,10000h

.text:00410467 jnz shortloc_410478

.text:00410469

.text:00410469 loc_410469: ; CODEXREF: sub_410410+50j

.text:00410469 push1Ch

.text:0041046B jmp shortloc_410477

.text:0041046D loc_41046D: ; CODEXREF: sub_410410+49j

.text:0041046D push24h

.text:0041046F jmp shortloc_410477

.text:00410471 loc_410471: ; CODEXREF: sub_410410+46j

.text:00410471 push1Ah

.text:00410473 jmp shortloc_410477

.text:00410475 loc_410475: ; CODEXREF: sub_410410+43j

.text:00410475 push17h

.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 |cmpeax, [ebp-38],比较是不是文件句柄

00411E2D |. |0F85 D2030000 |jnz00412205

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 short00411E5C

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 |je00412205

00411E5C |> |53 |pushebx ; /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 |. |FF15F8104000 |call[<&KERNEL32.DuplicateHandle>]; DuplicateHandle, 复制句柄

00411E78 |. |85C0 |test eax,eax

00411E7A |. |0F84 7C030000 |je004121FC

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 |pushC0000000 ; |Access = GENERIC_READ|GENERIC_WRITE

00411E8B |. |68 F4CD4000 |push0040CDF4 ; |FileName = "\?UnlockerDriver5"

00411E90 |. |FF15 84104000 |call[<&KERNEL32.CreateFileW>]; CreateFileW

打开驱动,并写入将要解析的句柄

00411E96 |. |3BC3 |cmp eax,ebx

00411E98 |. |8945 D0 |mov [ebp-30],eax

00411E9B |. |0F84 52030000 |je004121F3

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 |. |FF1588104000 |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 dwordptr es:[edi]

00411ED4 |. |66:AB |stos word ptres:[edi]

00411ED6 |. |53 |push ebx ;/pOverlapped

00411ED7 |. |AA |stos byte ptres:[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 |. |FF1580104000 |call[<&KERNEL32.ReadFile>]; ReadFile

送入的部分包括文件句柄,以及对应的object指针,送入指针的目的是为了在驱动内部进行核对。

读出时,就是句柄对应文件的全局一致名称。

00411F17 |> FF75 0C|push dword ptr [ebp+C] ; /Pattern ="DeviceHarddiskVolume1"

00411F1A |. 66:899D68F1F>|mov [ebp-E98], bx ; |

00411F21 |. FFB5 C0FBFFFF |pushdword 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 dwordptr es:[edi] ; |

00411F36 |. 66:AB |stos word ptres:[edi] ; |

00411F38 |. FF15 58114000|call[<&SHLWAPI.StrStrW>]; StrStrW

通过模式查询来对比文件名称,然后再转换成通常所见的"C:..."的模式,并最终确定文件名的一致性。找到之后通过如下代码确定进程的名字。

0041206B |. 8D45 BC ||lea eax,[ebp-44]

0041206E |. 50 ||push eax

0041206F |. 6A 04 ||push4

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 short004120AE

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

  

爱华网本文地址 » http://www.aihuau.com/a/25101015/275208.html

更多阅读

暖气片工作原理 暖气片原理结构图

暖气片工作原理——简介暖气通常有水暖和气暖两种,一般暖气片指水暖,就是利用壁挂炉或者锅炉加热循环水,再通过管材链接到暖气片,最终通过暖气片将适宜的温度输出,形成室内温差,最后进行热循环使整个室内温度均匀上升。而气暖则是加热空气

小天鹅干洗机的工作原理解说 干洗机的原理

我们把采用四氯乙烯作为洗涤溶剂的干洗设备称为四氯乙烯干洗机,对采用石油作为溶剂的干洗设备称为石油干洗机;环保型干洗机是指在运行全过程中符合环保要求,对环境的危害没有或低于国家标准要求。小天鹅干洗机根据以下可能对环境和人

采暖锅炉的工作原理及流程分析 采暖锅炉原理

壁挂炉的结构、基本原理和工作流程1、工作原理当壁挂炉启动之后,在系统压力正常之后,水泵启动推动采暖媒介水流动,打开水流开关或水流传感器,输出水流信号(开关信号或脉冲信号)给主控器,主控器接到水流信号之后,启动风机进

蒸汽发生器的工作原理 电蒸汽发生器厂家

? ? ?因为蒸汽发生器和常规的锅炉不一样,因为它不需要年检,所以最近有很多的用户问我蒸汽发生器的原理,蒸汽发生器是怎么工作的,今天就由我给大家分析一下蒸汽发生器的工作原理蒸汽发生器在水汽系统方面,给水在加热器中加热到一定温度,经

声明:《Unlocker1.8.5工作原理分析 三级管工作原理图分析》为网友男人必须傲分享!如侵犯到您的合法权益请联系我们删除