身为一只菜鸡,代码分析不出来没关系,但是壳一定要是会脱,不会脱壳的菜鸡那跟咸鱼有什么区别?
写代码是不可能写代码的,只能靠抄代码才能维持一下生活。所以呢,我觉得用最少的代码写一个脱壳机还是有必要的。
1.加壳原理
讲糙一点就是,apk里套apk,把真实的apk套在壳程序apk里。(母猪戴胸罩,一套又一套,听懂掌声)
听懂掌声
2.脱壳原理
不管壳程序怎么搞,最终还是要又真实的dex可以运行对吧?那我们把内存中运行的真实dex导出来不会好了(什么是dex?别问,问就是自己百度)
3.art脱壳机编写:
(1)环境:1.frIDA
2.真机
(2)dlopen.so
dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名就是一个动态库so文件,标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。就是说所有的So都会在这里进行加载,那么我们先来分析一下这个so
dlopen Hook
[JavaScript] 纯文本查看 复制代码 function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("dlopen:", path);
}
},
onLeave: function (retval) {
}
})
}
setImmediate(hook_dlopen);
运行结果
frida -U --no-pause -f com.xxx.xxxx-l dexDump.js
1
这个libart.so就是我们接下要分析的so
(3)libart.so
libart.so是4.4以上art虚拟机的,最终的dex将会在art虚拟机上运行
[JavaScript] 纯文本查看 复制代码 function hook_art(){
var libart = Process.findModuleByName("libart.so");
var symbols = libart.enumerateSymbols(); //枚举模块的符号
var addr_classloaer=null;
for (var i = 0; i < symbols.length; i++) {
var name = symbols[i].name;
// console.log(name) //有需要打印一下,但是太长了我就不打印了
if (name.indexOf("ClassLinker") >= 0 && name.indexOf("DefineClass") >= 0 && name.indexOf("Thread") >= 0 && name.indexOf("DexFile") >= 0 ) {
console.log("找到了classloader,打印一下呗");
console.log(name, symbols[i].address);
addr_classloaer =symbols[i].address;
}
}
}
function hook_dlopen() {
var hooked = false;//hooked作为判断标识,是否已hook
var dlname="dlopen";
// var dlname="android_dlopen_ext"; // 高版本Android系统使用android_dlopen_ext
Interceptor.attach(Module.findExportByName(null, dlname), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf("libart.so") >= 0) {
this.bb = true;//bb作为判断标识,是否art已载入
}
}
},
onLeave: function (retval) {
if (this.bb& !hooked){
console.log("开始hook art")
hook_art();
hooked=true;
}
}
})
}
setImmediate(hook_dlopen);
运行结果
2
(4)classloader Dump dex
最终的dex文件是在classloader函数中的参数中,我们只需要把文件dump出来就可以了
[JavaScript] 纯文本查看 复制代码 //frida -U --no-pause -f com.xx.xxxx -l dexDump.js
function Wrifile(dex_path,dex_buffer){
var fd = new File(dex_path, "wb");
if (fd && fd != null) {
fd.write(dex_buffer);
fd.flush();
fd.close();
console.log("导出文件:", dex_path);
}
}
function hook_art(){
var libart = Process.findModuleByName("libart.so");
var symbols = libart.enumerateSymbols(); //枚举模块的符号
var addr_classloaer=null;
for (var i = 0; i < symbols.length; i++) {
var name = symbols[i].name;
// console.log(name) //有需要打印一下,但是太长了我就不打印了
if (name.indexOf("ClassLinker") >= 0 && name.indexOf("DefineClass") >= 0 && name.indexOf("Thread") >= 0 && name.indexOf("DexFile") >= 0 ) {
console.log("找到了classloader,打印一下呗");
console.log(name, symbols[i].address);
addr_classloaer =symbols[i].address;
}
}
var dex_maps = {};//用一个map来作为判断是否已读取的标识
if (addr_classloaer) {
Interceptor.attach(addr_classloaer, {
onEnter: function (args) {
var dex_file = args[5];
var base = ptr(dex_file).add(Process.pointerSize).readPointer();
var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();
if (dex_maps[base] == undefined) {
dex_maps[base] = size;
var magic = ptr(base).readCString();
if (magic.indexOf("dex") == 0) {
var path = "/sdcard/DCIM/";//这边可以随便定义一个路径,建议是以包名为路径
var dex_path = path + base.toString(16) + "_" + size.toString(16) + ".dex";
var dex_buffer = ptr(base).readByteArray(size);
Wrifile(dex_path,dex_buffer)
}
}
}, onLeave: function (retval) {
}
});
}
}
function hook_dlopen() {
var hooked = false;//hooked作为判断标识,是否已hook
var dlname="dlopen";
// var dlname="android_dlopen_ext"; // 高版本Android系统使用android_dlopen_ext
Interceptor.attach(Module.findExportByName(null, dlname), {
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf("libart.so") >= 0) {
this.bb = true;//bb作为判断标识,是否art已载入
}
}
},
onLeave: function (retval) {
if (this.bb& !hooked){
console.log("开始hook art")
hook_art();
hooked=true;
}
}
})
}
setImmediate(hook_dlopen);
运行结果
3
|