mirror of https://github.com/UMSKT/peacestone.git
update
This commit is contained in:
parent
dee5bf889f
commit
cf89342b36
144
notes.txt
144
notes.txt
|
@ -1,4 +1,15 @@
|
||||||
; Obfuscated jmp structure
|
NOTES
|
||||||
|
|
||||||
|
These are observations made while looking through WARBIRD code.
|
||||||
|
|
||||||
|
Some are relevant to deobfuscation, while some are just oddities and curiosities.
|
||||||
|
|
||||||
|
Hopefully, once I have a better understanding of everything, these notes will look a lot nicer!
|
||||||
|
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
Obfuscated jmp structure
|
||||||
|
|
||||||
; where reg is one of: eax, ecx, edx, ebx, ebp, esi, edi
|
; where reg is one of: eax, ecx, edx, ebx, ebp, esi, edi
|
||||||
|
|
||||||
; push is sometimes replaced with the following equivalent instructions
|
; push is sometimes replaced with the following equivalent instructions
|
||||||
|
@ -6,7 +17,7 @@
|
||||||
; mov [esp], reg
|
; mov [esp], reg
|
||||||
|
|
||||||
; IDA (and perhaps other disassemblers idk) confuses the first parts of the stub for constant bytes
|
; 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
|
; This combined with the random instruction replacement will make the stub look different, but its infact always the same
|
||||||
|
|
||||||
push reg
|
push reg
|
||||||
push reg
|
push reg
|
||||||
|
@ -27,18 +38,32 @@ ret 4
|
||||||
; after verifier finishes, it adds 0x10 to the value at [esp]
|
; 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
|
; now esp points to the target jump address, and another ret is done to "return" to it
|
||||||
|
|
||||||
|
; rarely (~1% of the time), the last three instructions are replaced with:
|
||||||
|
; xchg [esp], reg
|
||||||
|
; ret
|
||||||
|
; the principle of operation and results are the same, except only 2 values are placed on the stack
|
||||||
|
|
||||||
; =======BYTE MARKERS=======
|
; =======BYTE MARKERS=======
|
||||||
;
|
; Useful for finding obfuscated jumps in a disassembler
|
||||||
mov reg, [esp+4]
|
|
||||||
ret 4
|
lea esp, [esp - 4]
|
||||||
; eax - 8b 44 24 04 c2 04 00
|
mov [esp], reg
|
||||||
; ecx - 8b 4c 24 04 c2 04 00
|
; eax - 8d 64 24 fc 89 04 24
|
||||||
; edx - 8b 54 24 04 c2 04 00
|
; ecx - 8d 64 24 fc 89 0c 24
|
||||||
; ebx - 8b 5c 24 04 c2 04 00
|
; edx - 8d 64 24 fc 89 14 24
|
||||||
; ebp - 8b 6c 24 04 c2 04 00
|
; ebx - 8d 64 24 fc 89 1c 24
|
||||||
; esi - 8b 74 24 04 c2 04 00
|
; ebp - 8d 64 24 fc 89 2c 24
|
||||||
; edi - 8b 7c 24 04 c2 04 00
|
; esi - 8d 64 24 fc 89 34 24
|
||||||
;
|
; edi - 8d 64 24 fc 89 3c 24
|
||||||
|
|
||||||
|
push reg
|
||||||
|
; eax - 50
|
||||||
|
; ecx - 51
|
||||||
|
; edx - 52
|
||||||
|
; ebx - 53
|
||||||
|
; ebp - 55
|
||||||
|
; esi - 56
|
||||||
|
; edi - 57
|
||||||
|
|
||||||
; 0x11223344 is an example address
|
; 0x11223344 is an example address
|
||||||
lea reg, [0x11223344]
|
lea reg, [0x11223344]
|
||||||
|
@ -49,3 +74,96 @@ lea reg, [0x11223344]
|
||||||
; ebp - 8d 2d 44 33 22 11
|
; ebp - 8d 2d 44 33 22 11
|
||||||
; esi - 8d 35 44 33 22 11
|
; esi - 8d 35 44 33 22 11
|
||||||
; edi - 8d 3d 44 33 22 11
|
; edi - 8d 3d 44 33 22 11
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
xchg [esp], reg
|
||||||
|
ret
|
||||||
|
; eax - 87 04 24 c3
|
||||||
|
; ecx - 87 0c 24 c3
|
||||||
|
; edx - 87 14 24 c3
|
||||||
|
; ebx - 87 1c 24 c3
|
||||||
|
; ebp - 87 2c 24 c3
|
||||||
|
; esi - 87 34 24 c3
|
||||||
|
; edi - 87 3c 24 c3
|
||||||
|
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
Hidden functions in CRT initialization
|
||||||
|
|
||||||
|
Upon launch, msvcrt will run a list of functions to initialize the C++ runtime like so:
|
||||||
|
|
||||||
|
static bool __cdecl initialize_c()
|
||||||
|
{
|
||||||
|
_initialize_onexit_table(&__acrt_atexit_table);
|
||||||
|
_initialize_onexit_table(&__acrt_at_quick_exit_table);
|
||||||
|
|
||||||
|
// Do C initialization:
|
||||||
|
if (_initterm_e(__xi_a, __xi_z) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do C++ initialization:
|
||||||
|
_initterm(__xc_a, __xc_z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Of particular interest is the _initterm function, with the following implementation:
|
||||||
|
|
||||||
|
// Calls each function in [first, last). [first, last) must be a valid range of
|
||||||
|
// function pointers. Each function is called, in order.
|
||||||
|
extern "C" void __cdecl _initterm(_PVFV* const first, _PVFV* const last)
|
||||||
|
{
|
||||||
|
for (_PVFV* it = first; it != last; ++it)
|
||||||
|
{
|
||||||
|
if (*it == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(**it)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Both code examples taken from UCRT 10.0.14393.0)
|
||||||
|
|
||||||
|
The addresses __xc_a and __xc_z are symbols in the pdb, so we can find the list of these functions.
|
||||||
|
|
||||||
|
Many are not interesting (just allocating class memory), but some odd functions are included as well (see following sections).
|
||||||
|
|
||||||
|
When in doubt on where a function is being executed, consider checking at __xc_a!
|
||||||
|
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
Debugger detection
|
||||||
|
|
||||||
|
In _initterm initialization routines, the following device names are referenced:
|
||||||
|
|
||||||
|
\\.\SICE
|
||||||
|
\\.\NTICE
|
||||||
|
\\.\SIWVID
|
||||||
|
|
||||||
|
these correspond to the SoftICE kernel-level debugger. It seems to be rather famous for software cracking.
|
||||||
|
|
||||||
|
I have not looked hard into how these strings are used, but they appear to be XORed against some constant data, with the result held in an array somewhere in .data.
|
||||||
|
|
||||||
|
Not like its much of a hindrance anyway, since we have x64dbg :P
|
||||||
|
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
EnterObfuscatedMode
|
||||||
|
|
||||||
|
This function is also called from _initterm, and is always called from an obfuscated jump block.
|
||||||
|
|
||||||
|
The first steps are to perform a binary search within the block of data specified by the symbol WARBIRD::g_ObfuscatedBlockData.
|
||||||
|
|
||||||
|
(Fill in later)
|
||||||
|
|
||||||
|
Lastly, the return is called, and the function returns to the decrypted code.
|
|
@ -0,0 +1,24 @@
|
||||||
|
EnterObfuscatedMode notes
|
||||||
|
|
||||||
|
Stack:
|
||||||
|
A8 ebp
|
||||||
|
++ AC to all below
|
||||||
|
00 ecx
|
||||||
|
04 edi
|
||||||
|
08 ebx
|
||||||
|
0C eax / old stub (120F957)
|
||||||
|
10 esi / old stub offset (01001400)
|
||||||
|
14 edx
|
||||||
|
18 GetLastError()
|
||||||
|
1C EFLAGS
|
||||||
|
20 RetAddr
|
||||||
|
24 OldRetAddr (_initterm)
|
||||||
|
|
||||||
|
;; not fastcall calling convention!
|
||||||
|
ecx <- [esp+1C] // Right below RetAddr
|
||||||
|
edx <- STUB_FRAME (??)
|
||||||
|
|
||||||
|
val_3 -> {???}{7 bits select offset for something}{12 bits size decrypted data}
|
||||||
|
offset_1 -> key1
|
||||||
|
offset_2 -> {byte1:last_bit = skip_verify_offset_1}{verify_byte}{key2 as word}
|
||||||
|
offset_3 -> some addr?
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include <std/io.pat>
|
||||||
|
|
||||||
|
struct ObfBlockData {
|
||||||
|
u32 u0;
|
||||||
|
u32 u1;
|
||||||
|
u32 u2;
|
||||||
|
u32 u3;
|
||||||
|
u32 u4;
|
||||||
|
};
|
||||||
|
|
||||||
|
ObfBlockData entries[0x323B] @ 0;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
u32 first = 0;
|
||||||
|
u32 first_prev = 0;
|
||||||
|
u32 last = 0x323A;
|
||||||
|
u32 last_prev = 0x323A;
|
||||||
|
u32 sum = 0;
|
||||||
|
u32 stub_ret_offset = 0x20F989;
|
||||||
|
u32 index = 0;
|
||||||
|
u32 xor_value = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
index = (first + last) / 2;
|
||||||
|
xor_value = entries[index].u0 ^ sum;
|
||||||
|
|
||||||
|
if (stub_ret_offset >= xor_value) {
|
||||||
|
if (stub_ret_offset >= xor_value + (entries[index].u3 & 0xFFF)) {
|
||||||
|
first = index + 1;
|
||||||
|
last = last_prev;
|
||||||
|
first_prev = first;
|
||||||
|
sum = entries[index].u0 - entries[index].u3;
|
||||||
|
std::print("STAY {0:04X} >= {1:04X}\n", stub_ret_offset, xor_value + (entries[index].u3 & 0xFFF));
|
||||||
|
} else {
|
||||||
|
std::print("BREAK {0:04X} < {1:04X}\n", stub_ret_offset, xor_value + (entries[index].u3 & 0xFFF));
|
||||||
|
std::print("INDEX {}\n", index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
last = index - 1;
|
||||||
|
last_prev = last;
|
||||||
|
sum = entries[index].u0 + entries[index].u3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last < first) {
|
||||||
|
// set_handle_ret(handle_ret_addr);
|
||||||
|
std::print("ERROR\n", xor_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("XOR {0:04X}\n", xor_value);
|
||||||
|
std::print("FIRST {0:04X}\n", first);
|
||||||
|
std::print("LAST {0:04X}\n", last);
|
||||||
|
std::print("SUM {0:04X}\n", sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 val1 = entries[index].u1 + entries[index].u0;
|
||||||
|
u32 val2 = entries[index].u2 - entries[index].u0;
|
||||||
|
u32 val3 = entries[index].u4 + entries[index].u3;
|
||||||
|
std::print("VAL1 {0:04X}\n", val1);
|
||||||
|
std::print("VAL2 {0:04X}\n", val2);
|
||||||
|
std::print("VAL3 {0:04X}\n", val3);
|
||||||
|
};
|
|
@ -0,0 +1,131 @@
|
||||||
|
from qiling import *
|
||||||
|
from qiling.const import *
|
||||||
|
from capstone import *
|
||||||
|
from keystone import *
|
||||||
|
import pefile
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
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))
|
||||||
|
|
||||||
|
num_obd = ql.unpack(ql.mem.read(sym_data_inv["?g_nNumObfuscatedBlockData@WARBIRD@@3KA"], 4))
|
||||||
|
obd_addr = sym_data_inv["?g_ObfuscatedBlockData@WARBIRD@@3PAU_OBFUSCATED_BLOCK_DATA@1@A"]
|
||||||
|
obd = []
|
||||||
|
|
||||||
|
for i in range(num_obd):
|
||||||
|
obf_block = []
|
||||||
|
|
||||||
|
for j in range(5):
|
||||||
|
obf_block.append(ql.unpack(ql.mem.read(obd_addr + 4*(5*i + j), 4)))
|
||||||
|
|
||||||
|
obd.append(obf_block)
|
||||||
|
|
||||||
|
stub_frame = 0x120f989
|
||||||
|
stub_frame_offset = stub_frame - image_start
|
||||||
|
|
||||||
|
first = 0
|
||||||
|
first_prev = first
|
||||||
|
last = num_obd - 1
|
||||||
|
last_prev = last
|
||||||
|
sum_diff = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
index = (first + last) // 2
|
||||||
|
xor_value = obd[index][0] ^ sum_diff
|
||||||
|
|
||||||
|
if stub_frame_offset >= xor_value:
|
||||||
|
if stub_frame_offset >= xor_value + (obd[index][3] & 0xFFF):
|
||||||
|
first = index + 1
|
||||||
|
last = last_prev
|
||||||
|
first_prev = first
|
||||||
|
sum_diff = (obd[index][0] - obd[index][3]) % (1 << 32)
|
||||||
|
else:
|
||||||
|
print(f"XOR {hex(xor_value)} SUMDIFF {hex(sum_diff)} INDEX {hex(index)}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
last = index - 1
|
||||||
|
last_prev = last
|
||||||
|
sum_diff = (obd[index][0] + obd[index][3]) % (1 << 32)
|
||||||
|
|
||||||
|
print(f"XOR {hex(xor_value)} SUMDIFF {hex(sum_diff)} INDEX {hex(index)}")
|
||||||
|
|
||||||
|
val1 = (obd[index][1] + obd[index][0]) % (1 << 32)
|
||||||
|
val2 = (obd[index][2] - obd[index][0]) % (1 << 32)
|
||||||
|
val3 = (obd[index][4] + obd[index][3]) % (1 << 32)
|
||||||
|
unk3 = obd[index][3]
|
||||||
|
data_size = unk3 & 0xfff
|
||||||
|
xor_plus_binstart = image_start + xor_value
|
||||||
|
val1_bytes = val1.to_bytes(4, "little")
|
||||||
|
val2_bytes = val2.to_bytes(4, "little")
|
||||||
|
enc_bytes = ql.mem.read(xor_plus_binstart, data_size + 1)
|
||||||
|
dec_bytes = [0] * data_size
|
||||||
|
chksum = 0xa5
|
||||||
|
|
||||||
|
for i in range(data_size - 1, 0, -1):
|
||||||
|
enc_byte = enc_bytes[i]
|
||||||
|
b = enc_bytes[i - 1]
|
||||||
|
|
||||||
|
if i % 2 == 1:
|
||||||
|
if (enc_byte ^ val1_bytes[3]) % 2 == 1:
|
||||||
|
a = (enc_byte ^ val1_bytes[3] ^ val1_bytes[2] ^ 0x100) >> 1
|
||||||
|
else:
|
||||||
|
a = (enc_byte ^ val1_bytes[3]) >> 1
|
||||||
|
|
||||||
|
if (a ^ b) % 2 == 1:
|
||||||
|
dec_byte = (a ^ b ^ val2_bytes[1] ^ 0x100) >> 1
|
||||||
|
else:
|
||||||
|
dec_byte = (a ^ b) >> 1
|
||||||
|
else:
|
||||||
|
if (enc_byte ^ val1_bytes[1]) % 2 == 1:
|
||||||
|
a = (enc_byte ^ val1_bytes[1] ^ val1_bytes[0] ^ 0x100) >> 1
|
||||||
|
else:
|
||||||
|
a = (enc_byte ^ val1_bytes[1]) >> 1
|
||||||
|
|
||||||
|
if (a ^ b) % 2 == 1:
|
||||||
|
dec_byte = (a ^ b ^ val2_bytes[0] ^ 0x100) >> 1
|
||||||
|
else:
|
||||||
|
dec_byte = (a ^ b) >> 1
|
||||||
|
|
||||||
|
chksum ^= dec_byte
|
||||||
|
print(hex(dec_byte))
|
||||||
|
dec_bytes[i] = dec_byte
|
125
peacestone.py
125
peacestone.py
|
@ -2,13 +2,22 @@ from qiling import *
|
||||||
from qiling.const import *
|
from qiling.const import *
|
||||||
from capstone import *
|
from capstone import *
|
||||||
from keystone import *
|
from keystone import *
|
||||||
|
import pefile
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
# Set to binary name
|
# Set to binary name
|
||||||
BIN_NAME = "sppsvc.exe"
|
BIN_NAME = "sppsvc.exe"
|
||||||
|
|
||||||
# Set to function to be analyzed
|
# Set to function to be analyzed
|
||||||
ANLZ_FUNC = "?VerifyPKeyByInstalledPidConfig@CSLSLicenseManager@@IBEJPAVCSLPKey@@PBGPAUtagPKEY_BASIC_INFO@@PAPAGPAUDECODED_PKEY_DATA@@@Z"
|
# 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 = {
|
REG_NAMES = {
|
||||||
19: "EAX",
|
19: "EAX",
|
||||||
|
@ -31,7 +40,12 @@ sym_addrs = sorted(sym_data)
|
||||||
ks = Ks(KS_ARCH_X86, KS_MODE_32)
|
ks = Ks(KS_ARCH_X86, KS_MODE_32)
|
||||||
md = Cs(CS_ARCH_X86, CS_MODE_32)
|
md = Cs(CS_ARCH_X86, CS_MODE_32)
|
||||||
md.detail = True
|
md.detail = True
|
||||||
|
md.skipdata = True
|
||||||
ql = Qiling(["./rootfs/sppsvc.exe"], "./rootfs")
|
ql = Qiling(["./rootfs/sppsvc.exe"], "./rootfs")
|
||||||
|
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):
|
def func_boundary(fun_name):
|
||||||
f_start = sym_data_inv[ANLZ_FUNC]
|
f_start = sym_data_inv[ANLZ_FUNC]
|
||||||
|
@ -41,90 +55,117 @@ def func_boundary(fun_name):
|
||||||
return f_start, f_end
|
return f_start, f_end
|
||||||
|
|
||||||
def save_patched_exe():
|
def save_patched_exe():
|
||||||
for region in ql.mem.get_mapinfo():
|
print("Fixing up sections...")
|
||||||
if region[3] == BIN_NAME:
|
|
||||||
exe_start = region[0]
|
|
||||||
exe_end = region[1]
|
|
||||||
|
|
||||||
with open(BIN_NAME.replace(".exe", ".stoned.exe"), "wb") as f:
|
with open(BIN_NAME.replace(".exe", ".stoned.exe"), "wb") as f:
|
||||||
f.write(ql.mem.read(exe_start, exe_end - exe_start))
|
f.write(ql.mem.read(exe_start, exe_end - exe_start))
|
||||||
|
|
||||||
|
def assemble(instrs):
|
||||||
|
return bytes(ks.asm(instrs)[0])
|
||||||
|
|
||||||
|
"""
|
||||||
def remove_verify_stubs():
|
def remove_verify_stubs():
|
||||||
global ql
|
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:
|
if __name__ == "__main__":
|
||||||
stop = False
|
print("peacestone copyleft UMSKT project 2023")
|
||||||
f_code = ql.mem.read(f_start, f_end - f_start)
|
print()
|
||||||
instrs = list(md.disasm(f_code, f_start))
|
|
||||||
|
|
||||||
print("INSTRS @ " + hex(f_start))
|
# remove_verify_stubs()
|
||||||
|
# save_patched_exe()
|
||||||
|
|
||||||
for i in instrs:
|
pe_data = ql.mem.read(image_start, image_size)
|
||||||
print(i)
|
f = open("log.txt", "w")
|
||||||
|
|
||||||
|
for match in re.finditer(STUB_RET4_REGEX, pe_data):
|
||||||
|
match_addr = image_start + match.start()
|
||||||
|
stub_code = ql.mem.read(match_addr - 0x1000, 0x1000)
|
||||||
|
|
||||||
|
try:
|
||||||
|
stub_start_offset = list(re.finditer(PUSH_REGEX, stub_code))[0].start()
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
stub_start_addr = match_addr - 0x1000 + stub_start_offset
|
||||||
|
instrs = list(md.disasm(ql.mem.read(stub_start_addr, 0x47), stub_start_addr))
|
||||||
ret = 0
|
ret = 0
|
||||||
for i, inst in enumerate(instrs):
|
|
||||||
if inst.mnemonic == "ret" and inst.op_str == "4":
|
for i, instr in enumerate(instrs):
|
||||||
|
print(instr)
|
||||||
|
|
||||||
|
if instr.mnemonic == "ret" and instr.op_str == "4":
|
||||||
ret = i
|
ret = i
|
||||||
break
|
break
|
||||||
|
|
||||||
if ret < 8:
|
if ret < 8:
|
||||||
stop = True
|
continue
|
||||||
|
|
||||||
|
# min 7 backwards -> first push instr, then stop
|
||||||
|
# much better than whatever this is supposed to be
|
||||||
|
|
||||||
stub_start_index = ret - 8
|
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":
|
if instrs[stub_start_index].mnemonic == "mov" or instrs[stub_start_index].mnemonic == "push":
|
||||||
stub_start_index = ret - 7
|
stub_start_index += 1
|
||||||
elif instrs[stub_start_index].mnemonic != "lea":
|
elif instrs[stub_start_index].mnemonic != "lea":
|
||||||
stop = True
|
print("CANT DEAL WITH THIS")
|
||||||
|
continue
|
||||||
|
|
||||||
stub_start = instrs[stub_start_index].address
|
stub_start = instrs[stub_start_index].address
|
||||||
|
|
||||||
try:
|
try:
|
||||||
used_reg = list(md.disasm(instrs[stub_start_index].bytes, 0))[0].operands[0].value.reg
|
used_reg = list(md.disasm(instrs[stub_start_index].bytes, 0))[0].operands[0].value.reg
|
||||||
except:
|
except:
|
||||||
stop = True
|
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()
|
used_reg_name = REG_NAMES[used_reg].lower()
|
||||||
|
|
||||||
if stop:
|
|
||||||
print("VERIFY STUB REMOVAL FINISHED")
|
|
||||||
break
|
|
||||||
|
|
||||||
print("ADDRESS ASSIGNED @ " + hex(stub_start))
|
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
|
len_stub = (instrs[ret].address + 3) - stub_start
|
||||||
ql.mem.write(stub_start, NOP * len_stub)
|
# ql.mem.write(stub_start, NOP * len_stub)
|
||||||
ql.mem.write(chksum_data, NOP * 16)
|
# 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}"]))
|
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
|
jmp_insert_addr = 0
|
||||||
|
|
||||||
for inst in instrs[max(0,stub_start_index-4):stub_start_index][::-1]:
|
for inst in instrs[max(0,stub_start_index-4):stub_start_index][::-1]:
|
||||||
|
print(inst)
|
||||||
if inst.bytes in push_instrs:
|
if inst.bytes in push_instrs:
|
||||||
ql.mem.write(inst.address, NOP * len(inst.bytes))
|
# ql.mem.write(inst.address, NOP * len(inst.bytes))
|
||||||
jmp_insert_addr = inst.address
|
jmp_insert_addr = inst.address
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
f_start = chksum_data + 0x10
|
if jmp_insert_addr == 0:
|
||||||
|
print("CANT DEAL WITH THIS")
|
||||||
|
continue
|
||||||
|
|
||||||
print("NOPPED STARTING @ " + hex(jmp_insert_addr))
|
print("NOPPED STARTING @ " + hex(jmp_insert_addr))
|
||||||
print("NEXT: " + hex(f_start + offset))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
try:
|
||||||
print("peacestone copyleft UMSKT project 2023")
|
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)
|
||||||
|
|
||||||
|
f.write(f"J R4 S {hex(jmp_insert_addr)} H {hex(handler_addr)} N {hex(next_addr)}\n")
|
||||||
|
|
||||||
|
# input()
|
||||||
print()
|
print()
|
||||||
|
|
||||||
remove_verify_stubs()
|
|
||||||
save_patched_exe()
|
|
Loading…
Reference in New Issue