• APK加固之静态脱壳机编写入门


    目录:

    0x00APK加固简介与静态脱壳机的编写思路

    1.大家都知道Android中的程序反编译比较简单,辛苦开发出一个APK轻易被人反编译了,所以现在就有很多APK加固的第三方平台,比如爱加密和梆梆加固等。

    2.一般的加固保护通常能够提供如下保护:加密、防逆向、防篡改、反调试、反窃取等功能,编写静态脱壳机须要信息有加密后的原始DEX数据、解密算法、解密密钥、要想获得这些信息我们首先要解决的问题是过反调试、动态分析解密流程、获取密钥,获得原始DEX数据存放位置、分析解密算法。

    0x01壳简单分析

    1.整体来看一下加固前APK包和加固后的APK包结构相关变化,如图1所示。

          图1

    图1所示加固后的APK增加了librsprotect.so、librsprotect_x86.so、rsprotect.dat文件,发生变化的有AndroidManifest.xml、classes.dex文件。

    2.反编译加固后APK,APK中的AndroidManifest.xml文件的入口被修改,如图2所示。

          图2

    3.入口类中主要会调用librsprotect.so中的3个函数,如图3所示。

    private native void initialize(Context paramContext);

    private native Application makeApplication(String paramString);

    private native void applicationOnCreate();

          图3

    0x02 SO文件脱壳

    1.既然主要是调用librsprotect.so中的函数,我们将librsprotect.so放到IDA Pro中分析,发现代码都是乱码 图4所示,说明被加密了。

          图4

    2.一般加壳的SO的壳代码都在INIT段或INIT_ARRAY段,我们先看下被加壳以后的SO信息,用readelf -a命令查看,图5所示

          图5

    可以看到INIT值为0x2ea91,到 IDA中看看该地址的内容,就是壳的入口了,明显是UPX的加壳,图6所示,有人会问你为什么会知道是UPX的壳,“只是因为在人群中多看了你一眼,再也没能忘掉你容颜!(^_^)”。

          图6

    3.尝试用upx -d脱壳,因为这样脱方便、干净、省事、提示图7所示的信息。

          图7

    查看UPX源码后发现可能是没有找到UPX!的标志,用16进制工具打开librsprotect.so发现标志被改成了RSP!,将其改成UPX!后再将尝试,出现 图8所示的信息。

          图8

    出现这种错误可能是做变形处理了或者是版本不对,通过分析librsprotect.so的壳代码好像没有变形处理,所以决定重新编一个3.92版本的来试试,编译好后脱壳成功,如图9所示。

          图9

    将脱壳后的so放到IDA Pro中分析,代码正常,图10所示,SO脱壳完成。

          图10

    0x03 反调试分析

    1.如何使用IDA调试android的SO模块,网上教程也太多太多了,这里不多说,将脱壳后的librsprotect.so替换掉原始有壳的SO后(也可不用替换没影响,这里只是为了测试)签名安装进行动态分析。

    2.通过动态调试该加壳程序,它用到的反调试方法是首先试探性读取/proc/pid/status获取进程状态去判断是否有调试器,如果发现被调试就kill掉本进程,如图11所示

          图11

    3.通过读取/proc/net/tcp查看正在运行应用的本地端口号是否有android_server端口,如果有就创建一个反调试线程,如图12所示,每隔几秒检查一次,过反调试就很简单了直接把返回值改成假就成了。

          图12 android_server运行后端口

          图13

    0x04解密流程分析

    1. 根据算法中的常量值猜测该算法为MD5,如图14所示

          图14

    2.获取包名并计算MD5值 图15所示,将该值做为密钥。

    com.droider.crackme0201

    F2 E8 F0 62 85 17 9C 3C 99 F5 67 9F A6 27 FC 55

          图15

    2.打开并读取/data/data/com.droider.crackme0201/files/.rsdata/rsprotect.dat数据,该文件是从APK包中的assets文件夹中拷贝过来的,判断前4字节是"RSFL"是否与so中的相同,不同则退出,rsprotect.dat前0x1000字节存放原始DEX大小与循环解密的次数,每次解密0x1000字节,根据密钥初始化流程发现解密算法为RC4,图16示(也可以看IDB)。

          图16

    0x05脱壳机编写

    1.通过分析,已经知道了壳的数据、密钥、算法、解密过程, 现在来写脱壳机。

    必要步骤如下:

    1。解包获得rsprotect.da数据。

    2.XML解析获得包名。

    3.MD5计算获得密钥。

    4.RC4解密rsprotect.dat中的数据。

    代码流程:

      1 #include"stdafx.h"
      2 #include<afxwin.h>
      3 #include<stdio.h>
      4 #include<windows.h>
      5 #include<process.h>
      6 #include<assert.h>
      7 #include<string>
      8 #include<iostream>
      9 #include"CMarkup.h"
     10 #include"md5.h"
     11 #include"rc4.h"
     12 #include<string>
     13 usingnamespacestd;
     14 BOOLGetPackName(char* pathXml, charoutPackName[256])
     15 {
     16     CMarkupxml;
     17     boolflag;
     18     CStringpackName;
     19     CStringAppandroidname;
     20     MCD_STRmyapkName;
     21     MCD_STRattribName;
     22     char* strXML = "\AndroidManifest.xml";
     23     strcat(pathXml,strXML);
     24     flag = xml.Load((MCD_STR)pathXml);
     25     if ( FALSE == flag)
     26     {
     27         printf("获得包名失败...
    ");
     28         returnFALSE;
     29     }
     30     flag = xml.FindElem((MCD_STR)"manifest");
     31     //获取包名.........
     32     for(intattribIndex=0;;attribIndex++)
     33     {
     34         attribName=xml.GetAttribName(attribIndex);
     35         if (attribName.GetLength()!=0)  //方法若返回empty string,即表示属性结束,结束循环
     36         {
     37             MCD_STRattribVal = xml.GetAttrib(attribName);//否则读取属性值,以子元素加入dxml
     38             //------判断是否为包名...
     39             packName = attribName.GetString();
     40             if ( 0 == strcmp(packName.GetString(), "package"))
     41             {
     42                 myapkName = attribVal;
     43                 strcpy(outPackName, attribVal.GetString());
     44                 returnTRUE;
     45             }
     46         }
     47         else
     48             break;
     49     } 
     50     returnFALSE;
     51 }
     52 voidStrToHex(BYTE *pbDest, BYTE *pbSrc, intnLen)
     53 {
     54     charh1,h2;
     55     BYTEs1,s2;
     56     inti;
     57     for (i=0; i<nLen; i++)
     58     {
     59         h1 = pbSrc[2*i];
     60         h2 = pbSrc[2*i+1];
     61         s1 = toupper(h1) - 0x30;
     62         if (s1> 9) 
     63             s1 -= 7;
     64         s2 = toupper(h2) - 0x30;
     65         if (s2> 9) 
     66             s2 -= 7;
     67         pbDest[i] = s1*16 + s2;
     68     }
     69 }
     70 int_tmain(intargc, _TCHAR* argv[])
     71 {
     72 charstrAPK[512] = {0};
     73     charapkd[256] = "";
     74     charFileDirectory[512] = {0};
     75     chardexDirectory[512] = {0};
     76     charPackName[256] = {0};
     77     BOOLret = FALSE;
     78     structrc4_staterc4_test;
     79     FILE *fp;
     80     DWORDfileSize = 0;
     81     BYTE *ptr = NULL;
     82     DWORDDecOffset = 0X1000;//文件偏移
     83     DWORDDecSize = 0X0;//大小
     84     DWORDindex = 0;//循环解密的次数
     85     stringkey;
     86     printf("请输入要脱壳的apk包路径:
    ");
     87     scanf("%s",strAPK);
     88     if (NULL == strAPK)
     89     {
     90         printf("路径不能为空!
    ");
     91         return -1;
     92     }
     93     strcpy(apkd, "java -jar apktool.jar d ");
     94     strcat(apkd, strAPK);
     95     //--------bat解包
     96     CFileapktool("apktool.bat", CFile::modeCreate | CFile::modeReadWrite);
     97     apktool.Write(apkd, strlen(apkd));
     98     apktool.Write("
    ", strlen("
    "));
     99     apktool.Close();
    100     char* cmd1 = "apktool.bat";
    101     STARTUPINFOsi1;
    102     GetStartupInfo(&si1);
    103     si1.dwFlags = STARTF_USESHOWWINDOW;
    104     si1.wShowWindow = SW_HIDE;
    105     PROCESS_INFORMATIONpi1;
    106     CreateProcess(NULL, 
    107         (LPSTR)cmd1, 
    108         NULL, 
    109         NULL, 
    110         FALSE, 
    111         CREATE_NEW_CONSOLE,
    112         NULL, 
    113         NULL, 
    114         &si1, 
    115         &pi1);
    116     printf("正在解包apk...
    ");
    117     WaitForSingleObject(pi1.hProcess,INFINITE);
    118     DeleteFile("apktool.bat");
    119     printf("解包完成...
    ");
    120     //-----判断是否解包成功..........
    121     strncpy(FileDirectory, strAPK, strlen(strAPK)-strlen(".apk"));
    122     DWORDdwFileAtt;
    123     dwFileAtt = GetFileAttributes(FileDirectory);
    124     //判断是否为目录
    125     if( dwFileAtt != FILE_ATTRIBUTE_DIRECTORY)
    126     {
    127         printf("apk解包失败!...
    ");
    128         return 0;
    129     }
    130     printf("apk解包成功!...
    ");
    131     strcpy(dexDirectory, FileDirectory);
    132     //得到包名
    133     ret = GetPackName(FileDirectory,PackName);
    134     if (FALSE == ret)
    135     {
    136         printf("获得包名失败...
    ");
    137         return -1;
    138     }
    139     //计算MD5值
    140     MD5md5(PackName);
    141     key = md5.md5();
    142     strcat(dexDirectory, "\assets\rsprotect.dat");
    143     fp=fopen(dexDirectory, "rb");
    144     if(fp==NULL)
    145         printf("打开文件失败!...");
    146     //求文件大小
    147     fseek(fp, 0, SEEK_END);  
    148     fileSize = ftell(fp); 
    149     fseek(fp, 0, SEEK_SET);
    150     ptr = (BYTE*)malloc(fileSize);
    151     if (NULL == ptr)
    152     {
    153         puts("malloc error");
    154     }
    155     memset(ptr,fileSize,0);
    156     fread(ptr, sizeof(BYTE), fileSize, fp);
    157     fclose(fp);
    158     //--解密dex
    159     DecSize = *(DWORD*)(ptr+12);
    160     index = *(DWORD*)(ptr+0x10);
    161     ptr += DecOffset;
    162     if (0 == DecSize)
    163     {
    164         printf("要解密的dex大小出错
    ");
    165         return -1;
    166     }
    167     memset(&rc4_test,0,sizeof(rc4_test));
    168     unsignedcharDecKey[32] = {0};
    169     for (inti=0; i<32; i++)
    170     {
    171         DecKey[i] = key[i];
    172     }
    173     unsignedcharkey1[32] ={0};
    174     StrToHex(key1, DecKey, 0x10);
    175     //--生成解密后的dex文件
    176     fp = fopen("classes.dex","wb");
    177     if (NULL == fp)
    178     {
    179         printf("File open error
    ");
    180     }
    181     for (inti=0; i<index; i++)
    182     {
    183         //初始化Key
    184         init_Key(&rc4_test, key1, 0x10);
    185         //解密数据
    186         rc4_crypt(&rc4_test, ptr, DecOffset);
    187 
    188         fwrite(ptr, sizeof(BYTE), DecOffset, fp);
    189         ptr+=DecOffset;
    190     }
    191     fclose(fp);
    192     if (NULL != ptr)
    193     {
    194         free(Temp);
    195         ptr = NULL;
    196         Temp = NULL;
    197     }
    198 
    199     printf("解密完成!^_^
    ");
    200     return 0;
    201 }

    0x06 测试与总结

    1.运行UnPack.exe输入要解密的APK包路径,成功解密后重新打包并正常反编译,如图17 图18所示。

          图17

          图18

    2.以上就是简单实现一般APK加固静态脱壳机的编写步骤,由于该加固核心so文件使用UPX默认加壳并未做变形处理,导致so被轻松的静态脱卓,而so模块中的反调试手段比较初级且模块化,可以非常简单的手工patch函数一处反回值就可完全过掉,总的来说无论是静态脱壳还是动态dump都是很容易的。

    完。

     样本及PDF IDB下载

    http://yunpan.cn/cFzNPXB27awau (提取码:6937)

  • 相关阅读:
    Linux vim 入门 配置 及 使用初步
    Java网络编程之TCP通信
    [ javascript ] getElementsByClassName与className和getAttribute!
    找出二叉查找树中指定结点的”下一个&quot;结点(也即中序后继)
    DNS分别在什么情况下使用UDP和TCP
    高仿京东APP首页“京东快报”自己主动向上滚动的广告条
    HDU 5016 Mart Master II (树上点分治)
    Myeclipse10完美破解过程
    git format-patch 用法【转】
    Git下的冲突解决【转】
  • 原文地址:https://www.cnblogs.com/2014asm/p/4924342.html
Copyright © 2020-2023  润新知