Skip to content

Instantly share code, notes, and snippets.

@zeroSteiner
Created December 16, 2014 18:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroSteiner/44c95ccefeb9dc61e323 to your computer and use it in GitHub Desktop.
Save zeroSteiner/44c95ccefeb9dc61e323 to your computer and use it in GitHub Desktop.
Safe Exception Handler Analysis Tool
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# safeseh_inspect.py
#
# Copyright 2014 Spencer McIntyre
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of the project nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import os
import struct
try:
import miasm2.analysis.machine
import miasm2.expression.expression
except ImportError:
print('missing requirement miasm2, https://github.com/cea-sec/miasm')
os._exit(0)
try:
import pefile
except ImportError:
print('missing requirement pefile, https://pypi.python.org/pypi/pefile')
os._exit(0)
try:
import jarvis
except ImportError:
print('missing requirement jarvis, https://gist.github.com/zeroSteiner/7920683')
os._exit(0)
IMAGE_FILE_MACHINE_I386 = 0x014c
def main():
jar = jarvis.Jarvis()
parser = jar.build_argparser()
parser.add_argument('-i', '--ins', dest='instruction_count', default=10, type=int, help='the number of instructions to disassemble')
parser.add_argument('--no-color', dest='use_colors', default=True, action='store_false', help='disable colored output')
parser.add_argument('--no-dis', dest='show_disassembly', default=True, action='store_false', help='do not show the disassembly')
parser.add_argument('pefile', help='the PE file to process')
args = parser.parse_args()
jar.use_colors = args.use_colors
jar.print_status('loading pe file...')
with jar.print_operation_time("parsed the pe file in in {time:.3f} seconds", verbose=True):
pe = pefile.PE(args.pefile)
if pe.NT_HEADERS.FILE_HEADER.Machine != IMAGE_FILE_MACHINE_I386:
jar.print_error('this pe file is not a 32-bit executable')
return
if pe.OPTIONAL_HEADER.IMAGE_DLLCHARACTERISTICS_NO_SEH:
jar.print_error('this pe file does not use SEH')
return
pe_prefix = os.path.splitext(os.path.basename(args.pefile))[0]
image_base = pe.OPTIONAL_HEADER.ImageBase
jar.vprint_status("image base address at: 0x{0:08x}".format(image_base))
de_load_config = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
seh_table = de_load_config.SEHandlerTable
seh_count = de_load_config.SEHandlerCount
jar.print_status("SEHandlerTable VA: 0x{0:08x} SEHandlerCount: {1}".format(seh_table, seh_count))
machine = miasm2.analysis.machine.Machine('x86_32').mn
cursor = seh_table - image_base
for entry in range(seh_count):
handler_data = pe.get_data(cursor + (entry * 4), 4)
handler_address = struct.unpack('I', handler_data)[0]
jar.print_status(" {0:04}: RVA: 0x{1:08x} VA: 0x{2:08x}".format(entry, handler_address, handler_address + image_base))
if not args.show_disassembly:
continue
ins_cursor = handler_address
for ins_num in range(0, args.instruction_count):
ins_data = pe.get_data(ins_cursor, machine.max_instruction_len)
ins = machine.dis(ins_data, 32)
if ins.name in ('CALL', 'JMP') and len(ins.args) == 1 and isinstance(ins.args[0], miasm2.expression.expression.ExprInt):
resolved_address = image_base
resolved_address += ins_cursor
resolved_address += struct.unpack('i', struct.pack('I', ins.args[0].arg.arg))[0]
resolved_address += len(ins.b)
ins_str = "{0: <10} {1}.{2:08X}".format(ins.name, pe_prefix, resolved_address)
else:
ins_str = str(ins)
# stop yelling at me
ins_str = ins_str.lower()
jar.print_line(" {0:08x} {1}".format(ins_cursor + image_base, ins_str))
ins_cursor += len(ins.b)
return 0
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment