当前位置: 代码迷 >> Android >> 美团Android资源搅混保护的具体实践
  详细解决方案

美团Android资源搅混保护的具体实践

热度:69   发布时间:2016-04-27 22:43:11.0
美团Android资源混淆保护的具体实践

原文章美团Android资源混淆保护实践,但是该文章并没有给出具体的混淆方案,只是放了一个函数,函数的实现过程需要自己去实现,本篇文章也并没有实现该函数,只是对实现该函数有一个前期的准备。

在android 5.0的系统源码中,要修改的代码位于

/frameworks/base/tools/aapt/Resource.cpp

未修改前的代码

static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,                                  ResourceTable* table,                                  const sp<ResourceTypeSet>& set,                                  const char* resType){    String8 type8(resType);    String16 type16(resType);    bool hasErrors = false;    ResourceDirIterator it(set, String8(resType));    ssize_t res;    while ((res=it.next()) == NO_ERROR) {        if (bundle->getVerbose()) {            printf("    (new resource id %s from %s)\n",                   it.getBaseName().string(), it.getFile()->getPrintableSource().string());        }        String16 baseName(it.getBaseName());        const char16_t* str = baseName.string();        const char16_t* const end = str + baseName.size();        while (str < end) {            if (!((*str >= 'a' && *str <= 'z')                    || (*str >= '0' && *str <= '9')                    || *str == '_' || *str == '.')) {                fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",                        it.getPath().string());                hasErrors = true;            }            str++;        }        String8 resPath = it.getPath();        resPath.convertToResPath();        table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),                        type16,                        baseName,                        String16(resPath),                        NULL,                        &it.getParams());        assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);    }    return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;}

美团给我们的差异代码如下

String8 obfuscationName;String8 obfuscationPath = getObfuscationName(resPath, obfuscationName);table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),        type16,        baseName, // String16(obfuscationName),        String16(obfuscationPath), // resPath        NULL,        &it.getParams());assets->addResource(it.getLeafName(), obfuscationPath/*resPath*/, it.getFile(), type8);

只是调用了getObfuscationName函数进行混淆,返回值就是混淆后的路径,然后在addEntry和addResource中将resPath替换为了obfuscationPath。

如果要我们自己实现这个混淆过程,首先我们得熟悉里面的一些属性的意义,比如getBaseName,getLeafName,getParams, convertToResPath等方法的意义。

  • getBaseName是获得资源的名字,不包含后缀,如icon
  • getLeafName是获得资源的名字,包含后缀,如icon.png
  • getParams是获得限定符,如xxhdpi-v4,获得的是一个对象,可以调用它的toString()方法将其转换为字符串形式,也就是前面的xxhdpi-v4,注意不包含前缀-
  • convertToResPath是将路径中的\\转换为/,如res\\drawable会被转换为res/drawable,该方法位于String8这个类中
  • resType,这个是参数传进来的,代表资源的类型,内部使用了 String8 type8(resType);转换为了String8 类型,我们直接使用就可以了,但是这个值不包含限定符,如drawable

默认的打包过程,资源文件都是在res目录下,现在我们通过一定的逻辑,将资源转移到r目录下。

首先获得限定符

String8 params=it.getParams().toString();

由于该限定符可能为空,我们需要用if进行处理,后面再说。

再定义一个用于保存我们转换后的目录

String8 obfuscationPath("");

如果资源限定符是空的,那么我们直接将目录进行拼接,别把限定符包含进去就可以了

                if(params.isEmpty()){            obfuscationPath.append("r/");//我们放在r目录下,所以最开始的是r/            obfuscationPath.append(type8);//资源类型            obfuscationPath.append("/");//添加/,因为是目录            obfuscationPath.append(it.getLeafName());//之后跟上全面,包含后缀的        }

那么假设限定符不为空呢,当然我们需要将其拼接上去

                if(params.isEmpty()){            //......        }else{            obfuscationPath.append("r/");//我们放在r目录下,所以最开始的是r/            obfuscationPath.append(type8);//资源类型            obfuscationPath.append("-");//增加资源限定符前缀-            obfuscationPath.append(params);//将资源限定符添加上去            obfuscationPath.append("/");//添加/,因为是目录            obfuscationPath.append(it.getLeafName());//之后跟上全面,包含后缀的        }

然后后面打包资源的时候使用obfuscationPath代替resPath即可

table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),                        type16,                        baseName,                        String16(obfuscationPath),                        NULL,                        &it.getParams());        assets->addResource(it.getLeafName(), obfuscationPath, it.getFile(), type8);

完整的函数如下

static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,                                  ResourceTable* table,                                  const sp<ResourceTypeSet>& set,                                  const char* resType){    String8 type8(resType);    String16 type16(resType);    bool hasErrors = false;    ResourceDirIterator it(set, String8(resType));    ssize_t res;    while ((res=it.next()) == NO_ERROR) {        if (bundle->getVerbose()) {            printf("    (new resource id %s from %s)\n",                   it.getBaseName().string(), it.getFile()->getPrintableSource().string());        }        String16 baseName(it.getBaseName());        const char16_t* str = baseName.string();        const char16_t* const end = str + baseName.size();        while (str < end) {            if (!((*str >= 'a' && *str <= 'z')                    || (*str >= '0' && *str <= '9')                    || *str == '_' || *str == '.')) {                fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",                        it.getPath().string());                hasErrors = true;            }            str++;        }        String8 resPath = it.getPath();        resPath.convertToResPath();        //String8 obfuscationPath = getObfuscationName(resPath, obfuscationName);        String8 params=it.getParams().toString();        String8 obfuscationPath("");        if(params.isEmpty()){            obfuscationPath.append("r/");            obfuscationPath.append(type8);            obfuscationPath.append("/");            obfuscationPath.append(it.getLeafName());        }else{            obfuscationPath.append("r/");            obfuscationPath.append(type8);            obfuscationPath.append("-");            obfuscationPath.append(params);            obfuscationPath.append("/");            obfuscationPath.append(it.getLeafName());        }        fprintf(stderr, "our Path:%s\n",obfuscationPath.string());        table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),                        type16,                        baseName,                        String16(obfuscationPath),                        NULL,                        &it.getParams());        assets->addResource(it.getLeafName(), obfuscationPath, it.getFile(), type8);    }    return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;}

这样进行编译,编译的步骤参考之前写的博客http://blog.csdn.net/sbsujjbcy/article/details/47778879

替换我们的aapt进行编译。

这里写图片描述

可以看到我们自己输出的目录,不在是res目录下,而是r目录下,这时候将打包好的apk文件进行反编译。你发现res目录下并没有我们自己写的资源文件,全是系统自身的,然后多了一个unknown目录,里面按着原目录排列着我们的资源文件

这里写图片描述

很显然,我们已经成功地将res中的文件转移到了r文件夹中。那么资源混淆也就变得简单了。我们参考我们java中的类的混淆,我们的类会被混淆成a,b,c之类的符号,于是我们只要定义一个转换函数,将原目录映射到a,b,c等目录,比如anim映射为a,color映射为b,….因此类推。然后对应目录中的文件也依次重命名。a-z不够用的可以使用aa-zz,甚至aaa-zzz。关键就是这么一个转换函数。

这个函数的实现其实应该不难,但是出于个人太懒,就没去写这个函数了,读者有兴趣可以在上面的代码基础上进行修改。

版权声明:本文为博主原创文章,未经博主允许不得转载。

3楼scgg48484昨天 16:07
学习了!
2楼dream_zze昨天 19:29
。。n。n。
1楼u012583459昨天 17:29
good
  相关解决方案