OMG 发表于 2015-10-31 12:54:11

(转载)APK加固之静态脱壳机编写入门

本文转载于吾爱破解的我是小三大牛,版权归属原作者,
原帖地址http://www.52pojie.cn/thread-426890-1-1.html

本教程只适合菜鸟学习,大牛请过......

目录
http://attach.52pojie.cn/forum/201510/30/204228c3zcgu2bsucqq2c4.jpg


0x00 APK加固简介与静态脱壳机的编写思路1.大家都知道Android中的程序反编译比较简单,辛苦开发出一个APK轻易被人反编译了,所以现在就有很多APK加固的第三方平台,比如爱加密和梆梆加固等。2.一般的加固保护通常能够提供如下保护:加密、防逆向、防篡改、反调试、反窃取等功能,编写静态脱壳机须要信息有加密后的原始DEX数据、解密算法、解密密钥、要想获得这些信息我们首先要解决的问题是过反调试、动态分析解密流程、获取密钥,获得原始DEX数据存放位置、分析解密算法。0x01 壳简单分析
1.整体来看一下加固前APK包和加固后的APK包结构相关变化,如图1所示。http://attach.52pojie.cn/forum/201510/30/204415zpik6pb8k4lzitez.jpg   图1图1所示加固后的APK增加了librsprotect.so、librsprotect_x86.so、rsprotect.dat文件,发生变化的有AndroidManifest.xml、classes.dex文件。2.反编译加固后APK,APK中的AndroidManifest.xml文件的入口被修改,如图2所示。http://attach.52pojie.cn/forum/201510/30/204455u49k5n7lme2eajnl.jpg   图23.入口类中主要会调用librsprotect.so中的3个函数,如图3所示。private native void initialize(ContextparamContext);private native ApplicationmakeApplication(String paramString);
private native void applicationOnCreate();http://attach.52pojie.cn/forum/201510/30/204524xjbwagwwg73bjwjw.jpg   图30x02SO文件静态脱壳1.既然主要是调用librsprotect.so中的函数,我们将librsprotect.so放到IDA Pro中分析,发现代码都是乱码 图4所示,说明被加密了。http://attach.52pojie.cn/forum/201510/30/204603ge6duuz7vxwekefk.jpg   图42.一般加壳的SO的壳代码都在INIT段或INIT_ARRAY段,我们先看下被加壳以后的SO信息,用readelf -a命令查看,图5所示http://attach.52pojie.cn/forum/201510/30/204636alfholh4nrre4ovn.jpg   图5可以看到INIT值为0x2ea91,到 IDA中看看该地址的内容,就是壳的入口了,明显是UPX的加壳,图6所示,有人会问你为什么会知道是UPX的壳,“只是因为在人群中多看了你一眼,再也没能忘掉你容颜!(^_^)”。http://attach.52pojie.cn/forum/201510/30/204721agnue8p5k31oe188.jpg   图63.尝试用upx -d脱壳,因为这样脱方便、干净、省事、提示图7所示的错误信息。http://attach.52pojie.cn/forum/201510/30/204748zpgfgw6rb6w35ccd.jpg   图7查看UPX源码后发现可能是没有找到UPX!的标志的原因,用16进制工具打开librsprotect.so发现标志被改成了RSP!,将其改成UPX!后再将尝试,出现 图8所示的信息。http://attach.52pojie.cn/forum/201510/30/204829i34z3wulwwla0wis.jpg   图8出现这种错误可能是做变形处理了或者是版本不对,通过分析librsprotect.so的壳代码好像没有变形处理,所以决定重新编一个3.92版本的来试试,编译好后脱壳成功,如图9所示。http://attach.52pojie.cn/forum/201510/30/204859db5a287ivuzj8lb3.jpg   图9将脱壳后的so放到IDA Pro中分析,代码正常,图10所示,SO脱壳完成。http://attach.52pojie.cn/forum/201510/30/204958m6n9mf8r81m8f41m.jpg   图100x03反调试分析1.如何使用IDA调试android的SO模块,网上教程也太多太多了,这里不多说,将脱壳后的librsprotect.so替换掉原始有壳的SO后(也可不用替换没影响,这里只是为了测试)签名安装进行动态分析。
2.通过动态调试该加壳程序,它用到的反调试方法是首先试探性读取/proc/pid/status获取进程状态去判断是否有调试器,如果发现被调试就kill掉本进程,如图11所示http://attach.52pojie.cn/forum/201510/30/205029xrad87fg7f70767k.jpg   图113.通过读取/proc/net/tcp查看正在运行应用的本地端口号是否有android_server端口,如果有就创建一个反调试线程,如图12、13所示,每隔几秒检查一次,过反调试就很简单了直接把返回值改成假就成了。http://attach.52pojie.cn/forum/201510/30/205149w3ctf7et4fg3ps9f.jpg   图12 android_server运行后端口http://attach.52pojie.cn/forum/201510/30/205237dgl4mhgddwgzgqzl.jpg   图130x04 解密流程分析
1.      根据算法中的常量值猜测该算法为MD5,图14所示http://attach.52pojie.cn/forum/201510/30/205333us0m5895z7e0lw0x.jpg   图142.获取包名并计算MD5值 图15所示,将该值做为密钥。com.droider.crackme0201F2 E8 F0 62 85 17 9C 3C 99 F5 67 9F A6 27 FC 55http://attach.52pojie.cn/forum/201510/30/205415de8gfk39gzfd0qkd.jpg   图152.打开并读取/data/data/com.droider.crackme0201/files/.rsdata/rsprotect.dat数据,该文件是从APK包中的assets文件夹中拷贝过来的,判断前4字节是"RSFL"是否与so中的相同,不同则退出,rsprotect.dat前0x1000字节存放原始DEX大小与循环解密的次数,每次解密0x1000字节,根据密钥初始化流程发现解密算法为RC4,图16示。http://attach.52pojie.cn/forum/201510/30/205524w6mcm6zm4cmct0a5.jpg   图160x05 脱壳机编写1.通过分析,已经知道了壳的数据、密钥、算法、解密过程, 现在来写脱壳机。必要步骤如下:1。解包获得rsprotect.da数据。2.XML解析获得包名。3.MD5计算获得密钥。4.RC4解密rsprotect.dat中的数据。
代码流程:#include"stdafx.h"
#include<afxwin.h>
#include<stdio.h>
#include<windows.h>
#include<process.h>
#include<assert.h>
#include<string>
#include<iostream>
#include"CMarkup.h"
#include"md5.h"
#include"rc4.h"
#include<string>
usingnamespacestd;
BOOLGetPackName(char* pathXml, charoutPackName)
{
    CMarkupxml;
    boolflag;
    CStringpackName;
    CStringAppandroidname;
    MCD_STRmyapkName;
    MCD_STRattribName;
    char* strXML = "\\AndroidManifest.xml";
    strcat(pathXml,strXML);
    flag = xml.Load((MCD_STR)pathXml);
    if ( FALSE == flag)
    {
      printf("获得包名失败...\n");
      returnFALSE;
    }
    flag = xml.FindElem((MCD_STR)"manifest");
    //获取包名.........
    for(intattribIndex=0;;attribIndex++)
    {
      attribName=xml.GetAttribName(attribIndex);
      if (attribName.GetLength()!=0)//方法若返回empty string,即表示属性结束,结束循环
      {
            MCD_STRattribVal = xml.GetAttrib(attribName);//否则读取属性值,以子元素加入dxml
            //------判断是否为包名...
            packName = attribName.GetString();
            if ( 0 == strcmp(packName.GetString(), "package"))
            {
                myapkName = attribVal;
                strcpy(outPackName, attribVal.GetString());
                returnTRUE;
            }
      }
      else
            break;
    }
    returnFALSE;
}
voidStrToHex(BYTE *pbDest, BYTE *pbSrc, intnLen)
{
    charh1,h2;
    BYTEs1,s2;
    inti;
    for (i=0; i<nLen; i++)
    {
      h1 = pbSrc;
      h2 = pbSrc;
      s1 = toupper(h1) - 0x30;
      if (s1> 9)
            s1 -= 7;
      s2 = toupper(h2) - 0x30;
      if (s2> 9)
            s2 -= 7;
      pbDest = s1*16 + s2;
    }
}
int_tmain(intargc, _TCHAR* argv[])
{
charstrAPK = {0};
    charapkd = "";
    charFileDirectory = {0};
    chardexDirectory = {0};
    charPackName = {0};
    BOOLret = FALSE;
    structrc4_staterc4_test;
    FILE *fp;
    DWORDfileSize = 0;
    BYTE *ptr = NULL;
    DWORDDecOffset = 0X1000;//文件偏移
    DWORDDecSize = 0X0;//大小
    DWORDindex = 0;//循环解密的次数
    stringkey;
    printf("请输入要脱壳的apk包路径:\n");
    scanf("%s",strAPK);
    if (NULL == strAPK)
    {
      printf("路径不能为空!\n");
      return -1;
    }
    strcpy(apkd, "java -jar apktool.jar d ");
    strcat(apkd, strAPK);
    //--------bat解包
    CFileapktool("apktool.bat", CFile::modeCreate | CFile::modeReadWrite);
    apktool.Write(apkd, strlen(apkd));
    apktool.Write("\r\n", strlen("\r\n"));
    apktool.Close();
    char* cmd1 = "apktool.bat";
    STARTUPINFOsi1;
    GetStartupInfo(&si1);
    si1.dwFlags = STARTF_USESHOWWINDOW;
    si1.wShowWindow = SW_HIDE;
    PROCESS_INFORMATIONpi1;
    CreateProcess(NULL,
      (LPSTR)cmd1,
      NULL,
      NULL,
      FALSE,
      CREATE_NEW_CONSOLE,
      NULL,
      NULL,
      &si1,
      &pi1);
    printf("正在解包apk...\n");
    WaitForSingleObject(pi1.hProcess,INFINITE);
    DeleteFile("apktool.bat");
    printf("解包完成...\n");
    //-----判断是否解包成功..........
    strncpy(FileDirectory, strAPK, strlen(strAPK)-strlen(".apk"));
    DWORDdwFileAtt;
    dwFileAtt = GetFileAttributes(FileDirectory);
    //判断是否为目录
    if( dwFileAtt != FILE_ATTRIBUTE_DIRECTORY)
    {
      printf("apk解包失败!...\n");
      return 0;
    }
    printf("apk解包成功!...\n");
    strcpy(dexDirectory, FileDirectory);
    //得到包名
    ret = GetPackName(FileDirectory,PackName);
    if (FALSE == ret)
    {
      printf("获得包名失败...\n");
      return -1;
    }
    //计算MD5值
    MD5md5(PackName);
    key = md5.md5();
    strcat(dexDirectory, "\\assets\\rsprotect.dat");
    fp=fopen(dexDirectory, "rb");
    if(fp==NULL)
      printf("打开文件失败!...");
    //求文件大小
    fseek(fp, 0, SEEK_END);
    fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    ptr = (BYTE*)malloc(fileSize);
    if (NULL == ptr)
    {
      puts("malloc error");
    }
    memset(ptr,fileSize,0);
    fread(ptr, sizeof(BYTE), fileSize, fp);
    fclose(fp);
    //--解密dex
    DecSize = *(DWORD*)(ptr+12);
    index = *(DWORD*)(ptr+0x10);
    ptr += DecOffset;
    if (0 == DecSize)
    {
      printf("要解密的dex大小出错\n");
      return -1;
    }
    memset(&rc4_test,0,sizeof(rc4_test));
    unsignedcharDecKey = {0};
    for (inti=0; i<32; i++)
    {
      DecKey = key;
    }
    unsignedcharkey1 ={0};
    StrToHex(key1, DecKey, 0x10);
    //--生成解密后的dex文件
    fp = fopen("classes.dex","wb");
    if (NULL == fp)
    {
      printf("File open error\n");
    }
    for (inti=0; i<index; i++)
    {
      //初始化Key
      init_Key(&rc4_test, key1, 0x10);
      //解密数据
      rc4_crypt(&rc4_test, ptr, DecOffset);

      fwrite(ptr, sizeof(BYTE), DecOffset, fp);
      ptr+=DecOffset;
    }
    fclose(fp);
    if (NULL != ptr)
    {
      free(Temp);
      ptr = NULL;
      Temp = NULL;
    }

    printf("解密完成!^_^\n");
    return 0;
}


0x06测试与总结
1.运行UnPack.exe输入要解密的APK包路径,成功解密后重新打包并正常反编译,如图17、18所示。http://attach.52pojie.cn/forum/201510/30/205752v6nozgptbbgsiff6.jpg   图17http://attach.52pojie.cn/forum/201510/30/205816xj5giy2fij277f3j.jpg   图182.以上就是简单实现一般APK加固静态脱壳机的编写步骤,由于该加固核心so文件使用UPX默认加壳并未做变形处理,导致so被轻松的静态脱卓,而so模块中的反调试手段比较初级且模块化,可以非常简单的手工patch函数一处反回值就可完全过掉,总的来说无论是静态脱壳还是动态dump都是很容易的。
完。样本 src pdf IDB 下载


溱潼马 发表于 2015-11-3 12:41:07

,,,,,,,,噢

nixiangweilai 发表于 2015-11-3 20:20:56

{:4_86:}好厉害

农村人 发表于 2015-12-15 09:31:25

楼主 能不能给我样本 谢谢。

742477143 发表于 2016-6-5 22:08:57

不错。。。。。。。。。。。。。。。。。。。
页: [1]
查看完整版本: (转载)APK加固之静态脱壳机编写入门