相信很多人都知道Loaddll.exe这个程序,它为Ollydbg提供了调试DLL的功能
但是又有多少人知道Loaddll.exe是如何工作的?
在介绍Loaddll.exe如何工作前,先介绍一下它的来源吧.
从官网上下载的Ollydbg解压后,里面没有Loaddll.exe这个程序,
但是如果你尝试用Ollydbg载入DLL调试,那么Ollydbg目录下就会自动生成一下Loaddll.exe程序.
Loaddll.exe来源于Ollydbg的资源,参考Ollydbg资源中的KNOWNERSTYPE\RES_LOADDLL
知道了Loaddll.exe的来源后,再来思考一下,它是如何为Ollydbg提供调试DLL功能的
一般情况下使用DLL又两种手段,
1>在编译程序时,连接到程序里面
[如果DLL丢失或升级等,程序易出错,导致不能运行]
2>动态调用DLL
使用的函数有LoadLibrary与GetProcAddress
在这里就是采用LoadLibraryA载入DLL实现的动态分析
能介绍一下Ollydbg是如何利用Loaddll载入DLL,调试分析的吗?
==========================================================
DLL在载入时,会完成初始化的操作,一般DLL的入口形式如下:
DllEntry proc hInstance,dwReason,dwReaserved
.if dwReason == DLL_PROCESS_ATTACH
mov eax,TRUE
;表示任何程序可以使用
.elseif dwReason == DLL_PROCESS_DETACH
.elseif dwReason == DLL_THREAD_ATTACH
.elseif dwReason == DLL_THREAD_DETACH
.endif
RET
DllEntry endp
end DllEntry
假设DLL_PROCESS_ATTACH[DLL初始化成功],会执行某个函数[假设函数名为 "Run"]的操作
==================================================
Ollydbg自带的LoadDLL载入DLL时,会自动执行这个函数Run
[有人肯定要说,使因为DLL初始化成功,符合执行条件]
那么请问:
某些前辈的开发的加强版Loaddll.exe,则不会执行DLL的内容,当DLL加载时,自动停在入口处
能解释一下,前辈们是如何做到的吗?
下面是我分析得出来的结论,希望大家喜欢!
================================================================================
Ollydbg载入程序,注意在菜单Debug>>Arguments,填入DLL的地址,例如:C:\1.dll,
这时弹出一个对话框,提示重新再如Loaddll.exe,输入的参数生效.
按下Ctrl+F2,重新运行程序.
00401075 |. 6A 24 push 24 ; /Style = MB_YESNO|MB_ICONQUESTION|MB_APPLMODAL
00401077 |. 68 4B304000 push 0040304B ; |Title = "Question"
0040107C |. 68 00304000 push 00403000 ; |Text = "Would you like to load the .dll on a base......
00401081 |. 6A 00 push 0 ; |hOwner = NULL
00401083 |. E8 5E010000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
00401088 |. 83F8 06 cmp eax, 6 ; 点击Yes返回值是6
0040108B |. 75 16 jnz short 004010A3
0040108D |. 6A 00 push 0 ; /lParam = NULL
0040108F |. 68 06114000 push 00401106 ; |DlgProc = LoadDll.00401106
00401094 |. 6A 00 push 0 ; |hOwner = NULL
00401096 |. 6A 66 push 66 ; |pTemplate = 66
00401098 |. FF35 C0304000 push dword ptr [4030C0] ; |hInst = 00400000
0040109E |. E8 31010000 call <jmp.&user32.DialogBoxParamA> ; \DialogBoxParamA
利用函数DialogBoxParamA创建一个窗口,窗口过程地址为:401106,具体过程如下:
00401106 /. 55 push ebp
00401107 |. 8BEC mov ebp, esp
00401109 |. 817D 0C 11010>cmp dword ptr [ebp+C], 111
00401110 |. 0F85 A8000000 jnz 004011BE
00401116 |. 817D 10 F1030>cmp dword ptr [ebp+10], 3F1
0040111D |. 0F85 AB000000 jnz 004011CE
00401123 |. 6A 64 push 64 ; /Count = 64 (100.)
00401125 |. 68 34334000 push 00403334 ; |Buffer = LoadDll.00403334
0040112A |. 68 F0030000 push 3F0 ; |ControlID = 3F0 (1008.)
0040112F |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401132 |. E8 A9000000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401137 |. 83F8 08 cmp eax, 8
0040113A |. 77 05 ja short 00401141
从上面的过程我们可以看出,输入的数据被保存在00403334处.单步向下调试
0040113F |. /73 13 jnb short 00401154
00401141 |> |6A 10 push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401143 |. |6A 00 push 0 ; |Title = NULL
00401145 |. |68 84304000 push 00403084 ; |Text = "Invalid input or imagebase already allocated!"
0040114A |. |FF75 08 push dword ptr [ebp+8] ; |hOwner
0040114D |. |E8 94000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
00401152 |. |EB 68 jmp short 004011BC
00401154 |> \68 34334000 push 00403334 ; ASCII "10000000"
00401159 |. E8 AE000000 call 0040120C ; 关键过程
00401159处的过程为关键过程,过程的具体信息如下:
0040120C /$ 55 push ebp ; 关键过程
0040120D |. 8BEC mov ebp, esp ; 调试到此处的堆栈显示如下
0040120F |. 53 push ebx
00401210 |. 56 push esi
00401211 |. 57 push edi
00401212 |. 8B7D 08 mov edi, dword ptr [ebp+8] ; DLL载入的基地址 edi=403334 [403334]=10000000
00401215 |. 8B75 08 mov esi, dword ptr [ebp+8] ; DLL载入的基地址
00401218 |> 8A07 /mov al, byte ptr [edi]
0040121A |. 47 |inc edi ; 运算后的EDI=ESI +基地址的长度
0040121B |. 0AC0 |or al, al
0040121D |.^ 75 F9 \jnz short 00401218
0040121F |. 2BF7 sub esi, edi ; ESI = ESI-EDI
00401221 |. 33DB xor ebx, ebx
00401223 |. 03FE add edi, esi ; 经过运算,现在的EDI就是401215处的ESI
00401225 |. 33D2 xor edx, edx
00401227 |. F7D6 not esi ; 运算后的ESI,表示基地址的长度
00401229 |. EB 23 jmp short 0040124E
0040122B |> 8A07 /mov al, byte ptr [edi] ; 基地址10000000的又一个运算
0040122D |. 3C 41 |cmp al, 41
0040122F |. 72 0C |jb short 0040123D ; al < 41 满足条件
00401231 |. 2C 57 |sub al, 57
00401233 |. 80D2 00 |adc dl, 0
00401236 |. C0E2 05 |shl dl, 5
00401239 |. 02C2 |add al, dl
0040123B |. EB 02 |jmp short 0040123F
0040123D |> 2C 30 |sub al, 30
0040123F |> 8D4E FF |lea ecx, dword ptr [esi-1] ; mov ecx,esi [注意]
00401242 |. 83E0 0F |and eax, 0F ; 运算后eax=1
00401245 |. C1E1 02 |shl ecx, 2 ; ecx=7[111] shl后 得到[11100]1C
00401248 |. D3E0 |shl eax, cl ; EAX=1 CL=1C 运算后EAX=10000000 1后面的7个0,就是Shl来的
0040124A |. 03D8 |add ebx, eax ; 将DLL的有效基地址放在EBX中
0040124C |. 47 |inc edi
0040124D |. 4E |dec esi ; 字符长度 - 1
0040124E |> 0BF6 or esi, esi
00401250 |.^ 75 D9 \jnz short 0040122B
00401252 |. 8BC3 mov eax, ebx ; 将有效地址传入EAX中
00401254 |. 5F pop edi
00401255 |. 5E pop esi
00401256 |. 5B pop ebx
00401257 |. C9 leave
00401258 \. C2 0400 retn 4
==============================================0040120D处的堆栈数据====================================================
0040120D |. 8BEC mov ebp, esp ; [调试到此处是的堆栈显示如下]
0012FBBC /0012FBC8 塞.
0012FBC0 |0040115E ^@. RETURN to LoadDll.0040115E from LoadDll.0040120C
0012FBC4 |00403334 43@. ASCII "10000000"
0012FBC8 ]0012FBF4 酐.
这说明[EBP+8]是存放输入的基地址的地方
===============================================================================================================
从DLL关键过程出来后,
00401152 |. /EB 68 jmp short 004011BC
00401154 |> |68 34334000 push 00403334 ; ASCII "10000000"
00401159 |. |E8 AE000000 call 0040120C ; 关键过程
0040115E |. |8BC8 mov ecx, eax
00401160 |. |81E1 FF0F0000 and ecx, 0FFF
00401166 |. |66:83F9 00 cmp cx, 0
0040116A |. |76 13 jbe short 0040117F
0040116C |. |6A 10 push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040116E |. |6A 00 push 0 ; |Title = NULL
00401170 |. |68 84304000 push 00403084 ; |Text = "Invalid input or imagebase already allocated!"
00401175 |. |FF75 08 push dword ptr [ebp+8] ; |hOwner
00401178 |. |E8 69000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
0040117D |. |EB 3D jmp short 004011BC
0040117F |> |A3 30334000 mov dword ptr [403330], eax ; 输入的基地址被保存在这里
00401184 |. |6A 04 push 4 ; /Protect = PAGE_READWRITE 此处的属性值需要注意[没有可执行属性] 这就是断点的原因
00401186 |. |68 00200000 push 2000 ; |AllocationType = MEM_RESERVE
0040118B |. |68 00000100 push 10000 ; |Size = 10000 (65536.)
00401190 |. |FF35 30334000 push dword ptr [403330] ; |Address = 10000000
00401196 |. |E8 69000000 call <jmp.&kernel32.VirtualAlloc> ; \VirtualAlloc
0040119B |. |0BC0 or eax, eax ; 利用VirtualAlloc指定DLL初始化的地址
0040119D |. |75 13 jnz short 004011B2
注意00401184处,关键就在这个VirtualAlloc函数上,他设置的属性中不包括执行属性
上面的分析是为了找出VirtualAlloc处的Address是从何而来
当VirtualAlloc未设置可执行属性时,程序用LoadLibraryA载入DLL时就会产生异常,也就被中断在DLL的入口.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)