“ 只要有想见的人,就不是孤单一个人。——《夏目友人帐》S3E4 ”
01环境版本
环境:
电脑,Windows 11 专业版 23H2
f87K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6v1K9h3q4G2f1%4g2u0L8X3k6G2f1$3g2U0i4K6u0r3d9X3W2S2L8#2y4#2d9h3&6X3L8#2y4W2j5#2)9#2k6W2b7H3x3r3I4K6i4K6g2X3g2$3W2F1x3e0p5`.
软件:
Florida,16.1.8
308K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6k6L8r3q4J5L8$3c8Q4x3V1k6r3L8r3!0J5K9h3c8S2i4K6u0r3M7X3g2D9k6h3q4K6k6i4y4Q4x3V1k6@1j5h3N6Q4x3V1j5`.16.1.8
frida-dexdump
865K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Z5L8s2g2%4j5g2)9J5c8X3k6J5K9h3c8S2i4K6u0V1k6r3g2^5k6s2g2E0M7l9`.`.
02操作步骤
1、梆梆加固企业版

2、查看APP完整路径,拷贝libDexHelper.so到本地
adb shell dumpsys window | grep mCurrentFocus
adb shell pm path com.xxx.xxx
adb shell "su -c 'cp /data/app/xxx/com.xxx.xxx/lib/arm64/libDexHelper.so /data/local/tmp/ && chmod 777 /data/local/tmp/libDexHelper.so'"
adb pull /data/local/tmp/libDexHelper.so ./dumps

3、libDexHelper.so导入IDA只有32个有名字的符号(25个导入函数+ 7个标记)

4、快捷键G跳到0,D切换显示模式

5、0x40查看ELF64 Program Header

6、导入parse_elf.py定位壳入口
0x26000查看.dynamic 段,DT_INIT_ARRAY = 0x14D08,DT_RELA = 0x26E8
0x26E8 查看重定位表
.init_array[0]的值= 0x4780

import struct
import idc
print("=" * 60)
print("第1步:ELF Header")
print("=" * 60)
e_phoff = struct.unpack_from('<Q', idc.get_bytes(0x20, 8), 0)[0]
e_phentsize = struct.unpack_from('<H', idc.get_bytes(0x36, 2), 0)[0]
e_phnum = struct.unpack_from('<H', idc.get_bytes(0x38, 2), 0)[0]
print(f"Program Header 偏移: 0x{e_phoff:X}")
print(f"每个条目大小: {e_phentsize} 字节")
print(f"条目数量: {e_phnum}")
print(f"-> 下一步去地址 0x{e_phoff:X}")
print("")
print("=" * 60)
print("第2步:Program Header Table")
print("=" * 60)
type_names = {
0: 'NULL', 1: 'LOAD', 2: 'DYNAMIC', 3: 'INTERP',
4: 'NOTE', 6: 'PHDR', 0x6474e550: 'GNU_EH_FRAME',
0x6474e551: 'GNU_STACK', 0x6474e552: 'GNU_RELRO'
}
dynamic_vaddr = None
for i in range(e_phnum):
off = e_phoff + i * e_phentsize
data = idc.get_bytes(off, e_phentsize)
p_type = struct.unpack_from('<I', data, 0)[0]
p_offset = struct.unpack_from('<Q', data, 8)[0]
p_vaddr = struct.unpack_from('<Q', data, 16)[0]
p_filesz = struct.unpack_from('<Q', data, 32)[0]
tname = type_names.get(p_type, f'0x{p_type:X}')
marker = ' <<<' if p_type == 2 else ''
print(f"[{i}] {tname:15s} offset=0x{p_offset:08X} vaddr=0x{p_vaddr:08X} size=0x{p_filesz:X}{marker}")
if p_type == 2:
dynamic_vaddr = p_vaddr
if dynamic_vaddr:
print(f"\n-> 找到 DYNAMIC 段! 下一步去地址 0x{dynamic_vaddr:X}")
if dynamic_vaddr:
print("")
print("=" * 60)
print(f"第3步: .dynamic 段 (地址 0x{dynamic_vaddr:X})")
print("=" * 60)
tag_names = {
0: 'DT_NULL', 1: 'DT_NEEDED', 4: 'DT_HASH',
5: 'DT_STRTAB', 6: 'DT_SYMTAB', 7: 'DT_RELA',
8: 'DT_RELASZ', 10: 'DT_STRSZ', 12: 'DT_INIT',
13: 'DT_FINI', 25: 'DT_INIT_ARRAY', 26: 'DT_FINI_ARRAY',
27: 'DT_INIT_ARRAYSZ', 28: 'DT_FINI_ARRAYSZ'
}
init_array_addr = None
rela_addr = None
rela_size = None
addr = dynamic_vaddr
while addr < dynamic_vaddr + 0x1000:
data = idc.get_bytes(addr, 16)
d_tag = struct.unpack_from('<Q', data, 0)[0]
d_val = struct.unpack_from('<Q', data, 8)[0]
if d_tag == 0:
print(f"0x{addr:08X}: DT_NULL (结束)")
break
tname = tag_names.get(d_tag, f'DT_0x{d_tag:X}')
marker = ''
if d_tag == 25:
marker = ' <<< .init_array 地址!'
init_array_addr = d_val
elif d_tag == 27:
marker = ' (.init_array 大小)'
elif d_tag == 7:
marker = ' <<< 重定位表地址'
rela_addr = d_val
elif d_tag == 8:
marker = ' <<< 重定位表大小'
rela_size = d_val
print(f"0x{addr:08X}: {tname:25s} = 0x{d_val:08X}{marker}")
addr += 16
if rela_addr and init_array_addr:
print("")
print("=" * 60)
print(f"第4步: 重定位表 (地址 0x{rela_addr:X})")
print("=" * 60)
count = rela_size // 24
for i in range(count):
off = rela_addr + i * 24
data = idc.get_bytes(off, 24)
r_offset = struct.unpack_from('<Q', data, 0)[0]
r_info = struct.unpack_from('<Q', data, 8)[0]
r_addend = struct.unpack_from('<q', data, 16)[0]
r_type = r_info & 0xFFFFFFFF
type_names_r = {0x401: 'RELATIVE', 0x403: 'GLOB_DAT'}
tname = type_names_r.get(r_type, f'0x{r_type:X}')
marker = ''
if r_offset == init_array_addr:
marker = ' <<< 这就是壳的入口地址!'
print(f"[{i}] 修改 0x{r_offset:08X} <- 0x{r_addend:X} 类型={tname}{marker}")
print("")
print("=" * 60)
print("结论")
print("=" * 60)
if init_array_addr and rela_addr:
print(f".init_array 地址: 0x{init_array_addr:X}")
print(f"壳的真正入口: 0x4780")
print(f"")
print(f"-> 按 G 输入 4780 跳转到壳入口, 然后按 F5 反编译")
7、0x4780,F5反编译

8、0x33F8,F5反编译,壳主函数

9、在壳主函数中找到关键调用
0x36BC: BL sub_3184 (RC4解密)
0x3728: BL sub_2B1C (XOR解密)
0x454C: BL sub_2CD0 (ELF重定位)

10、解密壳

import struct
with open('libDexHelper.so', 'rb') as f:
data = f.read()
print(f"[*] 原始文件大小: {hex(len(data))}")
PAYLOAD_OFFSET = 0x8000
XOR_KEY = 0x19
HEADER_DECRYPT_LEN = 0x40
KEY_MATERIAL_OFFSET = 0x104E3D
KEY_MATERIAL_LEN = 0x14
HEADER_KEY_LEN = 0x10
key_material = data[KEY_MATERIAL_OFFSET:KEY_MATERIAL_OFFSET + KEY_MATERIAL_LEN]
print(f"[*] 密钥材料 ({hex(KEY_MATERIAL_OFFSET)}): {key_material.hex()}")
header_key = key_material[:HEADER_KEY_LEN]
print(f"[*] 头部解密key: {header_key.hex()}")
encrypted_payload = data[PAYLOAD_OFFSET:]
print(f"[*] 加密payload大小: {hex(len(encrypted_payload))}")
def rc4_like_decrypt(data, key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
result = bytearray(len(data))
i = j = 0
for k in range(len(data)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
keystream_byte = S[(S[i] + S[j]) % 256]
result[k] = data[k] ^ keystream_byte
return bytes(result)
header_encrypted = encrypted_payload[:HEADER_DECRYPT_LEN]
header_decrypted = rc4_like_decrypt(header_encrypted, header_key)
print(f"[*] 头部解密完成")
body_encrypted = encrypted_payload[HEADER_DECRYPT_LEN:]
body_decrypted = bytes([b ^ XOR_KEY for b in body_encrypted])
print(f"[*] 正文XOR解密完成 (key=0x{XOR_KEY:02X})")
decrypted_payload = header_decrypted + body_decrypted
if decrypted_payload[:4] == b'\x7fELF':
print(f"[+] ELF魔数验证通过!")
else:
print(f"[-] 警告: ELF魔数不匹配: {decrypted_payload[:4].hex()}")
elf_magic = decrypted_payload[:4]
ei_class = decrypted_payload[4]
ei_data = decrypted_payload[5]
e_type = struct.unpack('<H', decrypted_payload[16:18])[0]
e_machine = struct.unpack('<H', decrypted_payload[18:20])[0]
e_entry = struct.unpack('<I', decrypted_payload[24:28])[0]
e_phoff = struct.unpack('<I', decrypted_payload[28:32])[0]
e_shoff = struct.unpack('<I', decrypted_payload[32:36])[0]
e_phnum = struct.unpack('<H', decrypted_payload[42:44])[0]
e_shnum = struct.unpack('<H', decrypted_payload[48:50])[0]
print(f"\n[*] 内层ELF信息:")
print(f" 类型: {'64位' if ei_class == 2 else '32位'}")
print(f" 字节序: {'小端' if ei_data == 1 else '大端'}")
print(f" e_type: {hex(e_type)}")
print(f" e_machine: {hex(e_machine)}")
print(f" 入口点: {hex(e_entry)}")
print(f" Program Header偏移: {hex(e_phoff)}")
print(f" Section Header偏移: {hex(e_shoff)}")
print(f" Program Header数量: {e_phnum}")
print(f" Section Header数量: {e_shnum}")
output_file = 'libDexHelper_inner.so'
with open(output_file, 'wb') as f:
f.write(decrypted_payload)
print(f"\n[+] 解壳完成! 保存到: {output_file}")
print(f"[+] 文件大小: {hex(len(decrypted_payload))}")
11、libDexHelper_inner.so导入IDA,函数数量2211个

12、使用florida 16.1.8绕过frida检测

13、使用frida-dexdump -d模式
D:\path\python\python39\python39.exe -m frida_dexdump -U -f com.xxx.xxx --sleep 15 -d -o C:\Users\Administrator\Desktop\frida\dump_output

14、修复dex

import os
import struct
import zlib
import hashlib
def fix_dex_checksum(filepath):
"""修复 DEX 文件的 checksum 和签名"""
with open(filepath, 'rb') as f:
data = bytearray(f.read())
if data[:4] != b'dex\n':
print(f"[跳过] {os.path.basename(filepath)}: 不是 DEX 文件")
return False
old_checksum = struct.unpack_from('<I', data, 8)[0]
sha1 = hashlib.sha1(bytes(data[32:])).digest()
data[12:32] = sha1
new_checksum = zlib.adler32(bytes(data[12:])) & 0xFFFFFFFF
struct.pack_into('<I', data, 8, new_checksum)
with open(filepath, 'wb') as f:
f.write(data)
print(f"[修复] {os.path.basename(filepath)}: checksum=0x{new_checksum:08X}")
return True
def main():
dex_dir = r'C:\Users\Administrator\Desktop\frida\dump_output'
print("=" * 60)
print("DEX 文件 checksum 修复工具 (v2)")
print("=" * 60)
fixed = 0
failed = 0
for filename in sorted(os.listdir(dex_dir)):
if filename.endswith('.dex'):
filepath = os.path.join(dex_dir, filename)
try:
if fix_dex_checksum(filepath):
fixed += 1
except Exception as e:
print(f"[错误] {filename}: {e}")
failed += 1
print()
print("=" * 60)
print(f"修复完成: {fixed} 个文件")
print(f"失败: {failed} 个文件")
print("=" * 60)
if __name__ == '__main__':
main()
15、AndroidManifest.xml找一个WebViewActivity

16、去脱壳的代码中查看WebViewActivity验证

[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。