【银行逆向百例】18Android逆向之libDexHelper.so梆梆加固壳解密+frida-dexdump脱壳"

admin 2026-10-22 00:16:34 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

【银行逆向百例】18Android逆向之libDexHelper.so梆梆加固壳解密+frida-dexdump脱壳

原创

挖个洞先
挖个洞先

挖个洞先

2026年6月8日 09:00
北京

在小说阅读器读本章

去阅读

“ 只要有想见的人,就不是孤单一个人。——《夏目友人帐》S3E4 ”

01

—

环境版本

环境:

电脑,Windows 11 专业版 23H2

https://github.com/JiaoSuInfoSec/JiaoSuInfoSec_T00ls_Win11

软件:

Florida,16.1.8

https://github.com/Ylarod/Florida/releases/tag/16.1.8

frida-dexdump

https://github.com/hluwa/frida-dexdump

02

—

操作步骤

1、梆梆加固企业版

2、查看APP完整路径,拷贝libDexHelper.so到本地

adb shell dumpsys window | grep mCurrentFocusadb shell pm path com.xxx.xxxadb 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 structimport idc# ========== 第1步:解析 ELF Header ==========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}\")# ========== 第2步:解析 Program Headers ==========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 = Nonefor 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_vaddrif dynamic_vaddr:    print(f\"\
-> 找到 DYNAMIC 段! 下一步去地址 0x{dynamic_vaddr:X}\")# ========== 第3步:解析 .dynamic 段 ==========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    # ========== 第4步:解析重定位表 ==========    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      # 内层payload起始偏移XOR_KEY = 0x19                # 正文XOR keyHEADER_DECRYPT_LEN = 0x40     # 头部解密长度# 从文件尾部偏移0x104E3D读取密钥材料KEY_MATERIAL_OFFSET = 0x104E3DKEY_MATERIAL_LEN = 0x14HEADER_KEY_LEN = 0x10         # 前0x10字节作为头部解密keykey_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()}\")# 提取加密的payloadencrypted_payload = data[PAYLOAD_OFFSET:]print(f\"[*] 加密payload大小: {hex(len(encrypted_payload))}\")# 第一步:对头部0x40字节做RC4-like解密# RC4-like: 简单的流密码,用key生成密钥流def rc4_like_decrypt(data, key):    # 初始化S盒    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\"[*] 头部解密完成\")# 第二步:对正文做单字节XOR解密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})\")# 拼接完整的解壳后payloaddecrypted_payload = header_decrypted + body_decrypted# 验证ELF魔数if decrypted_payload[:4] == b'\\x7fELF':    print(f\"[+] ELF魔数验证通过!\")else:    print(f\"[-] 警告: ELF魔数不匹配: {decrypted_payload[:4].hex()}\")# 解析ELF头elf_magic = decrypted_payload[:4]ei_class = decrypted_payload[4]  # 32/64位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\"\
[*] 内层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\"\
[+] 解壳完成! 保存到: {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 osimport structimport zlibimport hashlibdef fix_dex_checksum(filepath):    \"\"\"修复 DEX 文件的 checksum 和签名\"\"\"    with open(filepath, 'rb') as f:        data = bytearray(f.read())    # 检查 DEX 魔数    if data[:4] != b'dex\
':        print(f\"[跳过] {os.path.basename(filepath)}: 不是 DEX 文件\")        return False    # 读取当前值    old_checksum = struct.unpack_from('<I', data, 8)[0]    # 第一步:计算 SHA-1 签名(对偏移 32 开始的数据)    sha1 = hashlib.sha1(bytes(data[32:])).digest()    # 第二步:写入签名到偏移 12-31    data[12:32] = sha1    # 第三步:计算 adler32 checksum(对偏移 12 开始的数据,包含签名)    new_checksum = zlib.adler32(bytes(data[12:])) & 0xFFFFFFFF    # 第四步:写入 checksum 到偏移 8-11    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 Truedef 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验证

插眼

机器人安全-UR机器人RCE漏洞复现与审计" 网络安全文章

机器人安全-UR机器人RCE漏洞复现与审计"

本文详细分析了UR机器人CVE-2026-8153漏洞的复现与审计过程,通过环境搭建、代码审计追溯漏洞链,指出DashboardServer在29999端口无认证接受任意TCP连接,导致用户输入未经滤直接拼接至shell命令,最终通过构造payload实现RCE,并提供了具体复现POC。
工具推荐|LeakDetector智能信息泄露侦察工具,自动筛选高价值敏感文件" 网络安全文章

工具推荐|LeakDetector智能信息泄露侦察工具,自动筛选高价值敏感文件"

LeakDetector是一款面向红队渗透测试的自动化信息泄露侦察工具,通过Bing搜索引擎语法结合Playwright浏览器自动化技术,智能筛选高价值敏感文件并生成可视化审计报告。工具具备抗反爬机制和深度内容分析引擎,支持批量目标扫描、风险分级和CAPTCHA绕过功能。文档包含工具截图、获取方式及作者的安全服务推广内容。
第19篇全栈AI·Shannon-AI渗透测试中文增" 网络安全文章

第19篇全栈AI·Shannon-AI渗透测试中文增"

本文剖析Shannon-AI渗透测试工具,指出其核心价值在于将人工审计流程产品化而非攻击自动化。文章从输入污染、多智能体误判放大等维度揭示流程攻击面,强调合规验证须在授权隔离环境中进行。建议企业团队优先完善证据链,明确自动化边界,强制保留人工复核,并将可追溯且面向修复的报告作为交付标准,确保安全研究的合法性与落地价值。
评论:0   参与:  0