feat: pyarmor_runtime preprocess (gh-29)
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from util import dword, bytes_sub
|
||||||
|
|
||||||
|
|
||||||
GLOBAL_CERT = bytes.fromhex("""
|
GLOBAL_CERT = bytes.fromhex("""
|
||||||
@@ -22,6 +25,9 @@ af 09 fb 04 54 a9 ea c0 c1 e9 32 6c 77 92 7f 9f
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("runtime")
|
||||||
|
|
||||||
|
|
||||||
class RuntimeInfo:
|
class RuntimeInfo:
|
||||||
def __init__(self, file_path: str) -> None:
|
def __init__(self, file_path: str) -> None:
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
@@ -65,19 +71,35 @@ class RuntimeInfo:
|
|||||||
if data[cur + 11 : cur + 18] == b"\x00" * 7:
|
if data[cur + 11 : cur + 18] == b"\x00" * 7:
|
||||||
raise ValueError(f"{self.file_path} is a runtime template")
|
raise ValueError(f"{self.file_path} is a runtime template")
|
||||||
|
|
||||||
self.part_1 = data[cur : cur + 20]
|
# Align with pyd file and executable address:
|
||||||
|
# In .pyd files b"pyarmor-vax" locates at 0x???2C
|
||||||
|
# But not .so
|
||||||
|
data = bytearray(bytes_sub(data, cur - 0x2C, 0x800))
|
||||||
|
|
||||||
cur += 36
|
if data[0x5C] & 1 != 0:
|
||||||
part_2_offset = int.from_bytes(data[cur : cur + 4], "little")
|
logger.error(
|
||||||
part_2_len = int.from_bytes(data[cur + 4 : cur + 8], "little")
|
'External key file ".pyarmor.ikey" is not supported yet, but it will be supported once we get a sample (like this one). Please open an issue on https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot/issues to make this tool stronger.'
|
||||||
part_3_offset = int.from_bytes(data[cur + 8 : cur + 12], "little")
|
)
|
||||||
cur += 16
|
raise NotImplementedError(f'{self.file_path} uses ".pyarmor.ikey"')
|
||||||
self.part_2 = data[cur + part_2_offset : cur + part_2_offset + part_2_len]
|
|
||||||
|
|
||||||
cur += part_3_offset
|
if dword(data, 0x4C) != 0:
|
||||||
part_3_len = int.from_bytes(data[cur + 4 : cur + 8], "little")
|
xor_flag = 0x60 + dword(data, 0x48)
|
||||||
cur += 32
|
xor_target = 0x60 + dword(data, 0x50)
|
||||||
self.part_3 = data[cur : cur + part_3_len]
|
xor_length = int.from_bytes(data[xor_flag + 1 : xor_flag + 4], "little")
|
||||||
|
if data[xor_flag] == 1:
|
||||||
|
for i in range(xor_length):
|
||||||
|
# MUT data
|
||||||
|
data[xor_target + i] ^= data[xor_flag + 4 + i]
|
||||||
|
|
||||||
|
self.part_1 = bytes_sub(data, 0x2C, 20)
|
||||||
|
|
||||||
|
part_2_offset = dword(data, 0x50)
|
||||||
|
part_2_len = dword(data, 0x54)
|
||||||
|
self.part_2 = bytes_sub(data, 0x60 + part_2_offset, part_2_len)
|
||||||
|
|
||||||
|
var_a1 = 0x60 + dword(data, 0x58)
|
||||||
|
part_3_len = dword(data, var_a1 + 4)
|
||||||
|
self.part_3 = bytes_sub(data, var_a1 + 0x20, part_3_len)
|
||||||
|
|
||||||
def calc_aes_key(self) -> bytes:
|
def calc_aes_key(self) -> bytes:
|
||||||
return hashlib.md5(
|
return hashlib.md5(
|
||||||
|
|||||||
6
oneshot/util.py
Normal file
6
oneshot/util.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def dword(buffer, idx: int) -> int:
|
||||||
|
return int.from_bytes(buffer[idx : idx + 4], "little")
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_sub(buffer, start: int, length: int) -> int:
|
||||||
|
return buffer[start : start + length]
|
||||||
Reference in New Issue
Block a user