SHOW:
|
|
- or go back to the newest paste.
1 | import sys | |
2 | from struct import pack | |
3 | from io import BytesIO | |
4 | ||
5 | from ctypes import * | |
6 | from winappdbg import Debug, EventHandler | |
7 | import pefile | |
8 | ||
9 | from capstone import * | |
10 | from capstone.x86 import * | |
11 | ||
12 | from unicorn import * | |
13 | from unicorn.x86_const import * | |
14 | ||
15 | from keystone import * | |
16 | ||
17 | SECTION_HEADER_SIZE = 0x28 | |
18 | IMAGE_DESCRIPTOR_HEADER_SIZE = 0x14 | |
19 | ||
20 | md = Cs(CS_ARCH_X86, CS_MODE_64) | |
21 | md.detail = True | |
22 | ||
23 | global target_pe | |
24 | global mem_pages | |
25 | ||
26 | # ---------------------------------------------------------------------------------------------------------------- | |
27 | def align_data(data, blocksize): | |
28 | r = data | |
29 | rm = len( r ) % blocksize | |
30 | if rm != 0: | |
31 | r += (blocksize - rm) * b'\x00' | |
32 | return r | |
33 | ||
34 | # ---------------------------------------------------------------------------------------------------------------- | |
35 | def align_int(integer, blocksize): | |
36 | r = integer | |
37 | rm = r % blocksize | |
38 | if rm != 0: | |
39 | r += (blocksize - rm) | |
40 | return r | |
41 | ||
42 | # ---------------------------------------------------------------------------------------------------------------- | |
43 | def image_import_descriptor(OriginalFirstThunk, Name, FirstThunk): | |
44 | """ | |
45 | +00 DWORD OriginalFirstThunk | |
46 | +04 DWORD TimeDateStamp | |
47 | +08 DWORD ForwarderChain | |
48 | +12 DWORD Name | |
49 | +16 DWORD FirstThunk | |
50 | """ | |
51 | return pack('<LLLLL', OriginalFirstThunk, 0, 0, Name, FirstThunk) | |
52 | ||
53 | # ---------------------------------------------------------------------------------------------------------------- | |
54 | def image_import_by_name(hint, name): | |
55 | """ | |
56 | +00 WORD Hint | |
57 | +02 BYTE Name | |
58 | """ | |
59 | return pack('<H', hint) + name.encode() + b'\0' | |
60 | ||
61 | ||
62 | # ---------------------------------------------------------------------------------------------------------------- | |
63 | def image_thunk_data(imageimportbyname_rva, x64, lastthunk=True): | |
64 | if x64: | |
65 | r = pack('<Q', imageimportbyname_rva ) | |
66 | if lastthunk: | |
67 | r += pack('<Q', 0 ) | |
68 | else: | |
69 | r = pack('<L', imageimportbyname_rva ) | |
70 | if lastthunk: | |
71 | r += pack('<L', 0 ) | |
72 | return r | |
73 | ||
74 | # ---------------------------------------------------------------------------------------------------------------- | |
75 | def build_image_import_by_name(names, base=0): | |
76 | funcrva = [] | |
77 | r = b'' | |
78 | for i, name in enumerate(names): | |
79 | funcrva.append( len( r ) ) | |
80 | r += image_import_by_name(i, name) | |
81 | return funcrva, r | |
82 | ||
83 | # ---------------------------------------------------------------------------------------------------------------- | |
84 | def build_image_thunk_data(offsets, x64, base=0): | |
85 | r = b'' | |
86 | offd = dict() | |
87 | for i in range(len(offsets)): | |
88 | r += image_thunk_data(base + offsets[i], x64, bool(i==len(offsets)-1)) | |
89 | return r | |
90 | ||
91 | # ---------------------------------------------------------------------------------------------------------------- | |
92 | def section_header(roff, rsize, voff, vsize, name=b'.h4x'): | |
93 | """ | |
94 | +00 BYTE name[8] | |
95 | +08 DWORD virtualsize | |
96 | +12 DWORD virtualaddress | |
97 | +16 DWORD rawsize | |
98 | +20 DWORD rawaddress | |
99 | +24 DWORD relocaddress | |
100 | +28 DWORD linenumbers | |
101 | +32 WORD nrofrelocs | |
102 | +34 WORD nroflinenumbers | |
103 | +36 DWORD characteristics | |
104 | """ | |
105 | return name.ljust(8, b'\x00') + pack('<LLLLLLHHL', vsize, voff, rsize, roff, 0, 0, 0, 0, 0xC0000040) | |
106 | ||
107 | # ---------------------------------------------------------------------------------------------------------------- | |
108 | def rebuild_import_table(file_data, impt): | |
109 | # collect some pe info from the file for later | |
110 | pe = pefile.PE(data=file_data, fast_load=True) | |
111 | is64bits = bool(pe.OPTIONAL_HEADER.Magic == 0x20b) | |
112 | nrofsections = pe.FILE_HEADER.NumberOfSections | |
113 | secalignment = pe.OPTIONAL_HEADER.SectionAlignment | |
114 | filealignment = pe.OPTIONAL_HEADER.FileAlignment | |
115 | sizeofheaders = pe.OPTIONAL_HEADER.SizeOfHeaders | |
116 | lastsecoffset = pe.sections[nrofsections-1].__file_offset__ | |
117 | lastsection = pe.sections[nrofsections-1] | |
118 | lastviraddr = lastsection.VirtualAddress + lastsection.Misc_VirtualSize | |
119 | sizeofimage = pe.OPTIONAL_HEADER.SizeOfImage | |
120 | pe.close() | |
121 | ||
122 | imp_tbl = b'' | |
123 | imp_disc = b'' | |
124 | rvas = dict() | |
125 | for dllname in impt: | |
126 | importbyname_offsets, importbyname_data = build_image_import_by_name(impt[dllname]) | |
127 | thunk_data = build_image_thunk_data(importbyname_offsets, is64bits, sizeofimage + len(imp_tbl) + len(dllname) + 1) | |
128 | ||
129 | name_rva = len(imp_tbl) | |
130 | ||
131 | imp_tbl += dllname.encode() + b'\x00' | |
132 | imp_tbl += importbyname_data | |
133 | imp_tbl = align_data(imp_tbl, 8) | |
134 | firstthunk_rva = sizeofimage + len(imp_tbl) | |
135 | imp_tbl += thunk_data | |
136 | ||
137 | rvas[dllname] = dict() | |
138 | for i, funcname in enumerate(impt[dllname]): | |
139 | rvas[dllname][funcname] = firstthunk_rva + (i * 8) | |
140 | ||
141 | imp_disc += image_import_descriptor( firstthunk_rva, sizeofimage + name_rva, firstthunk_rva ) | |
142 | ||
143 | imp_disc += image_import_descriptor( 0, 0, 0 ) | |
144 | imp_tbl = align_data(imp_tbl, 4) | |
145 | ||
146 | import_dir_rva = sizeofimage + len(imp_tbl) | |
147 | imp_tbl += imp_disc | |
148 | newsec_data = align_data(imp_tbl, filealignment) | |
149 | ||
150 | newsec_rawsize = len(newsec_data) | |
151 | # get the alignd virtual offset and size | |
152 | newsec_viraddr = align_int(lastviraddr, secalignment) | |
153 | newsec_virsize = align_int(newsec_rawsize, secalignment) | |
154 | newsec_rawaddr = len(file_data) | |
155 | ||
156 | # create a section header | |
157 | newsec_header = section_header(newsec_rawaddr, newsec_rawsize, newsec_viraddr, newsec_virsize) | |
158 | ||
159 | # contruct the new pe file | |
160 | new_pe = file_data[:lastsecoffset + SECTION_HEADER_SIZE] | |
161 | new_pe += newsec_header | |
162 | new_pe += (sizeofheaders - len(new_pe)) * b'\x00' | |
163 | new_pe += file_data[sizeofheaders:] | |
164 | new_pe += newsec_data | |
165 | ||
166 | # parse the pe of the rebuild data | |
167 | pe = pefile.PE(data=new_pe, fast_load=True) | |
168 | # increase nr of sections | |
169 | pe.FILE_HEADER.NumberOfSections += 1 | |
170 | # update the imagesize | |
171 | pe.OPTIONAL_HEADER.SizeOfImage = newsec_viraddr + newsec_virsize | |
172 | # update the data_dir[imports] rva and size | |
173 | pe.OPTIONAL_HEADER.DATA_DIRECTORY[ pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'] ].VirtualAddress = import_dir_rva | |
174 | pe.OPTIONAL_HEADER.DATA_DIRECTORY[ pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'] ].Size = newsec_rawsize + IMAGE_DESCRIPTOR_HEADER_SIZE | |
175 | # since this is removed from the header we reset its values if set | |
176 | bound_imorts_dir = pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT'] | |
177 | if pe.OPTIONAL_HEADER.DATA_DIRECTORY[ bound_imorts_dir ].VirtualAddress != 0: | |
178 | pe.OPTIONAL_HEADER.DATA_DIRECTORY[ bound_imorts_dir ].VirtualAddress = 0 | |
179 | pe.OPTIONAL_HEADER.DATA_DIRECTORY[ bound_imorts_dir ].Size = 0 | |
180 | ||
181 | # write to new file | |
182 | return pe.write(), rvas | |
183 | ||
184 | # ---------------------------------------------------------------------------------------------------------------- | |
185 | def hook_code64(uc, address, size, user_data): | |
186 | code = uc.mem_read(address, size) | |
187 | insn = disassemble(code, 0) | |
188 | print_insn(insn) | |
189 | ||
190 | # ---------------------------------------------------------------------------------------------------------------- | |
191 | def emu(pe, offsets, imports, tracecode=False): | |
192 | imp = dict() | |
193 | imgbase = pe.OPTIONAL_HEADER.ImageBase | |
194 | imgsize = pe.OPTIONAL_HEADER.SizeOfImage | |
195 | ||
196 | esp = 0x1000000 | |
197 | ||
198 | mu = Uc(UC_ARCH_X86, UC_MODE_64) | |
199 | ||
200 | mu.mem_map(imgbase, imgsize) | |
201 | mu.mem_map(esp, 2 * 1024 * 1024) | |
202 | ||
203 | mu.mem_write(imgbase, bytes(pe.__data__)) | |
204 | mu.mem_write(esp, int(2 * 1024 * 1024) * b'\0') | |
205 | ||
206 | if tracecode: mu.hook_add(UC_HOOK_CODE, hook_code64) | |
207 | ||
208 | for offset, vm_eip in offsets: | |
209 | mu.reg_write(UC_X86_REG_ESP, esp + int(1 * 1024 * 1024)) | |
210 | try: | |
211 | mu.emu_start(imgbase + (vm_eip + 0x1000), imgbase + imgsize) | |
212 | except UcError as e: | |
213 | rip = mu.reg_read(UC_X86_REG_RIP) | |
214 | if rip in imports: | |
215 | print('0x%016x => %s' % (offset, imports[rip])) | |
216 | imp[offset] = imports[rip] | |
217 | else: | |
218 | print(f'0x{rip:016x} not found') | |
219 | return imp | |
220 | ||
221 | # ---------------------------------------------------------------------------------------------------------------- | |
222 | def print_insn(insn): | |
223 | print("0x%016x: %s %s" % (insn.address, insn.mnemonic.ljust(5, ' '), insn.op_str)) | |
224 | ||
225 | # ---------------------------------------------------------------------------------------------------------------- | |
226 | def disassemble(code, ep, maxinslen=12): | |
227 | for insn in md.disasm(code[ep:ep+maxinslen], ep): | |
228 | return insn | |
229 | ||
230 | # ---------------------------------------------------------------------------------------------------------------- | |
231 | def call_ins_in_range(code, minaddress, maxaddress): | |
232 | ep = 0 | |
233 | codelen = len(code) | |
234 | addresses = list() | |
235 | while ep < codelen: | |
236 | insn = disassemble(code, ep) | |
237 | if insn and insn.size == 5 and insn.id == X86_INS_CALL \ | |
238 | and insn.operands[0].type == X86_OP_IMM: | |
239 | addr = insn.operands[0].imm & 0xffffffff | |
240 | if addr >= minaddress and addr <= maxaddress: | |
241 | addresses.append( (ep, addr) ) | |
242 | ep += 1 | |
243 | return addresses | |
244 | ||
245 | # ---------------------------------------------------------------------------------------------------------------- | |
246 | def dump_fix(img, oep=None): | |
247 | pe = pefile.PE(data=img, fast_load=True) | |
248 | sectionAlignment = pe.OPTIONAL_HEADER.SectionAlignment | |
249 | for section in pe.sections: | |
250 | section.PointerToRawData = section.VirtualAddress | |
251 | vsize = align_int(section.Misc_VirtualSize, sectionAlignment) | |
252 | # section.SizeOfRawData = section.Misc_VirtualSize | |
253 | section.SizeOfRawData = vsize | |
254 | section.Misc_VirtualSize = vsize | |
255 | ||
256 | if oep: | |
257 | pe.OPTIONAL_HEADER.AddressOfEntryPoint = oep | |
258 | ||
259 | # disable aslr for now, we need to do a reloc correction actualy | |
260 | pe.OPTIONAL_HEADER.DllCharacteristics ^= 0x40 | |
261 | ||
262 | return pe.write() | |
263 | ||
264 | # ---------------------------------------------------------------------------------------------------------------- | |
265 | def analyse_vmp_api_stub(code, address): | |
266 | vmp_api = list() | |
267 | while True: | |
268 | insn = disassemble(code, address) | |
269 | if insn.id in [X86_INS_PUSH, X86_INS_POP]: | |
270 | vmp_api.append(insn) | |
271 | elif insn.id in [X86_INS_LEA, X86_INS_MOV] and (insn.operands[0].type == X86_OP_MEM or insn.operands[1].type == X86_OP_MEM): | |
272 | vmp_api.append(insn) | |
273 | elif insn.id == X86_INS_XCHG and insn.operands[0].type != insn.operands[1].type: | |
274 | vmp_api.append(insn) | |
275 | elif insn.id == X86_INS_RET: | |
276 | vmp_api.append(insn) | |
277 | break | |
278 | ||
279 | if insn.id == X86_INS_JMP: | |
280 | address = insn.operands[0].imm | |
281 | else: | |
282 | address += insn.size | |
283 | return vmp_api | |
284 | ||
285 | # ---------------------------------------------------------------------------------------------------------------- | |
286 | def action_callback_page_access( event ): | |
287 | global target_pe | |
288 | ||
289 | process = event.get_process() | |
290 | thread = event.get_thread() | |
291 | context = thread.get_context() | |
292 | ||
293 | img_base = process.get_image_base() | |
294 | ||
295 | rip = context['Rip'] | |
296 | ||
297 | # we need this rip range filter, because we keep hiting a vmp section first...?! | |
298 | if rip >= img_base + target_pe.sections[0].VirtualAddress \ | |
299 | and rip <= img_base + target_pe.sections[0].VirtualAddress + target_pe.sections[0].Misc_VirtualSize: | |
300 | print(f'page_access: OEP => 0x{rip:016x} - 0x{rip-img_base:016x}') | |
301 | event.debug.erase_all_breakpoints() | |
302 | ||
303 | img_pe = dump_fix(process.read( img_base, target_pe.OPTIONAL_HEADER.SizeOfImage ), rip-img_base) | |
304 | ||
305 | pe = pefile.PE(data=img_pe, fast_load=True) | |
306 | pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']]) | |
307 | ||
308 | # grab the imported dll names from the import table | |
309 | implookup = dict() | |
310 | for entry in pe.DIRECTORY_ENTRY_IMPORT: | |
311 | dllname = entry.dll.decode() | |
312 | mod = process.get_module_by_name(dllname) | |
313 | ||
314 | pedll = pefile.PE('c:\\windows\\system32\\' + dllname, fast_load=True) | |
315 | pedll.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']]) | |
316 | ||
317 | print(f'--> {dllname}') | |
318 | for exp in pedll.DIRECTORY_ENTRY_EXPORT.symbols: | |
319 | if exp.name == None: | |
320 | continue | |
321 | addr = mod.resolve( exp.name ) | |
322 | if addr not in implookup: | |
323 | implookup[addr] = (dllname, exp.name.decode()) | |
324 | pedll.close() | |
325 | ||
326 | # locate call's into the vmp section | |
327 | code_section_data = pe.sections[0].get_data() | |
328 | vm_section = pe.sections[4] | |
329 | ||
330 | calltos = call_ins_in_range( | |
331 | code_section_data, | |
332 | vm_section.VirtualAddress, | |
333 | vm_section.VirtualAddress + vm_section.Misc_VirtualSize | |
334 | ) | |
335 | ||
336 | print('got %d vmp_api call(s)' % len(calltos)) | |
337 | ||
338 | # emulate the vmp api stubs | |
339 | call2imp = emu(pe, calltos, implookup) | |
340 | ||
341 | pe.close() | |
342 | ||
343 | # filter imports for iat rebuilding | |
344 | imptbl = dict() | |
345 | for callfrom in call2imp: | |
346 | dll, func = call2imp[callfrom] | |
347 | if dll not in imptbl: | |
348 | imptbl[dll] = list() | |
349 | ||
350 | if func not in imptbl[dll]: | |
351 | imptbl[dll].append( func ) | |
352 | ||
353 | # rebuild import table | |
354 | img_pe, iat_rvas = rebuild_import_table(img_pe, imptbl) | |
355 | ||
356 | # patch vmp call instructions | |
357 | fio = BytesIO(img_pe) | |
358 | ks = Ks(KS_ARCH_X86, KS_MODE_64) | |
359 | for cfrom, cto in calltos: | |
360 | vmpapi = analyse_vmp_api_stub(img_pe, cto + 0x1000) | |
361 | if vmpapi[2].id == X86_INS_LEA and vmpapi[2].operands[1].type == X86_OP_MEM and vmpapi[2].operands[1].mem.disp == 1: | |
362 | """ | |
363 | 0x00000000000fd9eb: push rax | |
364 | 0x00000000000fd9f7: mov rax, qword ptr [rsp + 8] | |
365 | 0x0000000000242ad2: lea rax, [rax + 1] | |
366 | 0x00000000001b7986: mov qword ptr [rsp + 8], rax | |
367 | 0x00000000001b7992: lea rax, [rip - 0x1af489] | |
368 | 0x0000000000173257: mov rax, qword ptr [rax + 0xd5f69] | |
369 | 0x000000000017325e: lea rax, [rax + 0x7fde623c] | |
370 | 0x000000000023dff5: xchg qword ptr [rsp], rax | |
371 | 0x0000000000256c24: ret | |
372 | """ | |
373 | dllname, funcname = call2imp[cfrom] | |
374 | encoding, count = ks.asm('call qword ptr[0x%016x]' % (iat_rvas[dllname][funcname] - 0x1000 - cfrom)) | |
375 | fio.seek( cfrom + 0x1000 ) | |
376 | fio.write( bytes(encoding) ) | |
377 | elif vmpapi[0].id == X86_INS_POP and vmpapi[-1].size == 1: | |
378 | """ | |
379 | 0x000000000020c562: pop rbx | |
380 | 0x000000000020bcda: xchg qword ptr [rsp], rbx | |
381 | 0x00000000000fcc27: push rbx | |
382 | 0x00000000000fcc28: lea rbx, [rip - 0xf30ea] | |
383 | 0x0000000000162a3c: mov rbx, qword ptr [rbx + 0x261566] | |
384 | 0x00000000000e2e6f: lea rbx, [rbx + 0x19bd2ee5] | |
385 | 0x00000000000e2e76: xchg qword ptr [rsp], rbx | |
386 | 0x0000000000259b32: ret | |
387 | """ | |
388 | dllname, funcname = call2imp[cfrom] | |
389 | encoding, count = ks.asm('call qword ptr[0x%016x]' % (iat_rvas[dllname][funcname] - 0x1000 - (cfrom - 1))) | |
390 | fio.seek( (cfrom - 1) + 0x1000 ) | |
391 | fio.write( bytes(encoding) ) | |
392 | elif vmpapi[0].id == X86_INS_POP and vmpapi[-1].size == 3: | |
393 | """ | |
394 | 0x0000000000199b18: pop rbp | |
395 | 0x0000000000287e2e: xchg qword ptr [rsp], rbp | |
396 | 0x000000000016eeff: push rbp | |
397 | 0x000000000016ef04: lea rbp, [rip - 0x167a8b] | |
398 | 0x00000000002735e8: mov rbp, qword ptr [rbp + 0x10440d] | |
399 | 0x00000000000fcc15: lea rbp, [rbp + 0x1c97310c] | |
400 | 0x00000000001885a6: xchg qword ptr [rsp], rbp | |
401 | 0x0000000000150cb8: ret 8 | |
402 | """ | |
403 | dllname, funcname = call2imp[cfrom] | |
404 | # print(f'pop_call_ret8: 0x{img_base+0x1000+cfrom:016x} -> {funcname}') | |
405 | encoding, count = ks.asm('jmp qword ptr[0x%016x]' % (iat_rvas[dllname][funcname] - 0x1000 - (cfrom - 2))) | |
406 | fio.seek( (cfrom - 2) + 0x1000 ) | |
407 | fio.write( bytes(encoding) ) | |
408 | elif vmpapi[0].id == X86_INS_PUSH and vmpapi[-1].size == 3: | |
409 | """ | |
410 | 0x0000000000149386: push rsi | |
411 | 0x000000000014938a: lea rsi, [rip - 0x144344] | |
412 | 0x00000000001bfe00: mov rsi, qword ptr [rsi + 0x12b866] | |
413 | 0x0000000000224a7c: lea rsi, [rsi + 0x95a4b96] | |
414 | 0x00000000001f6ad4: xchg qword ptr [rsp], rsi | |
415 | 0x00000000001f6ad8: ret 8 | |
416 | """ | |
417 | dllname, funcname = call2imp[cfrom] | |
418 | # print(f'push_call_ret8: 0x{img_base+0x1000+cfrom:016x} -> {funcname}') | |
419 | dist = 1 if img_pe[(cfrom-1)+0x1000] == 0x48 else 0 | |
420 | encoding, count = ks.asm('jmp qword ptr[0x%016x]' % (iat_rvas[dllname][funcname] - 0x1000 - (cfrom - dist))) | |
421 | fio.seek( (cfrom - dist) + 0x1000 ) | |
422 | fio.write( bytes(encoding) ) | |
423 | ||
424 | else: | |
425 | print(f'unknown!!!! 0x{img_base+0x1000+cfrom:016x}') | |
426 | ||
427 | fio.seek(0) | |
428 | with open('dump.exe', 'wb') as fout: | |
429 | fout.write( fio.read() ) | |
430 | ||
431 | print('PE image dumped') | |
432 | print('Done') | |
433 | exit(1) | |
434 | ||
435 | # ---------------------------------------------------------------------------------------------------------------- | |
436 | def action_callback_NtProtectVirtualMemory( event ): | |
437 | global target_pe | |
438 | global mem_pages | |
439 | ||
440 | process = event.get_process() | |
441 | thread = event.get_thread() | |
442 | context = thread.get_context() | |
443 | ||
444 | img_base = process.get_image_base() | |
445 | img_size = target_pe.OPTIONAL_HEADER.SizeOfImage | |
446 | ||
447 | address = process.read_qword(context['Rdx']) | |
448 | size = process.read_qword(context['R8']) | |
449 | mode = context['R9'] | |
450 | ||
451 | if address >= img_base and address <= img_base + img_size: | |
452 | print(f'NtProtectVirtualMemory: address=0x{address:016x} size=0x{size:016x} prot={mode:x}') | |
453 | if address not in mem_pages: | |
454 | mem_pages.append( address ) | |
455 | elif len(mem_pages) > 1 and address == mem_pages[-1]: | |
456 | # memory bp on the .text(0) section | |
457 | event.debug.erase_all_breakpoints() | |
458 | pid = process.get_pid() | |
459 | pages = (align_int(target_pe.sections[0].Misc_VirtualSize, 4096) // 4096) | |
460 | print(f'page_breakpoint: address=0x{img_base + target_pe.sections[0].VirtualAddress:016x} pages={pages} size=0x{pages*4096:x}') | |
461 | event.debug.define_page_breakpoint( | |
462 | pid, | |
463 | img_base + target_pe.sections[0].VirtualAddress, | |
464 | pages=pages, | |
465 | action=action_callback_page_access) | |
466 | event.debug.enable_page_breakpoint(pid, img_base + target_pe.sections[0].VirtualAddress) | |
467 | ||
468 | # ---------------------------------------------------------------------------------------------------------------- | |
469 | class MyEventHandler( EventHandler ): | |
470 | def load_dll( self, event ): | |
471 | module = event.get_module() | |
472 | if module.match_name('ntdll.dll'): | |
473 | # set HWBP on ntdll.NtProtectVirtualMemory | |
474 | address = module.resolve( 'NtProtectVirtualMemory' ) | |
475 | tid = event.get_thread().get_tid() | |
476 | event.debug.define_hardware_breakpoint( | |
477 | tid, | |
478 | address, | |
479 | triggerFlag=Debug.BP_BREAK_ON_EXECUTION, | |
480 | sizeFlag=Debug.BP_WATCH_BYTE, | |
481 | action=action_callback_NtProtectVirtualMemory) | |
482 | event.debug.enable_hardware_breakpoint(tid, address) | |
483 | ||
484 | # ---------------------------------------------------------------------------------------------------------------- | |
485 | def debugger( argv ): | |
486 | global target_pe | |
487 | global mem_pages | |
488 | ||
489 | target_pe = pefile.PE(argv[0], fast_load=True) | |
490 | mem_pages = list() | |
491 | ||
492 | with Debug( MyEventHandler(), bKillOnExit = True ) as debug: | |
493 | debug.execv( argv ) | |
494 | debug.loop() | |
495 | ||
496 | # ---------------------------------------------------------------------------------------------------------------- | |
497 | if __name__ == "__main__": | |
498 | debugger( sys.argv[1:] ) |