【读书笔记】《windows PE 权威指南》重定位

  1 ;--------------------------------
  2 ;动态加载功能实现
  3 ;moriarty
  4 ;2012/04/13
  5 ;--------------------------------
  6 .386
  7 .model flat,stdcall
  8 option casemap:none
  9 
 10 include windows.inc
 11 
 12 ;声明函数
 13 _QLGetProcAddress    typedef proto     :dword, :dword
 14 
 15 ;声明函数引用
 16 _ApiGetProcAddress     typedef     ptr     _QLGetProcAddress
 17 
 18 _QLLoadLib     typedef        proto    :dword
 19 _ApiLoadLib     typedef        ptr    _QLLoadLib
 20 
 21 _QLMessageBoxA    typedef        proto    :dword, :dword, :dword
 22 _ApiMessageBoxA    typedef        ptr    _QLMessageBoxA
 23 
 24 
 25 ;代码段
 26 .code
 27 szText        db    'HelloWorldPE',0
 28 szGetProcAddr    db    'GetProcAddress',0
 29 szLoadLib    db    'LoadLibraryA',0
 30 szMessageBox    db    'MessageBoxA',0
 31 
 32 user32_DLL    db    'user32.dll',0,0
 33 
 34 ;定义函数
 35 _getProcAddress        _ApiGetProcAddress    ?
 36 _loadLibrary        _ApiLoadLib        ?
 37 _messageBox        _ApiMessageBoxA        ?
 38 
 39 hKernel32Base    dd    ?
 40 hUser32Base    dd    ?
 41 lpGetProcAddr    dd    ?
 42 lpLoadLib    dd    ?
 43 
 44 ;------------------------------------------
 45 ;根据kernel32.dll中的一个地址获取它的基址
 46 ;------------------------------------------
 47 _getKernelBase    proc    _dwKernelRetAddress
 48     
 49     LOCAL    @dwRet
 50     pushad
 51     mov     @dwRet, 0
 52     
 53     mov    edi, _dwKernelRetAddress
 54     and    edi, 0ffff0000h
 55     
 56     .repeat
 57         ;找到kernel32.dll的dos头
 58         .if word ptr [edi] == IMAGE_DOS_SIGNATURE
 59             mov     esi, edi
 60             add    esi, [esi+003ch]
 61             
 62             ;找到kernel32.dll的pe头标示
 63             .if word ptr [esi] == IMAGE_NT_SIGNATURE
 64                 mov    @dwRet, edi
 65                 .break
 66             .endif
 67         .endif
 68         
 69         sub    edi, 010000h
 70         .break    .if edi < 070000000h
 71     .until FALSE
 72     
 73     popad
 74     mov     eax, @dwRet
 75     
 76     ret
 77 
 78 _getKernelBase endp
 79 
 80 
 81 ;--------------------------------------------------------
 82 ;获取指定字符串的api函数的调用地址
 83 ;入口参数:    _hModule 为动态链接库的基址
 84 ;        _lpApi 为api函数名的首地址
 85 ;出口参数:eax为函数在虚拟地址空间中的真实地址
 86 ;--------------------------------------------------------
 87 _getApi    proc    _hModule, _lpApi
 88     
 89     LOCAL    @ret
 90     LOCAL    @dwLen
 91     
 92     pushad
 93     mov    @ret, 0
 94     
 95     ;计算api字符串的长度 (包括最后的0在内)
 96     mov    edi, _lpApi
 97     mov    ecx, -1
 98     xor    al , al
 99     cld
100     
101     repnz    scasb ;比较edi与al  直到内容相同退出(即最后一个 0 时退出)
102     
103     mov    ecx, edi ;在repnz  比对过程中 edi 最终指向 字符串末尾的地址
104     sub    ecx, _lpApi ;尾地址-首地址=字符串长度
105     mov    @dwLen, ecx
106     
107     ;从pe文件头的数据目录表 取出导出表首地址
108     mov    esi, _hModule
109     add    esi, [esi+3ch] ;[esi+3ch]指向dos头的e_lfaNew字段
110     
111     assume    esi :ptr IMAGE_NT_HEADERS ;esi指向IMAGE_NT_HEADERS 的结构
112     
113     mov    esi, [esi].OptionalHeader.DataDirectory.VirtualAddress; 指向数据目标表第一项(即DataDirectory[0]导出表)
114     add    esi, _hModule ;导出表的虚拟地址VA=偏移+基址
115     
116     assume    esi :ptr IMAGE_EXPORT_DIRECTORY ;
117     
118     ;查找指定名称的导出函数
119     mov    ebx, [esi].AddressOfNames; 导出函数名地址 数组
120     add    ebx, _hModule ;得到真实VA
121     
122     xor    edx, edx
123     .repeat
124         push     esi
125         mov    edi, [ebx]
126         add    edi, _hModule ;得到函数名的VA
127         
128         mov    esi, _lpApi
129         mov    ecx, @dwLen
130         repz    cmpsb    ;比较[edi]、[esi]的内容,将比对结果写入标志位,直到不相同或者ecx为0
131         
132         .if    ZERO? ;标志位为0 表示上面的的字符串比对 是匹配的
133             pop    esi
134             jmp    @F
135             
136         .endif
137         
138         pop    esi
139         add    ebx, 4 ;比对 函数名地址数组的 下一项函数名
140         inc    edx
141     .until    edx >=  [esi].NumberOfNames
142     jmp    _ret
143     
144 @@:
145     ;通过上面步骤得到的AddressOfNames的数组索引  获取函数调用的VA
146     sub    ebx, [esi].AddressOfNames
147     sub    ebx, _hModule    ;获得 指定函数名地址 到函数名数组首地址的偏移 
148     
149     shr    ebx, 1    ;偏移值 移位操作 =ebx/2 
150     
151     add    ebx, [esi].AddressOfNameOrdinals ;首地址+索引值   即 为指定 AddressOfNameOrdinals 的RVA
152     ;指定的AddressOfNameOrdinals 的VA  
153     ;注:AddressOfNameOrdinals里面存储的是AddressOfFunctions的索引
154     ;AddressOfFunctions里面存储的是函数调用的地址
155     add    ebx, _hModule    
156     
157     movzx    eax, word ptr [ebx]
158     shl    eax, 2    ;根据AddressOfNameOrdinals里面的索引值*2 = AddressOfFunctions首地址的偏移量
159     
160     add    eax, [esi].AddressOfFunctions ;函数调用数组首地址+偏移量= 所需求的那个 函数调用地址
161     add    eax, _hModule ;得到 指定函数调用的 VA
162     
163     mov    eax, [eax]
164     add    eax, _hModule
165     mov    @ret, eax
166     
167 _ret:
168     assume    esi :nothing
169     popad
170     mov    eax, @ret
171     
172     ret
173 _getApi endp
174 
175 ;------------------------------------------
176 ;函数真正开始执行的地方
177 ;------------------------------------------
178 start:
179     ;取当前函数的栈顶数据
180     ;函数刚开始的时候栈顶地址 一定存在于kernel32.dll地址空间内
181     ;正合适以此寻找kernel32.dll的基址
182     mov    eax, dword ptr [esp]
183     push    eax
184     call    @F
185 @@:
186     pop    ebx
187     sub    ebx, @B ;重定位功能:ebx存储基址
188     
189     pop    eax
190     
191     ;获取kernel32.dll的基址
192     invoke    _getKernelBase,eax
193     mov    [ebx + offset hKernel32Base], eax
194     
195     ;从基地址出发搜索GetProcAddress函数的地址
196     mov    eax, offset szGetProcAddr
197     add    eax, ebx ;eax存储的是 加了基址之后的szGetProcAddr的绝对地址VA
198     
199     mov    edi, offset hKernel32Base
200     mov    ecx, [edi + ebx]
201     
202     ;调用函数  获取GetProcAddress的调用地址
203     invoke    _getApi, ecx, eax
204     
205     mov    [ebx + offset lpGetProcAddr], eax
206     
207     ;将GetProcAddress的真实调用地址 存入_getProcAddress变量中
208     mov    [ebx + offset _getProcAddress], eax
209     
210     ;下面使用GetProcAddress函数的基址 调用该函数的功能
211     ;参数1:获取  LoadLibraryA 函数名字符串的地址
212     mov    eax, offset szLoadLib
213     add    eax, ebx
214     
215     ;参数2:获取 Kernel32.dll的基址
216     mov    edi, offset hKernel32Base
217     mov    ecx, [edi + ebx] 
218     
219     ;获取GetProcAddress的函数调用地址
220     mov    edx, offset _getProcAddress
221     add    edx, ebx
222     
223     ;调用函数GetProcAddress  获取LoadLibraryA的调用地址
224     ;GetProcAddress hKernel32Base,LoadLibraryA
225     push    eax
226     push    ecx
227     call    dword ptr [edx]
228     
229     ;将函数LoadLibraryA的调用地址存入_loadLibrary变量
230     mov    [ebx + offset _loadLibrary], eax
231     
232     ;使用LoadLibrary载入user32.dll
233     ;参数1:获取user32.dll字符串的地址
234     mov    eax, offset user32_DLL
235     add    eax, ebx
236     
237     ;函数LoadLibrary的调用地址
238     mov    edi, offset _loadLibrary
239     mov    edx, [ebx + edi]
240     
241     ;调用函数LoadLibraryA
242     ;LoadLibraryA "user32.dll"
243     push    eax
244     call    edx
245     
246     ;保存user32.dll的基址
247     mov    [ebx + offset hUser32Base], eax
248     
249     ;获取MessageBoxA的函数调用地址
250     ;参数1:MessageBoxA函数名的地址
251     mov    eax, offset szMessageBox
252     add    eax, ebx
253     
254     ;参数2:user32.dll的基址
255     mov     edi, offset hUser32Base
256     mov    ecx, [edi + ebx]
257     
258     ;参数3:GetProcAddress地址
259     mov    edx, offset _getProcAddress
260     add    edx, ebx
261     
262     ;模仿调用:GetProcAddress hUser32Base,MessageBoxA
263     push    eax
264     push    ecx
265     call    dword ptr [edx]
266     
267     ;保存MessageBoxA的函数调用地址
268     mov    [ebx + offset _messageBox], eax
269     
270     ;调用MessageBoxA这个方法
271     ;参数1:字符串szText
272     mov    eax, offset szText
273     add    eax, ebx
274     
275     ;参数2:函数MessageBoxA的地址
276     mov    edx, offset _messageBox
277     add    edx, ebx
278     
279     ;模仿调用MessageBoxA NULL, addr szText,Null,MB_OK
280     push     MB_OK
281     push     NULL
282     push     eax
283     push     NULL
284     call    dword ptr [edx]
285      
286     ret
287     
288     end start
289     
290     
291         
原文地址:https://www.cnblogs.com/moriarty/p/2451806.html