首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
64
39
[原创]某加固so层脱壳
发表于: 2025-2-12 13:52
58122
[原创]某加固so层脱壳
逆天而行
1
2025-2-12 13:52
58122
加固版本:2025/2/11 最新免费版 ### 第一步查看 加载so 第一步 :使用frida(最好魔改 不然有检测) 查看加载的so文件 ```Plain Text function main (){ Java.perform( function(){ hook_dlopen(); } ); } setImmediate(main) //刚注入的时候这个so还没加载,需要hook dlopen function inline_hook() { var base_hello_jni = Module.findBaseAddress("libxx.so"); console.log("base_hello_jni:", base_hello_jni); if (base_hello_jni) { console.log(base_hello_jni); //inline hook var addr_07320 = base_hello_jni.add(0x2E637);//指令执行的地址,不是变量所在的栈或堆 Interceptor.attach(addr_07320, { onEnter: function (args) { console.log("addr_07320 R0 R3:", this.context.r0,this.context.r3);//注意这里是怎么得到寄存器值的 }, onLeave: function (retval) { } }); } } //8.0以下所有的so加载都通过dlopen function hook_dlopen() { var dlopen = Module.findExportByName(null, "dlopen"); Interceptor.attach(dlopen, { onEnter: function (args) { this.call_hook = false; var so_name = ptr(args[0]).readCString(); console.log("dlopen:", ptr(args[0]).readCString()); }, onLeave: function (retval) { if (this.call_hook) {//dlopen函数找到了就hook so inline_hook(); } } }); // 高版本Android系统使用android_dlopen_ext var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); Interceptor.attach(android_dlopen_ext, { onEnter: function (args) { this.call_hook = false; var so_name = ptr(args[0]).readCString(); console.log("dlopen:", ptr(args[0]).readCString()); }, onLeave: function (retval) { if (this.call_hook) { inline_hook(); } } }); } function log(msg){ console.log(msg); } ``` 打印出来所有加载的so文件了: [Calvin designers::com.calvin.check_tset ]-> dlopen: /data/data/com.calvin.check_tset/.jiagu/libjiagu_64.so dlopen: liblog.so dlopen: libz.so dlopen: libc.so dlopen: libm.so dlopen: libstdc++.so dlopen: libdl.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libart.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libjiagu_64.so dlopen: libjgdtc.so dlopen: /data/app/~~gUGk1sgfebPev2a5rxao2w==/com.calvin.check_tset-Kj54dsV2miW8iYUVx-eTmA==/lib/arm64/libjgdtc.so dlopen: libandroid.so dlopen: /data/app/~~gUGk1sgfebPev2a5rxao2w==/com.calvin.check_tset-Kj54dsV2miW8iYUVx-eTmA==/lib/arm64/libcheck_tset.so dlopen: /vendor/lib64/hw/android.[hardware.graphics.mapper@4.0-impl-qti-display.so](mailto:hardware.graphics.mapper@4.0-impl-qti-display.so) dlopen: /vendor/lib64/hw/gralloc.lito.so dlopen: libadreno_app_profiles.so dlopen: libEGL_adreno.so dlopen: /vendor/lib64/hw/android.[hardware.graphics.mapper@4.0-impl-qti-display.so](mailto:hardware.graphics.mapper@4.0-impl-qti-display.so) dlopen: libadreno_utils.so dlopen: libandroid.so ### 第二步 dump 要分析的so文件 一眼观望全部so文件 , 接下来 把要分析的so文件 dump下来 看看 怎么回事 ```Plain Text function dump_so(so_name) { var libso = Process.getModuleByName(so_name); console.log("[name]:", libso.name); console.log("[base]:", libso.base); console.log("[size]:", ptr(libso.size)); console.log("[path]:", libso.path); var file_path = "/data/data/"+get_self_process_name()+"/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so"; var file_handle = new File(file_path, "wb"); if (file_handle && file_handle != null) { Memory.protect(ptr(libso.base), libso.size, 'rwx'); var libso_buffer = ptr(libso.base).readByteArray(libso.size); file_handle.write(libso_buffer); file_handle.flush(); file_handle.close(); console.log("[dump]:", file_path); } } setImmediate(function() { setTimeout(function() { dump_so("libjiagu_64.so"); }, 5000); // 等待5秒后执行 }); function get_self_process_name() { var openPtr = Module.getExportByName('libc.so', 'open'); var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']); var readPtr = Module.getExportByName("libc.so", "read"); var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]); var closePtr = Module.getExportByName('libc.so', 'close'); var close = new NativeFunction(closePtr, 'int', ['int']); var path = Memory.allocUtf8String("/proc/self/cmdline"); var fd = open(path, 0); if (fd != -1) { var buffer = Memory.alloc(0x1000); var result = read(fd, buffer, 0x1000); close(fd); result = ptr(buffer).readCString(); return result; } return "-1"; } ``` dump 下来的so是需要修复滴 使用so_fixer <a href="elink@b25K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6r3z5p5I4q4c8W2c8Q4x3V1k6e0L8@1k6A6P5r3g2J5"><mark class="encrypted">4fdK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6r3z5p5I4q4c8W2c8Q4x3V1k6e0L8@1k6A6P5r3g2J5</mark></a> 修复完毕后 就可以显示 全部的符号信息了 ### 第三步 trace 打印运行流程 现在 我们需要了解程序运行 调用的流程 也就是 函数 trace 这里使用 oacia 佬的方案 <a href="elink@808K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2j5h3y4A6j5g2)9J5c8Y4y4@1j5h3I4C8k6i4u0Q4y4h3k6@1M7X3q4U0k6g2)9#2k6Y4y4G2"><mark class="encrypted">3dcK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2j5h3y4A6j5g2)9J5c8Y4y4@1j5h3I4C8k6i4u0Q4y4h3k6@1M7X3q4U0k6g2)9#2k6Y4y4G2</mark></a> 打印 so函数的流程: call1:JNI_OnLoad call2:j_interpreter_wrap_int64_t call3:interpreter_wrap_int64_t call4:_Znwm call5:sub_1362C call6:_Znam call7:sub_10F54 call8:memset call9:sub_9C50 call10:sub_E114 call11:calloc call12:malloc call13:free call14:sub_E37C call15:_ZdaPv call16:sub_C680 call17:sub_CB38 call18:sub_9800 call19:sub_97DC call20:sub_CCA8 call21:sub_C86C call22:sub_993C call23:sub_1591C call24:sub_16094 call25:sub_16160 call26:sub_15C94 call27:sub_16954 call28:sub_15D14 call29:sub_159F0 call30:sub_1595C call31:sub_9778 call32:sub_CB90 call33:sub_CD8C call34:sub_CAD8 call35:sub_91E0 call36:dladdr call37:strstr call38:setenv call39:_Z9__arm_a_1P7_JavaVMP7_JNIEnvPvRi call40:sub_9CD0 call41:sub_9814 call42:sub_10698 call43:j__ZdlPv_1 call44:_ZdlPv call45:sub_9558 call46:sub_7D00 //这里存在 so字符串 call47:__strncpy_chk2 call48:sub_5ACC call49:sub_5F30 call50:sub_46A0 call51:sub_5B14 call52:_ZN9__arm_c_19__arm_c_0Ev call53:sub_A228 call54:sub_9844 call55:sub_97BC call56:sub_CF24 call57:sub_5E70 call58:sub_5F7C call59:memcpy call60:sub_6084 //疑似 加密函数 call61:sub_5974 call62:j__ZdlPv_3 call63:j__ZdlPv_2 call64:j__ZdlPv_0 call65:sub_A1DC call66:sub_9908 call67:sub_59CC call68:sub_5A24 //疑似 rc4 call69:sub_9E58 call70:sub_307C call71:uncompress //经典解压环节 call72:sub_CBF4 call73:sub_4538 // 这里好像 linker load call74:sub_4D34 call75:sub_4DAC call76:sub_543C //这里存在 异或加密 call77:sub_4F84 call78:sub_5140 // 存在 内存拷贝 权限修改 call79:mprotect call80:__strlen_chk call81:strncpy call82:sub_379C // linker 加载 call83:dlopen call84:sub_446C // 这里纯在 将 内存 区域 清零 call85:sub_3B54 // 链接有关 call86:sub_3D08 // 这里出现一些 疑似奇怪的检测 call87:sub_30B4 call88:dlsym call89:strcmp call90:sub_57A0 // 这里也是修改内存权限 为 可执行吧? call91:sub_4D78 call92:sub_5D28 call93:sub_7E38 call94:sub_47BC call95:sub_7F64 call96:sub_8854 call97:sub_8BE0 call98:sub_8138 call99:interpreter_wrap_int64_t_bridge call100:sub_9BD8 call101:sub_15C0C call102:puts call103:_Z9__arm_a_2PcmS_Rii ### 第四步 逐个分析 这里 我一个步骤一个步骤的看 发现函数 sub_9558() 调用 kill() 自己 进程 ```C++ v4 = sub_6F9C(); if ( (v4 & 1) != 0 ) { v5 = getpid(); v4 = kill(v5, 9); } v6 = sub_70A8(v4); v7 = sub_7204(v6); if ( (v7 & 1) != 0 ) { v8 = getpid(); v7 = kill(v8, 9); } result = sub_70A8(v7); v1 = qword_276180; ``` 第一个函数 : ```C++ FILE *sub_6F9C() { FILE *result; // x0 FILE *v1; // x19 char s[512]; // [xsp+8h] [xbp-248h] BYREF char v3[16]; // [xsp+208h] [xbp-48h] BYREF char filename[24]; // [xsp+218h] [xbp-38h] BYREF _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)); *(_QWORD *)&filename[6] = '\xA5\xD5\xC6\xD1\x8A\xD1\xC0\xCB'; *(_QWORD *)filename = '\xC0\xCB\x8A\xC6\xCA\xD7\xD5\x8A'; *(_QWORD *)&v3[6] = 0xA5E49DE1909F9595LL; *(_QWORD *)v3 = 0x9595959595959595LL; sub_6554(filename, 14LL); sub_6554(v3, 14LL); memset(s, 0, sizeof(s)); result = fopen(filename, "r"); if ( result ) { v1 = result; while ( !feof(v1) ) { fgets(s, 512, v1); if ( strstr(s, v3) ) { fclose(v1); return (FILE *)(&dword_0 + 1); } memset(s, 0, sizeof(s)); } fclose(v1); return 0LL; } return result; } ``` 字符串为加密 我们编写frida 打印一下 发现 frida 未打印 这个函数也未运行那就不管啦 在 sub_7D00 函数 里面居然出现了 so 这个字符串 ```C++ __int64 sub_7D00() { __int64 v0; // x19 _QWORD v2[7]; // [xsp+10h] [xbp-140h] BYREF _QWORD v3[2]; // [xsp+48h] [xbp-108h] BYREF __int128 v4; // [xsp+58h] [xbp-F8h] _OWORD v5[8]; // [xsp+68h] [xbp-E8h] BYREF __int128 v6; // [xsp+E8h] [xbp-68h] __int128 v7; // [xsp+F8h] [xbp-58h] __int128 v8; // [xsp+108h] [xbp-48h] __int128 v9; // [xsp+118h] [xbp-38h] _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)); v8 = 0u; v9 = 0u; v6 = 0u; v7 = 0u; memset(v5, 0, sizeof(v5)); v4 = 0u; _strncpy_chk2((char *)v5 + 4, "*.so", 128LL, 188LL, 5LL); v3[0] = &qword_2D260; v3[1] = 752309LL; *(_QWORD *)&v7 = off_2D178; LODWORD(v5[0]) = 1; *((_QWORD *)&v6 + 1) = &qword_E5178; DWORD2(v9) = 1; *((_QWORD *)&v7 + 1) = 0x400000002LL; LODWORD(v8) = 5; *((_QWORD *)&v8 + 1) = 0LL; *(_QWORD *)&v9 = 0LL; sub_5ACC(v2); v2[0] = (char *)off_2CF48 + 16; v0 = 0LL; if ( (sub_5F30(v2, (char *)&qword_E4D10 + 5, 1062LL) & 1) != 0 ) { v0 = sub_46A0(v3, v2); if ( *((_QWORD *)&v8 + 1) ) free(*((void **)&v8 + 1)); } sub_5D28(v2); return v0; } ``` 在之后 还遇到 一道类似加密算法的sub_6084函数: ```C++ _BYTE *__fastcall sub_6084(__int64 a1) { unsigned int v2; // w20 _BYTE *result; // x0 _BYTE *v4; // x10 unsigned int v5; // w8 int v6; // w11 int v7; // [xsp+8h] [xbp-48h] int v8; // [xsp+Ch] [xbp-44h] int v9; // [xsp+10h] [xbp-40h] int v10; // [xsp+14h] [xbp-3Ch] int v11; // [xsp+18h] [xbp-38h] int v12; // [xsp+1Ch] [xbp-34h] int v13; // [xsp+20h] [xbp-30h] int v14; // [xsp+24h] [xbp-2Ch] __int64 v15; // [xsp+28h] [xbp-28h] v15 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); v2 = *(_DWORD *)(a1 + 4); result = (_BYTE *)operator new[](v2); v4 = *(_BYTE **)(a1 + 8); v5 = 0; v6 = 7; *(_QWORD *)(a1 + 24) = result; do { *(&v7 + v6) = ((unsigned __int8)(*v4 ^ (*v4 >> 2) ^ (*v4 >> 4)) ^ (*v4 >> 6)) & 1; if ( v6 ) { --v6; } else { ++v5; v6 = 7; *result++ = ((_BYTE)v13 << 6) + ((_BYTE)v14 << 7) + 32 * v12 + 16 * v11 + 8 * v10 + 4 * v9 + 2 * v8 + v7; v4 = *(_BYTE **)(a1 + 8); v2 = *(_DWORD *)(a1 + 4); } *(_QWORD *)(a1 + 8) = ++v4; } while ( v5 <= v2 - 1 ); return result; ``` //继续按照trace流程 观察 代码 (找到能看懂的部分 就行) 发现一个类似 rc4 初始化算法的 部分 : ```C++ _BYTE *__fastcall sub_5A24(_BYTE *result, int a2, __int64 a3) { __int64 v3; // x9 char v4; // w11 unsigned __int8 v5; // w10 unsigned __int8 v6; // w11 char v7; // w13 _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)); if ( a2 >= 1 ) { v3 = (unsigned int)a2; do { v4 = *(_BYTE *)(a3 + 257); --v3; v5 = *(_BYTE *)(a3 + 256) + 2; *(_BYTE *)(a3 + 256) = v5; v6 = *(_BYTE *)(a3 + v5) + v4 + 1; *(_BYTE *)(a3 + 257) = v6; v7 = *(_BYTE *)(a3 + v5); *(_BYTE *)(a3 + v5) = *(_BYTE *)(a3 + v6); *(_BYTE *)(a3 + v6) = v7; *result++ ^= *(_BYTE *)(a3 + (unsigned __int8)(*(_BYTE *)(a3 + *(unsigned __int8 *)(a3 + 257)) + *(_BYTE *)(a3 + *(unsigned __int8 *)(a3 + 256)))); } while ( v3 ); } return result; } ``` 这里大致把能看懂的 打了注释 整个流程已经了解的差不多了 现在我们要解密 so文件 就需要深入的了解 自实现linker加载so 的原理 参考: [https://bbs.kanxue.com/thread-282316.htm](https://bbs.kanxue.com/thread-282316.htm?style=1#msg_header_h1_1) ```C++ // 1. 讀取so文件 if(!Read(path, fd, 0, sb.st_size)){ LOGD("Read so failed"); munmap(start_addr_, sb.st_size); close(fd); } // 2. 載入so if(!Load()) { LOGD("Load so failed"); munmap(start_addr_, sb.st_size); close(fd); } // 使被加載的so有執行權限, 否則在調用.init_array時會報錯 mprotect(reinterpret_cast<void *>(load_bias_), sb.st_size, PROT_READ | PROT_WRITE | PROT_EXEC); // 3. 預鏈接, 主要處理 .dynamic節 si_->prelink_image(); // 4. 正式鏈接, 在這裡處理重定位的信息 si_->link_image(); // 5. 調用.init和.init_array si_->call_constructors(); ``` ### 第五步 找到关键dump 流程大概是这样 这个加固 把流程 已经混淆,我们需要 找到 关键函数 dump下来 才能完成修复 ```C++ void *__fastcall sub_379C(__int64 a1) { unsigned int v2; // w8 __int64 v3; // x20 unsigned __int64 *v4; // x11 __int64 v5; // x13 void *result; // x0 __int64 v7; // x13 __int64 v8; // x14 __int64 v9; // x15 __int64 v10; // x16 __int64 v11; // x16 unsigned __int64 v12; // t1 unsigned __int64 v13; // t1 unsigned __int64 v14; // t1 unsigned __int64 v15; // t1 unsigned __int64 v16; // t1 unsigned __int64 v17; // t1 unsigned __int64 v18; // t1 unsigned __int64 v19; // t1 unsigned __int64 v20; // t1 unsigned __int64 v21; // t1 unsigned __int64 v22; // t1 __int64 v23; // x13 __int64 v24; // x14 __int64 v25; // x15 unsigned __int64 v26; // t1 unsigned __int64 v27; // t1 unsigned __int64 v28; // t1 unsigned __int64 v29; // t1 unsigned __int64 v30; // t1 unsigned int v31; // w23 _QWORD *v32; // x24 __int64 v33; // x8 const char *v34; // x20 __int64 v35; // x9 v2 = 0; _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)); v3 = *(_QWORD *)(a1 + 256); v4 = (unsigned __int64 *)(v3 + 8); while ( 2 ) { v5 = *(v4 - 1); result = 0LL; switch ( v5 ) { case 0LL: if ( !*(_QWORD *)(a1 + 272) || !*(_QWORD *)a1 ) return 0LL; *(_DWORD *)(a1 + 456) = v2; result = calloc(1uLL, 144LL * v2); *(_QWORD *)(a1 + 464) = result; if ( !result ) return result; v31 = 0; v32 = (_QWORD *)(v3 + 8); break; case 1LL: ++v2; v4 += 2; continue; case 2LL: v19 = *v4; v4 += 2; *(_QWORD *)(a1 + 296) = v19 / 0x18; continue; case 3LL: case 14LL: case 15LL: case 19LL: case 21LL: case 24LL: case 29LL: case 31LL: goto LABEL_2; case 4LL: *(_BYTE *)(a1 + 40) = 1; v23 = *(_QWORD *)(a1 + 400); v24 = *(unsigned int *)(*v4 + v23); *(_QWORD *)(a1 + 8) = v24; v25 = *(unsigned int *)(*v4 + v23 + 4); v23 += 8LL; *(_QWORD *)(a1 + 24) = v25; *(_QWORD *)(a1 + 16) = v23 + *v4; v26 = *v4; v4 += 2; *(_QWORD *)(a1 + 32) = v23 + 4 * v24 + v26; continue; case 5LL: v22 = *v4; v4 += 2; *(_QWORD *)(a1 + 272) = v22 + *(_QWORD *)(a1 + 400); continue; case 6LL: v21 = *v4; v4 += 2; *(_QWORD *)a1 = v21 + *(_QWORD *)(a1 + 400); continue; case 7LL: if ( *(_BYTE *)(a1 + 280) ) goto LABEL_2; v15 = *v4; v4 += 2; *(_QWORD *)(a1 + 304) = v15 + *(_QWORD *)(a1 + 400); continue; case 8LL: v20 = *v4; v4 += 2; *(_QWORD *)(a1 + 312) = v20 / 0x18; continue; case 9LL: case 11LL: if ( *v4 != 24 ) return 0LL; goto LABEL_2; case 10LL: v17 = *v4; v4 += 2; *(_QWORD *)(a1 + 448) = v17; continue; case 12LL: v18 = *v4; v4 += 2; *(_QWORD *)(a1 + 368) = v18 + *(_QWORD *)(a1 + 400); continue; case 13LL: v14 = *v4; v4 += 2; *(_QWORD *)(a1 + 376) = v14 + *(_QWORD *)(a1 + 400); continue; case 16LL: goto LABEL_31; case 17LL: case 18LL: case 22LL: return result; case 20LL: if ( *v4 != 7 ) return 0LL; goto LABEL_2; case 23LL: if ( *(_BYTE *)(a1 + 280) ) goto LABEL_2; v29 = *v4; v4 += 2; *(_QWORD *)(a1 + 288) = v29 + *(_QWORD *)(a1 + 400); continue; case 25LL: v13 = *v4; v4 += 2; *(_QWORD *)(a1 + 336) = v13 + *(_QWORD *)(a1 + 400); continue; case 26LL: v28 = *v4; v4 += 2; *(_QWORD *)(a1 + 352) = v28 + *(_QWORD *)(a1 + 400); continue; case 27LL: v16 = *v4; v4 += 2; *(_QWORD *)(a1 + 344) = (unsigned int)v16 >> 3; continue; case 28LL: v12 = *v4; v4 += 2; *(_QWORD *)(a1 + 360) = (unsigned int)v12 >> 3; continue; case 30LL: if ( (*v4 & 4) != 0 ) return 0LL; if ( (*v4 & 2) != 0 ) LABEL_31: *(_BYTE *)(a1 + 408) = 1; LABEL_2: v4 += 2; continue; case 32LL: v27 = *v4; v4 += 2; *(_QWORD *)(a1 + 320) = v27 + *(_QWORD *)(a1 + 400); continue; case 33LL: v30 = *v4; v4 += 2; *(_QWORD *)(a1 + 328) = (unsigned int)v30 >> 3; continue; default: if ( v5 != 1879047925 ) goto LABEL_2; *(_BYTE *)(a1 + 41) = 1; v7 = *(_QWORD *)(a1 + 400); v8 = *(unsigned int *)(*v4 + v7); *(_QWORD *)(a1 + 48) = v8; v9 = *(unsigned int *)(*v4 + v7 + 8); *(_DWORD *)(a1 + 72) = v9; *(_DWORD *)(a1 + 76) = *(_DWORD *)(*v4 + v7 + 12); v10 = v7 + *v4 + 16; *(_QWORD *)(a1 + 80) = v10; v11 = v10 + 8 * v9; *(_QWORD *)(a1 + 56) = v11; *(_QWORD *)(a1 + 64) = v11 + 4 * v8 - 4LL * *(unsigned int *)(*v4 + v7 + 4); if ( (((_DWORD)v9 - 1) & (unsigned int)v9) != 0 ) return 0LL; *(_DWORD *)(a1 + 72) = v9 - 1; v4 += 2; continue; } break; } while ( 1 ) { v33 = *(v32 - 1); if ( v33 == 1 ) break; if ( !v33 ) return &dword_0 + 1; LABEL_37: v32 += 2; } v34 = (const char *)(*(_QWORD *)(a1 + 272) + *v32); if ( _strlen_chk(v34, 0xFFFFFFFFFFFFFFFFLL) <= 0x80 && v31 < *(_DWORD *)(a1 + 456) ) { strncpy((char *)(*(_QWORD *)(a1 + 464) + 144LL * v31 + 8), v34, 0x7FuLL); result = dlopen(v34, 2); if ( !result ) return result; v35 = 144LL * v31; *(_QWORD *)(*(_QWORD *)(a1 + 464) + v35) = result; ++v31; *(_QWORD *)(*(_QWORD *)(a1 + 464) + v35 + 136) = 0LL; goto LABEL_37; } return 0LL; } ``` 这里 与 刚刚那个so linker项目 里面的预链接 相似 这里可以推断出 dynamic 而这里我们需要一个so info 的结构体 oacia佬的文章里 <a href="elink@831K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6G2j5h3y4A6j5g2)9J5k6h3c8W2N6W2)9J5c8U0x3$3x3q4)9J5k6r3A6A6j5h3N6#2i4K6u0r3"><mark class="encrypted">f4cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6G2j5h3y4A6j5g2)9J5k6h3c8W2N6W2)9J5c8U0x3$3x3q4)9J5k6r3A6A6j5h3N6#2i4K6u0r3</mark></a> 拿来就用 而且 推断出 大致结构体 : ```C++ struct soinfo { char data[232]; const Elf64_Phdr *phdr; linker_ctor_function_t *init_array_; size_t phnum; Elf64_Dyn *dynamic; const char *strtab_; soinfo *next; uint32_t flags_; Elf64_Rela *plt_rela_; size_t plt_rela_count_; Elf64_Rela *rela_; size_t rela_count_; size_t size; Elf64_Sym *symtab_; size_t nbucket_; size_t nchain_; uint32_t *bucket_; uint32_t *chain_; linker_ctor_function_t *preinit_array_; size_t preinit_array_count_; size_t init_array_count_; linker_dtor_function_t *fini_array_; Elf64_Addr base; size_t fini_array_count_; linker_ctor_function_t init_func_; linker_dtor_function_t fini_func_; }; ``` 这个结构的 不是准确的,只推断出 phdr phnum dynamic plt_rela_ plt_rela_count_ rela_ rela_count_ base 这几个变量的值 是确定的 其他的并不准确。 在 hook 预链接函数 处 进行dump ```C++ function my_hook_dlopen(soName = '') { Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); if (path.indexOf(soName) >= 0) { this.is_can_hook = true; } } }, onLeave: function (retval) { if (this.is_can_hook) { hook(); } } } ); } function addr_in_so(addr){ var process_Obj_Module_Arr = Process.enumerateModules(); for(var i = 0; i < process_Obj_Module_Arr.length; i++) { if(addr>process_Obj_Module_Arr[i].base && addr<process_Obj_Module_Arr[i].base.add(process_Obj_Module_Arr[i].size)){ console.log(addr.toString(16),"is in",process_Obj_Module_Arr[i].name,"offset: 0x"+(addr-process_Obj_Module_Arr[i].base).toString(16)); } } } //console.log('RegisterNatives called from:\\n' + Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\\n') + '\\n'); function hook_addr(){ var module = Process.findModuleByName("libjiagu_64.so"); Interceptor.attach(module.base.add(0x5E6C), { // fd, buff, len onEnter: function (args) { console.log(hexdump(args[0], { offset: 0,// 相对偏移 length: 0x38*0x6+0x20,//dump 的大小 header: true, ansi: true })); console.log(args[1]) console.log(args[2]) console.log(`base = ${module.base}`) }, onLeave: function (ret) { } }); } function hook() { console.log("hook fun dump so body"); // 获取libjiagu_64.so模块的基地址 var libjiagu = Process.getModuleByName("libjiagu_64.so"); if (!libjiagu) { console.error("未能找到libjiagu_64.so模块"); return; } var soinfo; var module = Process.findModuleByName("libjiagu_64.so"); Interceptor.attach(module.base.add(0x379C), { // fd, buff, len onEnter: function (args) { console.log("hook 379C method ! "); soinfo = args[0]; console.log("soinfo:",soinfo); var base = ptr(soinfo.add(0x190)).readPointer(); var ph = ptr(soinfo.add(0xE8)).readPointer(); var phnum = ptr(soinfo.add(0xF8)).readLong(); var rela = ptr(soinfo.add(0x130)).readPointer(); var relasz = 0x1823; var plt_rela = ptr(soinfo.add(0x120)).readPointer(); var plt_relasz =0x101; var size = (base).add(0x28).readLong(); var dymagic = ptr(soinfo.add(0x100)).readPointer(); var dy_size =0x290; console.log("base:",base); console.log("so size:",size); console.log("ph:",ph); console.log("phnum:",phnum); console.log("rela:",rela); console.log("relasz:",relasz); console.log("plt_rela:",plt_rela); console.log("plt_relasz:",plt_relasz); dump_so_body(base,size,"so.so"); dump_so_body(ph,phnum*0x38,"ph.so"); dump_so_body(rela,24 *relasz,"rela_0x1823.so"); dump_so_body(plt_rela,24 *plt_relasz,"plt_rela_0x101.so"); dump_so_body(dymagic,dy_size,"dymagic.so"); }, onLeave: function (ret) { var relasz = ptr(soinfo.add(0x138)).readPointer(); console.log("relasz:",relasz); var plt_relasz = ptr(soinfo.add(0x128)).readPointer(); console.log("plt_relasz:",plt_relasz); } }); } setImmediate(my_hook_dlopen,"libjiagu"); function dump_so_body(addr, size, body_name) { var file_path = "/data/data/"+get_self_process_name()+"/" +body_name + "_" + addr + "_" + size + ".body"; var file_handle = new File(file_path, "wb"); if (file_handle && file_handle != null) { Memory.protect(addr,size, 'rwx'); var libso_buffer = ptr(addr).readByteArray(size); file_handle.write(libso_buffer); file_handle.flush(); file_handle.close(); console.log("[dump]:", file_path); } } function get_self_process_name() { var openPtr = Module.getExportByName('libc.so', 'open'); var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']); var readPtr = Module.getExportByName("libc.so", "read"); var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]); var closePtr = Module.getExportByName('libc.so', 'close'); var close = new NativeFunction(closePtr, 'int', ['int']); var path = Memory.allocUtf8String("/proc/self/cmdline"); var fd = open(path, 0); if (fd != -1) { var buffer = Memory.alloc(0x1000); var result = read(fd, buffer, 0x1000); close(fd); result = ptr(buffer).readCString(); return result; } return "-1"; } ``` 将 dump 下的 组件 copy还原回去 细节修复 plt 与 got |d_tag|值|含义| |-|-|-| |DT_JMPREL|0x17|`.rela.plt` 在文件中的偏移| |DT_PLTRELSZ|0x2|`.rela.plt` 的大小| |DT_RELA|0x7|`.rela.dyn` 在文件中的偏移| |DT_RELASZ|0x8|`.rela.dyn` 的大小| 因为 我们在 链接前 dump的 所以无需修复 还原 即可 用ida 使用 我也是成功 恢复了 so文件中的 符号  ### 结束 闲言碎语 希望版主大大 加个精华, 小弟大三,想在简历里添加一笔 看雪有精华 文章! 参考: <mark class="encrypted">533K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6G2j5h3y4A6j5g2)9J5k6h3c8W2N6W2)9J5c8U0x3$3x3q4)9J5k6r3A6A6j5h3N6#2i4K6u0r3</mark>
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2025-2-12 13:53 被逆天而行编辑 ,原因: 修改标题
#基础理论
#逆向分析
#NDK分析
#脱壳反混淆
上传的附件:
so.so_0x6d95e69000_1562912.body.so
(1.49MB,105次下载)
libjiagu_64.so
(2.47MB,94次下载)
check_tset_1.0.apk
(6.05MB,105次下载)
check_tset_1.0_sign_10_jiagu_sign.apk
(6.64MB,110次下载)
收藏
・
64
点赞
・
39
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
铭信
你的帖子非常有用,感谢分享!
2026-9-7 21:55
欲拭泪
感谢你的积极参与,期待更多精彩内容!
2026-8-5 13:45
aut0run
感谢你分享这么好的资源!
2026-8-2 20:43
mb_rqgxdcwg
感谢你分享这么好的资源!
2026-2-25 18:41
黎明与黄昏
感谢你分享这么好的资源!
2026-2-10 20:35
x1aob1n
谢谢你的细致分析,受益匪浅!
2026-1-21 20:07
ulong9527
你的帖子非常有用,感谢分享!
2026-1-13 00:50
卡卡骨
谢谢你的细致分析,受益匪浅!
2025-12-11 19:24
mb_woeakafq
为你点赞!
2025-11-30 16:46
审判者虔诚
期待更多优质内容的分享,论坛有你更精彩!
2025-11-9 23:53
长亭外
你的分享对大家帮助很大,非常感谢!
2025-11-6 01:55
mb_ecfxuphh
你的帖子非常有用,感谢分享!
2025-11-3 16:38
wx_YIN重启
为你点赞!
2025-10-28 10:52
Yangser
这个讨论对我很有帮助,谢谢!
2025-10-14 23:18
mb_glmxrbwt
感谢你的贡献,论坛因你而更加精彩!
2025-9-27 12:54
PLEBFE
为你点赞!
2025-9-18 06:04
kisanakzz
为你点赞!
2025-8-4 18:39
mb_ukgikikw
为你点赞!
2025-7-8 11:22
零_721409
非常支持你的观点!
2025-7-6 08:14
Dr_Knox
期待更多优质内容的分享,论坛有你更精彩!
2025-6-28 12:16
马来
感谢你分享这么好的资源!
2025-6-12 20:52
mb_fjoohxih
你的帖子非常有用,感谢分享!
2025-6-11 18:23
萌新物语
+1
你的帖子非常有用,感谢分享!
2025-5-17 17:28
git_23169ayanerusakura
这个讨论对我很有帮助,谢谢!
2025-5-10 23:23
r8e8cd8
你的分享对大家帮助很大,非常感谢!
2025-4-16 20:04
zx_994305
谢谢你的细致分析,受益匪浅!
2025-4-1 17:11
mb_ecuabpmq
非常支持你的观点!
2025-3-7 12:07
52niu
为你点赞!
2025-2-19 12:15
七星舞月
+1
这个讨论对我很有帮助,谢谢!
2025-2-19 11:00
Shangwendada
+1
为你点赞!
2025-2-18 15:57
wogao
+1
谢谢你的细致分析,受益匪浅!
2025-2-17 19:52
Fanoteria
这个讨论对我很有帮助,谢谢!
2025-2-17 12:42
sinker_
谢谢你的细致分析,受益匪浅!
2025-2-14 22:59
你瞒我瞒
期待更多优质内容的分享,论坛有你更精彩!
2025-2-14 14:25
郑z
谢谢你的细致分析,受益匪浅!
2025-2-13 10:20
wuzhouzcx
这个讨论对我很有帮助,谢谢!
2025-2-13 09:21
ngiokweng
感谢你分享这么好的资源!
2025-2-12 15:37
zx_968369
+3
感谢你的积极参与,期待更多精彩内容!
2025-2-12 14:27
Ram98
你的分享对大家帮助很大,非常感谢!
2025-2-12 14:25
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
11
)
koflfy
雪 币:
102
活跃值:
(4071)
能力值:
( LV4,RANK:50 )
在线值:
发帖
8
回帖
480
粉丝
5
关注
私信
koflfy
1
2
楼
mark
2025-2-14 11:51
0
快乐的小跳蛙
雪 币:
910
活跃值:
(5347)
能力值:
( LV2,RANK:10 )
在线值:
发帖
2
回帖
87
粉丝
3
关注
私信
快乐的小跳蛙
3
楼
膜拜大佬出一篇分析native onCreate的文章
2025-2-14 14:04
0
曰你
雪 币:
101
活跃值:
(1296)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
27
粉丝
0
关注
私信
曰你
4
楼
2025-2-17 15:13
0
软件君子
雪 币:
537
能力值:
( LV1,RANK:0 )
在线值:
发帖
3
回帖
68
粉丝
3
关注
私信
软件君子
5
楼
可以 接下来可以分析签名校验了
2025-2-21 18:11
0
bluegatar
雪 币:
5
活跃值:
(5784)
能力值:
( LV2,RANK:10 )
在线值:
发帖
4
回帖
238
粉丝
6
关注
私信
bluegatar
6
楼
厉害了,我的盆友
2025-2-22 00:26
0
cpannn
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
73
粉丝
0
关注
私信
cpannn
7
楼
大佬,能否重新上传附件,现在下不了。
2025-2-25 21:43
0
木月
雪 币:
111
活跃值:
(40)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
11
粉丝
1
关注
私信
木月
8
楼
学习了
2025-3-1 20:51
0
r8e8cd8
雪 币:
486
活跃值:
(593)
能力值:
( LV3,RANK:20 )
在线值:
发帖
3
回帖
17
粉丝
36
关注
私信
r8e8cd8
9
楼
学到了!
2025-4-16 20:07
0
mb_irhdtnzm
雪 币:
23
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
155
粉丝
0
关注
私信
mb_irhdtnzm
10
楼
1
2025-6-5 20:52
0
凛然
雪 币:
223
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
2
粉丝
0
关注
私信
凛然
11
楼
感谢分享,学习了
2025-7-5 11:50
0
mb_xvdyvbpd
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
2
粉丝
0
关注
私信
mb_xvdyvbpd
12
楼
牛的
2025-11-19 01:51
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
逆天而行
1
12
发帖
124
回帖
90
RANK
关注
私信
他的文章
[原创]写一个简单的ollvm混淆还原
22595
[原创]写一个简单的VMP-不造轮子,何以知轮之精髓?
32146
[原创]写一个简单的trace
11530
[原创]写一个简单的签名检测
13721
[原创]某加固签名校验研究
16137
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
谁下载
×
babalove
zhongdong
熊趴趴来
wogao
马来
卡坦精
顽劣
vVv一
土豆花开
ldehua
doduhuang
曰你
北门观雪
好好好好尼玛
酒仙桥之虎
mb_undccabr
老小白
SomeMx
mb_rjdrqvpa
wx_YIN重启
审判者虔诚
mb_4nrpVMxJ
mb_eiufitqh
逆天而行
iCookiie
blank、
xiaohongmao
cpannn
mb_imnrancw
LXIN_520
mb_phgsyflk
mb_ldbucrik
molezsbd
mb_irhdtnzm
mb_zfqvurgb
小尘0000
软件君子
mb_szzzborh
mb_cbgtaveo
mb_qecctkwn
mb_kgfqphsx
mb_uaoofzgb
mb_mdxzzowp
mb_tmbvnlxt
mb_weyfdhks
mb_htgkuynr
mb_mddfhmcj
风兮木晓
npc0vo
mb_difjaqrr
mb_rqgxdcwg
mb_jawamylb
Our_OT
mb_vhwdzyqo
mb_gwuaipkf
homea_qwq
mb_sqopfzgf
mb_cuwdupme
ikunnn
git_89565rockfox0
mb_kujsrqqv
mb_rqxzprdz
mb_cebxltxx
mb_lthgjpwj
谁下载
×
babalove
zhongdong
熊趴趴来
wogao
马来
卡坦精
顽劣
vVv一
土豆花开
ldehua
doduhuang
曰你
北门观雪
酒仙桥之虎
39周
LDAx2012
mb_undccabr
老小白
SomeMx
mb_rjdrqvpa
wx_YIN重启
审判者虔诚
泠潋
mb_4nrpVMxJ
mb_eiufitqh
iCookiie
blank、
xiaohongmao
cpannn
带带弟弟吧
mb_ldbucrik
molezsbd
mb_irhdtnzm
mb_zfqvurgb
小尘0000
mb_szzzborh
mb_cbgtaveo
mb_kgfqphsx
mb_mdxzzowp
mb_tmbvnlxt
mb_xwntdpxy
mb_weyfdhks
mb_htgkuynr
风兮木晓
npc0vo
eeeeelukou
mb_difjaqrr
Our_OT
mb_enqsfnpo
mb_vhwdzyqo
homea_qwq
mb_sqopfzgf
鞠婧祎
mb_cuwdupme
ikunnn
git_10267xiaoYAN00517
git_89565rockfox0
mb_kujsrqqv
mb_rqxzprdz
mb_cebxltxx
mb_lthgjpwj
谁下载
×
babalove
zhongdong
熊趴趴来
wogao
马来
卡坦精
顽劣
葛旺
土豆花开
capnik
qqyspgj
ldehua
doduhuang
曰你
北门观雪
龙游天下178
LDAx2012
老小白
SomeMx
mb_rjdrqvpa
审判者虔诚
泠潋
Just_Cracker
mb_4nrpVMxJ
pexillove
mb_eiufitqh
iCookiie
blank、
xiaohongmao
cpannn
LXIN_520
mb_phgsyflk
mb_ldbucrik
molezsbd
mb_irhdtnzm
mb_zfqvurgb
小尘0000
mb_szzzborh
mb_cbgtaveo
mb_kgfqphsx
mb_nopxtzho
mb_uaoofzgb
mb_mdxzzowp
mb_kqchzxdu
mb_tmbvnlxt
mb_xwntdpxy
mb_weyfdhks
mb_uhrfjhgq
mb_htgkuynr
npc0vo
mb_difjaqrr
mb_jawamylb
Our_OT
mb_vhwdzyqo
mb_fgxlrlca
homea_qwq
mb_sqopfzgf
鞠婧祎
mb_cuwdupme
ikunnn
git_10267xiaoYAN00517
git_89565rockfox0
mb_kujsrqqv
mb_cebxltxx
mb_lthgjpwj
谁下载
×
canmd
babalove
zhongdong
熊趴趴来
wogao
马来
卡坦精
顽劣
vVv一
土豆花开
capnik
qqyspgj
ldehua
doduhuang
曰你
北门观雪
kinglinzi
LDAx2012
老小白
SomeMx
mb_rjdrqvpa
审判者虔诚
泠潋
Just_Cracker
mb_4nrpVMxJ
Sigil
mb_eiufitqh
墨儒蛮君
iCookiie
blank、
xiaohongmao
cpannn
LXIN_520
mb_phgsyflk
mb_ldbucrik
molezsbd
mb_irhdtnzm
mb_zfqvurgb
小尘0000
mb_szzzborh
mb_cbgtaveo
mb_kgfqphsx
mb_nopxtzho
mb_mdxzzowp
mb_pfsyglzv
mb_tmbvnlxt
mb_weyfdhks
mb_htgkuynr
npc0vo
eeeeelukou
mb_difjaqrr
mb_jawamylb
Our_OT
mb_vhwdzyqo
mb_ysikfhxt
mb_xuxsdbyf
homea_qwq
mb_sqopfzgf
mb_lzotvdxv
mb_cuwdupme
ikunnn
git_10267xiaoYAN00517
git_89565rockfox0
mb_kujsrqqv
mb_cebxltxx
mb_lthgjpwj
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部