Richor 发表于 2020-8-18 10:00

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);
运行结果



口袋C 发表于 2020-8-18 13:02

感谢楼主分享 不过我这有个更少代码量的 也分享出来吧 '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 */
      }
    }
});

涛之雨 发表于 2020-8-18 10:10

顺手看了一眼,是安卓的大佬啊!

frida想学的。。
环境搭起来就没继续看{:301_1001:}
有空学习一下

BaiTuGe 发表于 2020-8-18 10:15

有空学习一下有空学习一下

zisain 发表于 2020-8-18 10:42

牛逼啊兄弟

a6608816 发表于 2020-8-18 11:02

封面就可以给满分了

校长小哥哥 发表于 2020-8-18 11:13

只能脱360吧

Cra2yZer0 发表于 2020-8-18 11:19


有空学习一下有空学习一下

dglbl 发表于 2020-8-18 11:27

看不懂,要多加学习才行

mitchzh 发表于 2020-8-18 11:29

牛啊,mark一下,回头学习

zsjalive 发表于 2020-8-18 11:52

安卓的不是太懂,但是还是觉得可以的,感觉网上应该有挺多的吧。
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 67行代码写个脱壳机