peacestone/peacestone.py

184 lines
5.7 KiB
Python

from qiling import *
from qiling.const import *
from capstone import *
from keystone import *
import pefile
import json
import re
# Set to binary name
BIN_NAME = "sppsvc.exe"
# Set to function to be analyzed
# ANLZ_FUNC = "?VerifyPKeyByInstalledPidConfig@CSLSLicenseManager@@IBEJPAVCSLPKey@@PBGPAUtagPKEY_BASIC_INFO@@PAPAGPAUDECODED_PKEY_DATA@@@Z"
ANLZ_FUNC = "_wmain"
# These magic regexes are derived from the byte markers in notes.txt
PUSH_REGEX = rb"(?:\x8dd\$\xfc\x89[\x04\x0c\x14\x1c,4<]\$|[PQRSUVW]){2}\x8d[x\x05\r\x15\x1d-5=].{4}"
STUB_RET4_REGEX = rb"\x8b[DLT\\lt\|]\$\x04\xc2\x04\x00"
STUB_RET0_REGEX = rb"\x87[\x04\x0c\x14\x1c,4<]\$\xc3"
STUB_MAX_SIZE = 0x40 # Maximum size of jump stub
REG_NAMES = {
19: "EAX",
20: "EBP",
21: "EBX",
22: "ECX",
23: "EDI",
24: "EDX",
29: "ESI"
}
NOP = b"\x90"
with open("syms.json", "r") as f:
sym_data = json.loads(f.read())
sym_data = {int(a, 16): b for a, b in sym_data.items()}
sym_data_inv = {b: a for a, b in sym_data.items()}
sym_addrs = sorted(sym_data)
ks = Ks(KS_ARCH_X86, KS_MODE_32)
md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True
md.skipdata = True
ql = Qiling(["./rootfs/sppsvc.exe"], "./rootfs", verbose=QL_VERBOSE.OFF)
image_start = ql.loader.images[0].base
image_end = ql.loader.images[0].end
image_size = image_end - image_start
pe = pefile.PE(data=ql.mem.read(image_start, image_size))
def func_boundary(fun_name):
f_start = sym_data_inv[ANLZ_FUNC]
ind = sym_addrs.index(f_start)
f_end = sym_addrs[ind+1]
return f_start, f_end
def save_patched_exe():
print("Fixing up sections...")
with open(BIN_NAME.replace(".exe", ".stoned.exe"), "wb") as f:
f.write(ql.mem.read(exe_start, exe_end - exe_start))
def assemble(instrs):
return bytes(ks.asm(instrs)[0])
"""
def remove_verify_stubs():
global ql
"""
if __name__ == "__main__":
print("peacestone copyleft UMSKT project 2023")
print()
# remove_verify_stubs()
# save_patched_exe()
pe_data = ql.mem.read(image_start, image_size)
f = open("func_table.txt", "w")
for match in re.finditer(STUB_RET4_REGEX, pe_data):
print(hex(match.start()))
match_addr = image_start + match.start()
stub_code = ql.mem.read(match_addr - 0x50, 0x50)
try:
stub_start_offset = list(re.finditer(PUSH_REGEX, stub_code))[0].start()
except:
continue
stub_start_addr = match_addr - 0x50 + stub_start_offset
instrs = list(md.disasm(ql.mem.read(stub_start_addr, 0x47), stub_start_addr))
ret = 0
for i, instr in enumerate(instrs):
# print(instr)
if instr.mnemonic == "ret" and instr.op_str == "4":
ret = i
break
if ret < 8:
continue
# min 7 backwards -> first push instr, then stop
# much better than whatever this is supposed to be
stub_start_index = ret - 8
if instrs[ret-2].mnemonic == "mov":
stub_start_index -= 1
if instrs[stub_start_index].mnemonic == "mov" or instrs[stub_start_index].mnemonic == "push":
stub_start_index += 1
elif instrs[stub_start_index].mnemonic != "lea":
# print("CANT DEAL WITH THIS")
continue
stub_start = instrs[stub_start_index].address
try:
used_reg = list(md.disasm(instrs[stub_start_index].bytes, 0))[0].operands[0].value.reg
except:
raise Exception("CANT DEAL WITH THIS")
if used_reg not in REG_NAMES:
# print("CANT DEAL WITH THIS")
continue
used_reg_name = REG_NAMES[used_reg].lower()
print("ADDRESS ASSIGNED @ " + hex(stub_start))
len_stub = (instrs[ret].address + 3) - stub_start
# ql.mem.write(stub_start, NOP * len_stub)
# ql.mem.write(chksum_data, NOP * 16)
push_instrs = list(map(lambda c: bytearray(ks.asm(c)[0]), [f"push {used_reg_name}", f"lea esp, [esp-4]", f"mov [esp], {used_reg_name}"]))
jmp_insert_addr = 0
for inst in instrs[max(0,stub_start_index-4):stub_start_index][::-1]:
# print(inst)
if inst.bytes in push_instrs:
# ql.mem.write(inst.address, NOP * len(inst.bytes))
jmp_insert_addr = inst.address
else:
break
if jmp_insert_addr == 0:
# print("CANT DEAL WITH THIS")
continue
print("NOPPED STARTING @ " + hex(jmp_insert_addr))
try:
ql.run(begin=jmp_insert_addr, end=instrs[ret].address)
handler_addr = ql.arch.stack_pop()
ql.arch.stack_pop()
next_addr = ql.arch.stack_pop()
except:
handler_addr = -1
next_addr = -1
print("HANDLER @ " + hex(handler_addr))
print("JUMP TARGET @ " + hex(next_addr))
print(ql.arch.regs.esp)
if handler_addr in sym_data:
handler_name = sym_data[handler_addr]
else:
handler_name = hex(handler_addr)
if next_addr in sym_data:
next_name = f"{hex(next_addr)} {sym_data[next_addr]}"
else:
next_name = hex(next_addr)
f.write(f"J R4 S {hex(jmp_insert_addr)} H {handler_name} N {next_name}\n")
# input()
print()
# input()