This commit is contained in:
WitherOrNot 2023-07-16 22:58:47 -04:00
commit dee5bf889f
5 changed files with 62899 additions and 0 deletions

51
notes.txt Normal file
View File

@ -0,0 +1,51 @@
; Obfuscated jmp structure
; where reg is one of: eax, ecx, edx, ebx, ebp, esi, edi
; push is sometimes replaced with the following equivalent instructions
; lea esp, [esp-4]
; mov [esp], reg
; IDA (and perhaps other disassemblers idk) confuses the first parts of the stub for constant bytes
; This combined with the random push replacement will make the stub look different, but its infact always the same
push reg
push reg
lea reg, [addr1]
lea reg, [reg+const1] ; reg = [actual routine - 10h] = start of checksum data
mov dword ptr [esp+4], reg
lea reg, [addr2]
lea reg, [reg+const2] ; reg = [verifier stub]
push reg
mov reg, dword ptr [esp+4] ; restore original value of reg
; now the stack looks like this
; [esp]: [verifier stub]
; [esp+4]: original value of reg
; [esp+8]: [checksum data]
ret 4
; here we jump to verifier
; now esp moves 8 bytes forward (4-byte ret address plus 4 from operand), so it points to the checksum data
; after verifier finishes, it adds 0x10 to the value at [esp]
; now esp points to the target jump address, and another ret is done to "return" to it
; =======BYTE MARKERS=======
;
mov reg, [esp+4]
ret 4
; eax - 8b 44 24 04 c2 04 00
; ecx - 8b 4c 24 04 c2 04 00
; edx - 8b 54 24 04 c2 04 00
; ebx - 8b 5c 24 04 c2 04 00
; ebp - 8b 6c 24 04 c2 04 00
; esi - 8b 74 24 04 c2 04 00
; edi - 8b 7c 24 04 c2 04 00
;
; 0x11223344 is an example address
lea reg, [0x11223344]
; eax - 8d 05 44 33 22 11
; ecx - 8d 0d 44 33 22 11
; edx - 8d 15 44 33 22 11
; ebx - 8d 1d 44 33 22 11
; ebp - 8d 2d 44 33 22 11
; esi - 8d 35 44 33 22 11
; edi - 8d 3d 44 33 22 11

130
peacestone.py Normal file
View File

@ -0,0 +1,130 @@
from qiling import *
from qiling.const import *
from capstone import *
from keystone import *
import json
# 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"
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
ql = Qiling(["./rootfs/sppsvc.exe"], "./rootfs")
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():
for region in ql.mem.get_mapinfo():
if region[3] == BIN_NAME:
exe_start = region[0]
exe_end = region[1]
with open(BIN_NAME.replace(".exe", ".stoned.exe"), "wb") as f:
f.write(ql.mem.read(exe_start, exe_end - exe_start))
def remove_verify_stubs():
global ql
f_start, f_end = func_boundary(ANLZ_FUNC)
f_start_orig = f_start
orig_ql_state = ql.save()
offset = 0
while f_start < f_end:
stop = False
f_code = ql.mem.read(f_start, f_end - f_start)
instrs = list(md.disasm(f_code, f_start))
print("INSTRS @ " + hex(f_start))
for i in instrs:
print(i)
ret = 0
for i, inst in enumerate(instrs):
if inst.mnemonic == "ret" and inst.op_str == "4":
ret = i
break
if ret < 8:
stop = True
stub_start_index = ret - 8
if instrs[stub_start_index].mnemonic == "mov" or instrs[stub_start_index].mnemonic == "push":
stub_start_index = ret - 7
elif instrs[stub_start_index].mnemonic != "lea":
stop = True
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:
stop = True
used_reg_name = REG_NAMES[used_reg].lower()
if stop:
print("VERIFY STUB REMOVAL FINISHED")
break
print("ADDRESS ASSIGNED @ " + hex(stub_start))
ql.run(begin=stub_start, end=instrs[stub_start_index+2].address)
chksum_data = ql.arch.regs.read(used_reg)
print("CHKSUM @ " + hex(chksum_data))
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]:
if inst.bytes in push_instrs:
ql.mem.write(inst.address, NOP * len(inst.bytes))
jmp_insert_addr = inst.address
else:
break
f_start = chksum_data + 0x10
print("NOPPED STARTING @ " + hex(jmp_insert_addr))
print("NEXT: " + hex(f_start + offset))
if __name__ == "__main__":
print("peacestone copyleft UMSKT project 2023")
print()
remove_verify_stubs()
save_patched_exe()

54189
sppsvc.yml Normal file

File diff suppressed because it is too large Load Diff

8508
syms.json Normal file

File diff suppressed because it is too large Load Diff

21
syms_to_json.py Normal file
View File

@ -0,0 +1,21 @@
import re
import json
# llvm-pdbutil pretty -externals sppsvc.pdb -load-address=0x01000000 > syms.txt
with open("syms.txt", "r") as f:
text = f.read()
symdata = re.findall(r" public \[(\w+)\] (\S+)", text, re.MULTILINE)
addrs = []
unique_syms = []
for addr, sym in symdata:
if addr not in addrs:
unique_syms.append((addr, sym))
addrs.append(addr)
unique_syms = dict(unique_syms)
with open("syms.json", "w") as g:
g.write(json.dumps(unique_syms, indent=4))