复现完oacia师傅的文章,360加固dex解密流程分析 | oacia = oaciaのBbBlog~ = DEVIL or SWEET
想继续来看看onCreate怎么实现的,继续使用oacia师傅的加固app
onCreate函数被注册为native函数

Init函数,attachBaseContext函数,都没有什么可用的线索。发现static函数里有一个jni函数interface11,

在attachBaseContext执行之前。所有 static {}
代码块会在类加载时执行(Application
类的 constructor
之前)
使用yang神的脚本,拦截JNI注册函数RegisterNative找到函数的地址
直接来看汇编代码,前面都是一些线程检测,以及寄存器操作等,来到最后跳转的是x8寄存器的值,没法直接反编译

用stalker,跟踪x8寄存器的变化,来打印跳转
根据trcace发现了两处oncreate,看看的后一个地址干了什么

后一个oncreate,都是一些内存和栈的操作。
0x13a170函数之后出现了第一个onCreate,blr x8进入关键部分。
jnitrace看一下流程

接下来关键操作分析0x13a61c函数之后出现了第二个oncreate,同样是blr x8,进入jni函数了。

可以确定,interface11方法将onCreate注册某个native方法上
我们去hook一下 _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi找地址
输出了oncreate

但是是动态注册,找不到moudel的名字,无法直接打印地址。我们直接去so里面寻找字节码。在0x178c50+0xe7000=0x26FC50

在ida中找到onCreate地址

交叉引用onCreate,发现他被sub_1A1908调用。
再看看sub_1A1908的交叉引用

来到函数sub_12DFE4,这里应该就是函数绑定的地方。

参数0ff_265F20,指向sub_137978,进入

sub_137978这个函数是关键操作,可以Edit->other->specify switch idiom,修复。
sub_137978的逻辑大概是先保存栈帧,然后开辟新的空间,跳转跟之前一样,继续打印x8的变化,跟踪函数
定位到我们的解释器sub_160D7C
使用ida8.3进行动态调试
关键从0x1450fc处开始 ,从这里开始hook会遇到rtld_db_dlactivity反调试,而且会引导我们进入错误的分支。
tld_db_dlactivity函数默认情况下为空函数,当有调试器时将被改为断点指令。Android反调试对抗中,SIGTRAP信号反调试可通过判断该函数的数值来判断是否处于被调试状态
我们直接hook0x15a630,从这里开始调试,我们可以找到加密的opcode

![image-20250313165951476]()
指向他地址的指针被存在x0寄存器之中

sub_16CE0C是第一个解密函数
存在花指令,直接nop掉,重新定义为函数,对数据进行异或操作。

步入跟踪,来到花指令后的代码
ADR(Address of Label) 是 ARM64 特有的伪指令,用于将当前 PC 相对的某个地址加载到寄存器。
这里,它将 loc_16D09C
的地址加载到 X8
,然后让 X8 的值减少 236(0xEC)
然后从栈中保存x30的地址,X30
通常用于存储返回地址,把 X8
赋值给 X30
,ret时就会返回我们设定的地址
进入真正的解密,解密逻辑为
从加密的opcode读取第一字节给w8,再从存加密opcode地址的地方读取第八个字节给w13 (c5)
w9存取的是加密的opcode读取第二字节,再减去w12(存加密opcode地址的地方读取第八个字节,和w13相等),这里结果为7
赋值,比较,与取W9
低八位(07),相减,如果 EQ
(ZF=1,前面 TST
结果是 0)W8 = W11
(0xE2)否则W8 = W10
(0xE7)
然后异或得w9=e5,保留 W13
低 8 位,来到左边分支,异或得到w10=6B

然后和之前一样的操作来到另外一部分解密操作,w8恢复到c5,把w9插入到w10的高位,然后把w8的低位复制到高位
然后w8和w10异或得到解密的opcode 20ae

把刚刚解密的opcode,和加密的opcode传入函数sub_15FC34,这里应该是下一步解密,来看看关键部分
x8赋值为主dex文件的地址,w9是存加密opcode地址+18的值(A0AF),然后从主dex文件加载值w10 w8
W11 = W9 / W10 (整数除法)
W9 = W9 - (W11 * W10)(求余数)
W9 = W9 << 8 (左移 8 位)
然后X9 的低 8 位用 X19 的低 8 位替换得到最后值23AE,得到偏移
最后再从他查询得到值 D2赋值给w0

接着会根据w0的值来进行二分查找。且这个值是会变化的,如果从前面的函数开始调试,会出现错误的值,正确查找来到解释器的位置。
解释器函数入参如下,重点关注a5 a6(加密的opcode)和a7 (已经解密的opcode),并且这里的a4就是我们之前得到的解密的主dex文件地址
sub_16CB4C 存在同样的花指令,去除,函数和sub_16CE0C,套路都是一样的,直接从最后的真实逻辑开始分析
X8 = X20 << 1(2),读取 X19+8 处的字节到 W13(c5),从 X26 + X8 处读取 1 字节到 W8(就是第三个加密的opcode21)
W9 = W9 - W12(c7-c5=2),赋值,测试 W9 的 bit 5 是否为 1,W12 = W9 & 0xFF,W13 = W8 - W13,W8 = (Z == 1) ? W11 : W10
W9 = W8 XOR W12,W8 = W13 & 0xFF,W10 = W8 XOR W11(8E)

再来到,和之前的逻辑差不多,最后返回0x154b=5451

而5451对应method_id 正是onCreate
sub_16C918发现也存在异或,分析关键部分
w8读取第五六位的加密opcode,(3718)w9取其高位。
这里w12依旧是之前的c5,四处解密都是用的同一个密钥。
W9 = W9 - W12,W12 = W8 - W12,W8 = W9 & 0xFF,W9 = (Z == 1) ? W11 : W10,W8 = W9 XOR W8
W9 = W12 & 0xFF,后面的逻辑跟前面基本上一样,最后返回0x0021。

以上我们得到最后解密的opcode,逆序一下
ae20(d2)4b152100,这里d2是二分查找的关键。
与源apk的字节码对比,只有操作数不同。剩下的以此类推。

参考:
360加固dex解密流程分析 | oacia = oaciaのBbBlog~ = DEVIL or SWEET
[原创]app加固分析狗尾续貂之dex vmp还原-Android安全-看雪-安全社区|安全招聘|kanxue.com
[分享]某vmp壳原理分析笔记-Android安全-看雪-安全社区|安全招聘|kanxue.com
[原创] 某DEX_VMP安全分析与还原-Android安全-看雪-安全社区|安全招聘|kanxue.com
vmp入门(一):android dex vmp还原和安全性论述
[RegisterNatives] java_class: com.stub.StubApp name: interface11 sig: (I)V fnPtr:
0x73fd0bb9d8
module_name: libjiagu_64.so module_base:
0x73fcf82000
offset:
0x1399d8
[RegisterNatives] java_class: com.stub.StubApp name: interface11 sig: (I)V fnPtr:
0x73fd0bb9d8
module_name: libjiagu_64.so module_base:
0x73fcf82000
offset:
0x1399d8
function
hookTargetFunc() {
var
baseAddr = Module.findBaseAddress(TargetLibName)
if
(baseAddr ==
null
) {
console.log(
'Please makesure '
+ TargetLibName +
' is Loaded, by setting extractNativeLibs true.'
);
return
;
}
Interceptor.attach(baseAddr.add(TargetFuncOffset), {
onEnter:
function
(args) {
console.log(
'\nCall '
+ TargetFuncOffset.toString(16).toUpperCase() +
' In'
)
this
.tid = Process.getCurrentThreadId();
var
so_addr = Module.findBaseAddress(so_name);
var
so_size = Process.getModuleByName(so_name).size;
Stalker.follow(
this
.tid, {
events: {
call:
true
,
ret:
false
,
exec:
false
,
block:
false
,
compile:
false
},
onReceive:
function
() {
},
transform:
function
(iterator) {
var
instruction = iterator.next();
const startAddress = instruction.address;
if
(startAddress){
do
{
if
(instruction.mnemonic.startsWith('br
')||instruction.mnemonic.startsWith('
blr
')||instruction.mnemonic.startsWith('
bl
')) {
try {
iterator.putCallout(function (context) {
var pc = context.pc; //获取pc寄存器,当前地址
var lr = context.lr;
var x8 = context.x8; //获取x8寄存器,
var module = Process.findModuleByAddress(pc);
if (module) {
try{
console.log(module.name + "!" +Memory.readCString(ptr(x8)));
}
catch (e){
}
console.log("x8 ====" + DebugSymbol.fromAddress(ptr(x8)));
}
});
} catch (e) {
console.log("error", e)
}
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
}
},
onCallSummary(summary) {
}
})
}, onLeave: function (retVal) {
console.log('
Call
' + TargetFuncOffset.toString(16).toUpperCase() + '
Out\n')
Stalker.unfollow(
this
.tid)
}
})
}
function
hookTargetFunc() {
var
baseAddr = Module.findBaseAddress(TargetLibName)
if
(baseAddr ==
null
) {
console.log(
'Please makesure '
+ TargetLibName +
' is Loaded, by setting extractNativeLibs true.'
);
return
;
}
Interceptor.attach(baseAddr.add(TargetFuncOffset), {
onEnter:
function
(args) {
console.log(
'\nCall '
+ TargetFuncOffset.toString(16).toUpperCase() +
' In'
)
this
.tid = Process.getCurrentThreadId();
var
so_addr = Module.findBaseAddress(so_name);
var
so_size = Process.getModuleByName(so_name).size;
Stalker.follow(
this
.tid, {
events: {
call:
true
,
ret:
false
,
exec:
false
,
block:
false
,
compile:
false
},
onReceive:
function
() {
},
transform:
function
(iterator) {
var
instruction = iterator.next();
const startAddress = instruction.address;
if
(startAddress){
do
{
if
(instruction.mnemonic.startsWith('br
')||instruction.mnemonic.startsWith('
blr
')||instruction.mnemonic.startsWith('
bl
')) {
try {
iterator.putCallout(function (context) {
var pc = context.pc; //获取pc寄存器,当前地址
var lr = context.lr;
var x8 = context.x8; //获取x8寄存器,
var module = Process.findModuleByAddress(pc);
if (module) {
try{
console.log(module.name + "!" +Memory.readCString(ptr(x8)));
}
catch (e){
}
console.log("x8 ====" + DebugSymbol.fromAddress(ptr(x8)));
}
});
} catch (e) {
console.log("error", e)
}
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
}
},
onCallSummary(summary) {
}
})
}, onLeave: function (retVal) {
console.log('
Call
' + TargetFuncOffset.toString(16).toUpperCase() + '
Out\n')
Stalker.unfollow(
this
.tid)
}
})
}
x8 ====0x7bfee7fe0c libjiagu_64.so!0x139e0c
x8 ====0x7bfee7fe64 libjiagu_64.so!0x139e64
x8 ====0x7bfee7fe7c libjiagu_64.so!0x139e7c
x8 ====0x7bfee80460 libjiagu_64.so!0x13a460
x8 ====0x7bfee7fe8c libjiagu_64.so!0x139e8c
x8 ====0x7bfee7ff60 libjiagu_64.so!0x139f60
x8 ====0x7bfee7ffa0 libjiagu_64.so!0x139fa0
x8 ====0x7bfee7ffc4 libjiagu_64.so!0x139fc4
x8 ====0x7bfee8010c libjiagu_64.so!0x13a10c
x8 ====0x7bfee80140 libjiagu_64.so!0x13a140
x8 ====0x7bfee80170 libjiagu_64.so!0x13a170
x8 ====0x7bfee80194 libjiagu_64.so!0x13a194
x8 ====0x7bfee801fc libjiagu_64.so!0x13a1fc
x8 ====0x7bfee804e4 libjiagu_64.so!0x13a4e4
x8 ====0x7bfee8024c libjiagu_64.so!0x13a24c
x8 ====0x7bfee80264 libjiagu_64.so!0x13a264
x8 ====0x7bfee802e8 libjiagu_64.so!0x13a2e8
x8 ====0x7bfee80328 libjiagu_64.so!0x13a328
x8 ====0x7bfee7fe64 libjiagu_64.so!0x139e64
x8 ====0x7bfee80600 libjiagu_64.so!0x13a600
x8 ====0x7bfee8061c libjiagu_64.so!0x13a61c
x8 ====0x7bfee7fe0c libjiagu_64.so!0x139e0c
x8 ====0x7bfee7fe64 libjiagu_64.so!0x139e64
x8 ====0x7bfee7fe7c libjiagu_64.so!0x139e7c
x8 ====0x7bfee80460 libjiagu_64.so!0x13a460
x8 ====0x7bfee7fe8c libjiagu_64.so!0x139e8c
x8 ====0x7bfee7ff60 libjiagu_64.so!0x139f60
x8 ====0x7bfee7ffa0 libjiagu_64.so!0x139fa0
x8 ====0x7bfee7ffc4 libjiagu_64.so!0x139fc4
x8 ====0x7bfee8010c libjiagu_64.so!0x13a10c
x8 ====0x7bfee80140 libjiagu_64.so!0x13a140
x8 ====0x7bfee80170 libjiagu_64.so!0x13a170
x8 ====0x7bfee80194 libjiagu_64.so!0x13a194
x8 ====0x7bfee801fc libjiagu_64.so!0x13a1fc
x8 ====0x7bfee804e4 libjiagu_64.so!0x13a4e4
x8 ====0x7bfee8024c libjiagu_64.so!0x13a24c
x8 ====0x7bfee80264 libjiagu_64.so!0x13a264
x8 ====0x7bfee802e8 libjiagu_64.so!0x13a2e8
x8 ====0x7bfee80328 libjiagu_64.so!0x13a328
x8 ====0x7bfee7fe64 libjiagu_64.so!0x139e64
x8 ====0x7bfee80600 libjiagu_64.so!0x13a600
x8 ====0x7bfee8061c libjiagu_64.so!0x13a61c
x8 ====0x7bfee807ac libjiagu_64.so!0x13a7ac
x8 ====0x7bfee807b8 libjiagu_64.so!0x13a7b8
x8 ====0x7bfee807f4 libjiagu_64.so!0x13a7f4
x8 ====0x7bfee80808 libjiagu_64.so!0x13a808
x8 ====0x7bfee80810 libjiagu_64.so!0x13a810
x8 ====0x7bfee8082c libjiagu_64.so!0x13a82c
x8 ====0x7bfee80854 libjiagu_64.so!0x13a854
x8 ====0x7bfee80854 libjiagu_64.so!0x13a854
x8 ====0x7bfee80838 libjiagu_64.so!0x13a838
x8 ====0x7bfee807e0 libjiagu_64.so!0x13a7e0
x8 ====0x7bfee807f4 libjiagu_64.so!0x13a7f4
x8 ====0x7bfee80870 libjiagu_64.so!0x13a870
x8 ====0x7bfee8087c libjiagu_64.so!0x13a87c
x8 ====0x7bfee80894 libjiagu_64.so!0x13a894
x8 ====0x7bfee80894 libjiagu_64.so!0x13a894
x8 ====0x7bfee80894 libjiagu_64.so!0x13a894
x8 ====0x7bfee80894 libjiagu_64.so!0x13a894
x8 ====0x7bfee808c4 libjiagu_64.so!0x13a8c4
x8 ====0x7bfee808dc libjiagu_64.so!0x13a8dc
x8 ====0x7bfee808e8 libjiagu_64.so!0x13a8e8
x8 ====0x7bfee807ac libjiagu_64.so!0x13a7ac
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-3-13 17:28
被海带编辑
,原因: