美文网首页Android程序员逆向、代码篡改、破解
Android二次打包之重新生成R文件

Android二次打包之重新生成R文件

作者: b923228cc7b5 | 来源:发表于2020-11-06 13:49 被阅读0次

    安卓经常需要打多个渠道包,当二次打包时,资源ID会重新生成。如果代码中有第三方SDK通过直接引用R文件的方式来获取资源ID,就会出现资源ID不匹配的问题。
    本文主要介绍解决此类问题的三种方法。

    一 背景

    为什么要二次打包

    大家都知道,国内安卓渠道众多,游戏想要上架渠道就要接入他们的sdk。这对于游戏开发商(CP)来说是一个不小的工作量。

    通过接入我们的聚合SDK,CP只需要提供一个母包,然后使用我们的打包工具就可以打出几十个渠道包,非常的高效。

    二次打包的基本原理

    打包工具的基本原理就是通过反编译,把SDK的代码和资源文件打入到游戏母包,然后重新打包签名,生成对应的渠道包。

    当然这其中会涉及到许多细节方面的东西,不是本文重点,就不展开了。

    二次打包之资源文件ID

    打包时,会生成两个文件。

    一个是resources.arsc.里面包含了所有资源文件的索引ID

    示例:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <public type="drawable" name="btn_login" id="2130837507" />
        <public type="layout" name="paydialog" id="2130903044" />
    </resources>
    

    另外一个是R文件,里面提供了代码中引用的name和resources.arsc中对应的ID

    示例:

    public final class R {
            public static final class drawable {
                public static final int btn_login = 2130837507;
        }
        public static final class layout {
            public static final int paydialog = 2130903049;
        }
    }
    

    这样我们通过引用R文件就可以找到对应的资源。

    但是,正如前面所说,resources.arsc和R.java是打包的时候才会生成。因此,二次打包会重新生成资源ID,如果我们通过直接引用R文件的方式来寻找资源,就会出现问题。

    二 解决方法

    1. 常规操作:动态获取资源ID

    这是最简单最通用的解决方法。

    比如说我们可以通过如下方式获取一个布局文件的ID:

    public static int getLayoutId(Context context,String paramString) {
       mContext = context;
        return getResourceId("layout", paramString);
    }
    
    protected static int getResourceId(String paramString1, String paramString2) {
            return mContext.getResources().getIdentifier(paramString2, paramString1,
                    mContext.getPackageName());
        }
      
    

    2. 奇技淫巧:替换掉第三方SDK中R文件的引用

    可是,如果第三方SDK就是通过R文件直接引用资源ID怎么办?

    比如说,他们引用了com.corpA.sdk.R,重新打包之后,会在我们的包名(com.corpB.demo)文件夹下生成新的R文件。因此,我们可以让SDK直接引用我们包名文件夹下的R文件。

    具体咋做呢?

    2.1 SDK代码反编译为smali文件

    使用apktool将第三方SDK的所有java代码反编译为smali文件。

    2.2 查找替换所有R文件的引用

    将smali文件中所有com/corpA/sdk/R替换为com/corp/demo/R。这样,重新打包后,第三方SDK在运行时会去我们包名下最新的R文件中寻找资源ID

    这种方法简单粗暴,不需要手动生成R文件,也不需要更改SDK中原来的R文件,直接通过打包脚本在反编译阶段改掉第三方SDK的代码。

    3. 曲线救国:直接修改第三方SDK中引用的R文件

    凡是总有例外,不是所有的SDK都能反编译的。比如有些SDK会将Java代码打成一个加密的Dex文件,放到asset文件夹下面。这种加密的Dex文件我们是无法反编译的,所以上面的方法2就行不通了。

    由于无法反编译,这个Dex里面的内容对于我们就是一个黑盒,我们无法知道它里面是否有直接引用R文件的情况,以及引用了哪里的R文件。

    这个其实没有很好地办法,只能先按照方法2来处理掉可以反编译的Java代码,然后打出一个包来进行测试。如果加密Dex中有直接引用到R文件,那么就会出现崩溃,从日志中我们就可以找到引用R文件的位置。然后我们就可以修改指定位置的R文件啦。

    过程有些繁琐,下面详细说明:

    3.1 找到引用R文件的位置

    通过崩溃日志,我们可以找到Dex文件中所引用的R文件的位置,假设该R文件的路径是com.corpA.sdk.R

    3.2 使用aapt手动生成R文件

    aapt(Android Asset Packaging Tool)是安卓sdk中负责将资源文件和代码进行打包的工具。基本上所有的打包工具都是在aapt的基础上进行封装和修改的,打包的过程比较繁琐,总结下来大概有如下几个步骤:

    1. 通过aapt工具,生成R.java和.arsc文件
    2. 通过aidl工具,把aidl文件打包成java文件
    3. 通过javac工具,将Java文件编译成.class文件
    4. 通过dx工具,把class文件和第三方jar打包成dex文件
    5. 通过apkbuilder工具,把dex和资源文件打包成apk文件
    6. 通过JarSinger工具,对apk进行签名
    7. 通过zipAlign工具,对apk做对齐处理

    我们要改的就是第一步:aapt生成R文件!步骤如下

    1. 反编译游戏母包和接入渠道SDK的插件包,将代码和资源文件合并

    2. 修改AndroidManifest.xml中的包名为最终渠道包的包名

    3. 使用Android sdk的aapt工具手动生成R文件

      前提是需要首先安装Andorid sdk相应的工具,并配置好环境变量。aapt指令比较复杂,核心就是下面这个:

    os.getenv('ANDROID_BUILD_TOOL') + "/aapt package -f -m -J " + temp_path + " -S " + res_path + " -I " + os.getenv('ANDROID_PLATFORM') + "/android.jar -M " + manifest_path
    

    其中temp_path是生成的R文件输出路径;res_path是res文件夹的路径;manifest_path是manifest的路径

    3.3 将R文件转换为Smali文件

    这个过程大概4个步骤: java --> class --> jar --> dex --> smali

    示例:

        # 1. build_r_class
        r_java_path = temp_path + os.sep + package_name.replace('.', os.sep) + os.sep + "R.java"
        cmd_build_r_class = "javac -source 1.6 -target 1.6 " + r_java_path
        execCmd(cmd_build_r_class)
        file_util.deleteFile(r_java_path)
    
        # 2. generate jar
        os.chdir("/data/soft/jenkins/workspace/Packaging_Tools-All" + os.sep + temp_path)
        cmd_generate_r_jar = "jar cvf " + "r.jar " + "com"
        execCmd(cmd_generate_r_jar)
    
        # 3. generate dex
        cmd_generate_dex = os.getenv('ANDROID_BUILD_TOOL') + "/dx --dex --output=" + "r.dex " +         "r.jar"
        execCmd(cmd_generate_dex)
    
        # 4. generate smali
        cmd_generate_smali = "java -jar " + "/data/soft/jenkins/workspace/Packaging_Tools-All" +         os.sep + "baksmali-2.0.jar " + "r.dex"
        execCmd(cmd_generate_smali)
    

    3.4 拷贝smali文件到对应位置

    1. 我们要拷贝一份smali文件到包名下对应目录。

    2. 然后同样拷贝到3.1步骤中R文件的路径(com.corpA.sdk.R)。同时,我们需要修改smali中R文件的引用为com.corpA.sdk.R。这样才能保持更Asset下面的Dex文件中的引用一致。

    至此,关于R文件的处理已经完成,然后重新打包就可以啦~

    相关文章

      网友评论

        本文标题:Android二次打包之重新生成R文件

        本文链接:https://www.haomeiwen.com/subject/kgdtbktx.html