版权声明:本文为博主原创文章,未经博主允许不得转载。
0x00
本文的源代码已经上传至github,地址为https://github.com/jltxgcy/DynamicFixDex。在Android2.3模拟器上可以运行。
ForceApkObj:用于动态加载的apk。类似于Android中的Apk的加固(加壳)原理解析和实现一文中的ForceApkObj工程。
FixDex:用于分离ForceApkObj里面的classes.dex,把他分离为classes_fix.dex和data.so,具体情况我们后来介绍。
DynamicDex:用于动态加载ForceApkObj工程生成的ForceApkObj.apk,也就是脱壳程序,类似于Android中的Apk的加固(加壳)原理解析和实现一文中的ReforceApk工程。
0x01
使用步骤:
1、将ForceApkObj工程生成的classes.dex拷贝到/sdcard/payload/目录下。
2、Run FixDex工程,点击Button按钮,此时在/sdcard/payload/目录下生成了classes_fix.dex和data.so。
3、把classes_fix.dex更名为classes.dex,然后替换ForceApkObj.apk里面的classes.dex,然后重新签名,生成新签名的ForceApkObj.apk。
4、把ForceApkObj.apk和data.so放入/sdcard/payload/目录中,运行DynamicDex工程。
0x02
ForceApkObj工程很简单,主MainActivity界面点击屏幕会打开SubActivity,SubActivity的代码如下:
- public class SubActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- handleException();
- }
- public void handleException() {
- Toast.makeText(this, "成功映射", Toast.LENGTH_LONG).show();
- }
- }
FixDex用于用于分离ForceApkObj里面的classes.dex,把他分离为classes_fix.dex和data.so。代码如下:
- public void onClick(View arg0) {
- int codeoff = FindCode.findCode("Lcom/example/forceapkobj/SubActivity;", "handleException");
- Log.d("jltxgcy", "codeoff:" + codeoff);
- try {
- File file = new File("/sdcard/payload/classes.dex");
- byte[] dexByte = readFileBytes(file);
- String strso = "/sdcard/payload/data.so";
- writeFile(strso, dexByte, codeoff - 16, exceptionCode.length + 16);
- System.arraycopy(exceptionCode, 0, dexByte, codeoff, exceptionCode.length);
- //修改DEX file size文件头
- fixFileSizeHeader(dexByte);
- //修改DEX SHA1 文件头
- fixSHA1Header(dexByte);
- //修改DEX CheckSum文件头
- fixCheckSumHeader(dexByte);
- String str = "/sdcard/payload/classes_fix.dex";
- writeFile(str, dexByte, 0, dexByte.length);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- const DexCode *code =
- dexFindClassMethod(&gDexFile, className, methodName);
- if(code == NULL){
- ALOGE("Error can not found setScoreHidden");
- return 0;
- }
- position = (u4 *)code - (u4 *)dexBase;
- ALOGD("codeoff:%d", ((u4 *)code - (u4 *)dexBase));
- return position * 4 + 16;
- struct DexCode {
- u2 registersSize;
- u2 insSize;
- u2 outsSize;
- u2 triesSize;
- u4 debugInfoOff; /* file offset to debug info stream */
- u4 insnsSize; /* size of the insns array, in u2 units */
- u2 insns[1];
- /* followed by optional u2 padding */
- /* followed by try_item[triesSize] */
- /* followed by uleb128 handlersSize */
- /* followed by catch_handler_item[handlersSize] */
- };
找到了真正执行的指令的偏移,然后我们把这个指令整个的DexCode拷贝到data.so中。
- writeFile(strso, dexByte, codeoff - 16, exceptionCode.length + 16);
然后把原来classes.dex中handleException的DexCode中insns[1],也就是真正执行的指令全部置为exceptionCode,也就是全0。这样如果不动态修复,当执行到这个方法时,就会报错。
- System.arraycopy(exceptionCode, 0, dexByte, codeoff, exceptionCode.length);
- String str = "/sdcard/payload/classes_fix.dex";
- writeFile(str, dexByte, 0, dexByte.length);
0x03
把classes_fix.dex更名为classes.dex,然后替换ForceApkObj.apk里面的classes.dex,然后重新签名,生成新签名的ForceApkObj.apk。
然后把ForceApkObj.apk和data.so放入/sdcard/payload/目录中,运行DynamicDex工程。
在Android加壳native实现一文中,我们讲述了如何在native加载ForceApkObj.apk,并替换原Application,执行ForceApkObj中的主MainActivity。在这里我们也可以用同样的方式加载ForceApkObj,执行ForceApkObj中的主MainActivity,但是执行到SubActivity时,由于在SubActivity类的onCreate方法中调用handleException方法,而这个方法指令在0x02步已经设置为全零。那么就会报错。
如果想要执行正确,我们有一个思路,也就是代码中的实现,如下:
- static void nativeParserDex(const char *className, const char *methodName)
- {
- void *base = NULL;
- int module_size = 0;
- char filename[512];
- char path[512];
- int fd=-1;
- int r=-1;
- int len=0;
- struct stat st;
- u4 *addr;
- int newCodeOffPosition;
- u1 *store = (u1 *) malloc(2);
- // simple test code here!
- for(int i=0; i<2; i++){
- sprintf(filename, "/mnt/sdcard/payload_odex/ForceApkObj.dex");
- base = get_module_base(-1, filename);
- if(base != NULL){
- break;
- }
- }
- if(base == NULL){
- ALOGE("Can not found module: %s", filename);
- return ;
- }
- module_size = get_module_size(-1, filename);
- // search dex from odex
- void *dexBase = searchDexStart(base);
- ALOGD("found dex start[%p]", dexBase);
- if(checkDexMagic(dexBase) == false){
- ALOGE("Error! invalid dex format at: %p", dexBase);
- return ;
- }
- DexHeader *dexHeader = (DexHeader *)dexBase;
- gDexFile.baseAddr = (u1*)dexBase;
- gDexFile.pHeader = dexHeader;
- gDexFile.pStringIds = (DexStringId*)((u4)dexBase+dexHeader->stringIdsOff);
- gDexFile.pTypeIds = (DexTypeId*)((u4)dexBase+dexHeader->typeIdsOff);
- gDexFile.pMethodIds = (DexMethodId*)((u4)dexBase+dexHeader->methodIdsOff);
- gDexFile.pFieldIds = (DexFieldId*)((u4)dexBase+dexHeader->fieldIdsOff);
- gDexFile.pClassDefs = (DexClassDef*)((u4)dexBase+dexHeader->classDefsOff);
- gDexFile.pProtoIds = (DexProtoId*)((u4)dexBase+dexHeader->protoIdsOff);
- //dumpDexHeader(dexHeader);
- //dumpDexStrings(&gDexFile);
- //dumpDexTypeIds(&gDexFile);
- //dumpDexProtos(&gDexFile);
- //dumpFieldIds(&gDexFile);
- //dumpClassDefines(&gDexFile);
- // 2. found Dex Class!
- const DexCode *code =
- dexFindClassMethod(&gDexFile, className, methodName);
- if(code == NULL){
- ALOGE("Error can not found setScoreHidden");
- return ;
- }
- codeOffPosition = ((u4 *)code - (u4 *)dexBase) * 4;
- ALOGD("codeOffPosition:%d", codeOffPosition);
- dexFindClassData(&gDexFile, className);
- u1 *codeData = (u1 *)codeOffPoint;
- ALOGD("codeOffPoint:%p,codeOffPointByte:%d,codeOffPointData:%d,%d", codeOffPoint, codeOffPointByte, *codeData,*(codeData + 1));
- sprintf(path, "/mnt/sdcard/payload_odex/data.so");
- fd = open(path,O_RDONLY,0666);
- if (fd==-1) {
- return ;
- }
- r=fstat(fd,&st);
- if(r==-1){
- close(fd);
- return ;
- }
- len=st.st_size;
- addr=(u4 *)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
- ALOGD("addr:%p", addr);
- newCodeOffPosition = ((u4 *)addr - (u4 *)dexBase) * 4;
- writeLeb128(store, newCodeOffPosition);
- ALOGD("newCodeOffPosition:%d, store:%d,%d", newCodeOffPosition, *store,*(store + 1));
- ALOGD("mprotect in");
- *codeData = *store;
- codeData++;
- *codeData = *(store + 1);
- ALOGD("codeOffPointData:%d,%d", *(codeData-1),*(codeData));
- }
讲的是大概的原理,大家参考源码多看看就能理解,如果有不懂,请留言。我在Android 2.3模拟器试验成功!