inquisiter 发表于 2017-7-23 19:26

几维dex加密分析

本帖最后由 inquisiter 于 2017-7-23 19:34 编辑

-EndFragment-->      
      
折腾了一周,纠结在ndk开发上,感觉jni 的接口很是难用,以后再学习吧。模拟器简直是个巨大的bug,巨难用,以后一定要搞个真机做调试。      
自己开发的一个crack,比较简单,主要是在ndk上 废了太多时间,有时间再研究吧。
      
完毕,上传几维在线加密。直接把加密apk放到改之理中,先看了下string。貌似没加密这里,

项目中的多了个Application的类,目测这里应该是加密点。为什么,应为这里可以提前Mainactivity执行,当然也可能不是。类外,据说加密之后可以防止反编译,这个我好想没看出来,查看smali源码,不过没找到引用”成功”的字符,尴尬了。

      
      
在application中找到这么个东西,调用了kdp中的a类,还有这个类本身叫做proxyAppliction,      
这让我想起了之前看的一个叫做用代{过}{滤}理方式动态加载的文章。      
http://blog.csdn.net/jiangwei0910410003/article/details/48104581      
kiwisecApplication中也调用了a类。      
这个a.class条用了居多的资源和库函数,在so里的,显然关键的保护就在这两个里面.kdpdata.so、/.classes.dex、zipFile、fileDataFile、classes_dex、zipDexFileName

      
      
不过有一点不明白,为什么kdpdata只在armabi文件夹中,先不管了。      
本地so其实没混淆也没加密。(这里其实后来明白了,动态加载会从这个文件夹加载到其他文件夹的)

      
      
接下来一个任务是过掉加固的反调试,另外一个是获取原apk的classes.dex文件。      
需要将android_server调试程序的名字修改一下,因为加固会通过查询/proc/pid/cmdline文件获取程序的名称来对android_server、gdb、ltrace、strace调试器进行反调试。在动态调试加固apk程序的时候,需要在几个关键点函数 open、strtol、mmap、memcmp处下断点,后面具体的分析。由于Android系统是由Linux系统修改而来,因此Android系统上很多的属性和Linux系统下的属性。Linux下进程和线程的信息是保存在文件中的,因此要查询进程或者线程的信息必须通过open或者fopen等方式操作文件来完成。      
      
调用open函数打开文件/proc/self/status时,可以确实是加固外壳apk程序通过TracePid的反调试      
      
long int strtol(const char *nptr,char **endptr,int base);trtol函数会将参数nptr字符串根据参数base来转换成长整型数。      
      
mmap将一个文件或者其它对象映射进内存。void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);      
prot:期望的内存保护标志flags:指定映射对象的类型,      
      
int memcmp(const void *buf1, const void *buf2, unsigned int count);      MOVS 同x86的mov LDR 加载内存数据到寄存器 STR 寄存器数据存入内存 B/BL 跳转/函数调用 TST/CMP 比较      
         
这些是调试用到的功课,复习一下。      
话说到了调试开始各种莫名的错误,我们直接打开调试程序附加显然是不行的,都没法找dex的地址和长度了,把app以调试模式打开,附加上了但程序根本运行不起来。      
adb shell am start -D -n com.example.testbao/.MainActivity      
      
最终查到要把程序通过命令行      
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700      
把调试模式的程序运行一下。      
      
Could not open Selected VM debug port (8700)      
又有坑了,什么玩意。      
继续查,原来这个命令要开启ddms,   sdk-tool里有这个脚本。直接运行下弹出个程序,这个不管,再jdb一下,啊终于成功了。      
我们找一下      
libdvm.so中的dvmDexFileOpenPartial函数的下断点。

      
      
找了好半天。      
运行一下,哈哈根本就没进来,程序直接就能运行了。看样子是自己重写了这个函数,不过发现根本没有反调试,这到省事了。      
不管是他自己实现dex解析加载,最终都是需要把dex文件加载到内存中,还是得用mmap函数来进行操作。

      
在mmap处下断点,这时候坑了,程序不知为什么出发了异常,应该是反调试。      

程序异常前是在sendloop的函数里,另开一个ida查看一下这个玩意。      
-EndFragment-->      
int __fastcall kiwisdk::KiwiNet::send_loop(kiwisdk::KiwiNet *this)      
{      
kiwisdk::KiwiNet *v2; // @1      
int fd; // @7      
int v4; // @6      
int v5; // @2      
int v6; // @2      
int v7; // @2      
int v8; // @2      
char v9; // @2      
int v10; // @2      
kiwisdk::KiwiNet *v11; // @1      
int v12; // @2      
int *v13; // @2      
char *v14; // @2      
int *v15; // @2      
int *v16; // @2      
int *v17; // @2      
int *v18; // @2      
unsigned int i; // @2      
int *v20; // @2      
int *v21; // @2      
int *v22; // @2      
int *v23; // @2      
      
v2 = this;      
sub_51EAC();      
v11 = v2;      
do      
{      
    v8 = 5;      
    v14 = &v9;      
    v13 = &v8;      
    v12 = 0;      
    *(_DWORD *)&v9 = 5;      
    v10 = 0;      
    std::__ndk1::this_thread::sleep_for<long long,std::__ndk1::ratio<1ll,1ll>>();      
    v15 = &v5;      
    v16 = &v5;      
    v17 = &v5;      
    v18 = &v5;      
    v7 = 0;      
    v6 = 0;      
    v5 = 0;      
    v21 = &v5;      
    v22 = &v5;      
    v23 = &v5;      
    v20 = &v5;      
    for ( i = 0; i <= 2; ++i )      
      v20 = 0;      
    if ( kiwisdk::KiwiNet::get_from_send_queue(v2, &v5) )      
    {      
      fd = -1;      
      if ( kiwisdk::KiwiNet::init_socket(v2, &fd) )      
      {      
      if ( kiwisdk::KiwiNet::connect_server(v2, fd) )      
      {      
          if ( kiwisdk::KiwiNet::send_data(v2, fd, &v5) )      
          {      
            shutdown(fd, 2);      
            close(fd);      
            v4 = 0;      
          }      
          else      
          {      
            shutdown(fd, 2);      
            close(fd);      
            fd = -1;      
            v4 = 2;      
          }      
      }      
      else      
      {      
          v4 = 2;      
      }      
      }      
      else      
      {      
      v4 = 2;      
      }      
    }      
    else      
    {      
      v4 = 2;      
    }      
    std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v5);      
}      
while ( !v4 || v4 == 2 );      
return sub_51E20();      
}      
这个函数里有两处关闭程序的代码,我们在分别在函数的最开始处和将要关闭处下断点看一下,当然我们的mmap断点也是要下的。运行。      
好消息,这段代码不会被触发了,其实不是不被触发,而是中断在了另一个线程里。这里我猜想作者不仅重写了加载dex到内存的函数,而且把mmap函数的下断调试的处理过程写到了另一个线程。而这个检测过程,我这里有两个思路,一是检测两次调用mmap调用的时间差,如果超过了机器运行所需的时间则触发中断。二是利用在mmap处的断点检测来触发中断程序。其实我更倾向于第一种,当然完全是猜想。      
      
跟踪了无数次,把程序都快跑烂了,发现这个地方最像。再一次dump出来,感觉那里还是有些不对。但dex的部分应该就是这样,估计有虚拟保护。但有一点,这个加固本身并没有加固除了主类之外的类,      

比方这个地方,有个des的类,完全是源码暴露, 据说这个加固还有什么签名验证的梗,之后再说吧。      
不过比较可惜似乎还有部分代码没还原出来,我感觉是加了虚拟保护,只能再说了。      
总结:      
1.       刚开始是各种的附加到程序初始化的过程。      
2.       然后寻找反调试,开始没找到,但当下到mmap断点的时候,有个线程会强制程序退出。      
3.       把这个作怪的线程挂掉,程序运行mmap,主要是应为dvmdexopenfile函数断不下,怀疑重写了。      
4.       在出现.dex字符串的时候反复跟踪,程序流程最终确定到.class.dex的位置dump出来dex,完毕。      
      
后续:      
之前好像有人也脱过几维的壳,我看了下是针对dll文件的,自己动态调试了下dex部分,不是很熟,分享下。      
      

zhjxujin 发表于 2017-7-23 19:36

楼主万岁万岁,万万岁

Crack-L 发表于 2017-7-23 19:42

感谢分享   

xyz1125 发表于 2017-7-23 22:04

感谢发布原创作品,吾爱破解论坛因你更精彩!

inquisiter 发表于 2017-7-23 22:08

多谢支持,{:1_893:}另外本人最近在找工作,有相关逆向分析的岗位么,windows或android都可以。

chenjingyes 发表于 2017-7-24 00:12

感谢楼主分享{:1_921:}

bubu 发表于 2017-7-24 10:18

正好在学习这方面的

y1ca 发表于 2017-7-24 17:40

这个斜体字看起来 眼睛快瞎了

无名侠 发表于 2017-7-26 23:52

支持楼主~几维的东西哈哈哈

inquisiter 发表于 2017-7-27 21:06

无名侠 发表于 2017-7-26 23:52
支持楼主~几维的东西哈哈哈

多谢支持
页: [1] 2
查看完整版本: 几维dex加密分析