67行代码写个脱壳机
身为一只菜鸡,代码分析不出来没关系,但是壳一定要是会脱,不会脱壳的菜鸡那跟咸鱼有什么区别?写代码是不可能写代码的,只能靠抄代码才能维持一下生活。所以呢,我觉得用最少的代码写一个脱壳机还是有必要的。
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
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function (args) {
var pathptr = args;
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
这个libart.so就是我们接下要分析的so
(3)libart.so
libart.so是4.4以上art虚拟机的,最终的dex将会在art虚拟机上运行
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.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.address);
addr_classloaer =symbols.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;
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);
运行结果
(4)classloader Dump dex
最终的dex文件是在classloader函数中的参数中,我们只需要把文件dump出来就可以了
//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.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.address);
addr_classloaer =symbols.address;
}
}
var dex_maps = {};//用一个map来作为判断是否已读取的标识
if (addr_classloaer) {
Interceptor.attach(addr_classloaer, {
onEnter: function (args) {
var dex_file = args;
var base = ptr(dex_file).add(Process.pointerSize).readPointer();
var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();
if (dex_maps == undefined) {
dex_maps = 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;
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);
运行结果
感谢楼主分享 不过我这有个更少代码量的 也分享出来吧 'use strict';
Interceptor.attach(Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"), {
onEnter: function (args) {
//dex起始位置
var begin = args
//打印magic
console.log("magic : " + Memory.readUtf8String(begin))
//dex fileSize 地址
var address = parseInt(begin,16) + 0x20
//dex 大小
var dex_size = Memory.readInt(ptr(address))
console.log("dex_size :" + dex_size)
//dump dex 到/data/data/pkg/目录下
var file = new File("/storage/emulated/0/Pictures/*.*.*/" + dex_size + ".dex", "wb")
file.write(Memory.readByteArray(begin, dex_size))
file.flush()
file.close()
},
onLeave: function (retval) {
if (retval.toInt32() > 0) {
/* do something */
}
}
}); 顺手看了一眼,是安卓的大佬啊!
frida想学的。。
环境搭起来就没继续看{:301_1001:}
有空学习一下 有空学习一下有空学习一下 牛逼啊兄弟 封面就可以给满分了 只能脱360吧
有空学习一下有空学习一下 看不懂,要多加学习才行 牛啊,mark一下,回头学习 安卓的不是太懂,但是还是觉得可以的,感觉网上应该有挺多的吧。