feat: pyarmor_runtime preprocess (gh-29)

This commit is contained in:
2025-11-04 22:56:03 +08:00
parent 10a3c24fac
commit e52a943769
2 changed files with 39 additions and 11 deletions

View File

@@ -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
View 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]