Advertisement
gavzik

VMProtect

Sep 25th, 2024
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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:] )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement