├── 1-逆向环境配置.md ├── 2-APK包结构.md ├── 3-反编译APK文件.md ├── 4-Android常用Linux命令介绍.md ├── 5-Android特有的adb命令介绍.md ├── 6-AndroidFrida逆向与抓包实践.md ├── 7-Frida环境搭建.md ├── 8-Frida脚本入门.md ├── 9-Objection入门.md ├── 91-Jadx-Jeb-GDA.md ├── 92-逆向1:Objection结合Jeb分析.md ├── 93-Frida开发思想——Frida三板斧.md ├── 94-APP攻防博弈.md ├── 95-Smali学习.md ├── 96-对未加固APP进行分析和破解.md ├── 97-对加固APP进行分析和破解的实战.md ├── 98-Xposed框架简述.md ├── 99-Android抓包详解.md ├── 991-HTTP(S)网络框架分析.md ├── 992-native基础.md ├── 993-Frida native层Hook.md ├── 994-Android逆向学习补充.md ├── APK ├── app-debug.apk ├── com.hello.qqc.apk ├── junior.apk ├── lingdongniao.apk ├── moveTV.apk └── zhibo.apk ├── README.md ├── image ├── HTTPS中间人抓包.png ├── Https通讯过程.png ├── NDK1.png ├── NDK2.png ├── adbdevices.png ├── 字段操作指令.png ├── 异常指令与跳转指令.png ├── 数据定义指令.png ├── 数据转换.png ├── 数据运算.png ├── 数组操作指令.png ├── 方法调用指令.png ├── 未加固1.png ├── 比较指令.png ├── 空指令与数据操作指令与返回指令.png └── 锁指令与实例操作指令.png ├── iqiyi广告去除.md └── 资源 ├── apktool_2.6.1.jar └── framework-xposed-v89-sdk25-x86.zip /1-逆向环境配置.md: -------------------------------------------------------------------------------- 1 | # 逆向环境配置 2 | 3 | ## 1、JDK 4 | 5 | ## 2、Android Studio 6 | 7 | ## 3、NDK 8 | 9 | ## 4、AndroidKiller 10 | 11 | ## 5、JEB 12 | 13 | 下载地址:[爱盘 - 最新的在线破解工具包 (52pojie.cn)](https://down.52pojie.cn/Tools/Android_Tools/) 14 | 15 | 使用:[(71条消息) 安卓逆向-jeb的安装及使用_bunner_的博客-CSDN博客_jeb安卓逆向](https://blog.csdn.net/bunner_/article/details/112756847) 16 | 17 | 需要保证JDK1.8版本121次更新之前的 18 | 19 | [(71条消息) JEB2.2.7闪退_请输入眤称的博客-CSDN博客_jeb闪退](https://blog.csdn.net/m0_38076341/article/details/117666056?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-117666056-blog-46672733.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-117666056-blog-46672733.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=6) 20 | 21 | ## 6、IDA 22 | 23 | ## 7、winHex 24 | 25 | 下载地址:[WinHex: Hex Editor & Disk Editor, Computer Forensics & Data Recovery Software (x-ways.net)](http://www.x-ways.net/winhex/index-m.html) 26 | 27 | 配置:[(71条消息) winhex基础教程(更新中)_AN-1的博客-CSDN博客_winhex](https://blog.csdn.net/qq_44440816/article/details/109380030) 28 | 29 | -------------------------------------------------------------------------------- /2-APK包结构.md: -------------------------------------------------------------------------------- 1 | # AndroidFrida逆向与抓包实践 2 | 3 | ## 1.1虚拟机环境准备 4 | 5 | 使用Kali Linux: 6 | 7 | [(72条消息) 超详细的最新版 2022.2 kali 安装步骤及拍摄快照的方法_小窦。的博客-CSDN博客_kali最新版](https://blog.csdn.net/weixin_51178129/article/details/126033729) 8 | 9 | 由于官网下载很慢,可以去镜像网站下载 10 | 11 | [Index of /kali-images/kali-2022.3/](http://old.kali.org/kali-images/kali-2022.3/) 12 | 13 | 因为虚拟机本身时区不是东八区,需要重新设置时区。 14 | 15 | 启动虚拟机后,打开terminal,使用如下命令设置时区: 16 | 17 | ```sh 18 | dpkg-reconfigure tzdata 19 | 在弹出的窗口中选择Asia-Shanghai 20 | ``` 21 | 22 | 另外,kali Linux系统中默认不带中文,当打开中文网页或者抓包时,若数据包的数据中有中文,则无法解析,故需要配置kali使其支持中文,执行如下命令: 23 | 24 | ``` 25 | apt update 26 | 27 | apt install xfonts-intl-chinese 28 | 29 | apt install ttf-wqy-microhei 30 | ``` 31 | 32 | 首先,配置好adb环境 33 | 34 | 然后配置python的隔离,这里选用miniconda,因为pyenv工具的最后一步解决依赖包的问题一直很棘手,甚至可能会导致系统桌面环境崩溃 35 | 36 | ``` 37 | wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x84_64.sh 38 | #下载安装脚本 39 | 40 | #赋予安装脚本可执行的权限 41 | chmod +x Miniconda3-latest-Linux-x86_64.sh 42 | 43 | #运行安装脚本 44 | sh Miniconda3-latest-Linux-x86_64.sh 45 | ``` 46 | 47 | 运行最后一条命令后会先要求阅读Lisense阅读完毕后输入yes 48 | 49 | 在提示需要手动执行一次conda init命令时输入yes,才能真正将conda安装成功。 50 | 51 | 安装完后重启一次terminal,执行conda create -n py380 python=3.8.0命令安装指定版本的python,其中py380为安装时conda激活Python3.8.0之后的代称,若想使用特定的python版本,可以使用 52 | 53 | ``` 54 | conda activate py380 55 | ``` 56 | 57 | 来激活对应的版本,在不需要使用该特定版本时,则可执行 58 | 59 | ``` 60 | conda deactivate 61 | ``` 62 | 63 | 命令来退出 64 | 65 | 配置安装htop工具,它是加强版的top工具,htop可以动态查看当前活跃的,系统占用率高的进程 66 | 67 | uptime是开机时间,load average是平均载荷 68 | 69 | 通过 70 | 71 | ``` 72 | apt-get install htop 73 | ``` 74 | 75 | 命令安装 76 | 77 | 在终端中输入htop即可使用。 78 | 79 | 系统网络负载工具jnettop 80 | 81 | 通过 82 | 83 | ``` 84 | apt-get install jnettop 85 | ``` 86 | 87 | 命令下载 88 | 89 | 该测试使用的是Nexus 5X作为测试机,首先打开测试机的设置,连续点击测试机的版本号,进入开发者模式,打开测试机的USB调试。 90 | 91 | 通过在terminal中的`adb devices`命令测试是否连接成功,成功与不成功的截图如下: 92 | 93 | ![](.\image\adbdevices.png) 94 | 95 | 打开Bootloader锁: 96 | 97 | 打开OEM解锁 98 | 99 | 下载镜像包https://developers.google.cn/android/images?h1=zh%3Dcn#bullhead 100 | 101 | 然后刷入Android8.0的系统和TWRP获取root权限,由于magisic是一个假root,所以更推荐获取SuperSU的压缩文件通过twrp刷入,之后下载kali-nethunter的压缩包,通过twrp刷入系统,通过adb shell确认是否获取root权限,可以通过Nethunter的终端运行各种Android原本不支持的Linux命令。 102 | 103 | 如果因为手机太小,可以通过配置SSH,使用xshell去连接具体参看书本《安卓Frida逆向与抓包实战(陈佳林)》 104 | -------------------------------------------------------------------------------- /3-反编译APK文件.md: -------------------------------------------------------------------------------- 1 | # 反编译APK文件 2 | 3 | ## 工具: 4 | 5 | 反编译工具 6 | 7 | 静态分析工具 8 | 9 | 动态调试工具 10 | 11 | 集成分析环境 12 | 13 | ## 1、开发Android程序 14 | 15 | Android项目: 16 | 17 | assets->资源文件 18 | 19 | bin->apk程序 20 | 21 | libs->.jar 22 | 23 | src->存放Java文件 24 | 25 | res->布局文件、图片文件、values文件、字符串资源等 26 | 27 | AndroidManifest.xml配置文件 28 | 29 | 上述的android项目结构较老旧,以新版本为主 30 | 31 | 32 | 33 | 34 | 35 | APK包结构: 36 | 37 | asset:资源目录文件夹|c++游戏引擎和LUA脚本,Unity3D开发的资源文件一般放在这个目录下面 38 | 39 | lib:so库存放的位置|so库文件由NDK文件编译 40 | 41 | META-INF:用于存放签名文件 42 | 43 | res文件夹:资源目录文件|除了音频视频,一般用于Java开发的资源文件都放在这个文件夹下面 44 | 45 | AndroidManifest.xml:基础配置清单 46 | 47 | class.dex:Java曾主要分析文件,它里面全是可执行代码,相当于Windows的PE文件 48 | 49 | resources.arcs:存放一些工程中编译的配置文件,以及索引res目录的一些文件。 50 | 51 | 52 | 53 | 如何安装APP 54 | 55 | 到对应文件目录下,打开cmd 56 | 57 | adb install 电脑上APK的路径 58 | 59 | 也可以直接拖进去安装 60 | 61 | ## 2、分析Android程序 62 | 63 | 将APK文件扩展名修改为.zip后,可以获得一个压缩包,解压缩后获得的是未经过反编译的APK文件压缩包,其中 64 | 65 | Android安装包文件目录 66 | 67 | res->资源文件(布局文件,图片,菜单) 68 | 69 | META-INF->签名文件夹 70 | 71 | resources.arsc->包含一些编译过程中遇到的一些信息,有关资源文件的一些内容 72 | 73 | classes.dex->可执行文件 74 | 75 | AndroidManifesst.xml->配置文件 76 | 77 | 78 | 79 | 反编译APK文件: 80 | 81 | 工具:AndroidKiller 82 | 83 | 对APK文件反编译后 84 | 85 | 在工程信息界面有包名,入口 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /4-Android常用Linux命令介绍.md: -------------------------------------------------------------------------------- 1 | # Android常用Linux命令介绍 2 | 3 | 1、cat命令 4 | 5 | 功能:该命令用于在Shell中方便的查看文本文件的内容。 6 | 7 | 2、echo和touch命令 8 | 9 | 功能:touch命令可以创建一个空文件;echo命令通过配合 “>”或者“>>”对文件进行写操作,其中“>”为覆盖写操作、 “>>”为扩展写操作。 10 | 11 | ``` 12 | echo "123" >> 2.txt 13 | ``` 14 | 15 | 3、grep命令 16 | 17 | 功能:该命令用于在shell中过滤出符合条件的输出。 18 | 19 | ``` 20 | cat 3.txt |grep 123 21 | ``` 22 | 23 | 4、ps命令 24 | 25 | 功能:该命令可输出当前设备正在运行的进程。在Android 8之 后,ps命令只能打印出当前进程,需要加上-e参数才能打印出全部的 进程。 26 | 27 | ``` 28 | ps 29 | ps -e 30 | ``` 31 | 32 | 5、netstat命令 33 | 34 | 功能:该命令输出App连接的IP、端口、协议等网络相关信息, 通常使用的参数组合为-alpe。netstat -alpe用于查看所有sockets连 接的IP和端口以及相应的进程名和pid,配合grep往往有奇效。 35 | 36 | ``` 37 | netstat -alpe | grep org.sfjoldyvukzzlpp 38 | ``` 39 | 40 | 6、lsof命令 41 | 42 | 功能:该命令可以用于查看对应进程打开的文件 43 | 44 | ``` 45 | lsof -p 21312 -l |grep db 46 | ``` 47 | 48 | 7、top命令 49 | 50 | 功能:该命令用于查看当前系统运行负载以及对应进程名和一些 其他的信息,和之前讲的htop作用一样,只是相对来说htop更加人性 化 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /5-Android特有的adb命令介绍.md: -------------------------------------------------------------------------------- 1 | # Android特有的adb命令介绍 2 | 3 | adb(Android Debug Bridge,Android调试桥)是一种功能 多样的命令行工具,可用于与设备进行通信。adb命令可用于执行各 种设备操作。 4 | 5 | 常用的adb命令主要有以下几种。 6 | 7 | (1)adb shell dumpsys activity top 8 | 9 | 功能:查看当前处于前台的Activity。 10 | 11 | ``` 12 | adb shell dumpsys activity top 13 | ``` 14 | 15 | 该命令经常与grep命令一同使用。 16 | 17 | (2)adb shell dumpsys package 18 | 19 | 功能:查看包信息,包括四大组件信息以及MIME等相关信息。 20 | 21 | ``` 22 | adb shell dumpsys d 23 | ``` 24 | 25 | (3)adb shell dbinfo 功能:用于查看App使用的数据库信息,包括执行操作的查询语 句等信息都会被打印出来。 26 | 27 | ``` 28 | adb shell dumpsys dbinfo org.sfjolbyvukzzlpp 29 | ``` 30 | 31 | 与lsof命令打印出来的性质一致。 32 | 33 | (4)adb shell screencap -p 34 | 35 | 功能:用于执行截图操作,并保存到目录下。 36 | 37 | (5)adb shell input text 38 | 39 | 功能:用于在屏幕选中的输入框内输入文字,可惜不能直接输入 中文。 40 | 41 | (6)adb shell pm命令 42 | 43 | 功能:pm命令是Android中packageManager的命令行,是用 于管理package的命令,比如通过pm list packages命令可以列出所 有安装的APK包名。 44 | 45 | ``` 46 | adb shell pm list packages 47 | ``` 48 | 49 | pm install命令用于安装APK文件,只是这里的APK文件不是在主机目录下,而是Android手机目录下。 50 | 51 | ``` 52 | adb shell pm install /sdcard/network-debug.apk 53 | ``` 54 | 55 | pm命令还有很多其他用法,比如pm uninstall命令,用于卸载 Android上的应用。 56 | 57 | (7)adb shell am命令 58 | 59 | 功能:am命令是一个重要的调试工具,主要用于启动或停止服 务、发送广播、启动Activity等。在逆向过程中,往往在需要以 Debug模式启动App时会使用这个命令。 对应命令格式为adb shell am start-activity -D -N <包名>/<类 名>。 60 | 61 | ``` 62 | adb shell am start-activity -D -N com.example.network/.MainActivity 63 | ``` 64 | 65 | 其实以上所有命令也可以通过执行adb shell进入Android的 shell中直接执行,只需要将开始的adb shell去掉就行。以Debug模 式启动App为例,其结果是一样的。 66 | 67 | 还有一些主机和Android交互相关的adb命令,比如: 68 | 69 | **adb install** :这个命令用于将主 机的apk安装到手机上,其中App路径是主机的目录。 70 | 71 | ``` 72 | adb install ./network-debug.apk 73 | ``` 74 | 75 | **adb push和adb pull**:这两个命令用于在主机和Android设备之 间交换文件,前者用于从主机推送文件到Android设备上,后者 用于从Android设备上获取文件到主机中。 76 | 77 | 记得携带获取后存放的路径。 78 | 79 | **adb logcat**:用于查看Android中日志的输出 80 | 81 | -------------------------------------------------------------------------------- /6-AndroidFrida逆向与抓包实践.md: -------------------------------------------------------------------------------- 1 | # AndroidFrida逆向与抓包实践 2 | 3 | ## 1.1虚拟机环境准备 4 | 5 | 使用Kali Linux: 6 | 7 | [(72条消息) 超详细的最新版 2022.2 kali 安装步骤及拍摄快照的方法_小窦。的博客-CSDN博客_kali最新版](https://blog.csdn.net/weixin_51178129/article/details/126033729) 8 | 9 | 由于官网下载很慢,可以去镜像网站下载 10 | 11 | [Index of /kali-images/kali-2022.3/](http://old.kali.org/kali-images/kali-2022.3/) 12 | 13 | 因为虚拟机本身时区不是东八区,需要重新设置时区。 14 | 15 | 启动虚拟机后,打开terminal,使用如下命令设置时区: 16 | 17 | ```sh 18 | dpkg-reconfigure tzdata 19 | 在弹出的窗口中选择Asia-Shanghai 20 | ``` 21 | 22 | 另外,kali Linux系统中默认不带中文,当打开中文网页或者抓包时,若数据包的数据中有中文,则无法解析,故需要配置kali使其支持中文,执行如下命令: 23 | 24 | ``` 25 | apt update 26 | 27 | apt install xfonts-intl-chinese 28 | 29 | apt install ttf-wqy-microhei 30 | ``` 31 | 32 | 首先,配置好adb环境 33 | 34 | 然后配置python的隔离,这里选用miniconda,因为pyenv工具的最后一步解决依赖包的问题一直很棘手,甚至可能会导致系统桌面环境崩溃 35 | 36 | ``` 37 | wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 38 | #下载安装脚本 39 | 40 | #赋予安装脚本可执行的权限 41 | chmod +x Miniconda3-latest-Linux-x86_64.sh 42 | 43 | #运行安装脚本 44 | sh Miniconda3-latest-Linux-x86_64.sh 45 | ``` 46 | 47 | 运行最后一条命令后会先要求阅读Lisense阅读完毕后输入yes 48 | 49 | 在提示需要手动执行一次conda init命令时输入yes,才能真正将conda安装成功。 50 | 51 | 安装完后重启一次terminal,执行conda create -n py380 python=3.8.0命令安装指定版本的python,其中py380为安装时conda激活Python3.8.0之后的代称,若想使用特定的python版本,可以使用 52 | 53 | ``` 54 | conda activate py380 55 | ``` 56 | 57 | 来激活对应的版本,在不需要使用该特定版本时,则可执行 58 | 59 | ``` 60 | conda deactivate 61 | ``` 62 | 63 | 命令来退出 64 | 65 | 配置安装htop工具,它是加强版的top工具,htop可以动态查看当前活跃的,系统占用率高的进程 66 | 67 | uptime是开机时间,load average是平均载荷 68 | 69 | 通过 70 | 71 | ``` 72 | apt-get install htop 73 | ``` 74 | 75 | 命令安装 76 | 77 | 在终端中输入htop即可使用。 78 | 79 | 系统网络负载工具jnettop 80 | 81 | 通过 82 | 83 | ``` 84 | apt-get install jnettop 85 | ``` 86 | 87 | 命令下载 88 | 89 | 该测试使用的是Nexus 5X作为测试机,首先打开测试机的设置,连续点击测试机的版本号,进入开发者模式,打开测试机的USB调试。 90 | 91 | 通过在terminal中的`adb devices`命令测试是否连接成功,成功与不成功的截图如下: 92 | 93 | ![](.\\image\adbdevices.png) 94 | 若图片查看不成功请转至该仓库的Image目录查看adbdevices.png图片 95 | 96 | 打开Bootloader锁: 97 | 98 | 打开OEM解锁 99 | 100 | 下载镜像包https://developers.google.cn/android/images?h1=zh%3Dcn#bullhead 101 | 102 | 然后刷入Android8.0的系统和TWRP获取root权限,由于magisic是一个假root,所以更推荐获取SuperSU的压缩文件通过twrp刷入,之后下载kali-nethunter的压缩包,通过twrp刷入系统,通过adb shell确认是否获取root权限,可以通过Nethunter的终端运行各种Android原本不支持的Linux命令。 103 | 104 | 如果因为手机太小,可以通过配置SSH,使用xshell去连接具体参看书本《安卓Frida逆向与抓包实战(陈佳林)》 105 | -------------------------------------------------------------------------------- /7-Frida环境搭建.md: -------------------------------------------------------------------------------- 1 | # Frida环境搭建 2 | 3 | ## Frida环境搭建 4 | 5 | Frida工作环境搭建十分简单 6 | 7 | 首先在计算机上的虚拟机kali中安装frida环境 8 | 9 | ``` 10 | pip install frida-tools 11 | frida --version 12 | ``` 13 | 14 | 如果需要在测试机上进行测试,还需要在测试机上安装和执行对应版本的server。 15 | 16 | 我们的测试机型是nexus 5X 17 | 18 | 首先执行getprop命令获取系统的架构。 19 | 20 | ``` 21 | getprop ro.product.cpu.abi 22 | ``` 23 | 24 | 一般nexus 5X的架构是arm64-8a 25 | 26 | 我们只需要在Frida的GitHub主页 的release页面 `https://github.com/frida/frida/releases`下载和计算机版本相同的frida server。 27 | 28 | 下载完毕后,将frida-server通过adb工具推送到Android测试机上,在Android中,使用adb push命令推 送文件到data目录一般需要root权限,但是这里有一个例外,那就 是/data/local/tmp目录。所以,frida-server一般会被存放在测试 机的/data/local/tmp/目录下;在将frida-server存放到测试机目录 下后,使用chmod命令赋予frida-server充分的权限,这样frida-server就可以执行了。 29 | 30 | ``` 31 | frida-server-12.2.25-android-arm64.xz 32 | 33 | #下载后的文件为该文件,先对该文件进行解压,解压后再采用adb push命令推送 34 | 35 | adb push frida-server-12.2.25-android-arm64 /data/local/tmp/ 36 | 37 | #先用adb shell 命令进入手机调试模式 38 | 39 | cd /data/local/tmp 40 | 41 | #然后给frida-server文件设置可执行权限使其可以运行 42 | chmod 755 frida-server-12.2.25-android-arm64 43 | 44 | #运行该frida-server文件,注意需要通过root权限去运行 45 | ./frida-server-12.2.25-android-arm64 46 | 47 | #然后再进行端口转发 48 | adb forward tcp:27042 tcp:27042 49 | adb forward tcp:27043 tcp:27043 50 | 51 | #完成后就可以在kali中运行简单的frida命令测试是否安装成功 52 | frida-ps -U 53 | ``` 54 | 55 | ## Frida重启失败的问题 56 | 57 | ``` 58 | Unable to start: Could not listen on address 127.0.0.1, port 27042: Error binding to address 127.0.0.1:27042: Address already in use 59 | ``` 60 | 61 | 通过 `netstat -tunlp`命令查询出端口进程id,然后通过`kill -9 [端口号]`杀死进程 62 | 63 | 64 | 65 | ## Frida 安装及 ERROR: Failed building wheel for frida 错误解决 66 | 67 | ``` 68 | # 方法一: 69 | pip3 install frida-tools -i https://pypi.mirrors.ustc.edu.cn/simple/ 70 | # 方法二: 71 | pip3 install frida -i https://pypi.mirrors.ustc.edu.cn/simple/ 72 | 73 | ``` 74 | 75 | Frida 支持 python2 和 python 3,如果你使用的是较新版本的 Linux 发行版(例如 Ubuntu 18.0.4 / Kali 2019),由于自带 python 版本较高,可能会出现以下情况 ERROR: Failed building wheel for frida 76 | 77 | ``` 78 | ERROR: Command errored out with exit status 1: 79 | command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-ritve202/frida/setup.py'"'"'; __file__='"'"'/tmp/pip-install-ritve202/frida/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-44k2wbq5/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python3.7/frida 80 | cwd: /tmp/pip-install-ritve202/frida/ 81 | Complete output (13 lines): 82 | running install 83 | running build 84 | running build_py 85 | creating build 86 | creating build/lib.linux-x86_64-3.7 87 | creating build/lib.linux-x86_64-3.7/frida 88 | copying frida/__init__.py -> build/lib.linux-x86_64-3.7/frida 89 | copying frida/core.py -> build/lib.linux-x86_64-3.7/frida 90 | running build_ext 91 | looking for prebuilt extension in home directory, i.e. /root/frida-12.8.15-py3.7-linux-x86_64.egg 92 | prebuilt extension not found in home directory, will try downloading it 93 | querying pypi for available prebuilds 94 | error: 95 | 96 | ``` 97 | 98 | 关键错误代码:`i.e. /root/frida-12.8.15-py3.7-linux-x86_64.egg`,也就是说,极有可能是因为缺少相关文件。 99 | 100 | 在 `[frida · PyPI](https://pypi.org/project/frida/#files)`中下载对应的Linux版本`frida-12.8.15-py3.7-linux-x86_64.egg` 101 | 102 | 下载完成后,使用`easy-install`命令安装即可 103 | 104 | ``` 105 | python3 /usr/lib/python3/dist-packages/easy_install.py frida-12.8.16-py3.6-linux-x86_64.egg 106 | ``` 107 | 108 | ## Frida IDE配置 109 | 110 | 在使 用Frida编写脚本时,有Frida的API智能提示功能是非常方便的。 Frida的作者非常体贴地提供了一个让VSCode、PyCharm等IDE支 持Frida的API智能提示的方式,具体配置方法如下: 111 | 112 | ``` 113 | curl -sL https://deb.nodesource.com/setup_18.x |bash - 114 | 115 | apt-get install -y nodejs 116 | 117 | node -v 118 | npm -v 119 | 120 | git clone https://github.com/oleavr/frida-agent-example.git 121 | 122 | cd frida-agent-example/ 123 | 124 | npm install 125 | ``` 126 | 127 | 最后在kali中安装Vscode 128 | 129 | sudo apt update 130 | sudo apt install curl gpg software-properties-common apt-transport-https 131 | 132 | 导入Microsoft GPG密钥到您的Kali Linux: 133 | 134 | curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - 135 | 136 | echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" | sudo tee /etc/apt/sources.list.d/vscode.list 137 | 138 | sudo apt update 139 | sudo apt install code 140 | 即可安装成功。 141 | -------------------------------------------------------------------------------- /8-Frida脚本入门.md: -------------------------------------------------------------------------------- 1 | # Frida脚本入门 2 | 3 | ## 0x01 Frida脚本的基本概念 4 | 5 | Frida脚本就是利用Frida动态插桩框架,使用Frida导出的API和方法对内存控件里的对象方法进行监视,修改或者替换的一段代码。 6 | 7 | Frida的API是用JavaScript实现的,所以可以充分利用JavaScript的匿名函数优势以及大量的Hook(钩子函数)和回调函数API。 8 | 9 | 10 | 11 | Frida版本的helloWorld 12 | 13 | ```js 14 | setTimeout( 15 | function(){ 16 | Java.perform(function(){ 17 | console.log("hello world") 18 | }) 19 | } 20 | ) 21 | ``` 22 | 23 | 首先我们把一个匿名函数作 为参数传给了setTimeout()函数,在这个匿名函数体中调用了Frida 的API函数Java.perform(),这个API函数本身又接受了一个匿名函数 作为参数,该匿名函数最终调用console.log()函数来打印一个"Hello world!"字符串。这里需要调用setTimeout()方法是因为该方法将函 数注册到JavaScript运行库中,然后在JavaScript运行库中调用 Java.perform()方法将函数注册到App的Java运行库中并在其中执行 该函数。 24 | 25 | 在手机上将FridaServer运行起来 26 | 27 | 需要将手机调整至开发者模式,USB连接 28 | 29 | 同时为了确认是否建立连接。可以通过frida-ps -U命令查看移动设备上正在运行的进程。 30 | 31 | ```sh 32 | abd shell 33 | cd /data/local/tmp 34 | ./frida-server-15.2.2-android-arm64 35 | ``` 36 | 37 | 在写好的脚本的目录中。通过以下命令可以以attach模式注入指定应用 38 | 39 | ```shell 40 | frida -U -l hello_world.js android.process.media 41 | ``` 42 | 43 | > ps:手机上的Frida-server需要与计算机上的Frida版本保持一致,否则会报错。 44 | 45 | ## 0x02 Java层Hook基础 46 | 47 | ### 1 载入类 48 | 49 | Java.use方法用于加载一个Java类,相当于Java中的`Class.forName()`。比如要加载一个String类: 50 | 51 | ``` 52 | var StringClass = Java.use("java.lang.String"); 53 | ``` 54 | 55 | 加载内部类: 56 | 57 | ``` 58 | var MyClass_InnerClass = Java.use("com.luoyesiqiu.MyClass$InnerClass"); 59 | ``` 60 | 61 | 其中InnerClass是MyClass的内部类 62 | 63 | ### 2 修改函数的实现 64 | 65 | 修改一个函数的实现是逆向调试中相当有用的。修改一个函数的实现后,如果这个函数被调用,我们的Javascript代码里的函数实现也会被调用。 66 | 67 | #### 2.1 函数参数类型表示 68 | 69 | 1. 对于基本类型,直接用它在Java中的表示方法就可以了,不用改变,例如: 70 | 71 | - int 72 | - short 73 | - char 74 | - byte 75 | - boolean 76 | - float 77 | - double 78 | - long 79 | 80 | 1. 基本类型数组,用左中括号接上基本类型的缩写 81 | 82 | 基本类型缩写表示表: 83 | 84 | | 基本类型 | 缩写 | 85 | | -------- | ---- | 86 | | boolean | Z | 87 | | byte | B | 88 | | char | C | 89 | | double | D | 90 | | float | F | 91 | | int | I | 92 | | long | J | 93 | | short | S | 94 | 95 | 例如:`int[]`类型,在重载时要写成`[I` 96 | 97 | 1. 任意类,直接写完整类名即可 98 | 99 | 例如:`java.lang.String` 100 | 101 | 1. 对象数组,用左中括号接上完整类名再接上分号 102 | 103 | 例如:`[java.lang.String;` 104 | 105 | #### 2.2 带参数的构造函数 106 | 107 | 修改参数为byte[]类型的构造函数的实现 108 | 109 | ```php 110 | ClassName.$init.overload('[B').implementation=function(param){ 111 | //do something 112 | } 113 | ``` 114 | 115 | > 注:ClassName是使用Java.use定义的类;param是可以在函数体中访问的参数 116 | 117 | 修改多参数的构造函数的实现 118 | 119 | ```php 120 | ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){ 121 | //do something 122 | } 123 | ``` 124 | 125 | #### 2.3 无参数构造函数 126 | 127 | ```php 128 | ClassName.$init.overload().implementation=function(){ 129 | //do something 130 | } 131 | ``` 132 | 133 | 调用原构造函数 134 | 135 | ```kotlin 136 | ClassName.$init.overload().implementation=function(){ 137 | //do something 138 | this.$init(); 139 | //do something 140 | } 141 | ``` 142 | 143 | > 注意:当构造函数(函数)有多种重载形式,比如一个类中有两个形式的func:`void func()`和`void func(int)`,要加上overload来对函数进行重载,否则可以省略overload 144 | 145 | #### 2.4 一般函数 146 | 147 | 修改函数名为func,参数为byte[]类型的函数的实现 148 | 149 | ```javascript 150 | ClassName.func.overload('[B').implementation=function(param){ 151 | //do something 152 | //return ... 153 | } 154 | ``` 155 | 156 | #### 2.5 无参数的函数 157 | 158 | ```delphi 159 | ClassName.func.overload().implementation=function(){ 160 | //do something 161 | } 162 | ``` 163 | 164 | > 注: 在修改函数实现时,如果原函数有返回值,那么我们在实现时也要返回合适的值 165 | 166 | ```javascript 167 | ClassName.func.overload().implementation=function(){ 168 | //do something 169 | return this.func(); 170 | } 171 | ``` 172 | 173 | ### 3. 调用函数 174 | 175 | 和Java一样,创建类实例就是调用构造函数,而在这里用`$new`表示一个构造函数。 176 | 177 | ```php 178 | var ClassName=Java.use("com.luoye.test.ClassName"); 179 | var instance = ClassName.$new(); 180 | ``` 181 | 182 | 实例化以后调用其他函数 183 | 184 | ```go 185 | var ClassName=Java.use("com.luoye.test.ClassName"); 186 | var instance = ClassName.$new(); 187 | instance.func(); 188 | ``` 189 | 190 | ### 4. 字段操作 191 | 192 | 字段赋值和读取要在字段名后加`.value`,假设有这样的一个类: 193 | 194 | ```java 195 | package com.luoyesiqiu.app; 196 | public class Person{ 197 | private String name; 198 | private int age; 199 | } 200 | ``` 201 | 202 | 写个脚本操作Person类的name字段和age字段: 203 | 204 | ```php 205 | var person_class = Java.use("com.luoyesiqiu.app.Person"); 206 | //实例化Person类 207 | var person_class_instance = person_class.$new(); 208 | //给name字段赋值 209 | person_class_instance.name.value = "luoyesiqiu"; 210 | //给age字段赋值 211 | person_class_instance.age.value = 18; 212 | //输出name字段和age字段的值 213 | console.log("name = ",person_class_instance.name.value, "," ,"age = " ,person_class_instance.age.value); 214 | ``` 215 | 216 | 输出: 217 | 218 | ```ini 219 | name = luoyesiqiu , age = 18 220 | ``` 221 | 222 | ### 5. 类型转换 223 | 224 | 用`Java.cast`方法来对一个对象进行类型转换,如将`variable`转换成`java.lang.String`: 225 | 226 | ```php 227 | var StringClass=Java.use("java.lang.String"); 228 | var NewTypeClass=Java.cast(variable,StringClass); 229 | ``` 230 | 231 | ### 6. Java.available字段 232 | 233 | 这个字段标记Java虚拟机(例如: Dalvik 或者 ART)是否已加载, 操作Java任何东西之前,要确认这个值是否为true 234 | 235 | ### 7. Java.perform方法 236 | 237 | Java.perform(fn)在Javascript代码成功被附加到目标进程时调用,我们核心的代码要在里面写。格式: 238 | 239 | ```javascript 240 | Java.perform(function(){ 241 | //do something... 242 | }); 243 | ``` 244 | 245 | 246 | 247 | ### 举例 248 | 249 | 首先编写一个简单的APP用于练习,APP的MainActivity代码如下: 250 | 251 | > 注意:创建工程的路径在/root/AndroidStudioProjects/reverseDemo1 252 | 253 | ```java 254 | package com.example.reversedemo; 255 | 256 | import androidx.appcompat.app.AppCompatActivity; 257 | 258 | import android.os.Bundle; 259 | import android.util.Log; 260 | 261 | public class MainActivity extends AppCompatActivity { 262 | 263 | @Override 264 | protected void onCreate(Bundle savedInstanceState) { 265 | super.onCreate(savedInstanceState); 266 | setContentView(R.layout.activity_main); 267 | while(true){ 268 | try { 269 | Thread.sleep(1000); 270 | } catch (InterruptedException e) { 271 | e.printStackTrace(); 272 | } 273 | fun(50,20); 274 | } 275 | } 276 | void fun(int x,int y){ 277 | Log.d("reverseDemo", String.valueOf(x+y)); 278 | } 279 | } 280 | ``` 281 | 282 | 该APP逻辑为每次间隔一年在logcat中打印出fun(50,30)函数的运行结果,最终结果显示在控制台中。 283 | 284 | ``` 285 | adb logcat |grep reverseDemo 286 | ``` 287 | 288 | 以下是编写的Frida脚本,目标是编写Hook fun()函数并打印出fun()函数的参数值。Frida脚本的最终代码如下所示: 289 | 290 | ```js 291 | function main(){ 292 | console.log("Script loaded successfully") 293 | Java.perform(function(){ 294 | console.log("Inside java perform function") 295 | var MainActivity = Java.use('com.example.reverseDemo1.MainActivity') 296 | MainActivity.fun.implementation = function(x,y){ 297 | console.log("x=>",x,"y=>",y) 298 | var ret_value = this.fun(x,y) 299 | return ret_value 300 | } 301 | }) 302 | } 303 | 304 | setImmediate(main) 305 | ``` 306 | 307 | 通过如下命令使用Frida的CLI模式以attach模式注入APP 308 | 309 | ``` 310 | frida -U -l demo1.js reverseDemo1 311 | ``` 312 | 313 | 该脚本使用function关键字定义了一个main()函数,用于存放Hook脚本,然后调用Frida的API函数Java.perform()将脚本中的内容注入到Java运行库,注意,这个API参数是一个匿名函数,函数内容是健康空和修改Java函数逻辑的主体内容。注意这里的Java.perform()函数非常重要,任何对APP中Java层的操作都必须包裹在这个函数中,否则Frida运行起来就会报错。 314 | 315 | 在Java.perform()函数包裹的匿名函数中,首先调用了Frida的 API函数Java.use(),这个函数的参数是Hook的函数所在类的类名, 参数的类型是一个字符串类型,比如Hook的fun()函数所在类的全名 为com.roysue.demo02.MainActivity,那么传递给这个函数的参数 就是"com.roysue.demo02.MainActivity"。这个函数的返回值动态 地为相应Java类获取一个JavaScript Wrapper,可以通俗地理解为 一个JavaScript对象。 在获取到对应的JavaScript对象后,通过“.”符号连接fun这个 对 应 的 函 数 名 , 然 后 加 上 implementation 关 键 词 表 示 实 现 MainActivity对象的fun()函数,最后通过“=”这个符号连接一个匿 名函数,参数内容和原Java的内容一致。不同的是,JavaScript是一 个弱类型的语言,不需要指明参数类型。此时一个针对MainActivity 类的fun()函数的Hook框架就完成了。 这个匿名函数的内容取决于逆向开发和分析人员想修改这个被 Hook的函数的哪些运行逻辑。比如调用console.log()函数把参数内容 打印出来,通过this.fun()函数再次调用原函数,并把原本的参数传递 给这个fun()函数。简而言之,就是重新执行原函数的内容,最后将这 个函数的返回值直接通过return指令返回。 在Hook一个函数时,还有一个地方需要注意,那就是最好不要 修改被Hook的函数的返回值类型,否则可能会引起程序崩溃等问 题,比如直接通过调用原函数将原函数的返回值返回。 当然,也可以传递不同的参数,简单修改程序逻辑即可。 316 | 317 | 当我们对APP增加一些新功能,修改代码如下: 318 | 319 | ```java 320 | package com.example.reversedemo1; 321 | 322 | import androidx.appcompat.app.AppCompatActivity; 323 | 324 | import android.os.Bundle; 325 | import android.util.Log; 326 | 327 | import java.util.Locale; 328 | 329 | public class MainActivity extends AppCompatActivity { 330 | 331 | @Override 332 | protected void onCreate(Bundle savedInstanceState) { 333 | super.onCreate(savedInstanceState); 334 | setContentView(R.layout.activity_main); 335 | while(true){ 336 | try { 337 | Thread.sleep(1000); 338 | } catch (InterruptedException e) { 339 | e.printStackTrace(); 340 | } 341 | fun(50,20); 342 | Log.d("reverseDemo.string", fun("LowerRcAse Me!!!")); 343 | } 344 | } 345 | void fun(int x,int y){ 346 | Log.d("reverseDemo", String.valueOf(x+y)); 347 | } 348 | String fun(String x){ 349 | return x.toLowerCase(); 350 | } 351 | } 352 | 353 | ``` 354 | 355 | fun函数有了重载,当参数是两个int类型的情况下返回两个整数之和,当参数为String类型时,返回字符串的 小写形式。如果使用之前的脚本,会报错如下: 356 | 357 | ``` 358 | Error: fun(): has more than one overload, use .overload() to choose from: 359 | .overload('java.lang.String') 360 | .overload('int', 'int') 361 | at X (frida/node_modules/frida-java-bridge/lib/class-factory.js:569) 362 | at K (frida/node_modules/frida-java-bridge/lib/class-factory.js:564) 363 | at set (frida/node_modules/frida-java-bridge/lib/class-factory.js:932) 364 | at (/frida/repl-2.js:6) 365 | at (frida/node_modules/frida-java-bridge/lib/vm.js:12) 366 | at _performPendingVmOps (frida/node_modules/frida-java-bridge/index.js:250) 367 | at (frida/node_modules/frida-java-bridge/index.js:225) 368 | at (frida/node_modules/frida-java-bridge/lib/vm.js:12) 369 | at _performPendingVmOpsWhenReady (frida/node_modules/frida-java-bridge/index.js:244) 370 | at perform (frida/node_modules/frida-java-bridge/index.js:204) 371 | at changeArgs (/frida/repl-2.js:12) 372 | at apply (native) 373 | at (frida/runtime/core.js:51)[Nexus 5X::reverseDemo1 ]-> 374 | 375 | ``` 376 | 377 | 这是函数的重载导致Frida不知道具体应该Hook哪 个 函 数 而 出 现 的 问 题 。 其 实 Frida 已 经 提 供 了 解 决 方 案 ( use .overload() ) , 就 是 指 定 函 数 签 名 , 将 报 错 中 的.overload('java.lang.String')或者.overload('int', 'int')添加到要 Hook的函数名后、关键词implementation之前。当然,相应的参数 和具体函数逻辑也得修改 378 | 379 | 具体代码如下: 380 | 381 | ```js 382 | function changeArgs(){ 383 | console.log("Script loaded successfully") 384 | Java.perform(function(){ 385 | console.log("Inside java perform function") 386 | var MainActivity = Java.use("com.example.reversedemo1.MainActivity") 387 | console.log("Java.Use.successfully")//定位类成功 388 | MainActivity.fun.overload('int','int').implementation= function(x,y){ 389 | console.log("x=>",x,"y=>",y) 390 | var ret_value = this.fun(2,5) 391 | return ret_value 392 | } 393 | }) 394 | } 395 | 396 | setImmediate(changeArgs) 397 | ``` 398 | 399 | ## 0x03 Java层主动调用 400 | 401 | ### 1、主动调用与被动调用 402 | 403 | 主动调用就是强制调用一个函数去执行。相对地,被动调用是由 App按照正常逻辑去执行函数,函数的执行完全依靠与用户交互完成 程序逻辑进而间接调用到关键函数,而主动调用则可以直接调用关键 函数,主动性更强,甚至可以直接完成关键数据的“自吐”。在逆向 分析过程中,如果不想分析详细的算法逻辑,可以直接通过主动传递 参数来调用关键算法函数,忽略方法函数的实现过程直接得到密文或 者明文,可以说这是各种算法调用的“克星”。 404 | 405 | ### 2、类方法与实例方法的主动调用 406 | 407 | 在Java中,类中的函数可分为两种:类函数和实例方法。通俗地 讲,就是静态的方法和动态的方法。类函数使用关键字static修饰, 和对应类是绑定的,如果类函数还被public关键词修饰着,在外部就 可以直接通过类去调用;实例方法则没有关键字static修饰,在外部 只能通过创建对应类的实例再通过这个实例去调用。在Frida中主动 调用的类型会根据方法类型区分开。如果是类函数的主动调用,直接 使用Java.use()函数找到类进行调用即可;如果是实例方法的主动调 用,则需要在找到对应的实例后对方法进行调用。这里用到了Frida 中非常重要的一个API函数Java.choose(),这个函数可以在Java的堆 中寻找指定类的实例。 408 | 409 | 首先,修改之前的APP MainActivity中的内容 410 | 411 | ```java 412 | package com.example.reversedemo1; 413 | 414 | import androidx.appcompat.app.AppCompatActivity; 415 | 416 | import android.os.Bundle; 417 | import android.util.Log; 418 | 419 | import java.util.Locale; 420 | 421 | public class MainActivity extends AppCompatActivity { 422 | 423 | @Override 424 | protected void onCreate(Bundle savedInstanceState) { 425 | super.onCreate(savedInstanceState); 426 | setContentView(R.layout.activity_main); 427 | while(true){ 428 | try { 429 | Thread.sleep(1000); 430 | } catch (InterruptedException e) { 431 | e.printStackTrace(); 432 | } 433 | fun(50,20); 434 | Log.d("reverseDemo.string", fun("LowerRcAse Me!!!")); 435 | } 436 | } 437 | void fun(int x,int y){ 438 | Log.d("reverseDemo", String.valueOf(x+y)); 439 | } 440 | String fun(String x){ 441 | return x.toLowerCase(); 442 | } 443 | void secret(){ 444 | Log.d("reverseDemo.secret", "secret: this is secret func"); 445 | } 446 | static void staticSecret(){ 447 | Log.d("reverseDemo.Secret", "this is staticSecret func"); 448 | } 449 | } 450 | 451 | ``` 452 | 453 | 在这个App中,两个新加的函数并没有被调用。接下 来,我们完成两个隐藏函数的主动调用,js脚本代码如下: 454 | 455 | ```js 456 | function demo3(){ 457 | console.log("Script loaded successfully") 458 | Java.perform(function(){ 459 | console.log("Inside java perform function") 460 | 461 | //静态函数主动调用 462 | var MainActivity = Java.use("com.example.reversedemo1.MainActivity") 463 | MainActivity.staticSecret() 464 | 465 | //动态方法主动调用 466 | Java.choose("com.example.reversedemo1.MainActivity",{ 467 | onMatch: function(instance){ 468 | console.log("instance found",instance) 469 | instance.secret() 470 | }, 471 | onComplete:function(){ 472 | console.log('search Complete') 473 | } 474 | }) 475 | 476 | }) 477 | } 478 | 479 | setImmediate(demo3) 480 | ``` 481 | 482 | 可以发现静态的staticSecret()函数 和Hook时使用的方式大同小异,都是使用Java.use这个API去获取 MainActivity类,在获取对应的类对象后通过“.”连接符连接 staticSecret方法名,最终以和Java中一样的方式直接调用静态方法 staticSecret()函数;动态方法secret需要先通过Java.choose这个 API从内存中获取相应类的实例对象,然后才能通过这个实例对象去 调用动态的secret()函数。如果使用“MainActivity.secret();”方式 (和staticSecret()函数一样)调用,那么会报错。 483 | 484 | 当将脚本注入APP后,APP运行的日志中就可以发现两个secret函数都已经执行了。 485 | 486 | ## 0x04 RPC及其自动化 487 | 488 | 在Frida中,可以使用python完成JavaScript脚本对进程的注入以及相应的Hook。 489 | 490 | 首先修改之前的MainActivity类中的代码 491 | 492 | ```java 493 | package com.example.reversedemo1; 494 | 495 | import androidx.appcompat.app.AppCompatActivity; 496 | 497 | import android.os.Bundle; 498 | import android.util.Log; 499 | 500 | import java.util.Locale; 501 | 502 | public class MainActivity extends AppCompatActivity { 503 | private String total = "hello"; 504 | @Override 505 | protected void onCreate(Bundle savedInstanceState) { 506 | super.onCreate(savedInstanceState); 507 | setContentView(R.layout.activity_main); 508 | while(true){ 509 | try { 510 | Thread.sleep(1000); 511 | } catch (InterruptedException e) { 512 | e.printStackTrace(); 513 | } 514 | fun(50,20); 515 | Log.d("reverseDemo.string", fun("LowerRcAse Me!!!")); 516 | } 517 | } 518 | void fun(int x,int y){ 519 | Log.d("reverseDemo", String.valueOf(x+y)); 520 | } 521 | String fun(String x){ 522 | return x.toLowerCase(); 523 | } 524 | void secret(){ 525 | total+=" secretFunc"; 526 | Log.d("reverseDemo.secret", "secret: this is secret func"); 527 | } 528 | static void staticSecret(){ 529 | Log.d("reverseDemo.Secret", "this is staticSecret func"); 530 | } 531 | } 532 | 533 | ``` 534 | 535 | 这次脚本的目的时获取total这个实例变量的值。 536 | 537 | Java中的变量也存在是否使用static修饰的区别。如果使用static修饰,可以直接通过类进行获取;实例变量,不适用static修饰,和特定的对象绑在一起。 538 | 539 | ```js 540 | function CallSecretFunc(){ 541 | console.log("Script loaded successfully") 542 | Java.perform(function(){ 543 | console.log("Inside java perform function") 544 | 545 | //动态方法主动调用 546 | Java.choose("com.example.reversedemo1.MainActivity",{ 547 | onMatch: function(instance){ 548 | console.log("instance found",instance) 549 | instance.secret() 550 | }, 551 | onComplete:function(){ 552 | console.log('search Complete') 553 | } 554 | }) 555 | 556 | }) 557 | } 558 | function getTotalValue(){ 559 | Java.perform(function(){ 560 | var MainActivity = Java.use("com.example.reversedemo1.MainActivity") 561 | Java.choose("com.example.reversedemo1.MainActivity",{ 562 | onMatch: function(instance){ 563 | console.log("total value= ",instance.total.value) 564 | instance.secret() 565 | }, 566 | onComplete:function(){ 567 | console.log('search Complete') 568 | } 569 | }) 570 | 571 | }) 572 | } 573 | 574 | setImmediate(getTotalValue) 575 | ``` 576 | 577 | 编写的python RPC脚本如下: 578 | 579 | ```python 580 | import frida,sys 581 | def on_message(message,data): 582 | if message['type']=='send': 583 | print("[*]{0}".format(message['payload'])) 584 | else: 585 | print(message) 586 | 587 | device=frida.get_usb_device() 588 | process=device.attach("com.example.reversedemo1") 589 | with open('callSecret.js') as f: 590 | jscode=f.read() 591 | script = process.create_script(jscode) 592 | script.on('message',on_message()) 593 | script.load() 594 | 595 | command="" 596 | while 1==1: 597 | command=input("\nEnter command:\n1:Exit\n2:Call secret function\n3:Get Total Value\nchoice:") 598 | if command=="1": 599 | break 600 | elif command=="2": 601 | script.exports.callsecretfunc() 602 | elif command=="3": 603 | script.exports.gettotalvalue() 604 | ``` 605 | 606 | -------------------------------------------------------------------------------- /9-Objection入门.md: -------------------------------------------------------------------------------- 1 | # Objection入门 2 | 3 | ## 0x01 Objection介绍 4 | 5 | Frida时提供的各种API基础之上可以实现无数的具体功能,Objection时一个将各种常用功能整合进工具中提供我们直接在命令行使用的利器,Objection甚至可以不写一行代码就能进行APP的逆向分析。 6 | 7 | Objection集成的功能主要支持Android和iOS两大移动平台。在 对Android的支持中,Objection可以快速完成诸如内存搜索、类和 模块搜索、方法Hook以及打印参数、返回值、调用栈等常用功能, 是一个非常方便的逆向必备工具和内存漫游神器。 8 | 9 | Objection主要有三大组成部分。 10 | 11 | 第一部分是指Objection重打包的相关组件。Objection可以将 Frida运行时所需要的frida-gadget.so重打包进App中,从而完成 Frida的无root调试。 12 | 13 | 第二部分是指Objection本身。Objection是一个Python的pypi 包,可以和包含frida-gadget.so这个so文件的App进行交互,运行 Frida的Hook脚本,并分析Hook的结果。 14 | 15 | 第三部分是指Objection从TypeScript项目编译而成的一个 agent.js文件。该文件在App运行过程中插了Frida运行库,使得 Objection支持的所有功能成为可能。 16 | 17 | Objection依托Frida完成了对应用的注入以及对函 数的Hook模板,使用时只需要将具体的类填充进去即可完成相应的 Hook测试,是一个非常好用的逆向工具。 18 | 19 | 20 | 21 | ## 0x02 Objection安装与使用 22 | 23 | ### 1、Objection的运行条件 24 | 25 | (1)、python的版本必须大于3.4 26 | 27 | 可以通过以下命令确认python的版本: 28 | 29 | ```sh 30 | python -V 31 | ``` 32 | 33 | (2)、Python包的管理软件pip的版本大于9.0。可通过pip查看版本的命令来确认版本: 34 | 35 | ```shell 36 | pip --version 37 | ``` 38 | 39 | ### 2、安装 40 | 41 | 使用pip命令安装Objection 42 | 43 | 官网的建议时直接执行以下命令 44 | 45 | ```sh 46 | pip3 install -U objection 47 | ``` 48 | 49 | 但是由于Frida的版本更新过快,不同版本的Frida支持的版本库可能会不同,所以尽量选择Objection在Frida相应版本发布后更新的版本,且发布版本实践应当尽量与Frida相应版本靠近。可以通过pypi官网查看Objection不同版本的发布时间,同时使用GitHub Frida的仓库查看相应的Frida发布时间进行对比确认。 50 | 51 | 我使用的时Frida的 15.2.2是2022年 Jul 22,最近的Objection版本时2021年的Apr 6。 52 | 53 | 那么先尝试一下能不能用。 54 | 55 | ### 3、使用 56 | 57 | Objection默认通过USB连接设备,这里不必和 Frida的命令行一样通过-U参数指定USB模式连接,同时主要通过-g 参数指定注入的进程并通过explore命令进入REPL模式。在进入 REPL模式后便可以使用Objection进行Hook的常用命令,这也是本 章中所要介绍的重点。 58 | 59 | Objection支持通过-N参数来指定网络中 的设备并通过-h参数和-p参数来指定对应设备的IP和端口以进行连 接,从而完成对网络设备的注入与Hook。除此之外还可以通过 patchapk命令将frida-gadget.so打包进App等。 60 | 61 | 以Android系统的基本应用“设置”为例来介绍Objection 的REPL模式常用命令。首先,在确认手机使用USB线连接上手机 后,运行相应版本的frida-server,运行“设置”应用以通过frida-ps 找到对应的App及其包名,具体操作如下: 62 | 63 | ``` 64 | frida-ps -U|grep setting 65 | ``` 66 | 67 | 但是在我的测试机中没有反应,通过 68 | 69 | ``` 70 | frida-ps -U 71 | ``` 72 | 73 | 命令发现设置应用的进程名就是设置,通过objection注入设置应用,注入成功后便进入了Objection的REPL界面,具体操作命令和结果如下: 74 | 75 | ```sh 76 | objection -g 设置 explore 77 | ``` 78 | 79 | 80 | 81 | ```sh 82 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 83 | └─# objection -g 设置 explore 84 | Using USB device `Nexus 5X` 85 | Agent injected and responds ok! 86 | 87 | _ _ _ _ 88 | ___| |_|_|___ ___| |_|_|___ ___ 89 | | . | . | | -_| _| _| | . | | 90 | |___|___| |___|___|_| |_|___|_|_| 91 | |___|(object)inject(ion) v1.11.0 92 | 93 | Runtime Mobile Exploration 94 | by: @leonjza from @sensepost 95 | 96 | [tab] for command suggestions 97 | com.android.settings on (google: 8.1.0) [usb] # 98 | 99 | ``` 100 | 101 | 在学习Objection的REPL界面命令之前,首先要了解空格键的作 用。在Objection REPL界面中,当不知道命令时通过按空格键就会 提示可用的命令,在出现提示后通过上下选择键及回车键便可以输入 命令 102 | 103 | ### 4、命令学习 104 | 105 | #### 4.1 help命令 106 | 107 | 不知道当前命令的效果是什么时,在当前命令 前加help(比如help env)再回车之后就会出现当前命令的解释信息 108 | 109 | #### 4.2 jobs命令 110 | 111 | 作业系统很好用,用于查看和管理当前所执行 Hook的任务,建议一定要掌握,可以同时运行多项Hook作业。 112 | 113 | #### 4.3 Frida命令 114 | 115 | Frida命令。查看Frida相关信息 116 | 117 | Frida版本过高会导致打印相关信息报错。可以考虑对Frida进行降版本处理。 118 | 119 | #### 4.4 内存漫游相关命令 120 | 121 | Objection可以快速便捷地打印出内存 中的各种类的相关信息,这对App快速定位有着无可比拟的优势,下 面介绍几个常用命令。 122 | 123 | ①可以使用以下命令列出内存中的所有类: 124 | 125 | ```sh 126 | android hooking list classes 127 | ``` 128 | 129 | ```sh 130 | com.android.settings on (google: 8.1.0) [usb] # android hooking list classes 131 | [B 132 | [C 133 | [D 134 | [F 135 | [I 136 | [J 137 | [Landroid.animation.Animator; 138 | [Landroid.animation.Keyframe$FloatKeyframe; 139 | [Landroid.animation.Keyframe$IntKeyframe; 140 | [Landroid.animation.PropertyValuesHolder; 141 | [Landroid.app.FragmentState; 142 | [Landroid.app.LoaderManagerImpl; 143 | [Landroid.bluetooth.BluetoothCodecConfig; 144 | [Landroid.content.pm.ActivityInfo; 145 | [Landroid.content.pm.ConfigurationInfo; 146 | [Landroid.content.pm.FeatureGroupInfo; 147 | [Landroid.content.pm.FeatureInfo; 148 | [Landroid.content.pm.InstrumentationInfo; 149 | [Landroid.content.pm.PathPermission; 150 | [Landroid.content.pm.PermissionInfo; 151 | ........ 152 | ........ 153 | sun.util.locale.LocaleSyntaxException 154 | sun.util.locale.LocaleUtils 155 | sun.util.locale.ParseStatus 156 | sun.util.locale.StringTokenIterator 157 | sun.util.logging.LoggingProxy 158 | sun.util.logging.LoggingSupport 159 | sun.util.logging.LoggingSupport$1 160 | sun.util.logging.PlatformLogger 161 | sun.util.logging.PlatformLogger$1 162 | sun.util.logging.PlatformLogger$Level 163 | void 164 | 165 | Found 5986 classes 166 | ``` 167 | 168 | ②可以使用以下命令在内存中所有已加载的类中搜索包含特定关 键词的类 169 | 170 | ```sh 171 | android hooking search classes 关键词 172 | ``` 173 | 174 | ```sh 175 | com.android.settings on (google: 8.1.0) [usb] # android hooking search classes display 176 | Note that Java classes are only loaded when they are used, so if the expected class has not been found, it might not have been loaded yet. 177 | [Landroid.icu.text.DisplayContext$Type; 178 | [Landroid.icu.text.DisplayContext; 179 | [Landroid.view.Display$Mode; 180 | android.hardware.display.DisplayManager 181 | android.hardware.display.DisplayManager$DisplayListener 182 | android.hardware.display.DisplayManagerGlobal 183 | android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate 184 | android.hardware.display.DisplayManagerGlobal$DisplayManagerCallback 185 | android.hardware.display.IDisplayManager 186 | android.hardware.display.IDisplayManager$Stub 187 | android.hardware.display.IDisplayManager$Stub$Proxy 188 | android.hardware.display.IDisplayManagerCallback 189 | android.hardware.display.IDisplayManagerCallback$Stub 190 | android.hardware.display.WifiDisplay$1 191 | android.hardware.display.WifiDisplaySessionInfo$1 192 | android.hardware.display.WifiDisplayStatus$1 193 | android.icu.impl.CurrencyData$CurrencyDisplayInfo 194 | android.icu.impl.CurrencyData$CurrencyDisplayInfoProvider 195 | android.icu.impl.ICUCurrencyDisplayInfoProvider 196 | android.icu.impl.ICUCurrencyDisplayInfoProvider$ICUCurrencyDisplayInfo 197 | android.icu.impl.ICUCurrencyDisplayInfoProvider$ICUCurrencyDisplayInfo$SpacingInfoSink 198 | android.icu.text.CurrencyDisplayNames 199 | android.icu.text.DisplayContext 200 | android.icu.text.DisplayContext$Type 201 | android.media.MediaRouter$WifiDisplayStatusChangedReceiver 202 | android.media.RemoteDisplay 203 | android.opengl.EGLDisplay 204 | android.support.v14.preference.PreferenceFragment$OnPreferenceDisplayDialogCallback 205 | android.support.v7.preference.PreferenceManager$OnDisplayPreferenceDialogListener 206 | android.util.DisplayMetrics 207 | android.view.Choreographer$FrameDisplayEventReceiver 208 | android.view.Display 209 | android.view.Display$HdrCapabilities 210 | android.view.Display$HdrCapabilities$1 211 | android.view.Display$Mode 212 | android.view.Display$Mode$1 213 | android.view.DisplayAdjustments 214 | android.view.DisplayEventReceiver 215 | android.view.DisplayInfo 216 | android.view.DisplayInfo$1 217 | android.view.DisplayListCanvas 218 | android.view.SurfaceControl$PhysicalDisplayInfo 219 | com.android.internal.app.NightDisplayController 220 | com.android.internal.app.NightDisplayController$1 221 | com.android.internal.app.NightDisplayController$Callback 222 | com.android.internal.hardware.AmbientDisplayConfiguration 223 | com.android.settings.DisplaySettings 224 | com.android.settings.DisplaySettings$1 225 | com.android.settings.Settings$AmbientDisplayPickupSuggestionActivity 226 | com.android.settings.Settings$AmbientDisplaySuggestionActivity 227 | com.android.settings.Settings$DisplaySettingsActivity 228 | com.android.settings.Settings$NightDisplaySettingsActivity 229 | com.android.settings.Settings$NightDisplaySuggestionActivity 230 | com.android.settings.Settings$WifiDisplaySettingsActivity 231 | com.android.settings.dashboard.conditional.NightDisplayCondition 232 | com.android.settings.display.DensityPreference 233 | com.android.settings.display.NightDisplaySettings 234 | com.android.settings.wfd.WifiDisplaySettings 235 | com.google.android.gles_jni.EGLDisplayImpl 236 | javax.microedition.khronos.egl.EGLDisplay 237 | 238 | Found 60 classes 239 | 240 | ``` 241 | 242 | ③可以使用以下命令从内存中搜索所有包含关键词key的方法: 243 | 244 | ``` 245 | android hooking search methods 246 | ``` 247 | 248 | ```sh 249 | com.android.settings on (google: 8.1.0) [usb] # android hooking search methods display 250 | Note that Java classes are only loaded when they are used, so if the expected class has not been found, it might not have been loaded yet. 251 | Warning, searching all classes may take some time and in some cases, crash the target application. 252 | Continue? [y/N]: y 253 | Found 5986 classes, searching methods (this may take some time)... 254 | android.app.ActionBar.getDisplayOptions 255 | android.app.ActionBar.setDefaultDisplayHomeAsUpEnabled 256 | android.app.ActionBar.setDisplayHomeAsUpEnabled 257 | android.app.ActionBar.setDisplayOptions 258 | android.app.ActionBar.setDisplayOptions 259 | android.app.ActionBar.setDisplayShowCustomEnabled 260 | android.app.ActionBar.setDisplayShowHomeEnabled 261 | android.app.ActionBar.setDisplayShowTitleEnabled 262 | android.app.ActionBar.setDisplayUseLogoEnabled 263 | android.app.Activity.dispatchMovedToDisplay 264 | android.app.Activity.onMovedToDisplay 265 | android.app.ActivityOptions.getLaunchDisplayId 266 | android.app.ActivityOptions.setLaunchDisplayId 267 | android.app.ActivityThread$ApplicationThread.scheduleActivityMovedToDisplay 268 | ....... 269 | ....... 270 | ``` 271 | 272 | ④搜索到我们感兴趣的类后,可以使用以下命令查看关心的类的 所有方法: 273 | 274 | ```sh 275 | android hooking list class_methods 276 | ``` 277 | 278 | ```sh 279 | com.android.settings on (google: 8.1.0) [usb] # android hooking list class_methods android.hardware.display.Di 280 | splayManager 281 | private android.view.Display android.hardware.display.DisplayManager.getOrCreateDisplayLocked(int,boolean) 282 | private void android.hardware.display.DisplayManager.addAllDisplaysLocked(java.util.ArrayList,int[]) 283 | private void android.hardware.display.DisplayManager.addPresentationDisplaysLocked(java.util.ArrayList,int[],int) 284 | public android.graphics.Point android.hardware.display.DisplayManager.getStableDisplaySize() 285 | public android.hardware.display.VirtualDisplay android.hardware.display.DisplayManager.createVirtualDisplay(android.media.projection.MediaProjection,java.lang.String,int,int,int,android.view.Surface,int,android.hardware.display.VirtualDisplay$Callback,android.os.Handler,java.lang.String) 286 | public android.hardware.display.VirtualDisplay android.hardware.display.DisplayManager.createVirtualDisplay(java.lang.String,int,int,int,android.view.Surface,int) 287 | public android.hardware.display.VirtualDisplay android.hardware.display.DisplayManager.createVirtualDisplay(java.lang.String,int,int,int,android.view.Surface,int,android.hardware.display.VirtualDisplay$Callback,android.os.Handler) 288 | public android.hardware.display.WifiDisplayStatus android.hardware.display.DisplayManager.getWifiDisplayStatus() 289 | public android.view.Display android.hardware.display.DisplayManager.getDisplay(int) 290 | public android.view.Display[] android.hardware.display.DisplayManager.getDisplays() 291 | public android.view.Display[] android.hardware.display.DisplayManager.getDisplays(java.lang.String) 292 | public void android.hardware.display.DisplayManager.connectWifiDisplay(java.lang.String) 293 | public void android.hardware.display.DisplayManager.disconnectWifiDisplay() 294 | public void android.hardware.display.DisplayManager.forgetWifiDisplay(java.lang.String) 295 | public void android.hardware.display.DisplayManager.pauseWifiDisplay() 296 | public void android.hardware.display.DisplayManager.registerDisplayListener(android.hardware.display.DisplayManager$DisplayListener,android.os.Handler) 297 | public void android.hardware.display.DisplayManager.renameWifiDisplay(java.lang.String,java.lang.String) 298 | public void android.hardware.display.DisplayManager.resumeWifiDisplay() 299 | public void android.hardware.display.DisplayManager.startWifiDisplayScan() 300 | public void android.hardware.display.DisplayManager.stopWifiDisplayScan() 301 | public void android.hardware.display.DisplayManager.unregisterDisplayListener(android.hardware.display.DisplayManager$DisplayListener) 302 | 303 | Found 21 method(s) 304 | ``` 305 | 306 | ⑤以上介绍的都是最基础的一些Java类相关的内容。在Android 中,正如笔者在第2章中介绍的,四大组件的相关内容是非常值得关 注的,Objection在这方面也提供了支持,可以通过以下命令列出进 程所有的activity(活动)。 307 | 308 | ``` 309 | android hooking list activities 310 | ``` 311 | 312 | ```sh 313 | com.android.settings on (google: 8.1.0) [usb] # android hooking list activities 314 | com.android.settings.ActivityPicker 315 | com.android.settings.AirplaneModeVoiceActivity 316 | com.android.settings.AllowBindAppWidgetActivity 317 | com.android.settings.AppWidgetPickActivity 318 | com.android.settings.BandMode 319 | com.android.settings.ConfirmDeviceCredentialActivity 320 | com.android.settings.CreateShortcut 321 | com.android.settings.CredentialStorage 322 | com.android.settings.CryptKeeper$FadeToBlack 323 | com.android.settings.CryptKeeperConfirm$Blank 324 | com.android.settings.DeviceAdminAdd 325 | com.android.settings.DeviceAdminSettings 326 | com.android.settings.Display 327 | com.android.settings.DisplaySettings 328 | com.android.settings.EncryptionInterstitial 329 | com.android.settings.FallbackHome 330 | com.android.settings.HelpTrampoline 331 | com.android.settings.LanguageSettings 332 | com.android.settings.ManageApplications 333 | com.android.settings.MonitoringCertInfoActivity 334 | com.android.settings.RadioInfo 335 | com.android.settings.RegulatoryInfoDisplayActivity 336 | com.android.settings.RemoteBugreportActivity 337 | com.android.settings.RunningServices 338 | com.android.settings.SecuritySettings 339 | com.android.settings.SetFullBackupPassword 340 | com.android.settings.SetProfileOwner 341 | com.android.settings.Settings 342 | com.android.settings.Settings 343 | com.android.settings.Settings$AccessibilityDaltonizerSettingsActivity 344 | com.android.settings.Settings$AccessibilitySettingsActivity 345 | com.android.settings.Settings$AccountSyncSettingsActivity 346 | com.android.settings.Settings$AdvancedAppsActivity 347 | com.android.settings.Settings$AllApplicationsActivity 348 | com.android.settings.Settings$AmbientDisplayPickupSuggestionActivity 349 | com.android.settings.Settings$AmbientDisplaySuggestionActivity 350 | com.android.settings.Settings$AndroidBeamSettingsActivity 351 | com.android.settings.Settings$ApnEditorActivity 352 | com.android.settings.Settings$ApnSettingsActivity 353 | com.android.settings.Settings$AppAndNotificationDashboardActivity 354 | com.android.settings.Settings$AppDrawOverlaySettingsActivity 355 | com.android.settings.Settings$AppMemoryUsageActivity 356 | com.android.settings.Settings$AppNotificationSettingsActivity 357 | com.android.settings.Settings$AppPictureInPictureSettingsActivity 358 | com.android.settings.Settings$AppWriteSettingsActivity 359 | com.android.settings.Settings$AssistGestureSettingsActivity 360 | com.android.settings.Settings$AutomaticStorageManagerSettingsActivity 361 | com.android.settings.Settings$AvailableVirtualKeyboardActivity 362 | com.android.settings.Settings$BatterySaverSettingsActivity 363 | com.android.settings.Settings$BluetoothSettingsActivity 364 | com.android.settings.Settings$CaptioningSettingsActivity 365 | com.android.settings.Settings$ChannelNotificationSettingsActivity 366 | com.android.settings.Settings$ChooseAccountActivity 367 | com.android.settings.Settings$ConfigureNotificationSettingsActivity 368 | com.android.settings.Settings$ConfigureWifiSettingsActivity 369 | com.android.settings.Settings$ConnectedDeviceDashboardActivity 370 | com.android.settings.Settings$CryptKeeperSettingsActivity 371 | com.android.settings.Settings$DataUsageSummaryActivity 372 | com.android.settings.Settings$DateTimeSettingsActivity 373 | com.android.settings.Settings$DevelopmentSettingsActivity 374 | com.android.settings.Settings$DeviceAdminSettingsActivity 375 | com.android.settings.Settings$DeviceInfoSettingsActivity 376 | com.android.settings.Settings$DisplaySettingsActivity 377 | com.android.settings.Settings$DoubleTapPowerSuggestionActivity 378 | com.android.settings.Settings$DoubleTwistSuggestionActivity 379 | com.android.settings.Settings$DreamSettingsActivity 380 | com.android.settings.Settings$EnterprisePrivacySettingsActivity 381 | com.android.settings.Settings$FactoryResetActivity 382 | com.android.settings.Settings$FingerprintEnrollSuggestionActivity 383 | com.android.settings.Settings$HighPowerApplicationsActivity 384 | com.android.settings.Settings$IccLockSettingsActivity 385 | com.android.settings.Settings$ImeiInformationActivity 386 | com.android.settings.Settings$KeyboardLayoutPickerActivity 387 | com.android.settings.Settings$LanguageAndInputSettingsActivity 388 | com.android.settings.Settings$LegacySupportActivity 389 | com.android.settings.Settings$LocalePickerActivity 390 | com.android.settings.Settings$LocationSettingsActivity 391 | com.android.settings.Settings$ManageAppExternalSourcesActivity 392 | com.android.settings.Settings$ManageApplicationsActivity 393 | com.android.settings.Settings$ManageAssistActivity 394 | com.android.settings.Settings$ManageDomainUrlsActivity 395 | com.android.settings.Settings$ManageExternalSourcesActivity 396 | com.android.settings.Settings$ManagedProfileSettingsActivity 397 | com.android.settings.Settings$MemorySettingsActivity 398 | com.android.settings.Settings$MobileDataUsageListActivity 399 | com.android.settings.Settings$NetworkDashboardActivity 400 | com.android.settings.Settings$NotificationAccessSettingsActivity 401 | com.android.settings.Settings$NotificationAppListActivity 402 | com.android.settings.Settings$NotificationStationActivity 403 | com.android.settings.Settings$OverlaySettingsActivity 404 | com.android.settings.Settings$PaymentSettingsActivity 405 | com.android.settings.Settings$PhysicalKeyboardActivity 406 | com.android.settings.Settings$PictureInPictureSettingsActivity 407 | com.android.settings.Settings$PowerUsageSummaryActivity 408 | com.android.settings.Settings$PrintJobSettingsActivity 409 | com.android.settings.Settings$PrintSettingsActivity 410 | com.android.settings.Settings$PrivacySettingsActivity 411 | com.android.settings.Settings$PrivateVolumeForgetActivity 412 | com.android.settings.Settings$PrivateVolumeSettingsActivity 413 | com.android.settings.Settings$PublicVolumeSettingsActivity 414 | com.android.settings.Settings$RunningServicesActivity 415 | com.android.settings.Settings$SavedAccessPointsSettingsActivity 416 | com.android.settings.Settings$ScreenLockSuggestionActivity 417 | com.android.settings.Settings$SecuritySettingsActivity 418 | com.android.settings.Settings$SimStatusActivity 419 | com.android.settings.Settings$SoundSettingsActivity 420 | com.android.settings.Settings$SpecialAccessSettingsActivity 421 | com.android.settings.Settings$SpellCheckersSettingsActivity 422 | com.android.settings.Settings$StatusActivity 423 | com.android.settings.Settings$StorageDashboardActivity 424 | com.android.settings.Settings$StorageUseActivity 425 | com.android.settings.Settings$SwipeToNotificationSuggestionActivity 426 | com.android.settings.Settings$SystemDashboardActivity 427 | com.android.settings.Settings$TestingSettingsActivity 428 | com.android.settings.Settings$TetherSettingsActivity 429 | com.android.settings.Settings$TextToSpeechSettingsActivity 430 | com.android.settings.Settings$TrustedCredentialsSettingsActivity 431 | com.android.settings.Settings$UsageAccessSettingsActivity 432 | com.android.settings.Settings$UserAndAccountDashboardActivity 433 | com.android.settings.Settings$UserDictionarySettingsActivity 434 | com.android.settings.Settings$UserSettingsActivity 435 | com.android.settings.Settings$VpnSettingsActivity 436 | com.android.settings.Settings$VrListenersSettingsActivity 437 | com.android.settings.Settings$WallpaperSettingsActivity 438 | com.android.settings.Settings$WebViewAppPickerActivity 439 | com.android.settings.Settings$WifiAPITestActivity 440 | com.android.settings.Settings$WifiCallingSettingsActivity 441 | com.android.settings.Settings$WifiCallingSuggestionActivity 442 | com.android.settings.Settings$WifiDisplaySettingsActivity 443 | com.android.settings.Settings$WifiInfoActivity 444 | com.android.settings.Settings$WifiP2pSettingsActivity 445 | com.android.settings.Settings$WifiSettingsActivity 446 | com.android.settings.Settings$WriteSettingsActivity 447 | com.android.settings.Settings$ZenAccessSettingsActivity 448 | com.android.settings.Settings$ZenModeEventRuleSettingsActivity 449 | com.android.settings.Settings$ZenModeExternalRuleSettingsActivity 450 | com.android.settings.Settings$ZenModePrioritySettingsActivity 451 | com.android.settings.Settings$ZenModeScheduleRuleSettingsActivity 452 | com.android.settings.Settings$ZenModeSettingsActivity 453 | com.android.settings.Settings$ZenModeVisualInterruptionSettingsActivity 454 | com.android.settings.SettingsLicenseActivity 455 | com.android.settings.SetupEncryptionInterstitial 456 | com.android.settings.ShowAdminSupportDetailsDialog 457 | com.android.settings.SmsDefaultDialog 458 | com.android.settings.SoundSettings 459 | com.android.settings.SubSettings 460 | com.android.settings.TetherProvisioningActivity 461 | com.android.settings.TetherSettings 462 | com.android.settings.UsageStatsActivity 463 | com.android.settings.UsbSettings 464 | com.android.settings.UserDictionarySettings 465 | com.android.settings.WebViewImplementation 466 | com.android.settings.accessibility.AccessibilitySettingsForSetupWizardActivity 467 | com.android.settings.accounts.AddAccountSettings 468 | com.android.settings.applications.InstalledAppDetails 469 | com.android.settings.applications.InstalledAppDetailsTop 470 | com.android.settings.applications.ManageApplications 471 | com.android.settings.applications.StorageUse 472 | com.android.settings.applications.autofill.AutofillPickerActivity 473 | com.android.settings.applications.autofill.AutofillPickerTrampolineActivity 474 | com.android.settings.backup.BackupSettingsActivity 475 | com.android.settings.bluetooth.BluetoothPairingDialog 476 | com.android.settings.bluetooth.BluetoothPermissionActivity 477 | com.android.settings.bluetooth.BluetoothSettings 478 | com.android.settings.bluetooth.DevicePickerActivity 479 | com.android.settings.bluetooth.RequestPermissionActivity 480 | com.android.settings.bluetooth.RequestPermissionHelperActivity 481 | com.android.settings.datausage.AppDataUsageActivity 482 | com.android.settings.development.AppPicker 483 | com.android.settings.development.DevelopmentSettingsDisabledActivity 484 | com.android.settings.deviceinfo.StorageWizardFormatConfirm 485 | com.android.settings.deviceinfo.StorageWizardFormatProgress 486 | com.android.settings.deviceinfo.StorageWizardInit 487 | com.android.settings.deviceinfo.StorageWizardMigrate 488 | com.android.settings.deviceinfo.StorageWizardMigrateConfirm 489 | com.android.settings.deviceinfo.StorageWizardMigrateProgress 490 | com.android.settings.deviceinfo.StorageWizardMoveConfirm 491 | com.android.settings.deviceinfo.StorageWizardMoveProgress 492 | com.android.settings.deviceinfo.StorageWizardReady 493 | com.android.settings.deviceinfo.UsbModeChooserActivity 494 | com.android.settings.fingerprint.FingerprintEnrollEnrolling 495 | com.android.settings.fingerprint.FingerprintEnrollFindSensor 496 | com.android.settings.fingerprint.FingerprintEnrollFinish 497 | com.android.settings.fingerprint.FingerprintEnrollIntroduction 498 | com.android.settings.fingerprint.FingerprintSettings 499 | com.android.settings.fingerprint.FingerprintSuggestionActivity 500 | com.android.settings.fingerprint.SetupFingerprintEnrollEnrolling 501 | com.android.settings.fingerprint.SetupFingerprintEnrollFindSensor 502 | com.android.settings.fingerprint.SetupFingerprintEnrollFinish 503 | com.android.settings.fingerprint.SetupFingerprintEnrollIntroduction 504 | com.android.settings.fuelgauge.BatterySaverModeVoiceActivity 505 | com.android.settings.fuelgauge.PowerUsageSummary 506 | com.android.settings.fuelgauge.RequestIgnoreBatteryOptimizations 507 | com.android.settings.inputmethod.InputMethodAndSubtypeEnablerActivity 508 | com.android.settings.inputmethod.UserDictionaryAddWordActivity 509 | com.android.settings.nfc.HowItWorks 510 | com.android.settings.nfc.PaymentDefaultDialog 511 | com.android.settings.notification.NotificationAccessConfirmationActivity 512 | com.android.settings.notification.RedactionInterstitial 513 | com.android.settings.notification.RedactionSettingsStandalone 514 | com.android.settings.notification.ZenModeVoiceActivity 515 | com.android.settings.password.ChooseLockGeneric 516 | com.android.settings.password.ChooseLockGeneric$InternalActivity 517 | com.android.settings.password.ChooseLockPassword 518 | com.android.settings.password.ChooseLockPattern 519 | com.android.settings.password.ConfirmDeviceCredentialActivity 520 | com.android.settings.password.ConfirmDeviceCredentialActivity$InternalActivity 521 | com.android.settings.password.ConfirmLockPassword 522 | com.android.settings.password.ConfirmLockPassword$InternalActivity 523 | com.android.settings.password.ConfirmLockPattern 524 | com.android.settings.password.ConfirmLockPattern$InternalActivity 525 | com.android.settings.password.SetNewPasswordActivity 526 | com.android.settings.password.SetupChooseLockGeneric 527 | com.android.settings.password.SetupChooseLockPassword 528 | com.android.settings.password.SetupChooseLockPattern 529 | com.android.settings.qstile.DevelopmentTileConfigActivity 530 | com.android.settings.search.SearchActivity 531 | com.android.settings.sim.SimDialogActivity 532 | com.android.settings.sim.SimPreferenceDialog 533 | com.android.settings.support.NewDeviceIntroSuggestionActivity 534 | com.android.settings.support.SupportDashboardActivity 535 | com.android.settings.wallpaper.WallpaperSuggestionActivity 536 | com.android.settings.wifi.RequestToggleWiFiActivity 537 | com.android.settings.wifi.WifiConfigInfo 538 | com.android.settings.wifi.WifiDialogActivity 539 | com.android.settings.wifi.WifiNoInternetDialog 540 | com.android.settings.wifi.WifiPickerActivity 541 | com.android.settings.wifi.WifiScanModeActivity 542 | com.android.settings.wifi.WifiSettings 543 | com.android.settings.wifi.WifiStatusTest 544 | com.google.android.libraries.hats20.SurveyPromptActivity 545 | com.google.android.settings.backup.BackupSuggestionActivity 546 | com.google.android.settings.external.ExternalSettingsTrampoline 547 | com.google.android.settings.gestures.AssistGestureSuggestion 548 | com.google.android.settings.gestures.assist.AssistGestureTrainingEnrollingActivity 549 | com.google.android.settings.gestures.assist.AssistGestureTrainingFinishedActivity 550 | com.google.android.settings.gestures.assist.AssistGestureTrainingIntroActivity 551 | com.google.android.settings.gestures.assist.bubble.AssistGestureBubbleActivity 552 | 553 | Found 238 classes 554 | ``` 555 | 556 | ⑥可以通过以下命令列出进程所有的service。 557 | 558 | ``` 559 | android hooking list services 560 | ``` 561 | 562 | ```sh 563 | com.android.settings on (google: 8.1.0) [usb] # android hooking list services 564 | android.bluetooth.BluetoothA2dp$2 565 | com.android.settings.SettingsDumpService 566 | com.android.settings.TetherService 567 | com.android.settings.bluetooth.BluetoothPairingService 568 | 569 | Found 4 classes 570 | ``` 571 | 572 | 需要列出其他两个组件的信息时,只要将对应的地方更换为 receivers和providers即可 573 | 574 | ``` 575 | com.android.settings on (google: 8.1.0) [usb] # android hooking list receivers 576 | com.android.settings.RestrictedSettingsFragment$1 577 | com.android.settings.SettingsActivity$1 578 | com.android.settings.SettingsInitialize 579 | com.android.settings.TestingSettingsBroadcastReceiver 580 | com.android.settings.bluetooth.BluetoothPairingRequest 581 | com.android.settings.bluetooth.BluetoothPermissionRequest 582 | com.android.settings.development.DevelopmentSettings$1 583 | com.android.settings.development.DevelopmentSettings$2 584 | com.android.settings.deviceinfo.StorageUnmountReceiver 585 | com.android.settings.sim.SimSelectNotification 586 | com.android.settings.users.ProfileUpdateReceiver 587 | com.android.settingslib.bluetooth.BluetoothDiscoverableTimeoutReceiver 588 | com.android.settingslib.drawer.SettingsDrawerActivity$PackageReceiver 589 | com.google.android.settings.phenotype.PhenotypeBroadcastReceiver 590 | 591 | Found 14 classes 592 | ``` 593 | 594 | providers组件目前没有发现支持 595 | 596 | ps:猜测可能没有支持吗? 597 | 598 | #### 4.5 Hook相关命令 599 | 600 | 作为Frida的核心功能,Hook总是不能绕 过的。同样地,Objection作为Frida优秀的开发工具,Hook相关的 命令是一定要实现的。事实上,Objection在这方面的表现确实令人 称赞。 601 | 602 | 通过以下命令对指定的方法进行Hook。 603 | 604 | ```sh 605 | android hooking watch class_method 606 | ``` 607 | 608 | 这里选择Java中File类的构造函数进行Hook,结果如下: 609 | 610 | 在上述命令中,加上了 --dump-args 、 --dumpbacktrace、--dump-return三个参数,分别用于打印函数的参数、 调用栈以及返回值。这三个参数对逆向分析的帮助是非常大的:有些 函数的明文和密文非常有可能放在参数和返回值中,而打印调用栈可 以让分析者快速进行调用链的溯源。 611 | 612 | ```sh 613 | com.android.settings on (google: 8.1.0) [usb] # android hooking watch class_method java.io.File.$init --dump-a 614 | rgs --dump-backtrace --dump-return 615 | ``` 616 | 617 | 另外需要注意的是,此时虽然只确定了Hook构造函数,但是默 认会Hook对应方法的所有重载。同时,在输出的最后一行显示 Registering job 7s9a29pxmt4,这表示这个Hook被作为一个“作 业”添加到Objection的作业系统中了,此时运行job list命令可以查 看到这个“作业”的相关信息,如图4-10所示。可以发现这里的Job ID对应的是7s9a29pxmt4,同时Hooks对应的6正是Hook的函数的 数量。 618 | 619 | ```sh 620 | com.android.settings on (google: 8.1.0) [usb] # android hooking watch class_method java.io.File.$init --dump-a 621 | rgs --dump-backtrace --dump-return 622 | (agent) Attempting to watch class java.io.File and method $init. 623 | (agent) Hooking java.io.File.$init(java.io.File, java.lang.String) 624 | (agent) Hooking java.io.File.$init(java.lang.String) 625 | (agent) Hooking java.io.File.$init(java.lang.String, int) 626 | (agent) Hooking java.io.File.$init(java.lang.String, java.io.File) 627 | (agent) Hooking java.io.File.$init(java.lang.String, java.lang.String) 628 | (agent) Hooking java.io.File.$init(java.net.URI) 629 | (agent) Registering job 042074. Type: watch-method for: java.io.File.$init 630 | ``` 631 | 632 | ```sh 633 | com.android.settings on (google: 8.1.0) [usb] # jobs list 634 | Job ID Hooks Type 635 | ------ ----- ------------------------------------ 636 | 042074 6 watch-method for: java.io.File.$init 637 | ``` 638 | 639 | 在“设置”应用中的任意位置进行点击时,会发现 java.io.File.File(java.io.File, java.lang.String)这一个函数被调用 了。在Backtrace之后打印的调用栈中,可以清楚地看到这个构造函数的调用来源。 640 | 641 | 调用栈的顺序是从下至上的, 根 据 Arguments 那一行会发现打开的文件路径是/data/user_de/0/com.android.settings/shared_prefs,文件 名 为 development.xml 。 虽 然 Return Value后打印的返回值为none,表明这个函数没有返回值,但是也是真实地打印了返回值。 642 | 643 | 测试结束后,可以根据“作业”的ID来删除“作业” ,取消对这 些函数的Hook 644 | 645 | ``` 646 | jobs kill 647 | ``` 648 | 649 | 除了可以直接Hook一个函数之外,Objection还可以通过执行如 下命令实现对指定类classname中所有函数的Hook(这里的所有函 数并不包括构造函数的Hook)。 650 | 651 | ``` 652 | android hooking watch class 653 | ``` 654 | 655 | 同样以java.io.File类为例: 656 | 657 | ``` 658 | android hooking watch class java.io.File 659 | ``` 660 | 661 | ``` 662 | com.android.settings on (google: 8.1.0) [usb] # android hooking watch class java.io.File 663 | (agent) Hooking java.io.File.createTempFile(java.lang.String, java.lang.String) 664 | (agent) Hooking java.io.File.createTempFile(java.lang.String, java.lang.String, java.io.File) 665 | (agent) Hooking java.io.File.listRoots() 666 | (agent) Hooking java.io.File.readObject(java.io.ObjectInputStream) 667 | (agent) Hooking java.io.File.slashify(java.lang.String, boolean) 668 | (agent) Hooking java.io.File.writeObject(java.io.ObjectOutputStream) 669 | (agent) Hooking java.io.File.canExecute() 670 | (agent) Hooking java.io.File.canRead() 671 | (agent) Hooking java.io.File.canWrite() 672 | (agent) Hooking java.io.File.compareTo(java.io.File) 673 | (agent) Hooking java.io.File.compareTo(java.lang.Object) 674 | (agent) Hooking java.io.File.createNewFile() 675 | (agent) Hooking java.io.File.delete() 676 | (agent) Hooking java.io.File.deleteOnExit() 677 | (agent) Hooking java.io.File.equals(java.lang.Object) 678 | (agent) Hooking java.io.File.exists() 679 | (agent) Hooking java.io.File.getAbsoluteFile() 680 | (agent) Hooking java.io.File.getAbsolutePath() 681 | (agent) Hooking java.io.File.getCanonicalFile() 682 | (agent) Hooking java.io.File.getCanonicalPath() 683 | (agent) Hooking java.io.File.getFreeSpace() 684 | (agent) Hooking java.io.File.getName() 685 | (agent) Hooking java.io.File.getParent() 686 | (agent) Hooking java.io.File.getParentFile() 687 | (agent) Hooking java.io.File.getPath() 688 | (agent) Hooking java.io.File.getPrefixLength() 689 | (agent) Hooking java.io.File.getTotalSpace() 690 | (agent) Hooking java.io.File.getUsableSpace() 691 | (agent) Hooking java.io.File.hashCode() 692 | (agent) Hooking java.io.File.isAbsolute() 693 | (agent) Hooking java.io.File.isDirectory() 694 | (agent) Hooking java.io.File.isFile() 695 | (agent) Hooking java.io.File.isHidden() 696 | (agent) Hooking java.io.File.isInvalid() 697 | (agent) Hooking java.io.File.lastModified() 698 | (agent) Hooking java.io.File.length() 699 | (agent) Hooking java.io.File.list() 700 | (agent) Hooking java.io.File.list(java.io.FilenameFilter) 701 | (agent) Hooking java.io.File.listFiles() 702 | (agent) Hooking java.io.File.listFiles(java.io.FileFilter) 703 | (agent) Hooking java.io.File.listFiles(java.io.FilenameFilter) 704 | (agent) Hooking java.io.File.mkdir() 705 | (agent) Hooking java.io.File.mkdirs() 706 | (agent) Hooking java.io.File.renameTo(java.io.File) 707 | (agent) Hooking java.io.File.setExecutable(boolean) 708 | (agent) Hooking java.io.File.setExecutable(boolean, boolean) 709 | (agent) Hooking java.io.File.setLastModified(long) 710 | (agent) Hooking java.io.File.setReadOnly() 711 | (agent) Hooking java.io.File.setReadable(boolean) 712 | (agent) Hooking java.io.File.setReadable(boolean, boolean) 713 | (agent) Hooking java.io.File.setWritable(boolean) 714 | (agent) Hooking java.io.File.setWritable(boolean, boolean) 715 | (agent) Hooking java.io.File.toPath() 716 | (agent) Hooking java.io.File.toString() 717 | (agent) Hooking java.io.File.toURI() 718 | (agent) Hooking java.io.File.toURL() 719 | (agent) Registering job 306157. Type: watch-class for: java.io.File 720 | ``` 721 | 722 | 此时执行以下命令`jobs list`可以发现一共Hook了56个函数,输出结果如下: 723 | 724 | ``` 725 | com.android.settings on (google: 8.1.0) [usb] # jobs list 726 | Job ID Hooks Type 727 | ------ ----- ----------------------------- 728 | 306157 56 watch-class for: java.io.File 729 | ``` 730 | 731 | 这里的调用顺序(自上 而下)和之前的调用栈的打印是不同的。 732 | 733 | #### 4.6主动调用android heap相关命令。 734 | 735 | 最后介绍Frida的一 大特色——主动调用在Objection中的使用。 736 | 737 | ①基于最简单的Java.choose的实现,在Frida脚本中,对实例的 搜索在Objection中是使用以下命令实现的: 738 | 739 | ``` 740 | android heap search instances 741 | ``` 742 | 743 | 744 | 745 | ``` 746 | android heap search instances java.io.File 747 | ``` 748 | 749 | ②在Objection中调用实例方法的方式有两种。第一种是使用以 下命令调用实例方法: 750 | 751 | ``` 752 | android heap execute 753 | ``` 754 | 755 | ``` 756 | 这里的实例方法指的是没有参数的实例方法。下面演示一下使用 757 | Handle值为0x3606所对应的实例来执行File的getPath方法 758 | ``` 759 | 760 | 如果要执行带参数的函数,则需要先执行以下命令: 761 | 762 | android heap evaluate 763 | 764 | 在进入一个迷你编辑器环境后,输入想要执行的脚本内容,确认 编辑完成,然后按Esc键退出编辑器,最后按回车键,即会开始执行 这行脚本并输出结果。这里的脚本内容和在编辑器中直接编写的脚本 内容是一样的(使用File类的canWrite()函数和setWritable()函数进 行测试) 765 | 766 | 在 这 个 脚 本 中 , Objection 设 定 clazz 用 于 代 表 Handle 值 为 0x3606所对应的实例,同时函数canWrite()用于返回这个实例所打 开的文件是否可写。setWritable()函数用于修改对应文件是否可写的 属性。 767 | 768 | heap evaluate既可以执行有参函数,也可以执行无参函数。 769 | -------------------------------------------------------------------------------- /91-Jadx-Jeb-GDA.md: -------------------------------------------------------------------------------- 1 | # Jadx/Jeb/GDA 2 | 3 | ## 1.Jadx 4 | 5 | 相比其他两个分析工具,Jadx是开源的。Jadx支持多个平台, 不管是Windows还是Linux、Mac,都可以无差别地运行这款软件。 6 | 7 | 从对应仓库下载Jadx最新版后,解压并切换到Jadx的bin目录 下,运行Jadx-gui就可以直接启动Jadx 8 | 9 | 在打开Jadx页面后,可以直接将目标APK拖曳进页面进行静态分 析 10 | 11 | 在功能上,可以通过从选中函数的右键快捷菜单来跳转到该函数 的声明之处(Go to declaration),或者跳转到该函数在App中被调 用之处(Find Usage) 12 | 13 | Jadx还支持完整的搜索功能。包括搜索类、方法和代码,可以通 过快捷键Ctrl+Shift+F打开搜索框 14 | 15 | 在新版的Jadx中还支持反混淆的功能,支持对不 可见字符和混淆的函数名等进行翻译,可以从注释中看到原本的信 息,非常有利于逆向开发和分析人员进行分析。 16 | 17 | 启动命令: 18 | 19 | ```sh 20 | cd jadx/ 21 | ./jadx-gui 22 | ``` 23 | 24 | 25 | 26 | ## 2. Jeb 27 | 28 | Jeb是一款强大的跨平台的Android 静态分析工具。与Jadx相比,Jadx拥有的功能Jeb几乎都有,而Jeb 在反编译的效果上比Jadx解析的准确度更好,此外Jeb还支持自定义 的脚本功能,这在自动化分析和对抗代码混淆中非常实用。 29 | 30 | 在Jeb界面中,页面左上方是工程的概览界面,在概览页面中会 有 AndroidManifest.xml 的 入 口 , Jeb 标 记 为 了 Manifest; Certificate是App所使用的签名证书,ByteCode是App中代码的入 口,最后还有一个Resources,它是存储着App所有资源信息的入 口 31 | 32 | 左下角是类似于Jadx界面的包结构界面,对应左上方 的是ByteCode。与Jadx一样,Jeb支持使用快捷键Ctrl+F进行代码 的搜索、X键进行交叉引用、使用TAB键把Smali反编译为Java类 等,这里不再赘述,具体用法留给读者自行探索。 33 | 34 | Jeb比Jadx更优秀的地方在于代码的反编译,其内部类的显示和代码翻译的准确性都比Jadx强 35 | 36 | ## 3. GDA 37 | 38 | 与以上两款工具相比,GDA(GGJoy Dex Analysizer)是中国 第一款也是唯一一款全交互式的现代反编译器,同时也是世界上最早 实现的Dalvik字节码反编译器。据官网介绍,GDA不只是反编译器, 同时也是一款轻便且功能强大的综合性逆向分析利器,它不依赖Java 环境并且支持apk、dex等文件的反编译,并且和Jeb一样,支持自定 义脚本的自动化分析。GDA还提供了字符串、方法、类和成员变量的 交叉引用及搜索功能、代码注释功能等。除了这些基础的功能,GDA 中包含了多个由作者独立研究的高速分析引擎:反编译引擎、漏洞检 测引擎、恶意行为检测引擎、污点传播分析引擎、反混淆引擎、apk 壳检测引擎等,尤其是恶意行为检测引擎和污点传播引擎与反编译核 心的完美融合,大大提高了无源码逆向工程的效率,相比于其他两款 专注于代码反编译的软件,GDA的反编译器还提供了很多实用功能, 如路径求解、漏洞检测、隐私泄露检测、查壳、Android设备内存 dump等。 39 | 40 | GDA分为免费版和PRO收费版,但是免费版的功能足以满足日常的学习使用。 41 | 42 | 这是下载地址:http://www.gda.wiki:9090/ 43 | 44 | 免安装,解压后可以直接点击图标双击运行 45 | -------------------------------------------------------------------------------- /92-逆向1:Objection结合Jeb分析.md: -------------------------------------------------------------------------------- 1 | # 逆向1:Objection结合Jeb分析 2 | 3 | 分析目标为之前的一个2022羊城杯的ctf的APP:BBButton.apk 4 | 5 | 软件我会放到APK文件夹下。 6 | 7 | 使用Jadx将App拖入分析,在Jadx初步分析完毕后,使用Jadx 查看AndroidManifest.xml内容。AndroidManifest.xml在资源文件目录下。 8 | 9 | 这个文件中会记载APP相关的属性,包括APP的包名、版本号、所申请的权限信息、四大组件信息以及入口类等信息。文件如下: 10 | 11 | ```xml 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ``` 25 | 26 | APP 的包名为com.crackme.bbbbutton,这是进程唯一的标志。 27 | 28 | 根据activity、service等标签可以确定APP的四大组件信息。 29 | 30 | > 该APP只有一个activity,因此这个MainActivity就是入口类,否则需要通过intent-filter标签中的action属性信息和category属性去标志入口类。 31 | > 32 | > 入口类的action属性信息为android.intent.action.MAIN,而相应的category属性名则为android.intent.category.LAUNCHER。 33 | 34 | 查看入口类的代码,在Jadx界面左部选中并双击MainActivity 后,Jadx会自动显示翻译出的MainActivity类的内容 35 | 36 | ```java 37 | package com.crackme.bbbbutton; 38 | 39 | import android.annotation.SuppressLint; 40 | import android.app.Activity; 41 | import android.os.Bundle; 42 | import android.view.View; 43 | import android.widget.Button; 44 | import android.widget.LinearLayout; 45 | import android.widget.TextView; 46 | import android.widget.Toast; 47 | import java.util.Arrays; 48 | 49 | /* loaded from: classes.dex */ 50 | public class MainActivity extends Activity implements View.OnClickListener { 51 | 52 | /* renamed from: c reason: collision with root package name */ 53 | public int f872c; 54 | public Button e; 55 | public TextView f; 56 | 57 | /* renamed from: b reason: collision with root package name */ 58 | public byte[] f871b = new byte[100]; 59 | public int d = 16; 60 | 61 | public static byte[] getBytes(byte[] bArr) { 62 | byte[] bArr2 = new byte[bArr.length / 4]; 63 | int i = 0; 64 | int i2 = 0; 65 | while (i < bArr.length / 4) { 66 | int i3 = i * 4; 67 | bArr2[i2] = (byte) ((bArr[i3] << 6) + (bArr[i3 + 1] << 4) + (bArr[i3 + 2] << 2) + bArr[i3 + 3]); 68 | i++; 69 | i2++; 70 | } 71 | return bArr2; 72 | } 73 | 74 | public final boolean a() { 75 | if (this.f872c == this.f871b.length) { 76 | Toast.makeText(this, "too many!\nplz reset!", 0).show(); 77 | return false; 78 | } 79 | return true; 80 | } 81 | 82 | public final void b(View view) { 83 | if (a()) { 84 | Toast.makeText(this, "button1", 0).show(); 85 | byte[] bArr = this.f871b; 86 | int i = this.f872c; 87 | this.f872c = i + 1; 88 | bArr[i] = 0; 89 | } 90 | } 91 | 92 | public final void c(View view) { 93 | if (a()) { 94 | Toast.makeText(this, "button2", 0).show(); 95 | byte[] bArr = this.f871b; 96 | int i = this.f872c; 97 | this.f872c = i + 1; 98 | bArr[i] = 1; 99 | } 100 | } 101 | 102 | public final void d(View view) { 103 | if (a()) { 104 | Toast.makeText(this, "button3", 0).show(); 105 | byte[] bArr = this.f871b; 106 | int i = this.f872c; 107 | this.f872c = i + 1; 108 | bArr[i] = 2; 109 | } 110 | } 111 | 112 | public final void e(View view) { 113 | if (a()) { 114 | Toast.makeText(this, "button4", 0).show(); 115 | byte[] bArr = this.f871b; 116 | int i = this.f872c; 117 | this.f872c = i + 1; 118 | bArr[i] = 3; 119 | } 120 | } 121 | 122 | public final void f(View view) { 123 | String str; 124 | if (this.f872c >= 16) { 125 | byte[] copyOfRange = Arrays.copyOfRange(this.f871b, 0, this.d); 126 | byte[] copyOfRange2 = Arrays.copyOfRange(this.f871b, this.d, this.f872c); 127 | Toast.makeText(this, "checking...", 0).show(); 128 | if (JniCheck.check1(copyOfRange) && JniCheck.check2(copyOfRange, copyOfRange2)) { 129 | str = "DASCTF{" + new String(getBytes(copyOfRange2)) + "}"; 130 | h(str); 131 | } 132 | } 133 | str = "failed!"; 134 | h(str); 135 | } 136 | 137 | public final void g(View view) { 138 | Toast.makeText(this, "reset", 0).show(); 139 | Arrays.fill(this.f871b, (byte) 0); 140 | this.f872c = 0; 141 | } 142 | 143 | public final void h(String str) { 144 | TextView textView = new TextView(this); 145 | this.f = textView; 146 | textView.setGravity(1); 147 | this.f.setText(str); 148 | this.f.setTextColor(-7667573); 149 | this.f.setTextSize(20.0f); 150 | ((LinearLayout) findViewById(R.id.layout)).addView(this.f); 151 | this.e.setEnabled(false); 152 | } 153 | 154 | @Override // android.view.View.OnClickListener 155 | @SuppressLint({"NonConstantResourceId"}) 156 | public void onClick(View view) { 157 | switch (view.getId()) { 158 | case R.id.button1 /* 2131230792 */: 159 | b(view); 160 | return; 161 | case R.id.button2 /* 2131230793 */: 162 | c(view); 163 | return; 164 | case R.id.button3 /* 2131230794 */: 165 | d(view); 166 | return; 167 | case R.id.button4 /* 2131230795 */: 168 | e(view); 169 | return; 170 | case R.id.buttonPanel /* 2131230796 */: 171 | default: 172 | return; 173 | case R.id.button_check /* 2131230797 */: 174 | f(view); 175 | return; 176 | case R.id.button_reset /* 2131230798 */: 177 | g(view); 178 | return; 179 | } 180 | } 181 | 182 | @Override // android.app.Activity 183 | public void onCreate(Bundle bundle) { 184 | super.onCreate(bundle); 185 | setContentView(R.layout.activity_main); 186 | this.e = (Button) findViewById(R.id.button_check); 187 | ((Button) findViewById(R.id.button1)).setOnClickListener(this); 188 | ((Button) findViewById(R.id.button2)).setOnClickListener(this); 189 | ((Button) findViewById(R.id.button3)).setOnClickListener(this); 190 | ((Button) findViewById(R.id.button4)).setOnClickListener(this); 191 | ((Button) findViewById(R.id.button_reset)).setOnClickListener(this); 192 | this.e.setOnClickListener(this); 193 | } 194 | } 195 | ``` 196 | 197 | 静态分析了以下代码逻辑,onCreate方法中对控件进行了绑定,并且设置了监听器1 198 | 199 | 监听器方法用于监听按钮是否被按下,为onClick方法,分别对button1~4和button_check和button_reset六个按钮控件设置了监听方法。 200 | 201 | 该入口类中设置了五个全局变量,分别是f872c(int),e(Button),f(TextView), 202 | 203 | f871b(byte[100]),d(int)=16 204 | 205 | 首先分析a方法,a方法中进行一个判断,当f872c的值与f871b数组的长度相同的时候提示按钮点击过多。 206 | 207 | 经过观察分析b,c,d,e四个方法,这四个方法分别对应着Button1~4.每个方法开始都调用a方法进行判断,b方法为当button1按钮被点击后,首先f872c+1,然后将0存入f871b数组中,c、d、e三个方法和b相似,只不过每次存入数组的数字为1,2,3。 208 | 209 | 然后设置两个数组分别是按钮日志的前16个和从16开始后计数器个,然后进行jniCheck里面的check1方法和check2方法,这两个方法都封装在libJniCheck.so库文件中,需要IDA的动态调试来分析功能。**待后面学习动调后继续更新。** 210 | 211 | > 根据上面的分析,我们可以知道f872c就是一个计数器,而f871b数组就是一个用来记录按钮控件点击记录的数组。所以简而言之,a方法就是用来判断点击次数是否超过100次。但是我也产生了一个疑惑,为什么一定要使用byte数组呢,在Java中哪怕是byte数组也是按照正常存储的,并不存在什么10进制转二进制的陷阱。那么就接着继续分析吧! 212 | 213 | 214 | 215 | ## ps:若软件为恶意软件 216 | 217 | 个恶意的App打开应用后,adb连接会自动断开,并 且无法通过USB连接上去。 再次使用Jadx查看App的包结构会发现有一个USBLock的相关 类 218 | 219 | 查看其中内容会发现,类中执行了一个setprop persist.sys.usb.config none 的 命 令 。 setprop 命 令 与 之 前 讲 的 getprop是相对的,setprop用于Android系统的某个属性。通过搜索 persist.sys.usb.config关键词会发现这个属性是与设置USB调试相 关的,当这个属性被设置为none时,就会导致adb调试被禁用,也就 达成了断开连接的作用。 220 | 221 | 如果无法使用USB连接,就无法通过adb启动frida-server,也 就无法使用Objection进行连接,推荐使用Termux软件。Termux是一个安卓手机的 Linux模拟器,可以在手机上模拟Linux环境。它提供一个命令行界 面,让用户与系统互动。其实它就是一个普通的手机App,可以从应 用商店下载安装。Termux的界面和一个普通的终端一样。 222 | 223 | 模拟器默认root过,此时只需要下载对应模拟器架构的fridaserver并用push推送到模拟器的对应目录下,这样不通过adb就可以 启动frida-server。 224 | 225 | Objection网络模式连接 226 | 227 | Objection默认使用USB模式连 接,但是可以通过-N参数使用网络模式,使用时只需要通过-h参数指 定IP地址、-p参数指定端口去实现网络模式连接。 228 | 229 | 通过输入“./frida-server -l 0.0.0.0:8888”并按回车键以网络模式运行frida-server,从而监听 任何对手机8888端口的网络连接。此时,通过以下命令可以查看 frida-server监听的端口。 230 | 231 | ``` 232 | 可以通过 233 | netstat -tulp | grep frida来验证 234 | ``` 235 | 236 | objection可以如此连接: 237 | 238 | ``` 239 | objection -N -h 192.168.31.52 -p 8888 -g 240 | com.shimeng.qq2693533893 explore 241 | ``` 242 | 243 | -------------------------------------------------------------------------------- /93-Frida开发思想——Frida三板斧.md: -------------------------------------------------------------------------------- 1 | # Frida开发思想——Frida三板斧 2 | 3 | ## Objection辅助定位 4 | 5 | Frida 需要每次手动编写代码去Hook从静态分析到的函数,进而观察其参 数和返回值是否与需求相符,Objection将常用的一些功能集成在一 起,使得逆向开发和分析人员在分析过程中不需要浪费精力在编写代 码上。 6 | 7 | 下面以 Junior.apk为例,样 本 来 自 于 《 Android Studio开发实战:从零基础到App上线(第2版)》一书中的Junior 样例,源代码在https://github.com/aqi00/android2上。 8 | 9 | 安装遍历APP的所有Activity 10 | 11 | 安装过程中报错INSTALL_FAILED_TEST_ONLY: installPackageLI 12 | 13 | 通过AndroidKiller将AndroidManifest.xml中的android:testOnly="true"改成false或者直接去掉,然后重新编译打包,再次安装即可 14 | 15 | 先打开手机的fridaserver,然后命令如下: 16 | 17 | ``` 18 | objection -g com.example.junior explore 19 | Using USB device `Nexus 5X` 20 | Agent injected and responds ok! 21 | 22 | _ _ _ _ 23 | ___| |_|_|___ ___| |_|_|___ ___ 24 | | . | . | | -_| _| _| | . | | 25 | |___|___| |___|___|_| |_|___|_|_| 26 | |___|(object)inject(ion) v1.11.0 27 | 28 | Runtime Mobile Exploration 29 | by: @leonjza from @sensepost 30 | 31 | [tab] for command suggestions 32 | com.example.junior on (google: 8.1.0) [usb] # 33 | 34 | ``` 35 | 36 | ``` 37 | com.example.junior on (google: 8.1.0) [usb] # ls 38 | Type Last Modified Read Write Hidden Size Name 39 | ---- ------------- ---- ----- ------ ---- ---- 40 | 41 | Readable: True Writable: True 42 | com.example.junior on (google: 8.1.0) [usb] # android hooking list activities 43 | com.example.junior.BbsActivity 44 | com.example.junior.CalculatorActivity 45 | com.example.junior.CaptureActivity 46 | com.example.junior.ClickActivity 47 | com.example.junior.ColorActivity 48 | com.example.junior.GravityActivity 49 | com.example.junior.IconActivity 50 | com.example.junior.MainActivity 51 | com.example.junior.MarginActivity 52 | com.example.junior.MarqueeActivity 53 | com.example.junior.NineActivity 54 | com.example.junior.PxActivity 55 | com.example.junior.ScaleActivity 56 | com.example.junior.ScreenActivity 57 | com.example.junior.ScrollActivity 58 | com.example.junior.ShapeActivity 59 | com.example.junior.StateActivity 60 | 61 | Found 17 classes 62 | com.example.junior on (google: 8.1.0) [usb] # 63 | 64 | ``` 65 | 66 | 我们发现整个App总共有 17个activity,这里以com.example.junior.CalculatorActivity为例进行分析 67 | 68 | ``` 69 | com.example.junior on (google: 8.1.0) [usb] # android intent launch_activity com.example. 70 | junior.CalculatorActivity 71 | (agent) Starting activity com.example.junior.CalculatorActivity... 72 | (agent) Activity successfully asked to start. 73 | 74 | ``` 75 | 76 | 观察测试机,我们可以发现activity被启动 77 | 78 | 每次点击等号后按钮的 计算结果都会被打印出来,我们找到了对应等号的按钮id为btn_equal,并且根据这个id找到了对饮的源码 79 | 80 | ``` 81 | else if (resid == R.id.btn_equal) { 82 | if (this.operator.length() == 0 || this.operator.equals("=")) { 83 | Toast.makeText(this, "请输入运算符", 0).show(); 84 | } else if (this.nextNum.length() <= 0) { 85 | Toast.makeText(this, "请输入数字", 0).show(); 86 | } else if (caculate()) { 87 | this.operator = inputText; 88 | this.showText += "=" + this.result; 89 | this.tv_result.setText(this.showText); 90 | } 91 | ``` 92 | 93 | 我们发现主要的代码点击后在caculate()方法中,接下来我们需要验证我们的想法 94 | 95 | 通过以下命令以及其执行结果如下: 96 | 97 | ``` 98 | com.example.junior on (google: 8.1.0) [usb] # android hooking list class_methods com.exam 99 | ple.junior.CalculatorActivity 100 | private boolean com.example.junior.CalculatorActivity.caculate() 101 | private void com.example.junior.CalculatorActivity.clear(java.lang.String) 102 | protected void com.example.junior.CalculatorActivity.onCreate(android.os.Bundle) 103 | public void com.example.junior.CalculatorActivity.onClick(android.view.View) 104 | 105 | Found 4 method(s) 106 | 107 | ``` 108 | 109 | 使用如下命令Hook这个函数来确认在点击 “等号”按钮后这个函数被调用了。在Hook上后,任意输入一个表 达式并点击“等号”按钮,会发现这个函数在点击“等号”按钮后被 调用,Hook结果如下 110 | 111 | ``` 112 | com.example.junior on (google: 8.1.0) [usb] # android hooking watch class_method com.exam 113 | ple.junior.CalculatorActivity.caculate --dump-args --dump-backtrace --dump-return 114 | (agent) Attempting to watch class com.example.junior.CalculatorActivity and method caculate. 115 | (agent) Hooking com.example.junior.CalculatorActivity.caculate() 116 | (agent) Registering job 113166. Type: watch-method for: com.example.junior.CalculatorActivity.caculate 117 | com.example.junior on (google: 8.1.0) [usb] # (agent) [113166] Called com.example.junior.CalculatorActivity.caculate() 118 | (agent) [113166] Backtrace: 119 | com.example.junior.CalculatorActivity.caculate(Native Method) 120 | com.example.junior.CalculatorActivity.onClick(CalculatorActivity.java:94) 121 | android.view.View.performClick(View.java:6294) 122 | android.view.View$PerformClick.run(View.java:24770) 123 | android.os.Handler.handleCallback(Handler.java:790) 124 | android.os.Handler.dispatchMessage(Handler.java:99) 125 | android.os.Looper.loop(Looper.java:164) 126 | android.app.ActivityThread.main(ActivityThread.java:6494) 127 | java.lang.reflect.Method.invoke(Native Method) 128 | com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 129 | com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 130 | 131 | (agent) [113166] Return Value: true 132 | ``` 133 | 134 | caculate方法在jadx中显示如下: 135 | 136 | ```java 137 | private boolean caculate() { 138 | if (this.operator.equals("+")) { 139 | this.result = String.valueOf(Arith.add(this.firstNum, this.nextNum)); 140 | } else if (this.operator.equals("-")) { 141 | this.result = String.valueOf(Arith.sub(this.firstNum, this.nextNum)); 142 | } else if (this.operator.equals("×")) { 143 | this.result = String.valueOf(Arith.mul(this.firstNum, this.nextNum)); 144 | } else if (this.operator.equals("÷")) { 145 | if (Double.parseDouble(this.nextNum) == 0.0d) { 146 | Toast.makeText(this, "被除数不能为零", 0).show(); 147 | return false; 148 | } 149 | this.result = String.valueOf(Arith.div(this.firstNum, this.nextNum)); 150 | } 151 | Log.d(TAG, "result=" + this.result); 152 | this.firstNum = this.result; 153 | this.nextNum = BuildConfig.FLAVOR; 154 | return true; 155 | } 156 | ``` 157 | 158 | 对减法的处理是通过调用Arith类中的sub()函数 来实现的。为了验证Arith类在内存中是真实存在的,我们通常使用以 下Objection命令来获取一个应用在内存中的所有类。 159 | 160 | ``` 161 | @android hooking list classes 162 | ``` 163 | 164 | 在运行这行命令后会列出很多类,甚至会超过整个 Terminal缓存空间,这时会出现一些类被缓存冲刷掉的情况,如果只 是简单地在终端窗口里查找,那么不一定能找到。其实Objection本 身有一个log文件,用于记录objection运行时产生的所有数据。这个 日志数据存放在~/.objection目录下的objection.log文件中 165 | 166 | 解 决 方 法 : 在 运 行 objection 注 入 App 之 前 , 首 先 切 换 到 ~/.objection目录下,将之前的objection.log文件删除或者改名 167 | 168 | ``` 169 | (base) ┌──(root㉿kali)-[/] 170 | └─# cd ~/.objection/ 171 | 172 | (base) ┌──(root㉿kali)-[~/.objection] 173 | └─# ls 174 | objection_history objection.log version_info 175 | 176 | (base) ┌──(root㉿kali)-[~/.objection] 177 | └─# mv objection.log objection_old.log 178 | 179 | (base) ┌──(root㉿kali)-[~/.objection] 180 | └─# cat objection.log 181 | cat: objection.log: 没有那个文件或目录 182 | 183 | ``` 184 | 185 | ``` 186 | (base) ┌──(root㉿kali)-[~/.objection] 187 | └─# objection -g com.example.junior explore 188 | Using USB device `Nexus 5X` 189 | Agent injected and responds ok! 190 | 191 | _ _ _ _ 192 | ___| |_|_|___ ___| |_|_|___ ___ 193 | | . | . | | -_| _| _| | . | | 194 | |___|___| |___|___|_| |_|___|_|_| 195 | |___|(object)inject(ion) v1.11.0 196 | 197 | Runtime Mobile Exploration 198 | by: @leonjza from @sensepost 199 | 200 | [tab] for command suggestions 201 | com.example.junior on (google: 8.1.0) [usb] # android hooking list classes 202 | [B 203 | [C 204 | [D 205 | [F 206 | [I 207 | [J 208 | [Landroid.animation.Animator; 209 | [Landroid.animation.Keyframe$FloatKeyframe; 210 | [Landroid.animation.PropertyValuesHolder; 211 | [Landroid.app.LoaderManagerImpl; 212 | [Landroid.arch.lifecycle.Lifecycle$Event; 213 | [Landroid.arch.lifecycle.Lifecycle$State; 214 | [Landroid.content.pm.ActivityInfo; 215 | [Landroid.content.pm.ConfigurationInfo; 216 | [Landroid.content.pm.FeatureGroupInfo; 217 | [Landroid.content.pm.FeatureInfo; 218 | [Landroid.content.pm.InstrumentationInfo; 219 | [Landroid.content.pm.PathPermission; 220 | [Landroid.content.pm.PermissionInfo; 221 | [Landroid.content.pm.ProviderInfo; 222 | [Landroid.content.pm.ServiceInfo; 223 | [Landroid.content.pm.Signature; 224 | [Landroid.content.res.Configuration; 225 | [Landroid.content.res.StringBlock; 226 | [Landroid.content.res.XmlBlock; 227 | [Landroid.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus; 228 | [Landroid.graphics.Bitmap$Config; 229 | ...... 230 | ...... 231 | sun.util.locale.LanguageTag 232 | sun.util.locale.LocaleObjectCache 233 | sun.util.locale.LocaleObjectCache$CacheEntry 234 | sun.util.locale.LocaleSyntaxException 235 | sun.util.locale.LocaleUtils 236 | sun.util.locale.ParseStatus 237 | sun.util.locale.StringTokenIterator 238 | sun.util.logging.LoggingProxy 239 | sun.util.logging.LoggingSupport 240 | sun.util.logging.LoggingSupport$1 241 | sun.util.logging.PlatformLogger 242 | sun.util.logging.PlatformLogger$1 243 | sun.util.logging.PlatformLogger$Level 244 | void 245 | 246 | Found 5233 classes 247 | ``` 248 | 249 | 在遍历完成后退出Objection注入模式以确保log文件刷新成功, 并重新通过cat命令查看这个objection.log文件,由于log文件过大, 因此还需要配合grep命令过滤文本,从而通过观察结果是否有输出来 判定内存中是否存在目标类Arith 250 | 251 | 在判定内存中确实存在Arith类后,我们进一步通过Objection命 令判断Arith类是否存在sub()函数 252 | 253 | ``` 254 | (base) ┌──(root㉿kali)-[~/.objection] 255 | └─# cat objection.log | grep com.example.junior.util.Arith 256 | 257 | ``` 258 | 259 | ``` 260 | com.example.junior on (google: 8.1.0) [usb] # android hooking list class_methods com.exampl 261 | e.junior.util.Arith 262 | public static java.lang.String com.example.junior.util.Arith.add(double,double) 263 | public static java.lang.String com.example.junior.util.Arith.add(java.lang.String,java.lang.String) 264 | public static java.lang.String com.example.junior.util.Arith.div(double,double) 265 | public static java.lang.String com.example.junior.util.Arith.div(double,double,int) 266 | public static java.lang.String com.example.junior.util.Arith.div(java.lang.String,java.lang.String) 267 | public static java.lang.String com.example.junior.util.Arith.div(java.lang.String,java.lang.String,int) 268 | public static java.lang.String com.example.junior.util.Arith.mul(double,double) 269 | public static java.lang.String com.example.junior.util.Arith.mul(java.lang.String,java.lang.String) 270 | public static java.lang.String com.example.junior.util.Arith.round(double,int) 271 | public static java.lang.String com.example.junior.util.Arith.sub(double,double) 272 | public static java.lang.String com.example.junior.util.Arith.sub(java.lang.String,java.lang.String) 273 | 274 | Found 11 method(s) 275 | 276 | ``` 277 | 278 | 在内存中确定这个函数存在后,便可以使用如下命令对这个函数 进行Hook了 279 | 280 | ``` 281 | com.example.junior on (google: 8.1.0) [usb] # android hooking watch class_method com.exampl 282 | e.junior.util.Arith.sub --dump-args --dump-backtrace --dump-return 283 | (agent) Attempting to watch class com.example.junior.util.Arith and method sub. 284 | (agent) Hooking com.example.junior.util.Arith.sub(double, double) 285 | (agent) Hooking com.example.junior.util.Arith.sub(java.lang.String, java.lang.String) 286 | (agent) Registering job 304689. Type: watch-method for: com.example.junior.util.Arith.sub 287 | com.example.junior on (google: 8.1.0) [usb] # (agent) [304689] Called com.example.junior.util.Arith.sub(java.lang.String, java.lang.String) 288 | (agent) [304689] Backtrace: 289 | com.example.junior.util.Arith.sub(Native Method) 290 | com.example.junior.CalculatorActivity.caculate(CalculatorActivity.java:169) 291 | com.example.junior.CalculatorActivity.onClick(CalculatorActivity.java:94) 292 | android.view.View.performClick(View.java:6294) 293 | android.view.View$PerformClick.run(View.java:24770) 294 | android.os.Handler.handleCallback(Handler.java:790) 295 | android.os.Handler.dispatchMessage(Handler.java:99) 296 | android.os.Looper.loop(Looper.java:164) 297 | android.app.ActivityThread.main(ActivityThread.java:6494) 298 | java.lang.reflect.Method.invoke(Native Method) 299 | com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 300 | com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 301 | 302 | (agent) [304689] Arguments com.example.junior.util.Arith.sub(7, 9) 303 | (agent) [304689] Return Value: -2 304 | 305 | ``` 306 | 307 | ## Frida脚本修改参数、主动调用 308 | 309 | 确认了Arith类的函数sub是最终计算器减法的真实执行函数,接下来通过Frida脚本进行进一步的利用。 310 | 311 | Hook脚本如下: 312 | 313 | ```js 314 | function main(){ 315 | Java.perform(function(){ 316 | var Arith = Java.use('com.example.junior.util.Arith') 317 | Arith.sub.implementation = function(str,str2){ 318 | var result = this.sub(str,str2) 319 | console.log('str,str2,result =>',str,str2,result) 320 | //打印java调用栈 321 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())) 322 | return result 323 | } 324 | }) 325 | } 326 | setImmediate(main) 327 | ``` 328 | 329 | 打印Java调用栈的代码就是将Android开发中获取调用栈的函数Log.getStackTraceString(Throwable e)翻译为JavaScript语言。 330 | 331 | 在 使 用 Frida 将 代 码 注 入 App 前 , 我 们 先 取 消 Objection 的 Hook,然后进行Frida脚本的注入。这里取消Objection对目标函数 的Hook是因为不能使用Objection和Frida对同一个函数进行Hook, 否则会报错。 332 | 333 | ``` 334 | frida -UF -l hook1.js 335 | ``` 336 | 337 | ``` 338 | (base) ┌──(root㉿kali)-[/home/zhy/VscodeWorkspace/Junior_hook] 339 | └─# frida -UF -l hook1.js 340 | ____ 341 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 342 | | (_| | 343 | > _ | Commands: 344 | /_/ |_| help -> Displays the help system 345 | . . . . object? -> Display information about 'object' 346 | . . . . exit/quit -> Exit 347 | . . . . 348 | . . . . More info at https://frida.re/docs/home/ 349 | . . . . 350 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 351 | Error: sub(): has more than one overload, use .overload() to choose from: 352 | .overload('double', 'double') 353 | .overload('java.lang.String', 'java.lang.String') 354 | at X (frida/node_modules/frida-java-bridge/lib/class-factory.js:569) 355 | at K (frida/node_modules/frida-java-bridge/lib/class-factory.js:564) 356 | at set (frida/node_modules/frida-java-bridge/lib/class-factory.js:932) 357 | at (/frida/repl-2.js:3) 358 | at (frida/node_modules/frida-java-bridge/lib/vm.js:12) 359 | at _performPendingVmOps (frida/node_modules/frida-java-bridge/index.js:250) 360 | at (frida/node_modules/frida-java-bridge/index.js:225) 361 | at (frida/node_modules/frida-java-bridge/lib/vm.js:12) 362 | at _performPendingVmOpsWhenReady (frida/node_modules/frida-java-bridge/index.js:244) 363 | at perform (frida/node_modules/frida-java-bridge/index.js:204) 364 | at main (/frida/repl-2.js:11) 365 | at apply (native) 366 | at (frida/runtime/core.js:51) 367 | [Nexus 5X::junior ]-> 368 | ``` 369 | 370 | 从这里我们可以发现sub函数进行了重载,所以我们需要修改脚本加入对于重载的选择 371 | 372 | ``` 373 | (base) ┌──(root㉿kali)-[/home/zhy/VscodeWorkspace/Junior_hook] 374 | └─# frida -UF -l hook1.js 375 | ____ 376 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 377 | | (_| | 378 | > _ | Commands: 379 | /_/ |_| help -> Displays the help system 380 | . . . . object? -> Display information about 'object' 381 | . . . . exit/quit -> Exit 382 | . . . . 383 | . . . . More info at https://frida.re/docs/home/ 384 | . . . . 385 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 386 | [Nexus 5X::junior ]-> str,str2,result => 7 2 -116 387 | java.lang.Throwable 388 | at com.example.junior.util.Arith.sub(Native Method) 389 | at com.example.junior.CalculatorActivity.caculate(CalculatorActivity.java:169) 390 | at com.example.junior.CalculatorActivity.onClick(CalculatorActivity.java:94) 391 | at android.view.View.performClick(View.java:6294) 392 | at android.view.View$PerformClick.run(View.java:24770) 393 | at android.os.Handler.handleCallback(Handler.java:790) 394 | at android.os.Handler.dispatchMessage(Handler.java:99) 395 | at android.os.Looper.loop(Looper.java:164) 396 | at android.app.ActivityThread.main(ActivityThread.java:6494) 397 | at java.lang.reflect.Method.invoke(Native Method) 398 | at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 399 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 400 | 401 | 402 | 403 | ``` 404 | 405 | 我们会发现1-7的结果,APP页面的最终结果变成了-122 406 | 407 | 这里的123直接传递实际上是有问题的,正确的传入代码如下: 408 | 409 | ```js 410 | function main(){ 411 | Java.perform(function(){ 412 | var Arith = Java.use('com.example.junior.util.Arith') 413 | Arith.sub.overload("java.lang.String","java.lang.String").implementation = function(str,str2){ 414 | //Arith.sub.implementation = function(str,str2){ 415 | 416 | //var result = this.sub(str,"123") 417 | var JavaString = Java.use('java.lang.String') 418 | var result = this.sub(str,JavaString.$new('123')) 419 | console.log('str,str2,result =>',str,str2,result) 420 | //打印java调用栈 421 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())) 422 | return result 423 | } 424 | }) 425 | } 426 | setImmediate(main) 427 | ``` 428 | 429 | ``` 430 | (base) ┌──(root㉿kali)-[/home/zhy/VscodeWorkspace/Junior_hook] 431 | └─# frida -UF -l hook1.js 432 | ____ 433 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 434 | | (_| | 435 | > _ | Commands: 436 | /_/ |_| help -> Displays the help system 437 | . . . . object? -> Display information about 'object' 438 | . . . . exit/quit -> Exit 439 | . . . . 440 | . . . . More info at https://frida.re/docs/home/ 441 | . . . . 442 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 443 | [Nexus 5X::junior ]-> str,str2,result => 4 9 -119 444 | java.lang.Throwable 445 | at com.example.junior.util.Arith.sub(Native Method) 446 | at com.example.junior.CalculatorActivity.caculate(CalculatorActivity.java:169) 447 | at com.example.junior.CalculatorActivity.onClick(CalculatorActivity.java:94) 448 | at android.view.View.performClick(View.java:6294) 449 | at android.view.View$PerformClick.run(View.java:24770) 450 | at android.os.Handler.handleCallback(Handler.java:790) 451 | at android.os.Handler.dispatchMessage(Handler.java:99) 452 | at android.os.Looper.loop(Looper.java:164) 453 | at android.app.ActivityThread.main(ActivityThread.java:6494) 454 | at java.lang.reflect.Method.invoke(Native Method) 455 | at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 456 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 457 | 458 | 459 | 460 | ``` 461 | 462 | 对sub()函数的调用,我们会发现在Java 中是直接通过Arith类对象来完成对sub()函数的调用。如果还是无法 确认,则可以通过将Objection注入到应用中,再使用如下命令打印 出Airth类的所有函数: 463 | 464 | ``` 465 | om.example.junior on (google: 8.1.0) [usb] # android hooking list class_methods com.exam 466 | ple.junior.util.Arith 467 | public static java.lang.String com.example.junior.util.Arith.add(double,double) 468 | public static java.lang.String com.example.junior.util.Arith.add(java.lang.String,java.lang.String) 469 | public static java.lang.String com.example.junior.util.Arith.div(double,double) 470 | public static java.lang.String com.example.junior.util.Arith.div(double,double,int) 471 | public static java.lang.String com.example.junior.util.Arith.div(java.lang.String,java.lang.String) 472 | public static java.lang.String com.example.junior.util.Arith.div(java.lang.String,java.lang.String,int) 473 | public static java.lang.String com.example.junior.util.Arith.mul(double,double) 474 | public static java.lang.String com.example.junior.util.Arith.mul(java.lang.String,java.lang.String) 475 | public static java.lang.String com.example.junior.util.Arith.round(double,int) 476 | public static java.lang.String com.example.junior.util.Arith.sub(double,double) 477 | public static java.lang.String com.example.junior.util.Arith.sub(java.lang.String,java.lang.String) 478 | 479 | Found 11 method(s) 480 | 481 | ``` 482 | 483 | 我们可以发现后面两条,然后编写函数调用的脚本 484 | 485 | ```js 486 | function callSub(){ 487 | Java.perform(function(){ 488 | var Arith = Java.use('com.example.junior.util.Arith') 489 | var JavaString = Java.use('java.lang.String') 490 | var result = Arith.sub(JavaString.$new("123"),JavaString.$new("111")) 491 | console.log("123-111=",result) 492 | }) 493 | } 494 | setImmediate(callSub) 495 | ``` 496 | 497 | 结果如下: 498 | 499 | ``` 500 | (base) ┌──(root㉿kali)-[/home/zhy/VscodeWorkspace/Junior_hook] 501 | └─# frida -UF -l hook2.js 502 | ____ 503 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 504 | | (_| | 505 | > _ | Commands: 506 | /_/ |_| help -> Displays the help system 507 | . . . . object? -> Display information about 'object' 508 | . . . . exit/quit -> Exit 509 | . . . . 510 | . . . . More info at https://frida.re/docs/home/ 511 | . . . . 512 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 513 | 123-111= 12 514 | [Nexus 5X::junior ]-> 515 | 516 | ``` 517 | 518 | ## 规模化利用RPC 519 | 520 | 批量数据的调用,就需要修改原有的主动调用脚本call.js 的内容,将原本只调用一次的sub()函数修改为可以调用多次的格式, 并且需要将完成主动调用的函数修改为导出的rpc函数。具体脚本如下: 521 | 522 | ``` 523 | function callSub(){ 524 | Java.perform(function(){ 525 | var Arith = Java.use('com.example.junior.util.Arith') 526 | var JavaString = Java.use('java.lang.String') 527 | var result = Arith.sub(JavaString.$new(a),JavaString.$new(b)) 528 | console.log(a,"-",b,"=",result) 529 | }) 530 | } 531 | rpc.exports = { 532 | sub : callSub, 533 | }; 534 | 535 | ``` 536 | 537 | python脚本如下: 538 | 539 | ``` 540 | import frida,sys 541 | def on_message(message,data): 542 | if message['type']=='send': 543 | print("[*]{0}".format(message['payload'])) 544 | else: 545 | print(message) 546 | 547 | device = frida.get_usb_device() 548 | 549 | process = device.attach('com.example.junior') 550 | with open('call.js')as f: 551 | jscode = f.read() 552 | script = process.create_script(jscode) 553 | 554 | script.on('message',on_message) 555 | script.load() 556 | 557 | for i in range(20,30): 558 | for j in range(0,10): 559 | script.exports.sub(str(i),str(j)) 560 | ``` 561 | 562 | 563 | 可以简单总结如下:第一步,使用Objection快速Hook 定位;第二步,通过Frida脚本进行关键函数的逻辑修改与主动调用;第三步,将Frida脚本的主动调用结合Python完成了对关键函数的大规模实例利用。 564 | -------------------------------------------------------------------------------- /94-APP攻防博弈.md: -------------------------------------------------------------------------------- 1 | # APP攻防博弈 2 | 3 | 以下内容摘自《安卓Frida逆向与抓包实战》第5章 4 | 5 | ```sh 6 | #查看文件类型 7 | file [APK文件] 8 | 9 | #解压 10 | unzip 11 | 12 | #查看压缩文件 13 | tree 14 | 15 | #安装 16 | adb install 17 | #有时会报错INSTALL_PARSE_FAILED_NO_CERTIFICATES这种没有签名的错误,而META-INF文件夹主要就是用于存储签名文件的。这里值得一提的是,Android本身的签名十分简单,和Apple签名机制严格由Apple官方控制不同,由于Android本身的开源导致Android市场的碎片化,Android最终也就没有限制App的签名方身份,而这一点在促进Android发展的同时也为重打包的技术创造了便利。 18 | ``` 19 | 20 | Smali语言,因为Android中的代码是运行在 Dalvik或Art虚拟机中的,与ARM汇编类似,而Smali语言可以当作 Android虚拟机的汇编语言。 21 | 22 | 目前apktool是Android逆向领域一个具有里程碑的神器,像Jadx、Jeb。没有保护的情况下想要破解一个软件,只要使用apktool等反编译工具就可以清楚且完整地看到一款产品的原始代码,观察到的代码几乎和源码一模一样。 23 | 24 | 应对这样的破解手段,开发人员开始利用各种各样的手段保 护自己的源代码。其中最常用的手段就是“代码混淆” ,例如使用 Google自带的混淆器ProGuard。ProGuard对安全领域来说只有一 个作用,就是更改类名、函数名和变量名。这样做的结果主要有两个 好处:一个是符号混淆,将开发者为了代码可读性所编写的类似于 SplashActivity、LoginActivity这种有意义的名称改成a、b这样无意 义的名称,以增加攻击者在破解软件时猜到相应类的实际作用的难 度;另一个是压缩文件大小,毕竟对于动辄成千上万的类,其符号字 符串在dex文件中的字节占比并不小。在Android Studio中要启用 ProGuard,只需要修改项目根目录下的App/build.gradle文件将其中buildTypes层级中minifyEnabled对应的值从 false更改为true即可,这样最终编译生成的release版本便会启用代 码混淆了。 25 | 26 | 还有一个具有代表性的“代码混淆”工具——DexGuard。 DexGuard与ProGuard是同一个开发者开发的,与ProGuard相比, DexGuard功能更加丰富,不过它是一个收费的商业软件。同时, DexGuard的混淆功能更加强大,不仅支持ProGuard的所有功能, 还支持字符串加密、花指令、资源加密等。发展到现在,DexGuard 甚至还添加了一些运行库的防护,已经不是一个单纯的代码优化器 了。 27 | 28 | 动态加载方案。所谓 动态加载,就是将需要保护的代码单独编译成一个二进制文件,将其 进行加密后保存在一个外部的二进制文件中。在外部程序运行的过程中,再将被保护的二进制文件进行解密并使用ClassLoader类加载器 来动态加载和运行被保护的代码。so库。 29 | 30 | 鉴于在Android中使用Java编写的App很容易被破解, 故反其道而行之,将所有的核心代码使用NDK套件(在Android中, NDK实际上是一个工具集,可以让开发者使用C/C++语言实现应用 的各个部分)进行开发,这样的结果往往是外部的Java代码最终只是 充当了一个二进制文件装载器的角色,实际的业务逻辑都被放置在更 难破解的so文件中。当然,基于“放置在客户端”都不可靠的原则, 为了App的安全,有的App甚至将重要的功能和数据都放置到云端, 在客户端尽量只进行结果的展示。 31 | 32 | 动态分析,在上面提到的最直接有效且低成本的动态加载保 护就变成最脆弱的一种保护方式。只要通过动态分析,不管是在加载 后的函数中设置断点以便从内存dump(转储)出来被保护的内容, 还是直接搜索进程的内存空间以便根据特征找到真实的dex文件并 dump下来,都能很轻松地应对这种保护方式。另外,针对其他静态 保护方式,由于动态分析是基于进程所处的运行状态,因此相比于在 静态分析时得到的无意义代码而在动态分析时包含的都是真实的数据 信息,这使得破解者在逆向分析的过程中有了很多真实的数据,从而 大大削减了代码保护的作用。 33 | 34 | 在动态分析过程中,首先需要将调试器附加上进程或者通过注入 将指令代码和数据注入目标进程中,然后才能对目标进程进行调试与 内存监控。要做到这一步,最基本的方法就是调用ptrace()函数对进 程进行附加或者是基于二次打包的方式对程序进行修改,而对抗的手 段主要分为两种:运行时检测和事先阻止。 35 | 36 | 还可以根据调试器在附加手机上进程的时候需要运行Server端进 行配合通信的这一特征,通过判断系统进程中是否存在Server相关的 进程名进行检测,或者利用Server和调试器之间需要进行通信的特性 监控这些Server打开时需要默认监听的端口进行检测。这种反调试手 段不仅仅针对IDA这类调试器有效,而且针对Frida也同样有效。 37 | 38 | 除此之外,针对调试器还可以通过指令执行时间差进行检测,因 为如果一段代码正在被调试,那么这段代码执行的耗时比正常执行所 消耗的时间会更长。 39 | 40 | 经典的双进程保护。双进程保护是利用一 个进程最多只能被一个进程ptrace附加的特性,事先在代码中自己 fork一个子进程ptrace,ptrace自己就可以防止再被其他进程 ptrace,也就变相地阻止了进程被调试。 41 | 42 | 在App逆向分析过程中最难绕过的保护手段就是App 加固。所谓App加固,其逻辑和上面讲述的动态加载类似,用加固厂 商的壳程序包裹真实的App,在真实动态运行时再通过壳程序执行释 放出来的真正App。App加固发展出各种各样的加固手段。在业界曾 有人根据加固手法将加固分成了五个不同的阶段,近年来已逐渐将加 固的五个阶段进一步按照不同阶段加固的特性重新分为三个不同阶 段。 43 | 44 | 第一个阶段被认为是DEX整体加固,这是App加固的初期。这时 App加固的核心原理是将DEX整体加密后动态加载,这一点在上面讲 动态加载保护技术时介绍过。刚开始,App整体加固是需要先解密文 件并在解密完成后写入到另外一个文件中,在解密完毕后调用 DexClassLoader或者其他类加载函数来加载解密后的文件。DEX整体加固的致命之处在于,代码数据总是结构完整地存储在 一段内存中,一旦反注入、反调试等措施被破解,这种保护就门户洞 开了。于是出现了第二代代码保护机制。 45 | 46 | 第二代代码保护习惯上被称为代码抽取保护。这一阶段App加固 的关键在于真正的代码数据并不与DEX的整体结构数据存储在一起, 就算DEX被完整地从内存中dump(转储)出来,也无法看到真正的 函数代码。 47 | 48 | 比如:某DEX整体加固被dump出来之后,使 用Jadx查看关键函数,发现其代码被nop这一无意义的代码填充了。 这种加固的核心原理是利用私有函数,通过对自身进程的Hook来拦 截函数被调用时的路径,在抽取的函数被真实调用之前,将无意义的 代码数据填充到对应的代码区中。代码抽取技术的出现从根本上解决了第一代整体加固保护的缺 陷,同时也正式宣告全手工单步调试脱壳时代的彻底终结。代码抽取 技术存在一定的兼容性和性能损耗的问题,为了顾及这一问题,正常 的加固手段并不会对App中所有函数进行抽取保护,特别是对一些无 关核心的第三方库的代码就不必进行代码抽取保护。另外,考虑到性 能问题,代码抽取保护通常在函数被第一次调用后就不再将函数内容 重新置空,这也正是如今依然存在一些从内存中把DEX整体dump出 来的方案,因为只需要在App运行时多触发几次程序逻,然后再进行 DEX的dump,即可得到更加完整的DEX文件 49 | 50 | DexHunter,其原理就是通过主动加载DEX中的所有类并dump出所 有方法对应的代码,最后将代码重构再填充回被抽取的DEX中。为了 对抗DexHunter,有的代码抽取方案在类加载的时并不恢复函数的代 码内容,而将恢复的时机进一步延后,这也就引出了后来的主动调用 方案——FUPK3/FART。FUPK3/FART的原理是对执行方法的入口 函数进行插桩操作,并在入口函数开始处判断是否带有主动调用的标 志,若属于主动调用则dump出相应函数的内容,再进行DEX文件的 重构。为了对抗这类脱壳的方式,加固厂商也采取过一些反制手段, 比如为App添加一个垃圾类,一旦这个垃圾类被加载就退出进程的执 行;亦或是采取监控特定文件读写的方式,比如一旦监控到进程要把 以dex035开头的文件内容dump出,就杀死进程,诸如此类。 51 | 52 | 第三代保护手段有一个重要的特点,就是将所有的Java代码变成 最终的Native层代码。两者不同的是VMP加固技术最早起源于PC的 虚拟机加固,其核心逻辑是将所有的代码使用自定义的解释器执行。 这时的代码不再依赖于系统本身,即使获得了所有的函数内容,也是 貌合神离、不知所云。这时唯一的解决方案可能是逆向对应的解释 器,以找到与系统解释器的映射关系。 53 | -------------------------------------------------------------------------------- /96-对未加固APP进行分析和破解.md: -------------------------------------------------------------------------------- 1 | # 对未加固APP进行分析和破解 2 | 3 | 参考《安卓Frida逆向与抓包实战》 4 | 5 | 目标是去除一个未加固的APP的升级提示弹窗zhibo.apk 6 | 7 | 首先确保手机处于USB调试状态 8 | 9 | ```sh 10 | adb devices 11 | ``` 12 | 13 | APK文件位于我的仓库中的apk文件夹下 14 | 15 | 首先安装apk文件到测试机 16 | 17 | ``` 18 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap5] 19 | └─# adb install zhibo.apk 20 | Performing Streamed Install 21 | Success 22 | ``` 23 | 24 | 通过Jadx打开APP,搜索关键词“升级”这里在搜索关键词中只得到一个结果,十分低效,一旦App进行 了加固处理或者关键词在App中出现结果过多,那么搜索关键词的这 个方法将使得分析者消耗大量的时间在无关的代码上,正确的方法应 该是从开发者的角度思考。 25 | 26 | ![](https://github.com/G-WS/Android-reverse/blob/main/image/%E6%9C%AA%E5%8A%A0%E5%9B%BA1.png?raw=true) 27 | 28 | App的升级提示是以一个弹窗来实现 的 。 在 Android 中 常 用 的 实 现 弹 窗 的 类 主 要 有 三 种 : android.App.Dialog 、 android.App.AlertDialog 和 android.widget.PopupWindow。 29 | 30 | 因此这里使用Objection对调用弹窗类的函数进行快速定位。 31 | 32 | 要使用Objection进行测试,首先需要获取App的包名。这里选 择使用Jadx反编译App并打开AndroidManifest.xml文件查看App包 名 33 | 34 | ``` 35 | 36 | ``` 37 | 38 | 在手机上启动frida 39 | 40 | ``` 41 | (base) ┌──(zhy㉿kali)-[~/桌面] 42 | └─$ su 43 | 密码: 44 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 45 | └─# adb shell 46 | bullhead:/ $ su 47 | bullhead:/ # cd data/local/tmp/ 48 | bullhead:/data/local/tmp # ls 49 | frida-server-14.2.17-android-arm64 oat 50 | frida-server-15.2.2-android-arm64 re.frida.server 51 | bullhead:/data/local/tmp # ./f 52 | frida-server-14.2.17-android-arm64 frida-server-15.2.2-android-arm64 53 | /frida-server-15.2.2-android-arm64 < 54 | ``` 55 | 56 | ``` 57 | ┌──(root💀r0env)-[~/Desktop] 58 | └─# objection -g com.hd.zhibo explore 59 | Using USB device `Nexus 5X` 60 | Agent injected and responds ok! 61 | 62 | _ _ _ _ 63 | ___| |_|_|___ ___| |_|_|___ ___ 64 | | . | . | | -_| _| _| | . | | 65 | |___|___| |___|___|_| |_|___|_|_| 66 | |___|(object)inject(ion) v1.11.0 67 | 68 | Runtime Mobile Exploration 69 | by: @leonjza from @sensepost 70 | 71 | [tab] for command suggestions 72 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.App.AlertDialog 73 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.App.AlertDialog 74 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.app.AlertDialog 75 | Class instance enumeration complete for android.app.AlertDialog 76 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.app.AlertDialog 77 | Class instance enumeration complete for android.app.AlertDialog 78 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.app.Dialog 79 | Class instance enumeration complete for android.app.Dialog 80 | com.hd.zhibo on (google: 8.1.0) [usb] # android heap search instances android.widget.PopupWindow 81 | Class instance enumeration complete for android.widget.PopupWindow 82 | Hashcode Class toString() 83 | ---------- -------------------------- --------------------------------- 84 | 8378703 android.widget.PopupWindow android.widget.PopupWindow@7fd94f 85 | 86 | ``` 87 | 88 | 进一步确认弹窗类型用一个Objection的插件WallBreaker( 代 码 仓 库 地 址 为 https://github.com/hluwa/Wallbreaker ) 89 | 90 | 。要使用Wallbreaker,仅需 将Wallbreaker下载到本地,再使用Objection加载插件的命令将其 加载即可,具体命令如下: 91 | 92 | ``` 93 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 94 | └─# git clone https://github.com/hluwa/Wallbreaker ~/.objection/plugins/Wallbreaker 95 | 96 | ``` 97 | 98 | 通过-P命令在注入应用时加载插件 99 | 100 | ``` 101 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 102 | └─# objection -g com.hd.zhibo explore -P ~/.objection/plugins/ 103 | Using USB device `Nexus 5X` 104 | Agent injected and responds ok! 105 | Loaded plugin: wallbreaker 106 | 107 | _ _ _ _ 108 | ___| |_|_|___ ___| |_|_|___ ___ 109 | | . | . | | -_| _| _| | . | | 110 | |___|___| |___|___|_| |_|___|_|_| 111 | |___|(object)inject(ion) v1.11.0 112 | 113 | Runtime Mobile Exploration 114 | by: @leonjza from @sensepost 115 | 116 | [tab] for command suggestions 117 | com.hd.zhibo on (google: 8.1.0) [usb] # 118 | 119 | ``` 120 | 121 | 插件中只要使用两个命令 122 | 123 | > objectsearch命令和Objection的 heap search命令的作用是相同的;objectdump命令则是用于将特 定实例中的具体内容打印出来。 124 | 125 | 以android.App.AlertDialog为例,先用objectsearch命令搜索 相关的实例;在搜索到实例后,再使用打印出来的十六进制值执行 objectdump命令。在第一步执行完objectsearch命令后就可以发现 内存中存在一个android.App.AlertDialog实例,在搜索到实例后再 使用这一步得到的十六进制的handle:0x2582去执行objectdump 命令,便可将0x2582所代表实例的内容打印出来 126 | 127 | 在打印结果中,注意在“=>”符号后的都是相应变量对应的值。 128 | 129 | ``` 130 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 131 | └─# objection -g com.hd.zhibo explore -P ~/.objection/plugins/ 132 | Using USB device `Nexus 5X` 133 | Agent injected and responds ok! 134 | Loaded plugin: wallbreaker 135 | 136 | _ _ _ _ 137 | ___| |_|_|___ ___| |_|_|___ ___ 138 | | . | . | | -_| _| _| | . | | 139 | |___|___| |___|___|_| |_|___|_|_| 140 | |___|(object)inject(ion) v1.11.0 141 | 142 | Runtime Mobile Exploration 143 | by: @leonjza from @sensepost 144 | 145 | [tab] for command suggestions 146 | com.hd.zhibo on (google: 8.1.0) [usb] # plugin wallbreaker objectsearch android.app.AlertDialog 147 | com.hd.zhibo on (google: 8.1.0) [usb] # plugin wallbreaker objectsearch android.widget.PopupWindow 148 | [0x217a]: android.widget.PopupWindow@303bde5 149 | com.hd.zhibo on (google: 8.1.0) [usb] # 150 | [0x217a]: android.widget.PopupWindow@303bde5 151 | com.hd.zhibo on (google: 8.1.0) [usb] # plugin wallbreaker objectdump 0x217a 152 | 153 | package android.widget 154 | 155 | class PopupWindow { 156 | 157 | /* static fields */ 158 | static I[] ABOVE_ANCHOR_STATE_SET; => [0x46a2]: 16842922 159 | static int ANIMATION_STYLE_DEFAULT; => -1 160 | static int DEFAULT_ANCHORED_GRAVITY; => 8388659 161 | static int INPUT_METHOD_FROM_FOCUSABLE; => 0 162 | static int INPUT_METHOD_NEEDED; => 1 163 | static int INPUT_METHOD_NOT_NEEDED; => 2 164 | 165 | /* instance fields */ 166 | boolean mAboveAnchor; => false 167 | Drawable mAboveAnchorBackgroundDrawable; => null 168 | boolean mAllowScrollingAnchorParent; => true 169 | WeakReference mAnchor; => null 170 | WeakReference mAnchorRoot; => null 171 | int mAnchorXoff; => 0 172 | int mAnchorYoff; => 0 173 | int mAnchoredGravity; => 0 174 | int mAnimationStyle; => -1 175 | boolean mAttachedInDecor; => false 176 | boolean mAttachedInDecorSet; => true 177 | Drawable mBackground; => [0x466a]: android.graphics.drawable.ColorDrawable@91d0ba 178 | View mBackgroundView; => null 179 | Drawable mBelowAnchorBackgroundDrawable; => null 180 | boolean mClipToScreen; => false 181 | boolean mClippingEnabled; => true 182 | View mContentView; => [0x463a]: com.zhibo.widget.linearlayout{58abb6b V.E...... ......I. 0,0-0,0} 183 | Context mContext; => [0x460a]: com.zhibo.media.channel_main@ee96c8 184 | PopupWindow$PopupDecorView mDecorView; => null 185 | float mElevation; => 0 186 | Transition mEnterTransition; => null 187 | Rect mEpicenterBounds; => null 188 | Transition mExitTransition; => null 189 | boolean mFocusable; => true 190 | int mGravity; => 0 191 | int mHeight; => -2 192 | int mHeightMode; => 0 193 | boolean mIgnoreCheekPress; => false 194 | int mInputMethodMode; => 0 195 | boolean mIsAnchorRootAttached; => false 196 | boolean mIsDropdown; => false 197 | boolean mIsShowing; => false 198 | boolean mIsTransitioningToDismiss; => false 199 | int mLastHeight; => 0 200 | int mLastWidth; => 0 201 | boolean mLayoutInScreen; => false 202 | boolean mLayoutInsetDecor; => false 203 | boolean mNotTouchModal; => false 204 | View$OnAttachStateChangeListener mOnAnchorDetachedListener; => [0x45da]: android.widget.PopupWindow$1@e375361 205 | View$OnAttachStateChangeListener mOnAnchorRootDetachedListener; => [0x45e2]: android.widget.PopupWindow$2@6fc3e86 206 | PopupWindow$OnDismissListener mOnDismissListener; => null 207 | View$OnLayoutChangeListener mOnLayoutChangeListener; => [0x459a]: android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4@617a347 208 | ViewTreeObserver$OnScrollChangedListener mOnScrollChangedListener; => [0x456a]: android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4$1@bba5774 209 | boolean mOutsideTouchable; => false 210 | boolean mOverlapAnchor; => false 211 | WeakReference mParentRootView; => null 212 | boolean mPopupViewInitialLayoutDirectionInherited; => false 213 | int mSoftInputMode; => 1 214 | int mSplitTouchEnabled; => -1 215 | Rect mTempRect; => [0x453a]: Rect(0, 0 - 0, 0) 216 | I[] mTmpAppLocation; => [0x4542]: 0,0 217 | I[] mTmpDrawingLocation; => [0x452a]: 0,0 218 | I[] mTmpScreenLocation; => [0x451a]: 0,0 219 | View$OnTouchListener mTouchInterceptor; => null 220 | boolean mTouchable; => true 221 | int mWidth; => 495 222 | int mWidthMode; => 0 223 | int mWindowLayoutType; => 1000 224 | WindowManager mWindowManager; => [0x44da]: android.view.WindowManagerImpl@cb4c49d 225 | 226 | /* constructor methods */ 227 | android.widget.PopupWindow(); 228 | android.widget.PopupWindow(int, int); 229 | android.widget.PopupWindow(Context); 230 | android.widget.PopupWindow(Context, AttributeSet); 231 | android.widget.PopupWindow(Context, AttributeSet, int); 232 | android.widget.PopupWindow(Context, AttributeSet, int, int); 233 | android.widget.PopupWindow(View); 234 | android.widget.PopupWindow(View, int, int); 235 | android.widget.PopupWindow(View, int, int, boolean); 236 | 237 | /* static methods */ 238 | static I[] -get0(); 239 | static boolean -get1(PopupWindow); 240 | static WeakReference -get2(PopupWindow); 241 | static View$OnTouchListener -get3(PopupWindow); 242 | static boolean -set0(PopupWindow, boolean); 243 | static void -wrap0(PopupWindow); 244 | static void -wrap1(PopupWindow, View, ViewGroup, View); 245 | 246 | /* instance methods */ 247 | void alignToAnchor(); 248 | int computeAnimationResource(); 249 | int computeFlags(int); 250 | int computeGravity(); 251 | PopupWindow$PopupBackgroundView createBackgroundView(View); 252 | PopupWindow$PopupDecorView createDecorView(View); 253 | void dismissImmediate(View, ViewGroup, View); 254 | View getAppRootView(View); 255 | Transition getTransition(int); 256 | void invokePopup(WindowManager$LayoutParams); 257 | boolean positionInDisplayHorizontal(WindowManager$LayoutParams, int, int, int, int, int, boolean); 258 | boolean positionInDisplayVertical(WindowManager$LayoutParams, int, int, int, int, int, boolean); 259 | void preparePopup(WindowManager$LayoutParams); 260 | void setLayoutDirectionFromAnchor(); 261 | boolean tryFitHorizontal(WindowManager$LayoutParams, int, int, int, int, int, int, int, boolean); 262 | boolean tryFitVertical(WindowManager$LayoutParams, int, int, int, int, int, int, int, boolean); 263 | void update(View, boolean, int, int, int, int); 264 | void update(); 265 | void update(int, int); 266 | void update(int, int, int, int); 267 | void update(int, int, int, int, boolean); 268 | void update(View, int, int); 269 | void update(View, int, int, int, int); 270 | void update(View, WindowManager$LayoutParams); 271 | void -android_widget_PopupWindow-mthref-0(); 272 | void attachToAnchor(View, int, int, int); 273 | WindowManager$LayoutParams createPopupLayoutParams(IBinder); 274 | void detachFromAnchor(); 275 | void dismiss(); 276 | boolean findDropDownPosition(View, WindowManager$LayoutParams, int, int, int, int, int, boolean); 277 | boolean getAllowScrollingAnchorParent(); 278 | View getAnchor(); 279 | int getAnimationStyle(); 280 | Drawable getBackground(); 281 | View getContentView(); 282 | WindowManager$LayoutParams getDecorViewLayoutParams(); 283 | float getElevation(); 284 | Transition getEnterTransition(); 285 | Transition getExitTransition(); 286 | int getHeight(); 287 | int getInputMethodMode(); 288 | int getMaxAvailableHeight(View); 289 | int getMaxAvailableHeight(View, int); 290 | int getMaxAvailableHeight(View, int, boolean); 291 | PopupWindow$OnDismissListener getOnDismissListener(); 292 | boolean getOverlapAnchor(); 293 | int getSoftInputMode(); 294 | Rect getTransitionEpicenter(); 295 | int getWidth(); 296 | int getWindowLayoutType(); 297 | boolean hasContentView(); 298 | boolean hasDecorView(); 299 | boolean isAboveAnchor(); 300 | boolean isAttachedInDecor(); 301 | boolean isClippingEnabled(); 302 | boolean isFocusable(); 303 | boolean isLayoutInScreenEnabled(); 304 | boolean isLayoutInsetDecor(); 305 | boolean isOutsideTouchable(); 306 | boolean isShowing(); 307 | boolean isSplitTouchEnabled(); 308 | boolean isTouchable(); 309 | boolean isTransitioningToDismiss(); 310 | void lambda$-android_widget_PopupWindow_9628(View, int, int, int, int, int, int, int, int); 311 | void setAllowScrollingAnchorParent(boolean); 312 | void setAnimationStyle(int); 313 | void setAttachedInDecor(boolean); 314 | void setBackgroundDrawable(Drawable); 315 | void setClipToScreenEnabled(boolean); 316 | void setClippingEnabled(boolean); 317 | void setContentView(View); 318 | void setDropDown(boolean); 319 | void setElevation(float); 320 | void setEnterTransition(Transition); 321 | void setEpicenterBounds(Rect); 322 | void setExitTransition(Transition); 323 | void setFocusable(boolean); 324 | void setHeight(int); 325 | void setIgnoreCheekPress(); 326 | void setInputMethodMode(int); 327 | void setLayoutInScreenEnabled(boolean); 328 | void setLayoutInsetDecor(boolean); 329 | void setOnDismissListener(PopupWindow$OnDismissListener); 330 | void setOutsideTouchable(boolean); 331 | void setOverlapAnchor(boolean); 332 | void setShowing(boolean); 333 | void setSoftInputMode(int); 334 | void setSplitTouchEnabled(boolean); 335 | void setTouchInterceptor(View$OnTouchListener); 336 | void setTouchModal(boolean); 337 | void setTouchable(boolean); 338 | void setTransitioningToDismiss(boolean); 339 | void setWidth(int); 340 | void setWindowLayoutMode(int, int); 341 | void setWindowLayoutType(int); 342 | void showAsDropDown(View); 343 | void showAsDropDown(View, int, int); 344 | void showAsDropDown(View, int, int, int); 345 | void showAtLocation(IBinder, int, int, int); 346 | void showAtLocation(View, int, int, int); 347 | void updateAboveAnchor(boolean); 348 | 349 | 350 | ``` 351 | 352 | **AlertDialog中布存在的原因是手机卡在初始界面没有弹出弹窗**目前没有什么解决办法,可能是APK存在问题 353 | 354 | 由于Hook在函数被调用后再触发就没有任何作用了,而样本App 的升级提示弹窗在App刚进入主页面就弹出了,因此必须保证样本刚 启动函数便被Hook上了。Objection作为一个成熟的工具也提供了这 样 的 功 能 , 只 需 在 Objection 注 入 App 时 加 上 参 数 --startupcommand或者-s,并在参数后加上要执行的命令即可,Objection的 注入命令如下: 355 | 356 | ``` 357 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 358 | └─# objection -g com.hd.zhibo explore -s "android hooking watch class android.app.AlertDialog" 359 | Using USB device `Nexus 5X` 360 | Agent injected and responds ok! 361 | Running a startup command... android hooking watch class android.app.AlertDialog 362 | (agent) Hooking android.app.AlertDialog.-get0(android.app.AlertDialog) 363 | (agent) Hooking android.app.AlertDialog.resolveDialogTheme(android.content.Context, int) 364 | (agent) Hooking android.app.AlertDialog.getButton(int) 365 | (agent) Hooking android.app.AlertDialog.getListView() 366 | (agent) Hooking android.app.AlertDialog.onCreate(android.os.Bundle) 367 | (agent) Hooking android.app.AlertDialog.onKeyDown(int, android.view.KeyEvent) 368 | (agent) Hooking android.app.AlertDialog.onKeyUp(int, android.view.KeyEvent) 369 | (agent) Hooking android.app.AlertDialog.setButton(int, java.lang.CharSequence, android.content.DialogInterface$OnClickListener) 370 | (agent) Hooking android.app.AlertDialog.setButton(int, java.lang.CharSequence, android.os.Message) 371 | (agent) Hooking android.app.AlertDialog.setButton(java.lang.CharSequence, android.content.DialogInterface$OnClickListener) 372 | (agent) Hooking android.app.AlertDialog.setButton(java.lang.CharSequence, android.os.Message) 373 | (agent) Hooking android.app.AlertDialog.setButton2(java.lang.CharSequence, android.content.DialogInterface$OnClickListener) 374 | (agent) Hooking android.app.AlertDialog.setButton2(java.lang.CharSequence, android.os.Message) 375 | (agent) Hooking android.app.AlertDialog.setButton3(java.lang.CharSequence, android.content.DialogInterface$OnClickListener) 376 | (agent) Hooking android.app.AlertDialog.setButton3(java.lang.CharSequence, android.os.Message) 377 | (agent) Hooking android.app.AlertDialog.setButtonPanelLayoutHint(int) 378 | (agent) Hooking android.app.AlertDialog.setCustomTitle(android.view.View) 379 | (agent) Hooking android.app.AlertDialog.setIcon(int) 380 | (agent) Hooking android.app.AlertDialog.setIcon(android.graphics.drawable.Drawable) 381 | (agent) Hooking android.app.AlertDialog.setIconAttribute(int) 382 | (agent) Hooking android.app.AlertDialog.setInverseBackgroundForced(boolean) 383 | (agent) Hooking android.app.AlertDialog.setMessage(java.lang.CharSequence) 384 | (agent) Hooking android.app.AlertDialog.setTitle(java.lang.CharSequence) 385 | (agent) Hooking android.app.AlertDialog.setView(android.view.View) 386 | (agent) Hooking android.app.AlertDialog.setView(android.view.View, int, int, int, int) 387 | (agent) Registering job 391415. Type: watch-class for: android.app.AlertDialog 388 | 389 | _ _ _ _ 390 | ___| |_|_|___ ___| |_|_|___ ___ 391 | | . | . | | -_| _| _| | . | | 392 | |___|___| |___|___|_| |_|___|_|_| 393 | |___|(object)inject(ion) v1.11.0 394 | 395 | Runtime Mobile Exploration 396 | by: @leonjza from @sensepost 397 | 398 | [tab] for command suggestions 399 | com.hd.zhibo on (google: 8.1.0) [usb] # 400 | 401 | 402 | ``` 403 | 404 | 调用栈得到样本App为了弹窗所创建的函数为 com.zhibo.media.channel_main. update_show()。使用Jadx观察 相应的函数内容 405 | 406 | Jadx中升级是否提示弹窗取决于外层的if判断条件是否全部满足,所以想让升级提示弹窗不在弹出,就需要修改对应的判断语句并重新打包。 407 | 408 | ```java 409 | public void update_show(Bundle bundle) { 410 | if (bundle == null || !bundle.containsKey("ver") || !bundle.containsKey("info") || !bundle.containsKey("path")) { 411 | return; 412 | } 413 | new AlertDialog.Builder(this).setTitle("发现新版本 " + bundle.getString("ver") + " 是否升级").setMessage(bundle.getString("info")).setPositiveButton("立刻升级", new o(this, bundle)).show(); 414 | } 415 | } 416 | ``` 417 | 418 | 通过apktools将apk文件进行反编译,具体命令以及结果显示如下 419 | 420 | ``` 421 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap5] 422 | └─# apktool d zhibo.apk 423 | Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true 424 | I: Using Apktool 2.6.1 on zhibo.apk 425 | I: Loading resource table... 426 | I: Decoding AndroidManifest.xml with resources... 427 | I: Loading resource table from file: /root/.local/share/apktool/framework/1.apk 428 | I: Regular manifest package... 429 | I: Decoding file-resources... 430 | I: Decoding values */* XMLs... 431 | I: Baksmaling classes.dex... 432 | I: Copying assets and libs... 433 | I: Copying unknown files... 434 | I: Copying original files... 435 | 436 | ``` 437 | 438 | update_show()的smali文件如下: 439 | 440 | 可以通过ctrl f快速搜索。 441 | 442 | ``` 443 | method public update_show(Landroid/os/Bundle;)V 444 | .locals 3 445 | 446 | if-eqz p1, :cond_0 447 | 448 | const-string v0, "ver" 449 | 450 | invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z 451 | 452 | move-result v0 453 | 454 | if-eqz v0, :cond_0 455 | 456 | const-string v0, "info" 457 | 458 | invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z 459 | 460 | move-result v0 461 | 462 | if-eqz v0, :cond_0 463 | 464 | const-string v0, "path" 465 | 466 | invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z 467 | 468 | move-result v0 469 | 470 | if-eqz v0, :cond_0 471 | 472 | new-instance v0, Landroid/app/AlertDialog$Builder; 473 | 474 | invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;->(Landroid/content/Context;)V 475 | 476 | new-instance v1, Ljava/lang/StringBuilder; 477 | 478 | const-string v2, "\u53d1\u73b0\u65b0\u7248\u672c " 479 | 480 | invoke-direct {v1, v2}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V 481 | 482 | const-string v2, "ver" 483 | 484 | invoke-virtual {p1, v2}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String; 485 | 486 | move-result-object v2 487 | 488 | invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 489 | 490 | move-result-object v1 491 | 492 | const-string v2, " \u662f\u5426\u5347\u7ea7" 493 | 494 | invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 495 | 496 | move-result-object v1 497 | 498 | invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 499 | 500 | move-result-object v1 501 | 502 | invoke-virtual {v0, v1}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder; 503 | 504 | move-result-object v0 505 | 506 | const-string v1, "info" 507 | 508 | invoke-virtual {p1, v1}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String; 509 | 510 | move-result-object v1 511 | 512 | invoke-virtual {v0, v1}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder; 513 | 514 | move-result-object v0 515 | 516 | const-string v1, "\u7acb\u523b\u5347\u7ea7" 517 | 518 | new-instance v2, Lcom/zhibo/media/o; 519 | 520 | invoke-direct {v2, p0, p1}, Lcom/zhibo/media/o;->(Lcom/zhibo/media/channel_main;Landroid/os/Bundle;)V 521 | 522 | invoke-virtual {v0, v1, v2}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder; 523 | 524 | move-result-object v0 525 | 526 | invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog; 527 | 528 | :cond_0 529 | return-void 530 | .end method 531 | ``` 532 | 533 | 结合反编出的java代码和smali代码进行分析,我们会发现只需要将if判断语句中的任意一个逻辑置反即可阻止最终升级提示窗口的弹出。笔 者选择修改if-eqz p1, :cond_0(如果p1寄存器中的内容为0就跳转 到:cond_0处)。这里将if-eqz改为if-nez(如果p1寄存器中的内容不 为0就跳转到:cond_0处) 534 | 535 | 再将APK文件重新打包 536 | 537 | ``` 538 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap5] 539 | └─# apktool b zhibo 540 | Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true 541 | I: Using Apktool 2.6.1 542 | I: Checking whether sources has changed... 543 | I: Smaling smali folder into classes.dex... 544 | I: Checking whether resources has changed... 545 | I: Building resources... 546 | I: Copying libs... (/lib) 547 | I: Building apk file... 548 | I: Copying unknown files/dir... 549 | I: Built apk... 550 | 551 | ``` 552 | 553 | 重打包后的App会保存在dist目录下。此时二次打包的应用并不 能直接安装到手机上,这是因为在Android上运行的App都是需要签 名的。 554 | 555 | 为此需要使用jarsigner或者其他Android认可的签名工具生成一个签名文件,并使用生成的签名文件对打包好的App进行签名,具体命令和结果如下: 556 | 557 | ``` 558 | (base) ┌──(root㉿kali)-[/home/…/桌面/chap5/zhibo/dist] 559 | └─# keytool -genkey -alias abc.keystore -keyalg RSA -validity 2000 -keystore abc.key 560 | Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true 561 | 输入密钥库口令: 562 | 再次输入新口令: 563 | 您的名字与姓氏是什么? 564 | [Unknown]: zhy 565 | 您的组织单位名称是什么? 566 | [Unknown]: xdu 567 | 您的组织名称是什么? 568 | [Unknown]: xdu 569 | 您所在的城市或区域名称是什么? 570 | [Unknown]: xian 571 | 您所在的省/市/自治区名称是什么? 572 | [Unknown]: shanxi 573 | 该单位的双字母国家/地区代码是什么? 574 | [Unknown]: 86 575 | CN=zhy, OU=xdu, O=xdu, L=xian, ST=shanxi, C=86是否正确? 576 | [否]: 577 | 您的名字与姓氏是什么? 578 | [zhy]: zhy 579 | 您的组织单位名称是什么? 580 | [xdu]: xdu 581 | 您的组织名称是什么? 582 | [xdu]: xdu 583 | 您所在的城市或区域名称是什么? 584 | [xian]: xian 585 | 您所在的省/市/自治区名称是什么? 586 | [shanxi]: shanxi 587 | 该单位的双字母国家/地区代码是什么? 588 | [86]: 86 589 | CN=zhy, OU=xdu, O=xdu, L=xian, ST=shanxi, C=86是否正确? 590 | [否]: y 591 | 电脑上没有jarsigner直接用AndroidKiller打包 592 | ``` 593 | 594 | -------------------------------------------------------------------------------- /97-对加固APP进行分析和破解的实战.md: -------------------------------------------------------------------------------- 1 | # 对加固APP进行分析和破解的实战 2 | 3 | 分析目标为一个加固的样本APK文件: 4 | 5 | com.hello.qqc.apk 6 | 7 | APK文件在本仓库的APK文件夹下。 8 | 9 | 使用adb命令完成APP的安装后在手机上启动APP 10 | 11 | 可以观察到APP在手动跳过欢迎页面后会弹出升级提示的弹窗。与上一小节的案例不同的是,此案例不管如何点击窗口外部的位置都无法消除弹窗,这是该APP的重要特征之一。 12 | 13 | 定位弹窗具体使用的类的方法与对未加固的APP消除弹窗一样,可以在弹窗出现后在内存中搜索相关类的实例。 14 | 15 | (未完待续) 16 | 17 | -------------------------------------------------------------------------------- /98-Xposed框架简述.md: -------------------------------------------------------------------------------- 1 | # Xposed框架简述 2 | 3 | ## 简介 4 | 5 | 是一种比Frida出现更早的Hook框架,Xposed是一套开源的、在Android root模式下运行的框架,可以在不修改App源代码的情况下,通过Hook方式去影响程序的运行。与Frida一样,Xposed广泛用于 Android安全测试中,不管是针对特定API的监控、恶意代码的分析还 是对系统功能的自定义,Xposed框架都能够以对Java代码Hook的方 式游刃有余地完成。可惜的是,Xposed最后的发行版是v89,发行时 间是2017年12月18日。 6 | 7 | 查看相应的代码提交记录,这个版本适配的是Nougat代号的 Android,通过Android官网提供的“代号、标记和Build号”对照表 可知这个代号对应的Android版本为Android 7.1和7.0。 8 | 9 | 虽然Xposed框架已经很久远了,但是基于Xposed框架开发的其他框架依然活跃,就是稳定性值得商榷。 10 | 11 | ## 框架安装 12 | 13 | 安装雷电模拟器(Android 7.1版本),在雷电游戏中心下载xposed框架,然后打开,可以发现没有激活 14 | 15 | 然后下载一个压缩包framework-xposed-v89-sdk25-x86.zip 16 | 17 | 这个压缩包我放在了同仓库的资源文件夹下面, 18 | 19 | 下载成功后进行解压。 20 | 21 | 新建一个文件夹xposed,将上面的解压后的文件夹中的的文件复制到该文件夹中。接下来输入adb命令: 22 | 23 | ``` 24 | adb mount 25 | adb push xposed /system 26 | 27 | adb shell 28 | su 29 | cd /system/xposed 30 | mount -o remount -w /system 31 | sh script.sh 32 | ``` 33 | 34 | 然后重启一下雷电模拟器,打开xposed,就会发现已经激活了。 35 | 36 | ## Xposed插件安装脚本编写 37 | 38 | Xposed模块从本质上来讲也是一个Android程序,因此编写Xposed模块也可以像编写Android程序一样使用Android Studio进行开发,要成功地使Xposed将Android Studio开发程序识别未Xposed模块,还需要特别配置一些文件。 39 | 40 | Hook的目标程序的MainActivity代码如下: 41 | 42 | ```java 43 | package com.example.reversedemo2; 44 | 45 | import androidx.appcompat.app.AppCompatActivity; 46 | 47 | import android.os.Bundle; 48 | import android.util.Log; 49 | 50 | import java.util.Locale; 51 | 52 | public class MainActivity extends AppCompatActivity { 53 | private String total = "hello"; 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_main); 58 | while(true){ 59 | try { 60 | Thread.sleep(1000); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } 64 | fun(50,20); 65 | Log.d("reverseDemo.string", fun("LowerRcAse Me!!!")); 66 | } 67 | } 68 | void fun(int x,int y){ 69 | Log.d("reverseDemo", String.valueOf(x+y)); 70 | } 71 | String fun(String x){ 72 | return x.toLowerCase(); 73 | } 74 | void secret(){ 75 | total+=" secretFunc"; 76 | Log.d("reverseDemo.secret", "secret: this is secret func"); 77 | } 78 | static void staticSecret(){ 79 | Log.d("reverseDemo.Secret", "this is staticSecret func"); 80 | } 81 | } 82 | 83 | ``` 84 | 85 | 选定的Hook的目标函数为String fun(String x) 86 | 87 | **首先**重新新建一个Android项目,选在EmptyActivity作为模板,成功创建后切换到工程试图后,修改AndroidManifest.xml文件在``标签前添加如下代码: 88 | 89 | ```xml 90 | 93 | 96 | 99 | ``` 100 | 101 | ```xml 102 | 103 | 106 | 107 | 117 | 118 | 121 | 124 | 127 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | ``` 140 | 141 | name 为xposedmodule的meta-data标签所对应的value为true,即会将应用标记为Xposed模块;name为xposeddescription的meta-data 标签所对应的value为Xposed模块的描述。xposedminversion所对 应的是Xposed模块支持的最低版本。 142 | 143 | 将此项目运行后即可将该程序安装到手机上,这个时候可以在XposedInstaller应用中选择模块,即可看到这个Xposed模块。 144 | 145 | 然后我们编译安装的App已经可以被Xposed识别为Xposed模块。但是这时该Xposed模块还是一个空架子,实际内容需要我们去填充。 146 | 147 | **第二步**在Android中引入第三方包含有Xposed的API的jar包XposedBridge.jar,使得我们编写的XposedDemo能够实现下一步的Hook操作。为了实现这一点,需要编辑build.gradle(Module)文件,添加如下代码: 148 | 149 | ``` 150 | repositories { 151 | jcenter() 152 | } 153 | ``` 154 | 155 | 然后再dependencies节点中添加如下代码 156 | 157 | ```java 158 | compileOnly 'de.robv.android.xposed:api:82' 159 | compileOnly 'de.robv.android.xposed:api:82:sources' 160 | ``` 161 | 162 | > 记录一次错误,以上的方法有问题,jcenter已经被弃用, 163 | > 164 | > 需要用另一种方法加入该库 165 | 166 | 正确的方法: 167 | 168 | 将XposedBridgeApi-54.jar拷贝到libs目录下 169 | 170 | 然后再dependencies中添加 171 | 172 | ```java 173 | compileOnly fileTree(dir: 'libs',includes: ['*.jar']) 174 | 175 | ``` 176 | 177 | 点击sync即可 178 | 179 | **第三步、编写真正的Hook代码** 180 | 181 | 编写Hook脚本 182 | 183 | ```java 184 | package com.example.xposeddemo; 185 | 186 | import de.robv.android.xposed.IXposedHookLoadPackage; 187 | import de.robv.android.xposed.XC_MethodHook; 188 | import de.robv.android.xposed.XposedBridge; 189 | import de.robv.android.xposed.XposedHelpers; 190 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 191 | 192 | public class XposedHookDemo implements IXposedHookLoadPackage { 193 | 194 | @Override 195 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 196 | if (loadPackageParam.packageName.equals("com.example.reversedemo2")){ 197 | XposedBridge.log(loadPackageParam.packageName+"has Hooked!"); 198 | Class clazz = loadPackageParam.classLoader.loadClass("com.example.reversedemo2.MainActivity"); 199 | XposedHelpers.findAndHookMethod(clazz, "fun", String.class, new XC_MethodHook() { 200 | @Override 201 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 202 | super.beforeHookedMethod(param); 203 | XposedBridge.log("input:" +param.args[0]); 204 | } 205 | 206 | @Override 207 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 208 | param.setResult("you has been hijacked"); 209 | } 210 | }); 211 | } 212 | } 213 | } 214 | 215 | ``` 216 | 217 | > 在这段代码中,Xposed是通过IXposedHookLoadPackage接口 中的handleLoadPackage方法来实现Hook并篡改程序输出结果的。 在handleLoadPackage函数中,由于Xposed的Hook默认是针对整 个系统的,因此需要先通过loadPackageParam.packageName去过 滤 目 标 包 名 ( 目 标 App 为 第 2 章 中 的 demo02 程 序 , 包 名 为 “com.roysue.demo02”)。 XposedBridge.log()是Xposed自己实现的打印日志log的函数。 接下来通过loadPackageParam.classLoader.loadClass()函数 实现对目标类对象的获取,等价于Frida脚本中所使用的Java.use()函 数 。 在 获 取 到 类 对 象 后 , 通 过 XposedHelpers.findAndHookMethod()函数寻找并对指定函数进行 Hook。其中,第一个参数为之前所获取到的clazz类对象,第二个参 数为目标函数名,第二个参数之后的参数则是目标函数的参数列表。 由于Hook的目标函数只有一个String类型的参数,因此这里写的是 String.class。这个函数的最后一个参数是对目标函数的一个Hook回 调(callback),这个回调固定为XC_MethodHook。 在 这 个 XC_MethodHook 回 调 中 , 需 要 实 现 beforeHookedMethod()和afterHookedMethod()两个回调函数,其 中beforeHookedMethod()函数可以通过参数param获取被Hook函 数的参数值,通常被用于在目标函数执行前获取和更改被Hook函数 的参数。在代码清单6-13中,只是演示获取了被Hook函数的参数值 并 使 用 XposedBridge.log() 把 参 数 值 打 印 出 来 。 afterHookedMethod()函数通常被用于获取和更改被Hook函数的返 回 值 , 这 里 直 接 将 函 数 的 返 回 值 修 改 为 “You has been hijacked”。 218 | 219 | 最后在XposedDemo中添加Xposed模块的入口点,是的Xposed框架知道从哪个函数执行Hook。 220 | 221 | 右键点击main文件夹,再一次选择New->Folder->Asserts Floder,在弹出的窗口中直接单机Finish按钮完成assets文件夹的创建 222 | 223 | 在该文件夹下新建一个xposed_init文件 224 | 225 | 加入Hook类的路径 226 | 227 | ``` 228 | com/example/xposeddemo/XposedHookDemo 229 | ``` 230 | 231 | 安装到测试机上,在XposedInstaller中管理模块的页面上勾选我们编写的XposedDemo。重启手机后就会生效,再次执行reverseDemo02就可以发现打印的日志内容变成了之前设置的“you have been hijacked”. 232 | 233 | 与Frida脚本的相比的优点,Xposed本身使用Java语法开发,开发体验十分流畅,而Frida却打破了Java和js的壁垒,需要编写脚本将Java转化为JavaScript,实际操作过程存在一个翻译的过程。 234 | 235 | 与Frida脚本相比的缺点:热更新能力差,每次修改模块内容之后需要重启手机,而Frida可以及时更新,甚至不需要重启运行脚本。Xposed框架不支持Native函数的Hook,需要配合一些其他Hook框架实现,Frida本身支持对Native层进行Hook。 236 | -------------------------------------------------------------------------------- /991-HTTP(S)网络框架分析.md: -------------------------------------------------------------------------------- 1 | # HTTP(S)网络框架分析 2 | 3 | - 原生Android网络HTTP通信库 4 | - 第三方HTTP(S)网络请求框架 5 | - okHttp,square公司的开源网络请求框架 6 | - okhttp3(大部分安卓第三方网络框架都基于其的封装,例如Retrofit2) 7 | - WebSocket,是HTML5规范的一部分,与HTTP不同,遵循全双工通信机制,是一种应用层协议,表示为 `ws://xxxx.xxxxxx.xxxx/?encoding=text HTTP/1.1`是一个传统的URL。通常也使用okhttp3这个第三方网络框架来完成。 8 | - XMPP(可扩展消息与存在协议)是目前主流的四种即时消息协议之一,前身是Jabber,一个开源形式组织产生的网络即时通信协议。 9 | 10 | ## HttpURLConnection 11 | 12 | ### HttpURLConnection基础开发流程 13 | 14 | demo如下 15 | 16 | ``` 17 | package com.example.httpurlconnectiondemo; 18 | 19 | import androidx.appcompat.app.AppCompatActivity; 20 | import android.os.Bundle; 21 | import android.util.Log; 22 | 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.net.HttpURLConnection; 26 | import java.net.MalformedURLException; 27 | import java.net.URL; 28 | 29 | public class MainActivity extends AppCompatActivity { 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_main); 35 | new Thread(new Runnable() { 36 | @Override 37 | public void run() { 38 | while(true){ 39 | try { 40 | URL url = new URL("https://www.baidu.com"); 41 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 42 | connection.setRequestMethod("GET"); 43 | connection.setRequestProperty("token","zhy"); 44 | connection.setConnectTimeout(8000); 45 | connection.setReadTimeout(8000); 46 | InputStream in = connection.getInputStream(); 47 | 48 | int bufferSize = 1024; 49 | byte[]buffer = new byte[bufferSize]; 50 | StringBuffer stringBuffer = new StringBuffer(); 51 | while((in.read(buffer))!=-1){ 52 | stringBuffer.append(new String(buffer)); 53 | } 54 | Log.d("zhy", stringBuffer.toString()); 55 | connection.disconnect(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | try { 60 | Thread.sleep(3*1000); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | }).start(); 67 | } 68 | } 69 | ``` 70 | 71 | ### HttpURLConnection“自吐”脚本开发 72 | 73 | - URL类的构造函数,其包含了目标网址的字符串。 74 | - setRequestMethod()和setRequestProperty()函数设置请求头 和请求参数等信息。 75 | - getInputStream()函数获取response。 76 | 77 | ```sh 78 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 79 | └─# frida-ps -U 80 | PID Name 81 | ----- --------------------------------------------------- 82 | 3358 ATFWD-daemon 83 | 10320 F-Droid 84 | 5837 Google 85 | 10517 Hangouts 86 | 11482 HttpUrlConnectionDemo 87 | 10945 Magisk Manager 88 | 9460 TIM 89 | 9211 YouTube 90 | 9025 adbd 91 | 3364 android.hardware.biometrics.fingerprint@2.1-service 92 | 437 android.hardware.cas@1.0-service 93 | 438 android.hardware.configstore@1.0-service 94 | 439 android.hardware.dumpstate@1.0-service.bullhead 95 | 440 android.hardware.graphics.allocator@2.0-service 96 | 441 android.hardware.usb@1.0-service 97 | 442 android.hardware.wifi@1.0-service 98 | 436 android.hidl.allocator@1.0-service 99 | 9027 android.process.media 100 | 3340 audioserver 101 | 3341 cameraserver 102 | 3359 cnd 103 | 3355 cnss-daemon 104 | 4031 com.android.bluetooth 105 | 5865 com.android.nfc 106 | 4289 com.android.phone 107 | 4074 com.android.systemui 108 | 10210 com.google.android.apps.gcs 109 | 6180 com.google.android.gms 110 | 5782 com.google.android.gms.persistent 111 | 5952 com.google.android.googlequicksearchbox 112 | 10579 com.google.android.googlequicksearchbox:search 113 | 7451 com.google.android.ims 114 | 6206 com.google.android.inputmethod.latin 115 | 10487 com.google.android.partnersetup 116 | 10908 com.google.android.storagemanager 117 | 8023 com.google.process.gapps 118 | 5970 com.google.process.gservices 119 | 8347 com.qualcomm.qcrilmsgtunnel 120 | 5879 com.qualcomm.qti.rcsbootstraputil 121 | 5891 com.qualcomm.qti.rcsimsbootstraputil 122 | 8325 com.qualcomm.telephony 123 | 5852 com.quicinc.cne.CNEService 124 | 11381 daemonsu:0 125 | 11383 daemonsu:0:11378 126 | 11393 daemonsu:0:11385 127 | 11435 daemonsu:0:11385 128 | 7152 daemonsu:10102 129 | 3312 daemonsu:master 130 | 7179 daemonsu:mount:0 131 | 3305 daemonsu:mount:master 132 | 3342 drmserver 133 | 11441 frida-helper-32 134 | 11385 frida-server-15.2.2-android-arm64 135 | 3362 gatekeeperd 136 | 443 healthd 137 | 369 hwservicemanager 138 | 3387 imsdatadaemon 139 | 3337 imsqmidaemon 140 | 1 init 141 | 3343 installd 142 | 9967 installer 143 | 3464 ip6tables-restore 144 | 3463 iptables-restore 145 | 3344 keystore 146 | 447 lmkd 147 | 3354 loc_launcher 148 | 3391 location-mq 149 | 9901 logcat 150 | 11394 logcat 151 | 367 logd 152 | 3392 lowi-server 153 | 3352 media.codec 154 | 3346 media.extractor 155 | 3347 media.metrics 156 | 3345 mediadrmserver 157 | 3348 mediaserver 158 | 3357 mm-qcamera-daemon 159 | 446 msm_irqbalance 160 | 3349 netd 161 | 3335 netmgrd 162 | 3333 perfd 163 | 500 pm-proxy 164 | 445 pm-service 165 | 3332 qmuxd 166 | 373 qseecomd 167 | 380 qseecomd 168 | 3334 qti 169 | 3353 rild 170 | 444 rmt_storage 171 | 368 servicemanager 172 | 9965 sh 173 | 11375 sh 174 | 3393 slim_daemon 175 | 7411 sshd [listener] 0 of 10-100 startups 176 | 3350 storaged 177 | 10991 su 178 | 11378 su 179 | 448 surfaceflinger 180 | 10999 sush 181 | 11384 sush 182 | 11437 sush 183 | 3780 system_server 184 | 3336 thermal-engine 185 | 449 thermalserviced 186 | 3356 time_daemon 187 | 3363 tombstoned 188 | 348 ueventd 189 | 370 vndservicemanager 190 | 400 vold 191 | 4604 wcnss_filter 192 | 4155 webview_zygote32 193 | 3351 wificond 194 | 4396 wpa_supplicant 195 | 3339 zygote 196 | 3338 zygote64 197 | 7417 信息 198 | 10836 地圖 199 | 200 | (base) ┌──(root㉿kali)-[/home/zhy/桌面] 201 | └─# objection -g HttpUrlConnectionDemo explore 202 | Checking for a newer version of objection... 203 | Using USB device `Nexus 5X` 204 | Agent injected and responds ok! 205 | 206 | _ _ _ _ 207 | ___| |_|_|___ ___| |_|_|___ ___ 208 | | . | . | | -_| _| _| | . | | 209 | |___|___| |___|___|_| |_|___|_|_| 210 | |___|(object)inject(ion) v1.11.0 211 | 212 | Runtime Mobile Exploration 213 | by: @leonjza from @sensepost 214 | 215 | [tab] for command suggestions 216 | ....example.httpurlconnectiondemo on (google: 8.1.0) [usb] # android hooking watch class_method java.net.URL.$init --dump-args --du 217 | mp-backtrace --dump-return 218 | (agent) Attempting to watch class java.net.URL and method $init. 219 | (agent) Hooking java.net.URL.$init(java.lang.String) 220 | (agent) Hooking java.net.URL.$init(java.lang.String, java.lang.String, int, java.lang.String) 221 | (agent) Hooking java.net.URL.$init(java.lang.String, java.lang.String, int, java.lang.String, java.net.URLStreamHandler) 222 | (agent) Hooking java.net.URL.$init(java.lang.String, java.lang.String, java.lang.String) 223 | (agent) Hooking java.net.URL.$init(java.net.URL, java.lang.String) 224 | (agent) Hooking java.net.URL.$init(java.net.URL, java.lang.String, java.net.URLStreamHandler) 225 | (agent) Registering job 068716. Type: watch-method for: java.net.URL.$init 226 | ....example.httpurlconnectiondemo on (google: 8.1.0) [usb] # (agent) [068716] Called java.net.URL.URL(java.lang.String) 227 | (agent) [068716] Backtrace: 228 | java.net.URL.(Native Method) 229 | com.example.httpurlconnectiondemo.MainActivity$1.run(MainActivity.java:24) 230 | java.lang.Thread.run(Thread.java:764) 231 | 232 | (agent) [068716] Arguments java.net.URL.URL(https://www.baidu.com) 233 | (agent) [068716] Called java.net.URL.URL(java.net.URL, java.lang.String) 234 | (agent) [068716] Backtrace: 235 | java.net.URL.(Native Method) 236 | java.net.URL.(URL.java:436) 237 | java.net.URL.(Native Method) 238 | com.example.httpurlconnectiondemo.MainActivity$1.run(MainActivity.java:24) 239 | java.lang.Thread.run(Thread.java:764) 240 | 241 | (agent) [068716] Arguments java.net.URL.URL((none), https://www.baidu.com) 242 | (agent) [068716] Called java.net.URL.URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) 243 | (agent) [068716] Backtrace: 244 | java.net.URL.(Native Method) 245 | java.net.URL.(URL.java:487) 246 | java.net.URL.(Native Method) 247 | java.net.URL.(URL.java:436) 248 | java.net.URL.(Native Method) 249 | com.example.httpurlconnectiondemo.MainActivity$1.run(MainActivity.java:24) 250 | java.lang.Thread.run(Thread.java:764) 251 | 252 | (agent) [068716] Arguments java.net.URL.URL((none), https://www.baidu.com, (none)) 253 | (agent) [068716] Return Value: (none) 254 | (agent) [068716] Return Value: (none) 255 | (agent) [068716] Return Value: (none) 256 | 257 | ``` 258 | 259 | 网址在URL的构造函数中出现了,而且出现了很多次 260 | 261 | ``` 262 | (agent) [068716] Arguments java.net.URL.URL((none), https://www.baidu.com) 263 | (agent) [068716] Called java.net.URL.URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) 264 | ``` 265 | 266 | 选择调用栈最浅的,也就是第一个出现的被调用的java.net.URL.$init(java.lang.String)函数完成自吐脚本 267 | 268 | ```js 269 | function main(){ 270 | Java.perform(function(){ 271 | var URL = Java.use('java.net.URL') 272 | URL.$init.overload('java.lang.String').implementation = function(urlstr){ 273 | console.log('url =>',urlstr) 274 | var result = this.$init(urlstr) 275 | return result 276 | } 277 | }) 278 | } 279 | 280 | setImmediate(main) 281 | ``` 282 | 283 | 测试结果如下: 284 | 285 | ``` 286 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap8] 287 | └─# frida -U -l HttpURLConnection.js HttpUrlConnectionDemo 288 | ____ 289 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 290 | | (_| | 291 | > _ | Commands: 292 | /_/ |_| help -> Displays the help system 293 | . . . . object? -> Display information about 'object' 294 | . . . . exit/quit -> Exit 295 | . . . . 296 | . . . . More info at https://frida.re/docs/home/ 297 | . . . . 298 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 299 | 300 | [Nexus 5X::HttpUrlConnectionDemo ]-> url => https://www.baidu.com 301 | url => https://www.baidu.com 302 | url => https://www.baidu.com 303 | url => https://www.baidu.com 304 | url => https://www.baidu.com 305 | url => https://www.baidu.com 306 | url => https://www.baidu.com 307 | url => https://www.baidu.com 308 | url => https://www.baidu.com 309 | url => https://www.baidu.com 310 | url => https://www.baidu.com 311 | url => https://www.baidu.com 312 | url => https://www.baidu.com 313 | url => https://www.baidu.com 314 | url => https://www.baidu.com 315 | url => https://www.baidu.com 316 | url => https://www.baidu.com 317 | url => https://www.baidu.com 318 | url => https://www.baidu.com 319 | url => https://www.baidu.com 320 | url => https://www.baidu.com 321 | 322 | ``` 323 | 324 | 根据上面整理的关键收发包函数可以发现剩下的都是HttpURLConnection类中的函数,因此可以用如下命令去watch整个HttpURLConnection类中的所有函数。 325 | 326 | ``` 327 | ....example.httpurlconnectiondemo on (google: 8.1.0) [usb] # android hooking watch class_method java.net.HttpURLConnection.$init -- 328 | dump-args --dump-backtrace --dump-return 329 | (agent) Attempting to watch class java.net.HttpURLConnection and method $init. 330 | (agent) Hooking java.net.HttpURLConnection.$init(java.net.URL) 331 | (agent) Registering job 118392. Type: watch-method for: java.net.HttpURLConnection.$init 332 | ....example.httpurlconnectiondemo on (google: 8.1.0) [usb] # (agent) [118392] Called java.net.HttpURLConnection.HttpURLConnection(java.net.URL) 333 | (agent) [118392] Backtrace: 334 | java.net.HttpURLConnection.(Native Method) 335 | com.android.okhttp.internal.huc.HttpURLConnectionImpl.(HttpURLConnectionImpl.java:114) 336 | com.android.okhttp.internal.huc.HttpURLConnectionImpl.(HttpURLConnectionImpl.java:119) 337 | com.android.okhttp.internal.huc.HttpsURLConnectionImpl.(HttpsURLConnectionImpl.java:34) 338 | com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:63) 339 | com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:54) 340 | com.android.okhttp.HttpHandler.openConnection(HttpHandler.java:44) 341 | java.net.URL.openConnection(URL.java:992) 342 | com.example.httpurlconnectiondemo.MainActivity$1.run(MainActivity.java:25) 343 | java.lang.Thread.run(Thread.java:764) 344 | 345 | (agent) [118392] Arguments java.net.HttpURLConnection.HttpURLConnection(https://www.baidu.com) 346 | (agent) [118392] Return Value: (none) 347 | (agent) [118392] Called java.net.HttpURLConnection.HttpURLConnection(java.net.URL) 348 | (agent) [118392] Backtrace: 349 | java.net.HttpURLConnection.(Native Method) 350 | javax.net.ssl.HttpsURLConnection.(HttpsURLConnection.java:66) 351 | com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.(DelegatingHttpsURLConnection.java:44) 352 | com.android.okhttp.internal.huc.HttpsURLConnectionImpl.(HttpsURLConnectionImpl.java:38) 353 | com.android.okhttp.internal.huc.HttpsURLConnectionImpl.(HttpsURLConnectionImpl.java:34) 354 | com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:63) 355 | com.android.okhttp.OkUrlFactory.open(OkUrlFactory.java:54) 356 | com.android.okhttp.HttpHandler.openConnection(HttpHandler.java:44) 357 | java.net.URL.openConnection(URL.java:992) 358 | com.example.httpurlconnectiondemo.MainActivity$1.run(MainActivity.java:25) 359 | java.lang.Thread.run(Thread.java:764) 360 | 361 | ``` 362 | 363 | 同时别忘记了Hook构造函数: 364 | 365 | ``` 366 | ....example.httpurlconnectiondemo on (google: 8.1.0) [usb] # android hooking watch class java.net.HttpURLConnection 367 | (agent) Hooking java.net.HttpURLConnection.getFollowRedirects() 368 | (agent) Hooking java.net.HttpURLConnection.setFollowRedirects(boolean) 369 | (agent) Hooking java.net.HttpURLConnection.disconnect() 370 | (agent) Hooking java.net.HttpURLConnection.getErrorStream() 371 | (agent) Hooking java.net.HttpURLConnection.getHeaderField(int) 372 | (agent) Hooking java.net.HttpURLConnection.getHeaderFieldDate(java.lang.String, long) 373 | (agent) Hooking java.net.HttpURLConnection.getHeaderFieldKey(int) 374 | (agent) Hooking java.net.HttpURLConnection.getInstanceFollowRedirects() 375 | (agent) Hooking java.net.HttpURLConnection.getPermission() 376 | (agent) Hooking java.net.HttpURLConnection.getRequestMethod() 377 | (agent) Hooking java.net.HttpURLConnection.getResponseCode() 378 | (agent) Hooking java.net.HttpURLConnection.getResponseMessage() 379 | (agent) Hooking java.net.HttpURLConnection.setChunkedStreamingMode(int) 380 | (agent) Hooking java.net.HttpURLConnection.setFixedLengthStreamingMode(int) 381 | 382 | ``` 383 | 384 | ``` 385 | [574683] Called java.net.HttpURLConnection.getFollowRedirects() 386 | (agent) [118392] Called java.net.HttpURLConnection.HttpURLConnection(java.net.URL) 387 | 388 | ``` 389 | 390 | 我们可以发现只有构造函数和一个java.net.HttpURLConnection.getFollowRedirects()函数被调用了。 391 | 392 | 393 | 394 | 通过如下命令获取HttpURLConnection的实例时发现布存在实例 395 | 396 | ``` 397 | android heap search instances java.net.HttpURLConnection 398 | ``` 399 | 400 | 可以在google官方api网站上发现,该类实际上是一个抽象类,在开发中可以直接使用一个抽象类去表示,但是在运行过程中是该抽象类的具体实现类在进行工作,所以我们需要确定具体实现类: 401 | 402 | - 第一种方法是纯逆向方法,通过之前的源代码,我们可以发现第一次出现HttpURLConnection的定义是通过URL类的openConnection()函数完成的,这个函数的返回值的类名是HttpURLConnection()函数的具体实现类 403 | 404 | ```js 405 | function main(){ 406 | Java.perform(function(){ 407 | var URL = Java.use('java.net.URL') 408 | URL.openConnection.overload().implementation = function(){ 409 | //console.log('url =>',urlstr) 410 | var result = this.openConnection() 411 | console.log('openConnection() returnType =>',result.$className) 412 | return result 413 | } 414 | }) 415 | } 416 | 417 | setImmediate(main) 418 | ``` 419 | 420 | ``` 421 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap8] 422 | └─# frida -U -l HttpURLConnection.js HttpUrlConnectionDemo 423 | 424 | ____ 425 | 426 | ​ / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 427 | | (_| | 428 | ​ > _ | Commands: 429 | /_/ |_| help -> Displays the help system 430 | . . . . object? -> Display information about 'object' 431 | . . . . exit/quit -> Exit 432 | . . . . 433 | . . . . More info at https://frida.re/docs/home/ 434 | . . . . 435 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 436 | ​ 437 | [Nexus 5X::HttpUrlConnectionDemo ]-> openConnection() returnType => com.android.okhttp.internal.huc.HttpsURLConnectionImpl 438 | openConnection() returnType => com.android.okhttp.internal.huc.HttpsURLConnectionImpl 439 | openConnection() returnType => com.android.okhttp.internal.huc.HttpsURLConnectionImpl 440 | openConnection() returnType => com.android.okhttp.internal.huc.HttpsURLConnectionImpl 441 | ``` 442 | 443 | - 由于我们手中有源代码,我们可以在对应的位置打断点,然后在AndroidStudio中进行调试 444 | 445 | 最终获取参数的自吐脚本如下: 446 | 447 | ```js 448 | function main(){ 449 | Java.perform(function(){ 450 | var HttpURLConnectionImpl = Java.use('com.android.okhttp.internal.huc.HttpURLConnectionImpl') 451 | HttpURLConnectionImpl.setRequestProperty.implementation = function(key,value){ 452 | //console.log('url =>',urlstr) 453 | var result = this.setRequestProperty(key,value) 454 | console.log('setRequestProperty=>',key,':',value) 455 | return result 456 | } 457 | }) 458 | } 459 | 460 | setImmediate(main) 461 | ``` 462 | 463 | ``` 464 | (base) ┌──(root㉿kali)-[/home/zhy/桌面/chap8] 465 | └─# frida -U -l HttpURLConnection.js HttpUrlConnectionDemo 466 | ____ 467 | / _ | Frida 15.2.2 - A world-class dynamic instrumentation toolkit 468 | | (_| | 469 | > _ | Commands: 470 | /_/ |_| help -> Displays the help system 471 | . . . . object? -> Display information about 'object' 472 | . . . . exit/quit -> Exit 473 | . . . . 474 | . . . . More info at https://frida.re/docs/home/ 475 | . . . . 476 | . . . . Connected to Nexus 5X (id=00e310eb64541e43) 477 | 478 | [Nexus 5X::HttpUrlConnectionDemo ]-> setRequestProperty=> token : zhy 479 | setRequestProperty=> token : zhy 480 | setRequestProperty=> token : zhy 481 | setRequestProperty=> token : zhy 482 | setRequestProperty=> token : zhy 483 | setRequestProperty=> token : zhy 484 | setRequestProperty=> token : zhy 485 | 486 | ``` 487 | 488 | -------------------------------------------------------------------------------- /994-Android逆向学习补充.md: -------------------------------------------------------------------------------- 1 | # Android逆向学习补充 2 | 3 | ## 调试方法 4 | 5 | ### 源程序修改 6 | 7 | 一种比较老旧是调试方法,使用apktool的-d选项 8 | 9 | - java -jar apktool.jar d -d 目标.apk -o 结果存放目录 10 | - 修改Android.mainfest文件,在application节点中添加 `android:debuggable="true"` 11 | - 在入口点的类的onCreate方法中添加 `invoke-static{},Landroid/os/Debug;->waitForDebugger()V` 12 | - 反编译修改过的APK文件 `java -jar apktool.jar b -d 代码目录 -o 目标APK名字` 13 | - 手动对apk文件进行签名 `java -jar signapk.jar testkey.x509.pem testkey.pk8 未签名apk名 签名APK名` 14 | - 在Android Studio中选择编译好的文件目录,导入代码,在相应位置下好断点(waitdebugger下一行设置好断点) 15 | - 设置远程调试选项,Run->Debug Configurations-> Remote Java Application,Host填写为localhost,端口为Debug开发的端口8700 16 | - 打开apk文件,知道看到wait for debugger 17 | -------------------------------------------------------------------------------- /APK/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/app-debug.apk -------------------------------------------------------------------------------- /APK/com.hello.qqc.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/com.hello.qqc.apk -------------------------------------------------------------------------------- /APK/junior.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/junior.apk -------------------------------------------------------------------------------- /APK/lingdongniao.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/lingdongniao.apk -------------------------------------------------------------------------------- /APK/moveTV.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/moveTV.apk -------------------------------------------------------------------------------- /APK/zhibo.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/APK/zhibo.apk -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-reverse 2 | 学习安卓逆向过程中编写的一些技术文档 3 | 4 | 会随着学习过程慢慢更新,未完待续 5 | 6 | 参考资料: 7 | 8 | 《Android Frida逆向与抓包实践(陈佳林)》 9 | 10 | 逆向1为对2022羊城杯的逆向赛题BBButton进行的分析(未结束) 11 | 12 | 文件编号次序如下: 13 | 1-9 91-99 991-999....以此类推 14 | -------------------------------------------------------------------------------- /image/HTTPS中间人抓包.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/HTTPS中间人抓包.png -------------------------------------------------------------------------------- /image/Https通讯过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/Https通讯过程.png -------------------------------------------------------------------------------- /image/NDK1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/NDK1.png -------------------------------------------------------------------------------- /image/NDK2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/NDK2.png -------------------------------------------------------------------------------- /image/adbdevices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/adbdevices.png -------------------------------------------------------------------------------- /image/字段操作指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/字段操作指令.png -------------------------------------------------------------------------------- /image/异常指令与跳转指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/异常指令与跳转指令.png -------------------------------------------------------------------------------- /image/数据定义指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/数据定义指令.png -------------------------------------------------------------------------------- /image/数据转换.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/数据转换.png -------------------------------------------------------------------------------- /image/数据运算.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/数据运算.png -------------------------------------------------------------------------------- /image/数组操作指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/数组操作指令.png -------------------------------------------------------------------------------- /image/方法调用指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/方法调用指令.png -------------------------------------------------------------------------------- /image/未加固1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/未加固1.png -------------------------------------------------------------------------------- /image/比较指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/比较指令.png -------------------------------------------------------------------------------- /image/空指令与数据操作指令与返回指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/空指令与数据操作指令与返回指令.png -------------------------------------------------------------------------------- /image/锁指令与实例操作指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/image/锁指令与实例操作指令.png -------------------------------------------------------------------------------- /资源/apktool_2.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/资源/apktool_2.6.1.jar -------------------------------------------------------------------------------- /资源/framework-xposed-v89-sdk25-x86.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G-WS/Android-reverse/9b8322cf01fae1ab4b0df617917f30a88efc8f85/资源/framework-xposed-v89-sdk25-x86.zip --------------------------------------------------------------------------------