Android APP逆向分析-脱壳

2 阅读2分钟

一、加固后的 APK 加载流程

APK 实际结构

com.xxx.yyy.apk

                                        ├── classes.dex


                                        │    只有壳


                                        ├── assets


                                        │   ├── 加固的厂商protect1.dex

                                        │   ├── 加固的厂商protect2.dex

                                        │   ├── 加固的厂商protect-sec.dex



                                        ├── lib


                                        │   └── lib加固的厂商protect.so



                                        └── 真正业务dex


                                             加密隐藏

启动:

                                        Android系统


                                              |

                                              |


                                        StubApplication.attachBaseContext()


                                              |

                                              |



                                        加载lib加固的厂商protect.so


                                              |

                                              |



                                        读取assets/加固的厂商protect*.dex



                                              |

                                              |




                                        解密真实classes.dex


                                              |

                                              |


                                        DexClassLoader


                                              |

                                              |


                                        替换Application


                                              |

                                              |


                                        真实的业务代码

二、脱壳方案:Frida + dumpdex

流程:

                                            手机

                                             |

                                             |

                                            启动APP

                                             |

                                             |

                                            StubApplication解密

                                             |

                                             |

                                            Frida注入

                                             |

                                             |

                                            dump内存dex

                                             |

                                             |

                                            jadx分析

三、环境准备

1. 手机要求

Root 、Magisk 、Frida Server

2、PC:

安装:

python3 -m pip install frida-tools

code ~/.zshrc

文件末尾:

   export PATH="$HOME/Library/Python/3.9/bin:$PATH"

保存退出,执行:

    source ~/.zshrc

    frida --version

3、手机启动:

frida-server

frida.re/docs/androi…

unxz frida-server-17.15.2-android-arm64.xz

 adb push frida-server-17.15.2-android-arm64 /data/local/tmp/

 adb shell "su -c chmod 755 /data/local/tmp/frida-server”
 (已重命名为frida-server)

问题:启动 frida-server 时遇到端口 27042 已被占用,这是因为该端口上已经有一个 frida-server 进程在运行(或者被其他程序占用)

方案:

1、杀掉旧进程,释放端口
netstat -tulnp | grep 27042
2、 杀掉该进程(假设 PID 是 1234)
kill -9 1234
3. 重新启动 frida-server
    ./frida-server

在PC端执行:

frida-ps -U

能看到:应用名称说明成功。

四、第一步:定位真实Dex加载位置

创建hook_dex.js,内容:

Java.perform(function(){


    var DexClassLoader =

        Java.use("dalvik.system.DexClassLoader");

    DexClassLoader.$init.overload(

        'java.lang.String',

        'java.lang.String',

        'java.lang.String',

        'java.lang.ClassLoader'

    )

    .implementation=function(

        dexPath,

        optimizedDirectory,

        librarySearchPath,

        parent

    ){

        console.log(

          "[Dex Loaded] "

          + dexPath

        );



        return this.$init(

            dexPath,

            optimizedDirectory,

            librarySearchPath,

            parent

        );

    };



});

运行:

frida -U \

-f com.unitree.doggo2 \

-l hook_dex.js 

在 Frida 控制台输入:

    %resume

然后回车,作用:继续运行 APP。

如果没有输出 Dex Loaded,退出当前 Frida,执行

        exit

修改 hook_dex.js

替换为:

Java.perform(function () {

    console.log("[+] Hook start");
    // hook DexClassLoader

    var DexClassLoader =

        Java.use("dalvik.system.DexClassLoader");


    DexClassLoader.$init

    .overload(

        'java.lang.String',

        'java.lang.String',

        'java.lang.String',

        'java.lang.ClassLoader'

    )

    .implementation=function(

        dexPath,

        optimizedDirectory,

        librarySearchPath,

        parent

    ){

        console.log(

            "\n[DexClassLoader]"

        );


        console.log(

            dexPath

        );


        return this.$init(

            dexPath,

            optimizedDirectory,

            librarySearchPath,

            parent

        );

    };


    try{


        var InMemoryDexClassLoader =

            Java.use(

            "dalvik.system.InMemoryDexClassLoader"

            );

        console.log(

            "[+] Found InMemoryDexClassLoader"

        );


    }catch(e){

        console.log(

        "[-] no InMemoryDexClassLoader"

        );

    }

});

重新启动:

        frida -U \

        -f com.unitree.doggo2 \

        -l hook_dex.js

输入:

                %resume

五、第二步:Dump真实dex

工具:frida-dexdump

python3 -m pip install frida-dexdump

先确认 dex 是否加载,输入:

    Java.enumerateLoadedClasses({

        onMatch:function(name){


            if(name.indexOf("unitree")>=0){

                console.log(name);


            }

        },

        onComplete:function(){}

    });

查看是否已经释放真实代码。

结果:

dalvik.system.PathClassLoader[

DexPathList[

[

zip file "/data/app/.../base.apk",

dex file "InMemoryDexFile[cookie=[0, ...]]",

dex file "InMemoryDexFile[cookie=[0, ...]]",

dex file "InMemoryDexFile[cookie=[0, ...]]",

dex file "InMemoryDexFile[cookie=[0, ...]]",

dex file "InMemoryDexFile[cookie=[0, ...]]"

]

]

]

说明: 不是 DexClassLoader,InMemoryDexFile也就是:内存加载 dex 模式。真实 dex:不是:/data/data/…/xxx.dex,是在:ART内存。

现在应该直接frida-dexdump。

python3 frida-dexdump \

-U \

-n appId

成功应该输出:dump/

    classes.dex

    classes2.dex

    classes3.dex

    classes4.dex

    classes5.dex ......

至此,脱壳完成。