XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
"""Bounded static PE call and pointer references; never attaches to or runs a game."""
import argparse
import bisect
import json
import re
import struct
from pathlib import Path

import pefile


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('image', type=Path)
    parser.add_argument('targets', nargs='+', type=lambda value: int(value, 0))
    parser.add_argument('--output', type=Path, required=True)
    args = parser.parse_args()
    pe = pefile.PE(str(args.image), fast_load=True)
    pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXCEPTION']])
    entries = sorted((e.struct.BeginAddress, e.struct.EndAddress, e.struct.UnwindData)
                     for e in pe.DIRECTORY_ENTRY_EXCEPTION)
    starts = [e[0] for e in entries]

    def parent(rva):
        i = bisect.bisect_right(starts, rva) - 1
        if i < 0 or not entries[i][0] <= rva < entries[i][1]:
            return None
        entry = entries[i]
        for _ in range(16):
            begin, end, unwind = entry
            header = pe.get_data(unwind, 4)
            if not header[0] >> 3 & 4:
                return hex(begin)
            entry = struct.unpack('<III', pe.get_data(unwind + 4 + ((header[2] + 1) & ~1) * 2, 12))
        raise ValueError('Unwind chain exceeds its bound')

    refs = []
    for section in pe.sections:
        data = section.get_data()
        if section.Characteristics & 0x20000000:
            for match in re.finditer(rb'[\x40-\x4f]?[\x66\xf2\xf3]?\x0f[\x10\x28\x6f][\x05\x0d\x15\x1d\x25\x2d\x35\x3d]....', data, re.S):
                rva=section.VirtualAddress+match.start();target=rva+len(match.group())+struct.unpack_from('<i',match.group(),len(match.group())-4)[0]
                if target in args.targets:refs.append(dict(kind='rip-simd-load',target=hex(target),rva=hex(rva),parent=parent(rva)))
            for match in re.finditer(rb'[\x40-\x4f]?[\x8d\x8b][\x05\x0d\x15\x1d\x25\x2d\x35\x3d]....', data, re.S):
                rva = section.VirtualAddress + match.start()
                instruction = match.group()
                target = rva + len(instruction) + struct.unpack_from('<i', instruction, len(instruction)-4)[0]
                if target in args.targets:
                    refs.append(dict(kind='rip-address' if instruction[-6] == 0x8d else 'rip-load',
                                     target=hex(target), rva=hex(rva), parent=parent(rva)))
            for match in re.finditer(rb'[\xe8\xe9]....', data, re.S):
                rva = section.VirtualAddress + match.start()
                target = rva + 5 + struct.unpack_from('<i', match.group(), 1)[0]
                if target in args.targets:
                    refs.append(dict(kind='call' if match.group()[0] == 0xe8 else 'jump',
                                     target=hex(target), rva=hex(rva), parent=parent(rva)))
        else:
            for target in args.targets:
                pointer = struct.pack('<Q', pe.OPTIONAL_HEADER.ImageBase + target)
                at = 0
                while (at := data.find(pointer, at)) >= 0:
                    refs.append(dict(kind='pointer', target=hex(target), rva=hex(section.VirtualAddress + at)))
                    at += len(pointer)
    result = dict(image=args.image.name, references=refs, scope='Offline static evidence only')
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(result, indent=2), encoding='utf-8')
    print(json.dumps(result, indent=2))


if __name__ == '__main__':
    main()