├── README.md ├── apply_all_signatures.py ├── color_as_default.py ├── create_suspended_process.py ├── find_ARMB_prologue.py ├── highlight_all_CALLs.py ├── load_IAT.py ├── mem2file.py ├── merge_functions.py ├── parse_ARM_SEH.py ├── parse_x64_SEH.py ├── rotate.py ├── show_SEH_chain.py └── visualize_binary.py /README.md: -------------------------------------------------------------------------------- 1 | scripts_for_RE 2 | ============== 3 | 4 | Python scripts for reverse engineering. 5 | 6 | create_suspended_process.py 7 | ---------------------------- 8 | Launches a suspended process. 9 | 10 | mem2file.py 11 | ---------------------------- 12 | Modifies the give raw PE memory dump file to load it with IDA properly. 13 | 14 | load_IAT.py 15 | ---------------------------- 16 | (IDA Only) Loads an output of a 'dps' command and apply it to the IDB file. 17 | 18 | parse_x64_SEH.py 19 | ---------------------------- 20 | (IDA Only) Locates SEH try blocks, exception filters and handlers for x64 Windows. 21 | 22 | parse_ARM_SEH.py 23 | ---------------------------- 24 | (IDA Only) Locates SEH try blocks, exception filters and handlers for Windows RT. 25 | 26 | merge_functions.py 27 | ---------------------------- 28 | (IDA Only) Merges a given function with the next function. 29 | 30 | visualize_binary.py 31 | ---------------------------- 32 | Generates a PNG image file that represents the contents of a specified file. 33 | 34 | apply_all_signatures.py 35 | ---------------------------- 36 | (IDA Only) Applies all FLIRT signatures in a /sig directory. 37 | 38 | color_as_default.py 39 | ---------------------------- 40 | (IDA Only) Changes all instructions color to default. 41 | 42 | find_ARMB_prologue.py 43 | ---------------------------- 44 | (IDA Only) Finds function-prologue-like byte sequences for ARMB. 45 | 46 | highlight_all_CALLs.py 47 | ---------------------------- 48 | (IDA Only) Highlights all function call instructions in a given binary file. 49 | 50 | show_SEH_chain.py 51 | ---------------------------- 52 | (IDA Only) Shows SEH chains (stack and handlers) for all threads. 53 | 54 | rotate.py 55 | ---------------------------- 56 | Provides \__ROR4__, \__ROR8__, \__ROL4__ and \__ROL8__ functions. 57 | 58 | -------------------------------------------------------------------------------- /apply_all_signatures.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # (IDA Pro Only) Applies all FLIRT signatures in a /sig directory. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2013 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | from idc import * 30 | from idaapi import * 31 | from idautils import * 32 | 33 | 34 | def main(): 35 | sig_dir = os.path.join(os.path.dirname(sys.executable), 'sig') 36 | for name in os.listdir(sig_dir): 37 | if name[-4:] == '.sig': ApplySig(name) 38 | 39 | 40 | if __name__=='__main__': 41 | main() 42 | 43 | -------------------------------------------------------------------------------- /color_as_default.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # (IDA Pro Only) Changes all instructions color to the default. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2013 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | from idc import * 30 | from idaapi import * 31 | from idautils import * 32 | 33 | 34 | def main(): 35 | # For each segment 36 | for segment_begin_ea in Segments(): 37 | segment_end_ea = SegEnd(segment_begin_ea) 38 | 39 | # For each instruction 40 | last_page = 0 41 | for ea in Heads(segment_begin_ea, segment_end_ea): 42 | # Print log if a processing page changed 43 | current_page = (ea & 0xfffff000) 44 | if last_page != current_page: 45 | last_page = current_page 46 | print 'Processing 0x%08X (Range of "%s" is 0x%08X - 0x%08X)' \ 47 | % (last_page, SegName(current_page), segment_begin_ea, \ 48 | segment_end_ea) 49 | 50 | SetColor(ea, CIC_ITEM, DEFCOLOR) 51 | 52 | 53 | if __name__=='__main__': 54 | main() 55 | 56 | -------------------------------------------------------------------------------- /create_suspended_process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Launches a suspended process. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2015 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | ''' 30 | Description: 31 | Launches a specified process with the CREATE_SUSPENDED flag. It can be used 32 | to examine an initial stage of the process. Noth that you may want to use 33 | %windir%\Sysnative to start a 64 bit process from a 32 bit python process. 34 | ''' 35 | import sys 36 | from ctypes import * 37 | 38 | WORD = c_ushort 39 | DWORD = c_ulong 40 | LPBYTE = POINTER(c_ubyte) 41 | LPTSTR = POINTER(c_char) 42 | HANDLE = c_void_p 43 | 44 | 45 | class STARTUPINFO(Structure): 46 | _fields_ = [ 47 | ('cb', DWORD), 48 | ('lpReserved', LPTSTR), 49 | ('lpDesktop', LPTSTR), 50 | ('lpTitle', LPTSTR), 51 | ('dwX', DWORD), 52 | ('dwY', DWORD), 53 | ('dwXSize', DWORD), 54 | ('dwYSize', DWORD), 55 | ('dwXCountChars', DWORD), 56 | ('dwYCountChars', DWORD), 57 | ('dwFillAttribute', DWORD), 58 | ('dwFlags', DWORD), 59 | ('wShowWindow', WORD), 60 | ('cbReserved2', WORD), 61 | ('lpReserved2', LPBYTE), 62 | ('hStdInput', HANDLE), 63 | ('hStdOutput', HANDLE), 64 | ('hStdError', HANDLE), 65 | ] 66 | 67 | 68 | class PROCESS_INFORMATION(Structure): 69 | _fields_ = [ 70 | ('hProcess', HANDLE), 71 | ('hThread', HANDLE), 72 | ('dwProcessId', DWORD), 73 | ('dwThreadId', DWORD), 74 | ] 75 | 76 | 77 | def main(): 78 | if len(sys.argv) != 2: 79 | print 'Launches a suspended process.' 80 | print ' > python {} '.format(sys.argv[0]) 81 | return 82 | exe_file = sys.argv[1] 83 | 84 | kernel32 = windll.kernel32 85 | CREATE_NEW_CONSOLE = 0x00000010 86 | CREATE_SUSPENDED = 0x00000004 87 | creation_flags = CREATE_NEW_CONSOLE | CREATE_SUSPENDED 88 | 89 | startupinfo = STARTUPINFO() 90 | processinfo = PROCESS_INFORMATION() 91 | startupinfo.cb = sizeof(startupinfo) 92 | print '[*] Starting: {}'.format(exe_file) 93 | if kernel32.CreateProcessA( 94 | None, exe_file, None, None, None, creation_flags, None, None, 95 | byref(startupinfo), byref(processinfo)): 96 | print '[+] Process started as PID: {}'.format(processinfo.dwProcessId) 97 | kernel32.CloseHandle(processinfo.hProcess) 98 | kernel32.CloseHandle(processinfo.hThread) 99 | else: 100 | print '[-] CreateProcessA failed with an error: 0x{:08x}'.format( 101 | kernel32.GetLastError()) 102 | 103 | 104 | if __name__ == '__main__': 105 | main() 106 | -------------------------------------------------------------------------------- /find_ARMB_prologue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # (IDA Pro Only) Finds function-prologue-like byte sequences for ARMB. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2013 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | from idc import * 30 | from idaapi import * 31 | from idautils import * 32 | 33 | 34 | def main(): 35 | # For each segment 36 | for segment_begin_ea in Segments(): 37 | segment_end_ea = SegEnd(segment_begin_ea) 38 | # For each instruction 39 | for ea in Heads(segment_begin_ea, segment_end_ea): 40 | code = Word(ea) 41 | if code == 0xe92d: # STMFD SP!, {...} 42 | print '0x%08X .. %-20s %s' \ 43 | % (ea, GetFunctionName(ea), GetDisasm(ea)) 44 | 45 | 46 | if __name__=='__main__': 47 | main() 48 | 49 | -------------------------------------------------------------------------------- /highlight_all_CALLs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # (IDA Only) Highlights all function call instructions in a given binary file. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ############################################################################### 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2013-2015 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ############################################################################### 29 | from idc import * 30 | from idaapi import * 31 | from idautils import * 32 | 33 | 34 | def main(): 35 | processor_name = GetCharPrm(INF_PROCNAME) 36 | if processor_name == 'metapc': 37 | call_instructions = ['call'] 38 | elif processor_name == 'ARM': 39 | call_instructions = ['BL', 'BL.W', 'BX', 'BLX'] 40 | else: 41 | print 'Unsupported processor type: %s' % (processor_name) 42 | return 43 | # For each segment 44 | for segment_begin_ea in Segments(): 45 | segment_end_ea = SegEnd(segment_begin_ea) 46 | # For each instruction 47 | last_page = 0 48 | for ea in list(Heads(segment_begin_ea, segment_end_ea)): 49 | # Print log if a processing page changed 50 | current_page = (ea & 0xffffffffffff0000) 51 | if last_page != current_page: 52 | last_page = current_page 53 | print('Processing 0x%016X (Range of "%s" is 0x%016X - 0x%016X)' % 54 | (last_page, SegName(current_page), segment_begin_ea, 55 | segment_end_ea) 56 | ) 57 | # Set colour if this instruction is any of call instructions 58 | disasm = GetDisasm(ea) 59 | for inst in call_instructions: 60 | if disasm.startswith(inst + ' '): 61 | SetColor(ea, CIC_ITEM, 0xd8bfd8) 62 | 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /load_IAT.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Loads an output of a 'dps' command and apply it to the IDB file. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2015-2021 Satoshi Tanda 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | import re 30 | import ida_kernwin 31 | import ida_bytes 32 | 33 | def main(): 34 | path = ida_kernwin.ask_file(0, '*.*', 'Select a dumped IAT file.') 35 | if not path: 36 | return 37 | for line in open(path, 'r'): 38 | line = line.replace('`', '') # take out ' if exists 39 | # parse an address 40 | if re.match('^[0-9a-f]{8} ', line): 41 | # 32bit 42 | addr = line[0:9] 43 | symbol = line[19:] 44 | bytewise = 4 45 | optype = ida_bytes.FF_DWORD 46 | elif re.match('^[0-9a-f]{16} ', line): 47 | # 64bit 48 | addr = line[0:17] 49 | symbol = line[27:] 50 | bytewise = 8 51 | optype = ida_bytes.FF_DWORD 52 | else: 53 | continue 54 | if re.match('^.+!.+$', symbol) is None: 55 | continue 56 | addr = int(addr, 16) 57 | _, api = symbol.rstrip().split('!') # only needs a function name 58 | 59 | # Remove garbage to make IDA understand API's signature 60 | 61 | # Discard after space (source code path) 62 | api = api.split(' ')[0] 63 | # Fix for ExitProcess often gets a wrong name 64 | if api.endswith('FSPErrorMessages::CMessageMapper::StaticCleanup+0xc'): 65 | api = api.replace('FSPErrorMessages::CMessageMapper::StaticCleanup+0xc', 'ExitProcess') 66 | # Fix for kernelbase.dll related stub functions 67 | if api.endswith('Implementation'): 68 | api = api.replace('Implementation', '') 69 | elif api.endswith('Stub'): 70 | api = api.replace('Stub', '') 71 | # IDA does not like + 72 | api = api.replace('+', '_') 73 | print(hex(addr), api) 74 | 75 | # Set a data type on the IDB 76 | ida_bytes.del_items(addr, bytewise, ida_bytes.DELIT_EXPAND) 77 | ida_bytes.create_data(addr, optype, bytewise, 0) 78 | if idc.set_name(addr, api, SN_CHECK | SN_NOWARN) == 1: 79 | continue 80 | # Try to name it as _N up to _99 81 | for i in range(100): 82 | if idc.set_name(addr, api + '_' + str(i), SN_CHECK | SN_NOWARN) == 1: 83 | break 84 | if i == 99: 85 | idc.set_name(addr, api, SN_CHECK) # Display an error message 86 | print ( 87 | 'Load an appropriate FLIRT signature if it is not applied yet.\n' 88 | 'Then, use [Options] > [General] > [Analysis] > [Reanalyze program] to' 89 | ' reflect those API signatures.' 90 | ) 91 | 92 | if __name__ == '__main__': 93 | main() 94 | -------------------------------------------------------------------------------- /mem2file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Modifies the give raw PE memory dump file to load it with IDA properly. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2015 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | ''' 30 | Description: 31 | Loads a raw memory dump file represents a PE image and modifies its header 32 | values for allowing IDA to populate data into the exact same location as on 33 | process memory. 34 | ''' 35 | import os 36 | import sys 37 | import pefile 38 | import binascii 39 | 40 | 41 | def main(): 42 | if len(sys.argv) != 2 and len(sys.argv) != 3: 43 | print('Fix a raw memory PE file to load it with IDA.') 44 | print(' > python {} [output_file]'.format(sys.argv[0])) 45 | return 46 | input_file_path = sys.argv[1] 47 | if len(sys.argv) == 3: 48 | output_file_path = sys.argv[2] 49 | else: 50 | name, extension = os.path.splitext(input_file_path) 51 | output_file_path = name + '_fixed' + extension 52 | pe = pefile.PE(input_file_path) 53 | # Invalidate the import directory rather than leaving it as is and letting 54 | # IDA interpret it. It will not work out. 55 | imp_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY[ 56 | 'IMAGE_DIRECTORY_ENTRY_IMPORT']] 57 | if imp_dir.VirtualAddress != 0: 58 | print('Import Directory RVA : {:08x} => 0'.format( 59 | imp_dir.VirtualAddress)) 60 | imp_dir.VirtualAddress = 0 61 | # Fix the section headers. 62 | index = 1 63 | for section in pe.sections: 64 | new_raw_size = max(section.SizeOfRawData, section.Misc_VirtualSize) 65 | print('Section {} : \'{}\' {}'.format( 66 | index, section.Name, binascii.hexlify(section.Name))) 67 | print(' SizeOfRawData : {:08x} => {:08x}'.format( 68 | section.SizeOfRawData, new_raw_size)) 69 | print(' PointerToRawData: {:08x} => {:08x}'.format( 70 | section.PointerToRawData, section.VirtualAddress)) 71 | section.SizeOfRawData = new_raw_size 72 | section.PointerToRawData = section.VirtualAddress 73 | index += 1 74 | pe.write(filename=output_file_path) 75 | 76 | 77 | if __name__ == '__main__': 78 | main() 79 | -------------------------------------------------------------------------------- /merge_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """(IDA Pro Only) Merges a given function with the next function 3 | 4 | Author: Satoshi Tanda 5 | 6 | Description: 7 | Merges a given function with the next function by extending the end. 8 | 9 | Usage: 10 | Load the script via [File] > [Script file...] 11 | or 12 | Call merge_functions function with or without parameters from the Python 13 | CLI window. 14 | 15 | Example: 16 | Python>merge_functions(0x00468D6E) 17 | The end of 'sub_468D68' was extended to 0x00468DB1 18 | """ 19 | 20 | LICENSE = """ 21 | The MIT License (MIT) 22 | 23 | Copyright (c) 2014 tandasat 24 | 25 | Permission is hereby granted, free of charge, to any person obtaining a copy of 26 | this software and associated documentation files (the "Software"), to deal in 27 | the Software without restriction, including without limitation the rights to 28 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 29 | the Software, and to permit persons to whom the Software is furnished to do so, 30 | subject to the following conditions: 31 | 32 | The above copyright notice and this permission notice shall be included in all 33 | copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 37 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 38 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 39 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 40 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 | """ 42 | from idc import * 43 | from idaapi import * 44 | from idautils import * 45 | 46 | 47 | def merge_functions(top_func_ea=None): 48 | """Merges a given function with the next function.""" 49 | if not top_func_ea: 50 | prompt = ('Please input any address ' + 51 | 'belongs to the function to be extended.') 52 | top_func_ea = idc.AskAddr(idaapi.get_screen_ea(), prompt) 53 | if top_func_ea == idc.BADADDR or not top_func_ea: 54 | return 55 | next_func = idaapi.get_next_func(top_func_ea) 56 | next_func_name = idc.GetFunctionName(next_func.startEA) 57 | name = idc.GetFunctionName(top_func_ea) 58 | if next_func_name[:4] != 'sub_': 59 | prompt = ( 60 | "A function '" + name + "' will be merged with a next function '" + 61 | next_func_name + "'.\nDo you want to continue?") 62 | if idc.AskYN(0, prompt) != 1: 63 | return 64 | end_ea = idaapi.get_next_func(top_func_ea).endEA 65 | idc.DelFunction(idaapi.get_next_func(top_func_ea).startEA) 66 | idc.SetFunctionEnd(top_func_ea, end_ea) 67 | print "'%s' was extended to 0x%08X" % (name, end_ea) 68 | idc.Jump(end_ea - 1) 69 | 70 | 71 | if __name__ == '__main__': 72 | merge_functions() 73 | -------------------------------------------------------------------------------- /parse_ARM_SEH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Locates SEH try blocks, exception filters and handlers for Windows RT files. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2015 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | 30 | 31 | class RuntimeFuncton(object): 32 | '''Represents RUNTIME_FUNCTION''' 33 | def __init__(self, address): 34 | self.begin_address = Dword(address) + idaapi.get_imagebase() 35 | self.unwind_info = Dword(address + 4) 36 | 37 | def _get_flag(self): 38 | return self.unwind_info & 3 39 | 40 | def _get_content(self): 41 | return self.unwind_info & ~3 42 | 43 | def get_xdata(self): 44 | # A pdata entry has xata when a Flag field is zero. 45 | if self._get_flag(): 46 | return None 47 | name = Name(self.begin_address & ~1) 48 | xdata_addr = (self._get_content() + idaapi.get_imagebase()) 49 | return XdataRecord(name, xdata_addr) 50 | 51 | 52 | class XdataRecord(object): 53 | '''Represents an xdata record''' 54 | def __init__(self, name, address): 55 | self.begin_address = address 56 | MakeDword(address) 57 | if name == '': 58 | name = '_loc_{:08X}'.format(address) 59 | name += '_xdata' 60 | if MakeNameEx(address, name, SN_CHECK | SN_NOWARN) == 0: 61 | MakeNameEx(address, '_' + name, SN_CHECK | SN_NOWARN) 62 | 63 | def get_exp_handler_info(self): 64 | xdata_header = Dword(self.begin_address) 65 | # Check an X field to determine if it has exception information 66 | if (xdata_header & 0x00100000) == 0: 67 | return None 68 | 69 | print('%08x : %s' % (self.begin_address, Name(self.begin_address))) 70 | # Check if either EpilogueCount field or CodeWords field has value 71 | if xdata_header & 0xFF800000: 72 | # Use 1st word 73 | epilogue_count = (xdata_header & 0x0F800000) >> 23 74 | code_words = (xdata_header & 0xF0000000) >> 28 75 | offset = self.begin_address + 4 76 | else: 77 | # It has an extra header; use 2nd word 78 | xdata_header_ex = Dword(self.begin_address + 4) 79 | MakeDword(self.begin_address + 4) 80 | epilogue_count = (xdata_header_ex & 0x0000FFFF) 81 | code_words = (xdata_header_ex & 0x00FF0000) >> 16 82 | offset = self.begin_address + 8 83 | # Consider EpilogueCount when an E field is zero. 84 | if (xdata_header & 0x00200000) == 0 and epilogue_count != 0: 85 | MakeDword(offset) 86 | MakeArray(offset, epilogue_count) 87 | offset += epilogue_count * 4 88 | addr = offset + code_words * 4 89 | MakeByte(offset) # skip Unwind Opcodes 90 | MakeArray(offset, code_words * 4) 91 | return ExceptionHandlerInformation(addr) # get Exception Info 92 | 93 | 94 | class ExceptionHandlerInformation(object): 95 | '''Represents Exception Handler Information (a.k.a, SCOPE_TABLE)''' 96 | def __init__(self, address): 97 | self.address = address 98 | self.exp_handler = Dword(address) + idaapi.get_imagebase() 99 | self.number_of_scope_entries = Dword(address + 4) 100 | self.address_of_scope_entries = address + 8 101 | self.scope_entries = [] 102 | # Some handlers have huge values such as 0xffffffe9 and are not 103 | # supported. 104 | if self.number_of_scope_entries > 0xff000000: 105 | return 106 | for i in range(0, self.number_of_scope_entries): 107 | self.scope_entries.append( 108 | ScopeEntry(self.address_of_scope_entries + i * 16)) 109 | 110 | def apply_to_database(self): 111 | _make_references(self.address, self.exp_handler, 'Handler ') 112 | MakeDword(self.address + 4) 113 | # Since nested SEH blocks show up first in the table, this reversing 114 | # makes comments prettier like this: 115 | # __try{ // outside SEH 116 | # __try{ // nested SEH 117 | # } // nested SEH 118 | # } // outside SEH 119 | for entry in reversed(self.scope_entries): 120 | entry.apply_to_database() 121 | 122 | 123 | class ScopeEntry(object): 124 | '''Represents an entry of SCOPE_TABLE''' 125 | def __init__(self, address): 126 | if Dword(address + 8) == 1: 127 | # Filter may have 1 in it. This is invalid and this code handle it 128 | # as __try/__except but without a valid except filter information. 129 | self.entry = TryInvalidExceptEntry(address) 130 | elif Dword(address + 12) == 0: 131 | # It is __try/__finally when Target has no value. 132 | self.entry = TryFinallyEntry(address) 133 | else: 134 | # It is __try/__except when Filter and Target have valid values. 135 | self.entry = TryExceptEntry(address) 136 | 137 | def apply_to_database(self): 138 | self.entry.apply_to_database() 139 | 140 | 141 | class SEHEntry(object): 142 | '''Implements common things for an SEH SCOPE_TABLE''' 143 | def __init__(self, address): 144 | self.address = address 145 | self.begin = Dword(address) + idaapi.get_imagebase() 146 | self.end = Dword(address + 4) + idaapi.get_imagebase() 147 | 148 | def apply_to_database(self): 149 | _make_references(self.address, self.begin, '__try { ') 150 | _make_references(self.address + 4, self.end, '} //try ') 151 | 152 | 153 | class TryExceptEntryBase(SEHEntry): 154 | '''Implements common things for a __try/__except style SCOPE_TABLE''' 155 | def __init__(self, address): 156 | super(TryExceptEntryBase, self).__init__(address) 157 | 158 | def apply_to_database(self, target, handler): 159 | super(TryExceptEntryBase, self).apply_to_database() 160 | _append_comment( 161 | self.begin, 162 | '__try {{ // till {:08X} }} __except( {:08X} ) {{ {:08X} }}'.format( 163 | self.end & ~1, 164 | handler & ~1, 165 | target & ~1)) 166 | _append_comment( 167 | self.end, 168 | '}} // from {:08X}'.format( 169 | self.begin & ~1)) 170 | _append_comment( 171 | target, 172 | '__except( {:08X} ) {{ here }} // __try {{ {:08X}-{:08X} }}'.format( 173 | handler & ~1, 174 | self.begin & ~1, 175 | self.end & ~1)) 176 | 177 | 178 | class TryExceptEntry(TryExceptEntryBase): 179 | '''Represents a __try/__except style SCOPE_TABLE''' 180 | def __init__(self, address): 181 | super(TryExceptEntry, self).__init__(address) 182 | self.handler = Dword(address + 8) + idaapi.get_imagebase() 183 | self.target = Dword(address + 12) + idaapi.get_imagebase() 184 | 185 | def apply_to_database(self): 186 | super(TryExceptEntry, self).apply_to_database( 187 | self.target, self.handler) 188 | _make_references(self.address + 8, self.handler, 'Filter ') 189 | _make_references(self.address + 12, self.target, 'ExpBody ') 190 | _append_comment( 191 | self.handler, 192 | '__except( here ) {{ {:08X} }} // __try {{ {:08X}-{:08X} }}'.format( 193 | self.target & ~1, 194 | self.begin & ~1, 195 | self.end & ~1)) 196 | 197 | 198 | class TryInvalidExceptEntry(TryExceptEntryBase): 199 | '''Represents a __try/__except style SCOPE_TABLE w/ invalid filter''' 200 | def __init__(self, address): 201 | super(TryInvalidExceptEntry, self).__init__(address) 202 | self.target = Dword(address + 12) + idaapi.get_imagebase() 203 | 204 | def apply_to_database(self): 205 | pass # An invalid handler will never be called 206 | 207 | 208 | class TryFinallyEntry(SEHEntry): 209 | '''Represents a __try/__finally style SCOPE_TABLE''' 210 | def __init__(self, address): 211 | super(TryFinallyEntry, self).__init__(address) 212 | self.handler = Dword(address + 8) + idaapi.get_imagebase() 213 | 214 | def apply_to_database(self): 215 | super(TryFinallyEntry, self).apply_to_database() 216 | _make_references(self.address + 8, self.handler, 'Finally ') 217 | MakeDword(self.address + 12) 218 | _append_comment( 219 | self.begin, 220 | '__try {{ // till {:08X} }} __finally {{ {:08X} }}'.format( 221 | self.end & ~1, 222 | self.handler & ~1)) 223 | _append_comment( 224 | self.end, 225 | '}} // from {:08X}'.format( 226 | self.begin & ~1)) 227 | _append_comment( 228 | self.handler, 229 | '__finally {{ here }} // __try {{ {:08X}-{:08X} }}'.format( 230 | self.begin & ~1, 231 | self.end & ~1)) 232 | 233 | 234 | def _append_comment(address, comment): 235 | old_comment = Comment(address & ~1) 236 | if old_comment == comment: # ignore duplicates 237 | return 238 | elif old_comment: 239 | old_comment += '\n' 240 | else: 241 | old_comment = '' 242 | MakeComm(address & ~1, old_comment + comment) 243 | 244 | 245 | def _make_references(from_address, to_address, comment): 246 | MakeDword(from_address) 247 | add_dref(from_address, to_address, XREF_USER | dr_O) 248 | name = Name(to_address & ~1) 249 | if name == '': 250 | name = '{:08X}'.format(to_address) 251 | _append_comment(from_address, comment + ': ' + name) 252 | 253 | 254 | def main(): 255 | # Enumerates .pdata section until 256 | segments = idaapi.get_segm_by_name('.pdata') 257 | address = segments.startEA 258 | segment_end = segments.endEA 259 | while address < segment_end: 260 | if Dword(address) == 0: 261 | break 262 | # try to get exception info from RUNTIME_FUNCTION and apply it 263 | runtime_function = RuntimeFuncton(address) 264 | xdata = runtime_function.get_xdata() 265 | if xdata: 266 | exception_info = xdata.get_exp_handler_info() 267 | if exception_info: 268 | exception_info.apply_to_database() 269 | address += 8 # size of RUNTIME_FUNCTION 270 | 271 | 272 | if __name__ == '__main__': 273 | main() 274 | -------------------------------------------------------------------------------- /parse_x64_SEH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Locates SEH try blocks, exception filters and handlers for x64 Windows files. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2015 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | 30 | 31 | class RuntimeFuncton(object): 32 | '''Represents RUNTIME_FUNCTION''' 33 | def __init__(self, address): 34 | self.begin_address = Dword(address) + idaapi.get_imagebase() 35 | self.unwind_info = Dword(address + 8) 36 | MakeStructEx(address, -1, 'RUNTIME_FUNCTION') 37 | 38 | def get_unwind_info(self): 39 | name = Name(self.begin_address) 40 | return UnwindInfo(name, self.unwind_info + idaapi.get_imagebase()) 41 | 42 | 43 | class UnwindInfo(object): 44 | '''Represents UNWIND_INFO''' 45 | _UNW_FLAG_NHANDLER = 0 46 | _UNW_FLAG_EHANDLER = 1 47 | _UNW_FLAG_UHANDLER = 2 48 | _UNW_FLAG_CHAININFO = 4 49 | 50 | def __init__(self, name, address): 51 | self.begin_address = address 52 | if name == '': 53 | name = '_loc_{:016X}'.format(address) 54 | name += '_unwind_info' 55 | if MakeNameEx(address, name, SN_CHECK | SN_NOWARN) == 0: 56 | MakeNameEx(address, '_' + name, SN_CHECK | SN_NOWARN) 57 | MakeStructEx(address, -1, 'UNWIND_INFO') 58 | 59 | def _has_exception_handler(self): 60 | flag = Byte(self.begin_address) >> 3 61 | if flag & self._UNW_FLAG_CHAININFO: 62 | return False 63 | return ( 64 | flag & self._UNW_FLAG_EHANDLER or 65 | flag & self._UNW_FLAG_UHANDLER 66 | ) 67 | 68 | def get_exp_handler_info(self): 69 | code_count = Byte(self.begin_address + 2) 70 | for i in range(0, code_count): 71 | MakeStructEx(self.begin_address + 4 + (i * 2), -1, 'UNWIND_CODE') 72 | if not self._has_exception_handler(): 73 | return 74 | print('%016X : %s' % (self.begin_address, Name(self.begin_address))) 75 | addr = self.begin_address + 4 + code_count * 2 76 | addr += (addr % 4) # 4 bytes aligned (0->0, 2->4, 4->4, ...) 77 | return ExceptionHandlerInformation(addr) # get Exception Info 78 | 79 | 80 | class ExceptionHandlerInformation(object): 81 | '''Represents Exception Handler Information (a.k.a, SCOPE_TABLE)''' 82 | def __init__(self, address): 83 | self.address = address 84 | self.exp_handler = Dword(address) + idaapi.get_imagebase() 85 | self.number_of_scope_entries = Dword(address + 4) 86 | self.address_of_scope_entries = address + 8 87 | self.scope_entries = [] 88 | # Only some handlers' date formats are supported. 89 | if not self._is_suppoeted_handler(Name(self.exp_handler)): 90 | return 91 | for i in range(0, self.number_of_scope_entries): 92 | self.scope_entries.append( 93 | ScopeEntry(self.address_of_scope_entries + i * 16)) 94 | 95 | def _is_suppoeted_handler(self, handler_name): 96 | SUPPORTED_HANDLER_NAMES = [ 97 | '__GSHandlerCheck_SEH', 98 | '__C_specific_handler', 99 | ] 100 | for name in SUPPORTED_HANDLER_NAMES: 101 | if handler_name.startswith(name): 102 | return True 103 | return False 104 | 105 | def apply_to_database(self): 106 | _make_references(self.address, self.exp_handler, 'Handler ') 107 | MakeDword(self.address + 4) 108 | # Since nested SEH blocks show up first in the table, this reversing 109 | # makes comments prettier like this: 110 | # __try{ // outside SEH 111 | # __try{ // nested SEH 112 | # } // nested SEH 113 | # } // outside SEH 114 | for entry in reversed(self.scope_entries): 115 | entry.apply_to_database() 116 | 117 | 118 | class ScopeEntry(object): 119 | '''Represents an entry of SCOPE_TABLE''' 120 | def __init__(self, address): 121 | if Dword(address + 8) == 1: 122 | # Filter may have 1 in it. This is invalid and this code handle it 123 | # as __try/__except but without a valid except filter information. 124 | self.entry = TryInvalidExceptEntry(address) 125 | elif Dword(address + 12) == 0: 126 | # It is __try/__finally when Target has no value. 127 | self.entry = TryFinallyEntry(address) 128 | else: 129 | # It is __try/__except when Filter and Target have valid values. 130 | self.entry = TryExceptEntry(address) 131 | 132 | def apply_to_database(self): 133 | self.entry.apply_to_database() 134 | 135 | 136 | class SEHEntry(object): 137 | '''Implements common things for an SEH SCOPE_TABLE''' 138 | def __init__(self, address): 139 | self.address = address 140 | self.begin = Dword(address) + idaapi.get_imagebase() 141 | self.end = Dword(address + 4) + idaapi.get_imagebase() 142 | 143 | def apply_to_database(self): 144 | _make_references(self.address, self.begin, '__try { ') 145 | _make_references(self.address + 4, self.end, '} //try ') 146 | 147 | 148 | class TryExceptEntryBase(SEHEntry): 149 | '''Implements common things for a __try/__except style SCOPE_TABLE''' 150 | def __init__(self, address): 151 | super(TryExceptEntryBase, self).__init__(address) 152 | 153 | def apply_to_database(self, target, handler): 154 | super(TryExceptEntryBase, self).apply_to_database() 155 | _append_comment( 156 | self.begin, 157 | '__try {{ // till {:016X} }} __except( {:016X} ) {{ {:016X} }}'.format( 158 | self.end, 159 | handler, 160 | target)) 161 | _append_comment( 162 | self.end, 163 | '}} // from {:016X}'.format( 164 | self.begin)) 165 | _append_comment( 166 | target, 167 | '__except( {:016X} ) {{ here }} // __try {{ {:016X}-{:016X} }}'.format( 168 | handler, 169 | self.begin, 170 | self.end)) 171 | 172 | 173 | class TryExceptEntry(TryExceptEntryBase): 174 | '''Represents a __try/__except style SCOPE_TABLE''' 175 | def __init__(self, address): 176 | super(TryExceptEntry, self).__init__(address) 177 | self.handler = Dword(address + 8) + idaapi.get_imagebase() 178 | self.target = Dword(address + 12) + idaapi.get_imagebase() 179 | 180 | def apply_to_database(self): 181 | super(TryExceptEntry, self).apply_to_database( 182 | self.target, self.handler) 183 | _make_references(self.address + 8, self.handler, 'Filter ') 184 | _make_references(self.address + 12, self.target, 'ExpBody ') 185 | _append_comment( 186 | self.handler, 187 | '__except( here ) {{ {:016X} }} // __try {{ {:016X}-{:016X} }}'.format( 188 | self.target, 189 | self.begin, 190 | self.end)) 191 | 192 | 193 | class TryInvalidExceptEntry(TryExceptEntryBase): 194 | '''Represents a __try/__except style SCOPE_TABLE w/ invalid filter''' 195 | def __init__(self, address): 196 | super(TryInvalidExceptEntry, self).__init__(address) 197 | self.target = Dword(address + 12) + idaapi.get_imagebase() 198 | 199 | def apply_to_database(self): 200 | pass # An invalid handler will never be called 201 | 202 | 203 | class TryFinallyEntry(SEHEntry): 204 | '''Represents a __try/__finally style SCOPE_TABLE''' 205 | def __init__(self, address): 206 | super(TryFinallyEntry, self).__init__(address) 207 | self.handler = Dword(address + 8) + idaapi.get_imagebase() 208 | 209 | def apply_to_database(self): 210 | super(TryFinallyEntry, self).apply_to_database() 211 | _make_references(self.address + 8, self.handler, 'Finally ') 212 | MakeDword(self.address + 12) 213 | _append_comment( 214 | self.begin, 215 | '__try {{ // till {:016X} }} __finally {{ {:016X} }}'.format( 216 | self.end, 217 | self.handler)) 218 | _append_comment( 219 | self.end, 220 | '}} // from {:016X}'.format( 221 | self.begin)) 222 | _append_comment( 223 | self.handler, 224 | '__finally {{ here }} // __try {{ {:016X}-{:016X} }}'.format( 225 | self.begin, 226 | self.end)) 227 | 228 | 229 | def _append_comment(address, comment): 230 | old_comment = Comment(address) 231 | if old_comment == comment: # ignore duplicates 232 | return 233 | elif old_comment: 234 | old_comment += '\n' 235 | else: 236 | old_comment = '' 237 | MakeComm(address, old_comment + comment) 238 | 239 | 240 | def _make_references(from_address, to_address, comment): 241 | MakeDword(from_address) 242 | add_dref(from_address, to_address, XREF_USER | dr_O) 243 | name = Name(to_address) 244 | if name == '': 245 | name = '{:016X}'.format(to_address) 246 | _append_comment(from_address, comment + ': ' + name) 247 | 248 | 249 | def main(): 250 | # Enumerates .pdata section until 251 | segments = idaapi.get_segm_by_name('.pdata') 252 | address = segments.startEA 253 | segment_end = segments.endEA 254 | while address < segment_end: 255 | if Dword(address) == 0: 256 | break 257 | # try to get exception info from RUNTIME_FUNCTION and apply it 258 | runtime_function = RuntimeFuncton(address) 259 | unwind_info = runtime_function.get_unwind_info() 260 | if unwind_info: 261 | exception_info = unwind_info.get_exp_handler_info() 262 | if exception_info: 263 | exception_info.apply_to_database() 264 | address += 12 # size of RUNTIME_FUNCTION 265 | 266 | 267 | if __name__ == '__main__': 268 | main() 269 | -------------------------------------------------------------------------------- /rotate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Provides __ROR4__, __ROR8__, __ROL4__ and __ROL8__ functions. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2014 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | def _rol(val, bits, bit_size): 30 | return (val << bits % bit_size) & (2 ** bit_size - 1) | \ 31 | ((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size))) 32 | 33 | def _ror(val, bits, bit_size): 34 | return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \ 35 | (val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1)) 36 | 37 | __ROR4__ = lambda val, bits: _ror(val, bits, 32) 38 | __ROR8__ = lambda val, bits: _ror(val, bits, 64) 39 | __ROL4__ = lambda val, bits: _rol(val, bits, 32) 40 | __ROL8__ = lambda val, bits: _rol(val, bits, 64) 41 | 42 | print('__ROR4__, __ROR8__, __ROL4__ and __ROL8__ were defined.') 43 | print('Try this in the Python interpreter:') 44 | print('hex(__ROR8__(0xD624722D3A28E80F, 0xD6))') 45 | 46 | -------------------------------------------------------------------------------- /show_SEH_chain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # (IDA Pro Only) Shows SEH chains (stack and handlers) for all threads. 4 | # 5 | # Author: Satoshi Tanda 6 | # 7 | ################################################################################ 8 | # The MIT License (MIT) 9 | # 10 | # Copyright (c) 2013 tandasat 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 | # the Software, and to permit persons to whom the Software is furnished to do so, 17 | # subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 26 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ################################################################################ 29 | from idc import * 30 | from idaapi import * 31 | from idautils import * 32 | 33 | 34 | def GetFsBase(tid): 35 | idc.SelectThread(tid) 36 | return idaapi.dbg_get_thread_sreg_base(tid, cpu.fs) 37 | 38 | 39 | def GetExceptionChain(tid): 40 | fs_base = GetFsBase(tid) 41 | exc_rr = Dword(fs_base) 42 | result = [] 43 | while exc_rr != 0xffffffff: 44 | prev = Dword(exc_rr) 45 | handler = Dword(exc_rr + 4) 46 | print '%6d %08X %08X' % (tid, exc_rr + 4, handler) 47 | exc_rr = prev 48 | result.append(handler) 49 | return result 50 | 51 | 52 | def main(): 53 | print 'TID Address Handler' 54 | curr_tid = idc.GetCurrentThreadId() 55 | result = {} 56 | for tid in idautils.Threads(): 57 | result[tid] = GetExceptionChain(tid) 58 | idc.SelectThread(curr_tid) 59 | 60 | 61 | if __name__=='__main__': 62 | main() 63 | 64 | -------------------------------------------------------------------------------- /visualize_binary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ################################################################################ 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (c) 2013 tandasat 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | # this software and associated documentation files (the "Software"), to deal in 9 | # the Software without restriction, including without limitation the rights to 10 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | # the Software, and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ################################################################################ 24 | """Generates a PNG image file that represents the contents of a specified file. 25 | 26 | Author: Satoshi Tanda 27 | 28 | Description: 29 | Reads a specified file and converts each bytes to a pixel, and generates PNG 30 | image file that is named .png in the same directory as an input 31 | file. The conversion rule follows the rule of a hex editor, Stirling. To use 32 | this script you need a PIL module: 33 | http://www.pythonware.com/products/pil/ 34 | 35 | Usage: 36 | $ python this.py 37 | 38 | Args: 39 | target_file: a target file path to create an image file. 40 | """ 41 | 42 | # Standard 43 | import sys 44 | import os 45 | import math 46 | 47 | # Third Party 48 | import Image 49 | 50 | # Original 51 | 52 | 53 | def main(arg_values, arg_length): 54 | """Main routine""" 55 | 56 | if arg_length != 2: 57 | help(os.path.splitext(os.path.basename(sys.argv[0]))[0]) 58 | return 59 | 60 | input_file_name = arg_values[1] 61 | input_file = open(input_file_name, "rb") 62 | input_data = bytearray(input_file.read()) 63 | if len(input_data) == 0: 64 | print "Empty file." 65 | return 66 | 67 | IMAGE_WIDTH = 128 68 | image_size = (IMAGE_WIDTH, 69 | int(math.ceil(len(input_data) / (IMAGE_WIDTH * 1.0)))) 70 | image = Image.new("RGB", image_size, "white") 71 | 72 | 73 | def convert_color(byte): 74 | """Decides a pixel color according to the rule of Stirling.""" 75 | 76 | if byte >= 0x80: 77 | return 0x000000 78 | elif byte >= 0x20: 79 | return 0x0000ff 80 | elif byte >= 0x01: 81 | return 0xffff00 82 | else: 83 | return 0xffffff 84 | 85 | 86 | def fill_image(input_data, image, image_size): 87 | """Puts color pixels on an image with color conversion""" 88 | 89 | y_range = range(image_size[1]) 90 | x_range = range(IMAGE_WIDTH) 91 | d_range = len(input_data) 92 | pix = image.load() 93 | index = 0 94 | for y in y_range: 95 | for x in x_range: 96 | pix[x, y] = convert_color(input_data[index]) 97 | index += 1 98 | if index >= d_range: 99 | return 100 | return 101 | 102 | 103 | fill_image(input_data, image, image_size) 104 | image.convert("P").save(input_file_name + ".png", "PNG") 105 | return 106 | 107 | 108 | if __name__ == "__main__": 109 | main(sys.argv, len(sys.argv)) 110 | 111 | --------------------------------------------------------------------------------