From 12a195b3cc9e23fb8e771e0e92d07593e4d6e442 Mon Sep 17 00:00:00 2001 From: thuri10 Date: Tue, 6 Feb 2024 00:45:02 +0300 Subject: [PATCH] Translate Markdown from Chinese to English --- FRIDA/A01/README.md | 383 +++------------ FRIDA/A02/README.md | 683 ++++----------------------- FRIDA/B01/README.md | 232 ++++----- FRIDA/B02/README.md | 210 ++++----- FRIDA/B03/README.md | 202 ++++---- FRIDA/B04/README.md | 375 +++++---------- README.md | 127 +++-- Student/001/README.md | 579 ++++------------------- Student/002/README.md | 404 ++++++---------- Student/003/README.md | 169 ++++--- Student/004/README.md | 173 +++---- Student/005/README.md | 633 +++++-------------------- Student/006/README.md | 1032 +++++------------------------------------ Student/007/README.md | 796 ++++--------------------------- Student/008/README.md | 68 ++- Student/009/README.md | 319 ++++--------- Student/010/README.md | 213 +++++---- 17 files changed, 1610 insertions(+), 4988 deletions(-) diff --git a/FRIDA/A01/README.md b/FRIDA/A01/README.md index 007702c..98e71e6 100644 --- a/FRIDA/A01/README.md +++ b/FRIDA/A01/README.md @@ -1,402 +1,147 @@ - - + + * [1.3 Android/iOS](#13-androidios) - * [1.3.1 Android root](#131-android-root) - * [1.3.1.1 硬件准备](#1311-硬件准备) - * [1.3.1.2 刷入官方`Android 8.1`](#1312-刷入官方android-81) - * [1.3.1.3 刷入`twrp recovery`](#1313-刷入twrp-recovery) - * [1.3.1.4 刷入`Magisk`](#1314-刷入magisk) - * [1.3.1.5 获取`root`权限](#1315-获取root权限) - * [1.3.2 Android frida-server 安装](#132-android-frida-server-安装) +* [1.3.1 Android root](#131-android-root) +* [1.3.1.1 Hardware Preparation] (#1311 - Hardware Preparation) +* [1.3.1.2 brush into official 'Android 8.1'] (#1312- brush into official android-81) +* [1.3.1.3 flush 'twrp recovery'] (#1313-flush twrp-recovery) +* [1.3.1.4 flush 'Magisk'] (#1314-flush magisk) +* [1.3.1.5 Get 'root' Permission] (#1315- Get root Permission) +* [1.3.2 Android frida-server Installation] (#132-android-frida-server-Installation) - + ### 1.3 Android/iOS #### 1.3.1 Android root -##### 1.3.1.1 硬件准备 +##### 1.3.1.1 Hardware Preparation -在手机硬件的考虑上,首先优选谷歌的"亲儿子":`Nexus`和`Pixel`系列,鉴于官网上测试案例在`Nexus 5X`手机,系统为`Android 8.1.0`版本上进行测试,我们也会选用这款手机、和这个系统版本来进行实验。 +In terms of mobile phone hardware, Google's 'Nexus' and 'Pixel' series are preferred first and foremost. In view of the fact that the test cases on the Web site were tested on the 'Nexus 5X' mobile phone, the system was tested on the 'Android 8.1.0' version, we will also use this mobile phone, and this version of the system, for experimentation. -官网也指出不可能在所有手机和所有ROM上完全进行测试,肯定是具体情况具体分析。 +The Web site also states that it is not possible to fully test on all mobile phones and all ROMs, and this is certainly a case-by-case analysis. ->Also note that most of our recent testing has been taking place on a Nexus 5X running Android 8.1.0. Older and newer ROMs may work, but if you’re running into basic issues like Frida crashing the system when launching an app, this is due to ROM-specific quirks. We cannot test on all possible devices, so we count on your help to improve on this. However if you’re just starting out with Frida it is strongly recommended to go with a Nexus device with factory software, or an official 8.x emulator image for arm or arm64. (x86 may work too but has gone through significantly less testing.) +>Also note that most of our recent testing has been taking place on a Nexus 5X running Android 8.1.0. Older and newer ROMs may work, but if you’re running into basic issues like Frida crashing the system when launching an app, this is due to ROM-specific quirks. We cannot test on all possible devices, so we count on your help to improve on this. However if you’re just starting out with Frida it is strongly recommended to go with a Nexus device with factory software, or an official 8.x emulator image for arm or arm64.(x86 may work too but has gone through significantly less testing.) -官网上依旧指出,选择`Android 8.x`系统的模拟器也是没问题的,架构必须选择`arm`或者`arm64`,`x86`可能会有`crash`的情况发生。 +The Web site continues to indicate that it is also OK to choose a simulator for the 'Android 8.x' system and that the architecture must choose either 'arm' or 'arm64', and that 'x86' may have a 'crash' situation. -`Nexus 5X`这款手机的硬件参数为: +'Nexus 5X' The hardware parameters for this phone are: ![](pic/1.3.1.1a.png) ![](pic/1.3.1.1b.png) -##### 1.3.1.2 刷入官方`Android 8.1` +##### 1.3.1.2 brush off official'Android 8.1' -frida官网指出的`factory software`就是谷歌的[官方工厂镜像网站](https://developers.google.com/android/images),打开这个网站可能需要科学上网。网站中间有一些操作指南,右边就是手机型号一览表,在这里我们选择`Nexus 5X`的型号`bullhead`。 +frida's website identifies 'factory software' as Google's [official factory image site](https://developers.google.com/android/images), which may require scientific Internet access. There are some instructions in the middle of the website, and on the right is the list of mobile models, where we choose the 'Nexus 5X' model 'bullhead'. ![](pic/1.3.1.2a.png) -可以看到从安卓6到安卓8均支持,而且最新支持到`8.1.0`。 +You can see support from Android 6 to Android 8, and the latest support goes to '8.1.0'. ![](pic/1.3.1.2b.png) ... ![](pic/1.3.1.2c.png) -我们使用`wget`命令来下载最新的`8.1.0 (OPM7.181205.001, Dec 2018)`版本,这样速度最快。 +We use the 'wget' command to download the latest version of '8.1.0 (OPM7.181205.001, Dec 2018)', which is the fastest. ![](pic/1.3.1.2d.png) -下载完成后记得校验`SHA-256 Checksum`,必须得于官网一致,否则下载文件已经损坏,无法使用,必须重新下载。 +Remember to verify 'SHA-256 Checksum' after the download is complete and you must agree with the official website or the download file is corrupt and cannot be used and you must download it again. -``` +```bash $ openssl dgst -sha256 bullhead-opm7.181205.001-factory-5f189d84.zip SHA256(bullhead-opm7.181205.001-factory-5f189d84.zip)= 5f189d84781a26b49aca0de84a941a32ae0150da0aab89f1d7709d56c31b3c0a ``` -可见`SHA-256 Checksum`与官网相同,接下来就是刷入该系统。 +It is clear that 'SHA-256 Checksum' is the same as the Web site and that the next step is to flush into the system. -首先将手机进入`fastboot`状态,操作流程如下: +First, enter the phone into the 'fastboot' state, following the procedure: -1. 将`USB`线断开,并确保手机有`80%`左右的电量; -2. 将手机完全关机; -3. 同时按住音量向下键和开机键; -4. 手机将进入`fastboot`状态; +1. Disconnect the 'USB' cable and ensure that the cell phone has about '80%' of the power; +2. Turn off your mobile phone completely; +3. Press and hold the Volume Down Arrow key and the Power On button at the same time; +4. The mobile phone will enter the 'fastboot' state; -状态如下: +The status is as follows: ![](pic/1.3.1.2e.jpeg) ->PS:如果手机并没有解锁,也就是`DEVICE STATE - `显示`locked`,那手机需要先解锁,得是`unlocked`,也就是图上的状态,才可以后续刷入`recovery`等操作。`Nexus 5X`手机解锁是比较简单的,这里不做演示了。 +>PS: If the phone is not unlocked, that is, 'DEVICE STATE - 'display 'locked', then the phone needs to be unlocked first, and 'unlocked', that is, the status on the diagram, before it can be subsequently swiped into 'recovery' and so on. 'Nexus 5X' phone unlocking is relatively simple and is not being demonstrated here. -手机用USB线连上电脑,运行脚本,将系统刷进手机。 +The phone uses a USB cable to connect to the computer, runs a script, and brushes the system into the phone. -``` +```bash $ unzip bullhead-opm7.181205.001-factory-5f189d84.zip -Archive: bullhead-opm7.181205.001-factory-5f189d84.zip - creating: bullhead-opm7.181205.001/ - inflating: bullhead-opm7.181205.001/radio-bullhead-m8994f-2.6.42.5.03.img - inflating: bullhead-opm7.181205.001/bootloader-bullhead-bhz32c.img - inflating: bullhead-opm7.181205.001/flash-all.bat - inflating: bullhead-opm7.181205.001/flash-all.sh - extracting: bullhead-opm7.181205.001/image-bullhead-opm7.181205.001.zip - inflating: bullhead-opm7.181205.001/flash-base.sh +Archive: bullhead-opm7.181205.001-factory-5f189d84.zip +creating: bullhead-opm7.181205.001/ +inflating: bullhead-opm7.181205.001/radio-bullhead-m8994f-2.6.42.5.03.img +inflating: bullhead-opm7.181205.001/bootloader-bullhead-bhz32c.img +inflating: bullhead-opm7.181205.001/flash-all.bat +inflating: bullhead-opm7.181205.001/flash-all.sh +extracting: bullhead-opm7.181205.001/image-bullhead-opm7.181205.001.zip +inflating: bullhead-opm7.181205.001/flash-base.sh $ cd bullhead-opm7.181205.001 $ ./flash-all.sh target reported max download size of 536870912 bytes -sending 'bootloader' (4610 KB)... -OKAY [ 0.197s] +sending 'bootloader'(4610 KB)... +OKAY [ 0.197s] writing 'bootloader'... -OKAY [ 0.174s] +OKAY [ 0.174s] finished. total time: 0.371s rebooting into bootloader... -OKAY [ 0.020s] +OKAY [ 0.020s] finished. total time: 0.020s target reported max download size of 536870912 bytes -sending 'radio' (56630 KB)... -OKAY [ 1.629s] +sending 'radio'(56630 KB)... +OKAY [ 1.629s] writing 'radio'... -OKAY [ 0.917s] +OKAY [ 0.917s] finished. total time: 2.546s rebooting into bootloader... -OKAY [ 0.020s] +OKAY [ 0.020s] finished. total time: 0.020s -extracting android-info.txt (0 MB) to RAM... -extracting boot.img (11 MB) to disk... took 0.125s +extracting android-info.txt(0 MB)to RAM... +extracting boot.img(11 MB)to disk... took 0.125s target reported max download size of 536870912 bytes archive does not contain 'boot.sig' archive does not contain 'dtbo.img' archive does not contain 'dt.img' -extracting recovery.img (17 MB) to disk... took 0.093s +extracting recovery.img(17 MB)to disk... took 0.093s archive does not contain 'recovery.sig' -extracting system.img (1909 MB) to disk... took 17.733s +extracting system.img(1909 MB)to disk... took 17.733s archive does not contain 'system.sig' archive does not contain 'vbmeta.img' -extracting vendor.img (185 MB) to disk... took 1.895s +extracting vendor.img(185 MB)to disk... took 1.895s archive does not contain 'vendor.sig' wiping userdata... -mke2fs 1.43.3 (04-Sep-2016) +mke2fs 1.43.3(04-Sep-2016) Creating filesystem with 2874363 4k blocks and 719488 inodes Filesystem UUID: 79218aab-c322-4823-a83f-5a26ad3fd27e Superblock backups stored on blocks: - 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208 +32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208 Allocating group tables: done Writing inode tables: done -Creating journal (16384 blocks): done +Creating journal(16384 blocks): done Writing superblocks and filesystem accounting information: done wiping cache... -mke2fs 1.43.3 (04-Sep-2016) +mke2fs 1.43.3(04-Sep-2016) Creating filesystem with 24576 4k blocks and 24576 inodes Allocating group tables: done Writing inode tables: done -Creating journal (1024 blocks): done +Creating journal(1024 blocks): done Writing superblocks and filesystem accounting information: done --------------------------------------------- +— Bootloader Version...: BHZ32c -Baseband Version.....: M8994F-2.6.42.5.03 -Serial Number........: 00f4d2e97de8bd23 --------------------------------------------- +Baseband Version.: M8994F-2.6.42.5.03 +Serial Number.: 00f4d2e97de8bd23 +— checking product... -OKAY [ 0.020s] -checking version-bootloader... -OKAY [ 0.020s] -checking version-baseband... -OKAY [ 0.020s] -sending 'boot' (11781 KB)... -OKAY [ 0.358s] -writing 'boot'... -OKAY [ 0.192s] -sending 'recovery' (17425 KB)... -OKAY [ 0.519s] -writing 'recovery'... -OKAY [ 0.294s] -erasing 'system'... -OKAY [ 0.452s] -sending sparse 'system' 1/4 (508768 KB)... -OKAY [ 14.932s] -writing 'system' 1/4... -OKAY [ 9.061s] -sending sparse 'system' 2/4 (524238 KB)... -OKAY [ 15.055s] -writing 'system' 2/4... -OKAY [ 9.168s] -sending sparse 'system' 3/4 (501061 KB)... -OKAY [ 15.391s] -writing 'system' 3/4... -OKAY [ 9.779s] -sending sparse 'system' 4/4 (421469 KB)... -OKAY [ 12.113s] -writing 'system' 4/4... -OKAY [ 7.220s] -erasing 'vendor'... -OKAY [ 0.110s] -sending 'vendor' (190332 KB)... -OKAY [ 5.327s] -writing 'vendor'... -OKAY [ 3.776s] -erasing 'userdata'... -OKAY [ 0.758s] -sending 'userdata' (4284 KB)... -OKAY [ 0.199s] -writing 'userdata'... -OKAY [ 0.104s] -erasing 'cache'... -OKAY [ 0.077s] -sending 'cache' (92 KB)... -OKAY [ 0.082s] -writing 'cache'... -OKAY [ 0.020s] -rebooting... - -finished. total time: 105.131s - -``` - -刷机完成后,手机会自动重启,重启完成后即可自动进入系统。 - -![](pic/1.3.1.2f.png) - -![](pic/1.3.1.2g.png) - -##### 1.3.1.3 刷入`twrp recovery` - -`recovery`相当于`Windows PE`微型系统,在`recovery`里我们也可以挂载磁盘,修改系统分区,使用`adb`命令,等一系列功能。详细的功能列表可以百度或者谷歌。 - -我们这里只需要下载`twrp`针对`bullhead`机型的镜像,刷进去即可。 - -![](pic/1.3.1.3a.png) - -我们下载最新的`twrp-3.2.3-0-bullhead.img`镜像文件。 - -然后将手机设置到`fastboot`模式,使用`fastboot`命令将镜像刷进去。 - -``` -$ fastboot flash recovery twrp-3.2.3-0-bullhead.img -target reported max download size of 536870912 bytes -sending 'recovery' (16289 KB)... -OKAY [ 0.499s] -writing 'recovery'... -OKAY [ 0.275s] -finished. total time: 0.774s -``` - -刷完之后,在手机上按两次音量向下键,选择`Recovery mode`,按电源键进入。 - -![](pic/1.3.1.3b.jpeg) - -稍等片刻之后,就会进入`twrp 3.2.3-0`系统。 - -![](pic/1.3.1.3c.jpeg) - -直接滑开即可。意味着允许修改系统。 - -![](pic/1.3.1.3d.jpeg) - -可以看到功能非常丰富。 - -![](pic/1.3.1.3e.jpeg) - -##### 1.3.1.4 刷入`Magisk` - -自从`SuperSU`卖给中国人,并且不再更新之后,大家已经不怎么使用这款`root`软件了,取而代之的是`Magisk`。 - -`Magisk`是由中国台湾省小伙儿`topjohnwu`开发的一款完全开源的`root`软件,其`github`项目托管主页在[这里](https://github.com/topjohnwu/Magisk)。 - -有关`Magisk`的详细中文介绍,大家可以看[这篇文章](https://xposed.appkg.com/2536.html),我们只是用`Magisk`来获取`root`权限。 - -在`github`项目主页的`release`页面,下载最新的卡刷包:`Magisk-v17.3.zip` - -然后使用`adb`命令将卡刷包上传到手机中去。 - -``` -$ adb push Magisk-v17.3.zip /sdcard/ -* daemon not running; starting now at tcp:5037 -* daemon started successfully -Magisk-v17.3.zip: 1 file pushed. 6.5 MB/s (4178628 bytes in 0.610s) -``` - -然后使用`twrp`将这个卡刷包安装进手机里。首先点选上一节最后一张图中的`Install`。 - -![](pic/1.3.1.4a.png) - -然后选择我们刚刚传输进去的`Magisk-v17.3.zip`卡刷包。 - -![](pic/1.3.1.4b.png) - -滑动确认安装。 - -![](pic/1.3.1.4c.png) - -安装完成后直接重启即可,`Reboot System` - -![](pic/1.3.1.4d.png) - -滑动重启。 - -![](pic/1.3.1.4e.png) - -重启后发现`Magisk Manager`已经安装好了,并且是作为系统App,卸载不了的。卸载只有安装官网`release`页面里的`Magisk-uninstaller-20181022.zip`卡刷包。 - -![](pic/1.3.1.4f.png) - -打开`App`可以看到安装成功。 - -![](pic/1.3.1.4g.png) - -##### 1.3.1.5 获取`root`权限 - -接下来就是使用`adb`命令进入安卓手机的`shell`,并且获取`root`权限,为下一节做好准备。 - -在手机上找到`设置→系统→关于手机→版本号`,点击`版本号`五下,打开`开发者选项`,然后进入`设置→系统→开发者选项`,打开`USB调试选项`。然后USB连接到电脑,使用`adb`命令连上去。手机上会出现授权,点击接受该指纹的电脑连接。 - -``` -$ adb shell -bullhead:/ $ -bullhead:/ $ -bullhead:/ $ whoami -shell -bullhead:/ $ -``` - -此时是`shell`权限,权限非常小。我们来切换到`root`用户: - -``` -bullhead:/ $ su - -``` - -此时手机上会出现`Magisk`的超级用户请求,点击允许,`com.android.shell`即可获取`root`权限。 - -![](pic/1.3.1.5a.png) - -点击允许之后,`su -`这个命令才会返回,然后运行`whoami`命令,可以看到已经是`root`了。 - -``` -bullhead:/ # -bullhead:/ # whoami -root -bullhead:/ # -bullhead:/ # -``` - -这时候就可以用`root`的权限来做一些事情了。 - -#### 1.3.2 Android frida-server 安装 - -上一节中刷机,获取`root`其实倒是蛮复杂的,这一节安装`frida-server`其实倒是简单很多,我们从官方github页面的`release`标签里,找到最新版的`frida-server`,注意要匹配系统和架构,比如`arm`和`arm64`就不能搞错,比如我们这里选择的就是`frida-server-12.2.26-android-arm64.xz`的版本。 - -下载完成后进行解压,获得`linux`原生的可执行文件,我们将它重命名为`frida-server`。 - -``` -$ file frida-server -frida-server-arm64: ELF 64-bit LSB shared object ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /system/bin/linker64, stripped -``` - -使用`adb`命令将其推送到手机上去。 - -``` -$ adb push frida-server /data/local/tmp/ -``` - -然后使用`adb shell`命令进入到手机中去,执行以下命令: - -``` -$ adb shell -bullhead:/ $ su - -bullhead:/ # whoami -root -bullhead:/ # cd /data/local/tmp/ -bullhead:/data/local/tmp # chmod 755 /data/local/tmp/frida-server -bullhead:/data/local/tmp # ./frida-server & -[1] 9849 -bullhead:/data/local/tmp # -``` - -`frida-server`即可运行成功。 - -此时在电脑上新开一个`shell`,运行`frida-ps -U`命令,即可显示手机中正在运行的进程。 - -``` -$ frida-ps -U - PID Name ----- --------------------------------------------------- -9628 - -5389 .esfm - 591 ATFWD-daemon -2953 adbd - 596 android.hardware.biometrics.fingerprint@2.1-service - 398 android.hardware.cas@1.0-service - 399 android.hardware.configstore@1.0-service - 400 android.hardware.dumpstate@1.0-service.bullhead - 401 android.hardware.graphics.allocator@2.0-service - 402 android.hardware.usb@1.0-service - 403 android.hardware.wifi@1.0-service - 397 android.hidl.allocator@1.0-service -7456 android.process.acore -3098 android.process.media - 567 audioserver - 568 cameraserver - 592 cnd - 588 cnss-daemon - 977 com.android.bluetooth -8829 com.android.connectivity.metrics -8275 com.android.keychain -3026 com.android.nfc -1175 com.android.phone -5184 com.android.settings -1020 com.android.systemui -7414 com.android.vending -3417 com.estrongs.android.pop -8247 com.estrongs.android.pop:local -5916 com.google.android.apps.gcs -8711 com.google.android.apps.messaging -8747 com.google.android.apps.messaging:rcs -7721 com.google.android.apps.photos -``` - -到这里我们在64位手机`Nexus 5X`的安卓原生`8.1`系统上安装`frida-server`就成功了。 +O +``` \ No newline at end of file diff --git a/FRIDA/A02/README.md b/FRIDA/A02/README.md index 94cd157..2b9592f 100644 --- a/FRIDA/A02/README.md +++ b/FRIDA/A02/README.md @@ -1,244 +1,101 @@ -* [一篇文章带你领悟`frida`的精髓(基于安卓8.1)](#一篇文章带你领悟frida的精髓基于安卓81) - * [`frida`是啥?](#frida是啥) - * [`frida`为什么这么火?](#frida为什么这么火) - * [frida实操环境](#frida实操环境) - * [基本能力I:hook参数、修改结果](#基本能力I:hook参数-修改结果) - * [基本能力II:参数构造、方法重载、隐藏函数的处理](#基本能力ii参数构造-方法重载-隐藏函数的处理) - * [中级能力:远程调用](#中级能力远程调用) - * [高级能力:互联互通、动态修改](#高级能力互联互通-动态修改) - * [打算做个成套的教程、目录已经想好了](#打算做个成套的教程-目录已经想好了) +* [One article takes you through the essence of 'frida' (based on Android 8.1)] (#One article takes you through the essence of frida based on Android 81) +* ['What is 'frida'?] (What is #frida) +* ['frida' Why is it so hot?] (Why is #frida so hot) +* [frida Implementation] (#frida Implementation) +* [Base Competencies I:hook Parameters, Modification Results] (#Base Competencies I:hook Parameters - Modification Results) +* [Basic CompetenceII: Parameter Construction, Method Overload, Handling of Hidden Functions] (# Basic Competence ii Parameter Construction - Method Overload - Handling of Hidden Functions) +* [Intermediate Competency: Remote Call] (#Intermediate Competency Remote Call) +* [Advanced Capabilities: Interoperability, Dynamic Modification] (#Advanced Capabilities Interoperability - Dynamic Modification) +* [Ready to do tutorials, catalog already taken] (#Ready to do tutorials - catalog already taken) -# 一篇文章带你领悟`frida`的精髓(基于安卓8.1) +# An article takes you through the essence of 'frida' (based on Android 8.1) -前阵子受[《Xposed模块编写的那些事》](https://www.freebuf.com/articles/terminal/114910.html)这篇文章的帮助很大,感觉有必要写一篇文章来回馈freebuf社区。现在最火爆的又是`frida`,该框架从`Java`层hook到`Native`层hook无所不能,虽然持久化还是要依靠`Xposed`和`hookzz`等开发框架,但是`frida`的动态和灵活对逆向以及自动化逆向的帮助非常巨大。 +Having been greatly helped by this article [What the Xposed Module Wrote](https://www.freebuf.com/articles/terminal/114910.html), I felt compelled to write an article to give back to the freebuf community. Now the most popular is 'frida', a framework that can do anything from 'Java' layer hook to 'Native' layer hook. While persistence relies on development frameworks such as 'Xposed' and 'hookzz', the dynamic and flexible nature of 'frida' can be very helpful in reverse and in automating reverse. -## `frida`是啥? +## What is 'frida'? -首先,`frida`是啥,github目录[Awesome Frida](https://github.com/dweinstein/awesome-frida)这样介绍`frida`的: +First, what is 'frida', and the github directory [Awesome Frida](https://github.com/dweinstein/awesome-frida) describes 'frida' in this way: >Frida is Greasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript into native apps that run on Windows, Mac, Linux, iOS and Android. Frida is an open source software. -`frida`是平台原生`app`的`Greasemonkey`,说的专业一点,就是一种动态插桩工具,可以插入一些代码到原生`app`的内存空间去,(动态地监视和修改其行为),这些原生平台可以是`Win`、`Mac`、`Linux`、`Android`或者`iOS`。而且`frida`还是开源的。 +'frida' is the 'Greasemonkey' of the native 'app' of the platform, and to be professional, it is a dynamic instrumentation tool that can insert some code into the memory space of the native 'app' (to dynamically monitor and modify its behavior), which native platforms can be 'Win', 'Mac', 'Linux', 'Android' or 'iOS'. And 'frida' is open source. -`Greasemonkey`可能大家不明白,它其实就是`firefox`的一套插件体系,使用它编写的脚本可以直接改变`firefox`对网页的编排方式,实现想要的任何功能。而且这套插件还是外挂的,非常灵活机动。 +'Greasemonkey' may not be understood as a set of plug-ins for 'firefox', and scripts written using it can directly alter the way 'firefox' organizes webpages to achieve any desired functionality. And the plug-in is external, very flexible. -`frida`也是一样的道理。 +'frida' makes the same sense. -## `frida`为什么这么火? +## 'frida' Why is it so popular? -动静态修改内存实现作弊一直是刚需,比如金山游侠,本质上`frida`做的跟它是一件事情。原则上是可以用`frida`把金山游侠,包括`CheatEngine`等"外挂"做出来的。 +Static and dynamic modification of memory cheating has always been a necessity, such as the Golden Mountain Ranger, essentially 'frida' does what it does. In principle, 'frida' can be used to "outboard" Jin Shan Rangers, including 'CheatEngine'. -当然,现在已经不是直接修改内存就可以高枕无忧的年代了。大家也不要这样做,做外挂可是违法行为。 +Of course, it's not the age when you can simply modify your memory to rest easy. Don't do that. It's illegal to do a plugging. -在逆向的工作上也是一样的道理,使用`frida`可以"看到"平时看不到的东西。出于编译型语言的特性,机器码在CPU和内存上执行的过程中,其内部数据的交互和跳转,对用户来讲是看不见的。当然如果手上有源码,甚至哪怕有带调试符号的可执行文件包,也可以使用`gdb`、`lldb`等调试器连上去看。 +The same is true in reverse work, where 'frida' is used to "see" what is not normally seen. Due to the characteristics of compiled language, machine code in the CPU and memory in the process of execution, its internal data interaction and jump, is invisible to the user. Of course, if you have source code on hand, even if you have a debuggeable executable package, you can use a debugger such as 'gdb', 'lldb', and so on. -那如果没有呢?如果是纯黑盒呢?又要对`app`进行逆向和动态调试、甚至自动化分析以及规模化收集信息的话,我们需要的是细粒度的流程控制和代码级的可定制体系,以及不断对调试进行动态纠正和可编程调试的框架,这就是`frida`。 +And if not? What if it's a black box? To reverse and dynamically debug the app, and even automate analysis and gather information on a large scale, we need fine-grained process control and code-level customizable systems, as well as a framework for constant dynamic correction of debugging and programmable debugging, which is 'frida'. -`frida`使用的是`python`、`JavaScript`等"胶水语言"也是它火爆的一个原因,可以迅速将逆向过程自动化,以及整合到现有的架构和体系中去,为你们发布"威胁情报"、"数据平台"甚至"AI风控"等产品打好基础。 +'frida' uses 'python', 'JavaScript' and other 'glue languages' as a reason for its popularity, quickly automating reverse processes and integrating them into existing architectures and systems to lay the groundwork for the release of products such as 'threat intelligence', 'data platforms' and even 'AI risk control'. ![](pic/01.png) -官宣屁屁踢甚至将其`敏捷开发`和`迅速适配到现有架构`的能力作为其核心卖点。 +Officials have even made their ability to 'agile development' and 'quickly adapt to existing architectures' their core selling points. -## frida实操环境 +## frida Implementation Environment -主机: +Host: ->Host:Macbook Air CPU: i5 Memory:8G -System:Kali Linux 2018.4 (Native,非虚拟机) +>Host:Macbook Air CPU: i5 Memory:8G +System:Kali Linux 2018.4 (Native, non-virtual machine) -客户端: +Client: ->client:Nexus 6 shamu CPU:Snapdragon 805 Mem:3G -System:lineage-15.1-20181123-NIGHTLY-shamu,android 8.1 +>client:Nexus 6 shamu CPU:Snapdragon 805 Mem:3G +System:lineage-15.1-20181123-NIGHTLY-shamu,android 8.1 -用`kali linux`的原因是工具很全面,权限很单一,只有一个`root`,作为原型开发很好用,否则`python`和`node`的各种权限、环境和依赖实在是烦。用`lineage`因为它有便利的`网络ADB调试`,可以省掉一个`usb`数据线连接的过程。(虽然真实的原因是没钱买新设备,`Nexus 6`[官方](https://developers.google.com/android/images)只支持到`7.1.1`,想上`8.1`只有`lineage`一个选择。)记得需要刷进去一个`lineage`的[`su`包](https://download.lineageos.org/extras),获取`root`权限,`frida`是需要在`root`权限下运行的。 +The reason for using 'kali linux' is that the tools are comprehensive, the permissions are single and only one 'root' is useful as a prototype development, otherwise the various permissions, environments and dependencies of 'python' and 'node' are simply annoying. Using 'lineage' because of its convenient 'network ADB debugging' can eliminate the need for a 'usb' cable connection. (Although the real reason is that there is no money to buy new equipment, 'Nexus 6' [official](https://developers.google.com/android/images) only supports '7.1.1' and '8.1' is only 'lineage'.)Remember to brush in a 'lineage' ['su' package](https://download.lineageos.org/extras) to get the 'root' permission and 'frida' is required to run under the 'root' permission. -首先到[官网](https://developer.android.com/studio/releases/platform-tools)下载一个`platform-tools`的linux版本——`SDK Platform-Tools for Linux`,下载解压之后可以直接运行里面的二进制文件,当然也可以把路径加到环境里去。这样`adb`和`fastboot`命令就有了。 +First, download a linux version of 'platform-tools' —'SDK Platform-Tools for Linux' from [the website](https://developer.android.com/studio/releases/platform-tools), download the extracted files and run the binaries directly inside, or, of course, add the path to the environment. Thus the 'adb' and 'fastboot' commands are available. -然后再将`frida-server`[下载](https://github.com/frida/frida/releases)下来,拷贝到安卓机器里去,使用`root`用户跑起来,保持`adb`的连接不要断开。 +Then you can take 'frida-server' [download](https://github.com/frida/frida/releases) off and copy it to the Android machine, run up using the 'root' user and keep 'adb' connected. -``` +```bash $ ./adb root # might be required $ ./adb push frida-server /data/local/tmp/ $ ./adb shell "chmod 755 /data/local/tmp/frida-server" $ ./adb shell "/data/local/tmp/frida-server &" ``` -最后在`kali linux`里安装好`frida`即可,在`kali`里安装`frida`真是太简单了,一句话命令即可,保证不出错。(可能会需要先安装`pip`,也是一句话命令:`curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py`) +Finally, install 'frida' in 'kali linux', and 'frida' in 'kali' is so simple, a one-sentence order, to make sure that nothing goes wrong. (You may need to install 'pip' first, or a one-sentence command: 'curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py') -``` +```bash pip install frida-tools ``` -然后用`frida-ps -U`命令连上去,就可以看到正在运行的进程了。 +Then connect it with the 'frida-ps -U' command and you can see the running process. ```js root@kali:~# frida-ps -U Waiting for USB device to appear... - PID Name ----- ----------------------------------------------- - 431 ATFWD-daemon -3148 adbd - 391 adspd -2448 android.ext.services - 358 android.hardware.cas@1.0-service - 265 android.hardware.configstore@1.0-service - 359 android.hardware.drm@1.0-service - 360 android.hardware.dumpstate@1.0-service.shamu - 361 android.hardware.gnss@1.0-service - 266 android.hardware.graphics.allocator@2.0-service - 357 android.hidl.allocator@1.0-service - ... - ... - ``` - -## 基本能力I:hook参数、修改结果 - -先自己写个`app`: - -```java -package com.roysue.demo02; - -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.util.Log; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - while (true){ - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - fun(50,30); - } - } - - void fun(int x , int y ){ - Log.d("Sum" , String.valueOf(x+y)); - } - -} - ``` - -原理上很简单,就是间隔一秒在控制台输出一下`fun(50,30)`函数的结果,`fun()`这个函数的作用是求和。那最终结果在控制台如下所示。 - -``` -$ adb logcat |grep Sum -11-26 21:26:23.234 3245 3245 D Sum : 80 -11-26 21:26:24.234 3245 3245 D Sum : 80 -11-26 21:26:25.235 3245 3245 D Sum : 80 -11-26 21:26:26.235 3245 3245 D Sum : 80 -11-26 21:26:27.236 3245 3245 D Sum : 80 -11-26 21:26:28.237 3245 3245 D Sum : 80 -11-26 21:26:29.237 3245 3245 D Sum : 80 -``` - -现在我们来写一段`js`代码,并用`frida-server`将这段代码加载到`com.roysue.demo02`中去,执行其中的`hook`函数。 - -``` -$ nano s1.js -``` - -```js -console.log("Script loaded successfully "); -Java.perform(function x() { - console.log("Inside java perform function"); - //定位类 - var my_class = Java.use("com.roysue.demo02.MainActivity"); - console.log("Java.Use.Successfully!");//定位类成功! - //在这里更改类的方法的实现(implementation) - my_class.fun.implementation = function(x,y){ - //打印替换前的参数 - console.log( "original call: fun("+ x + ", " + y + ")"); - //把参数替换成2和5,依旧调用原函数 - var ret_value = this.fun(2, 5); - return ret_value; - } -}); -``` - -然后我们在`kali`主机上使用一段`python`脚本,将这段`js`脚本"传递"给安卓系统里正在运行的`frida-server`。 - -``` -$ nano loader.py -``` - -```py -import time -import frida - -# 连接安卓机上的frida-server -device = frida.get_usb_device() -# 启动`demo02`这个app -pid = device.spawn(["com.roysue.demo02"]) -device.resume(pid) -time.sleep(1) -session = device.attach(pid) -# 加载s1.js脚本 -with open("s1.js") as f: - script = session.create_script(f.read()) -script.load() - -# 脚本会持续运行等待输入 -raw_input() -``` - -然后得保证`frida-server`正在运行,方法可以是在`kali`主机输入`frida-ps -U`命令,如果安卓机上的进程出现了,则`frida-server`运行良好。 - -还需要保证`selinux`是关闭的状态,可以在`adb shell`里,`su -`获得`root`权限之后,输入`setenforce 0`命令来获得,在`Settings→About Phone→SELinux status`里看到`Permissive`,说明`selinux`关闭成功。 - -然后在`kali`主机上输入`python loader.js`,可以观察到安卓机上`com.roysue.demo02`这个`app`马上重启了。然后`$ adb logcat|grep Sum`里的内容也变了。 - -``` -11-26 21:44:47.875 2420 2420 D Sum : 80 -11-26 21:44:48.375 2420 2420 D Sum : 80 -11-26 21:44:48.875 2420 2420 D Sum : 80 -11-26 21:44:49.375 2420 2420 D Sum : 80 -11-26 21:44:49.878 2420 2420 D Sum : 7 -11-26 21:44:50.390 2420 2420 D Sum : 7 -11-26 21:44:50.904 2420 2420 D Sum : 7 -11-26 21:44:51.408 2420 2420 D Sum : 7 -11-26 21:44:51.921 2420 2420 D Sum : 7 -11-26 21:44:52.435 2420 2420 D Sum : 7 -11-26 21:44:52.945 2420 2420 D Sum : 7 -11-26 21:44:53.459 2420 2420 D Sum : 7 -11-26 21:44:53.970 2420 2420 D Sum : 7 -11-26 21:44:54.480 2420 2420 D Sum : 7 -``` - -在`kali`主机上可以观察到: - -``` -$ python loader.py -Script loaded successfully -Inside java perform function -Java.Use.Successfully! -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) -original call: fun(50, 30) +PID Name +— — +431 ATFWD-daemon +3148 adbd +391 adspd +2448 android.ext.services +358 android.hardware.cas@1.0-service +265 android.hardware.configstore@1.0-service +359 android.hardware.drm@1.0-service +360 android.hardware.dumpstate@1.0-service.shamu +361 android.hardware.gnss@1.0-service +266 android.hardware.graphics.allocator@2.0-service +357 android.hidl.allocator@1.0-service +... +... ``` -说明脚本执行成功了,代码也插到`com.roysue.demo02`这个包里去,并且成功执行了,`s1.js`里的代码成功执行了,并且把交互结果传回了`kali`主机上。 - -## 基本能力II:参数构造、方法重载、隐藏函数的处理 +## Basic Competencies I:hook Parameters, Modification Results -我们现在把`app`的代码稍微写复杂一点点: +Write your own 'app' first: ```java package com.roysue.demo02; @@ -249,423 +106,53 @@ import android.util.Log; public class MainActivity extends AppCompatActivity { - private String total = "@@@###@@@"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - while (true){ +@Override +protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + while(true){ - fun(50,30); - Log.d("ROYSUE.string" , fun("LoWeRcAsE Me!!!!!!!!!")); - } + try { + Thread.sleep(1000); + } catch(InterruptedException e){ + e.printStackTrace(); } - void fun(int x , int y ){ - Log.d("ROYSUE.Sum" , String.valueOf(x+y)); + fun(50,30); } - - String fun(String x){ - total +=x; - return x.toLowerCase(); } - String secret(){ - return total; + void fun(int x , int y){ + Log.d("Sum" , String.valueOf(x+y)); } -} -``` - -`app`运行起来后在使用`logcat`打印出来的日志如下: -``` -$ adb logcat |grep ROYSUE -11-26 22:22:35.689 3051 3051 D ROYSUE.Sum: 80 -11-26 22:22:35.689 3051 3051 D ROYSUE.string: lowercase me!!!!!!!!! -11-26 22:22:36.695 3051 3051 D ROYSUE.Sum: 80 -11-26 22:22:36.696 3051 3051 D ROYSUE.string: lowercase me!!!!!!!!! -11-26 22:22:37.696 3051 3051 D ROYSUE.Sum: 80 -11-26 22:22:37.696 3051 3051 D ROYSUE.string: lowercase me!!!!!!!!! -11-26 22:22:38.697 3051 3051 D ROYSUE.Sum: 80 -11-26 22:22:38.697 3051 3051 D ROYSUE.string: lowercase me!!!!!!!!! -11-26 22:22:39.697 3051 3051 D ROYSUE.Sum: 80 -11-26 22:22:39.698 3051 3051 D ROYSUE.string: lowercase me!!!!!!!!! -``` - -可以看到`fun()`方法有了重载,在参数是两个`int`的情况下,返回两个`int`之和。在参数为`String`类型之下,则返回字符串的小写形式。 - -另外,`secret()`函数为隐藏方法,在`app`里没有被直接调用。 - -这时候如果我们直接使用上一节里面的`js`脚本和`loader.js`来加载的话,肯定会崩溃。为了看到崩溃的信息,我们对`loader.js`做一些处理。 - -```py -def my_message_handler(message , payload): #定义错误处理 - print message - print payload -... -script.on("message" , my_message_handler) #调用错误处理 -script.load() -``` - -再运行`$ python loader.py`的话,就会看到如下的错误信息返回: - -``` -$ python loader.py -Script loaded successfully -Inside java perform function -Java.Use.Successfully! -{u'columnNumber': 1, u'description': u"Error: fun(): has more than one overload, use .overload() to choose from:\n\t.overload('java.lang.String')\n\t.overload('int', 'int')", u'fileName': u'frida/node_modules/frida-java/lib/class-factory.js', u'lineNumber': 2233, u'type': u'error', u'stack': u"Error: fun(): has more than one overload, use .overload() to choose from:\n\t.overload('java.lang.String')\n\t.overload('int', 'int')\n at throwOverloadError (frida/node_modules/frida-java/lib/class-factory.js:2233)\n at frida/node_modules/frida-java/lib/class-factory.js:1468\n at x (/script1.js:14)\n at frida/node_modules/frida-java/lib/vm.js:43\n at M (frida/node_modules/frida-java/index.js:347)\n at frida/node_modules/frida-java/index.js:299\n at frida/node_modules/frida-java/lib/vm.js:43\n at frida/node_modules/frida-java/index.js:279\n at /script1.js:15"} -None -``` - -可以看出是一个`throwOverloadError`,这时候就是因为我们没有处理重载,造成的重载处理错误。这个时候就需要我们来处理重载了,在`js`脚本中处理重载是这样写的: - -```js -my_class.fun.overload("int" , "int").implementation = function(x,y){ -... -my_class.fun.overload("java.lang.String").implementation = function(x){ -``` - -其中参数均为两个`int`的情况下,上一节已经讲过了。参数为`String`类的时候,由于`String`类不是Java基本数据类型,而是`java.lang.String`类型,所以在替换参数的构造上,需要花点心思。 - -```js -var string_class = Java.use("java.lang.String"); //获取String类型 - -my_class.fun.overload("java.lang.String").implementation = function(x){ - console.log("*************************************"); - var my_string = string_class.$new("My TeSt String#####"); //new一个新字符串 - console.log("Original arg: " +x ); - var ret = this.fun(my_string); // 用新的参数替换旧的参数,然后调用原函数获取结果 - console.log("Return value: "+ret); - console.log("*************************************"); - return ret; -}; -``` - -这样我们对于重载函数的处理就算是ok了。我们到实验里来看下: - -``` -$ python loader.py -Script loaded successfully -Inside java perform function -original call: fun(50, 30) -************************************* -Original arg: LoWeRcAsE Me!!!!!!!!! -Return value: my test string##### -************************************* -original call: fun(50, 30) -************************************* -Original arg: LoWeRcAsE Me!!!!!!!!! -Return value: my test string##### -************************************* -original call: fun(50, 30) -************************************* -Original arg: LoWeRcAsE Me!!!!!!!!! -Return value: my test string##### -************************************* -``` - -然后`logcat`打出来的结果也变了。 - -``` -$ adb logcat |grep ROYSUE -11-26 22:23:29.597 3244 3244 D ROYSUE.Sum: 7 -11-26 22:23:29.673 3244 3244 D ROYSUE.string: my test string##### -11-26 22:23:30.689 3244 3244 D ROYSUE.Sum: 7 -11-26 22:23:30.730 3244 3244 D ROYSUE.string: my test string##### -11-26 22:23:31.740 3244 3244 D ROYSUE.Sum: 7 -11-26 22:23:31.789 3244 3244 D ROYSUE.string: my test string##### -11-26 22:23:32.797 3244 3244 D ROYSUE.Sum: 7 -11-26 22:23:32.833 3244 3244 D ROYSUE.string: my test string##### -``` - -最后再说一下隐藏方法的调用,`frida`对其的处理办法跟`Xposed`是非常像的,`Xposed`使用的是`XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);`方法,直接`findClass`,其实`frida`也非常类似,也是使用的直接到内存里去寻找的方法,也就是`Java.choose(className, callbacks)`函数,通过类名触发回掉函数。 - -```js -Java.choose("com.roysue.demo02.MainActivity" , { - onMatch : function(instance){ //该类有多少个实例,该回调就会被触发多少次 - console.log("Found instance: "+instance); - console.log("Result of secret func: " + instance.secret()); - }, - onComplete:function(){} -}); -``` - -最终运行效果如下: - -``` -$ python loader.py -Script loaded successfully -Inside java perform function -Found instance: com.roysue.demo02.MainActivity@92d5deb -Result of secret func: @@@###@@@ -original call: fun(50, 30) -************************************* -Original arg: LoWeRcAsE Me!!!!!!!!! -Return value: my test string##### -************************************* -original call: fun(50, 30) -************************************* -Original arg: LoWeRcAsE Me!!!!!!!!! -Return value: my test string##### -************************************* -original call: fun(50, 30) -``` - -这样隐藏方法也被调用起来了。 - - -## 中级能力:远程调用 - -上一小节中我们在安卓机器上使用`js`脚本调用了隐藏函数`secret()`,它在`app`内虽然没有被任何地方调用,但是仍然被我们的脚本"找到"并且"调用"了起来 - -这一小节我们要实现的是,不仅要在跑在安卓机上的`js`脚本里调用这个函数,还要可以在`kali`主机上的`py`脚本里,直接调用这个函数。 - -也就是使用`frida`提供的`RPC`功能(Remote Procedure Call)。 - -安卓`app`不需要有任何修改,这次我们要修改的是`js`脚本和`py`脚本。 - -``` -$ nano s3.js -``` - -```js -console.log("Script loaded successfully "); - -function callSecretFun() { //定义导出函数 - Java.perform(function () { //找到隐藏函数并且调用 - Java.choose("com.roysue.demo02.MainActivity", { - onMatch: function (instance) { - console.log("Found instance: " + instance); - console.log("Result of secret func: " + instance.secret()); - }, - onComplete: function () { } - }); - }); } -rpc.exports = { - callsecretfunction: callSecretFun //把callSecretFun函数导出为callsecretfunction符号,导出名不可以有大写字母或者下划线 -}; -``` - -然后我们可以在`kali`主机的`py`脚本里直接调用该函数: - -``` -$ nano loader3.py ``` -```py -import time -import frida - -def my_message_handler(message, payload): - print message - print payload - -device = frida.get_usb_device() -pid = device.spawn(["com.roysue.demo02"]) -device.resume(pid) -time.sleep(1) -session = device.attach(pid) -with open("s3.js") as f: - script = session.create_script(f.read()) -script.on("message", my_message_handler) -script.load() - -command = "" -while 1 == 1: - command = raw_input("Enter command:\n1: Exit\n2: Call secret function\nchoice:") - if command == "1": - break - elif command == "2": #在这里调用 - script.exports.callsecretfunction() -``` - -然后在`kali`主机上我们就可以看到以下的输出: +In principle, it is simple that 'fun()' is the function that outputs the result of the function 'fun(50,30)' at a time interval of one second at the console. The function of 'fun()' is summation. The final result is shown below in the console. +```bash +$ adb logcat |grep Sum +11-26 21:26:23.234 3245 3245 D Sum : 80 +11-26 21:26:24.234 3245 3245 D Sum : 80 +11-26 21:26:25.235 3245 3245 D Sum : 80 +11-26 21:26:26.235 3245 3245 D Sum : 80 +11-26 21:26:27.236 3245 3245 D Sum : 80 +11-26 21:26:28.237 3245 3245 D Sum : 80 +11-26 21:26:29.237 3245 3245 D Sum : 80 ``` -$ python loader3.py -Script loaded successfully -Enter command: -1: Exit -2: Call secret function -choice:2 -Found instance: com.roysue.demo02.MainActivity@2eacd80 -Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!! -Enter command: -1: Exit -2: Call secret function -choice:2 -Found instance: com.roysue.demo02.MainActivity@2eacd80 -Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!! -Enter command: -1: Exit -2: Call secret function -choice:2 -Found instance: com.roysue.demo02.MainActivity@2eacd80 -Result of secret func: @@@###@@@LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!!LoWeRcAsE Me!!!!!!!!! -Enter command: -1: Exit -2: Call secret function -choice:1 -``` - -这样我们就实现了在`kali`主机上直接调用安卓`app`内部的函数的能力。 -## 高级能力:互联互通、动态修改 +Now let's write a piece of 'js' code and load it into 'com.roysue.demo02' using 'frida-server' to execute the 'hook' function in it. -最后我们要实现的功能是,我们不仅仅可以在`kali`主机上调用安卓`app`里的函数。我们还可以把数据从安卓`app`里传递到`kali`主机上,在主机上进行修改,再传递回安卓`app`里面去。 - -我们编写这样一个`app`,其中最核心的地方在于判断用户是否为`admin`,如果是,则直接返回错误,禁止登陆。如果不是,则把用户和密码上传到服务器上进行验证。 - -```java -package com.roysue.demo04; - -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.util.Base64; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -public class MainActivity extends AppCompatActivity { - - EditText username_et; - EditText password_et; - TextView message_tv; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - - password_et = (EditText) this.findViewById(R.id.editText2); - username_et = (EditText) this.findViewById(R.id.editText); - message_tv = ((TextView) findViewById(R.id.textView)); - - this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - - if (username_et.getText().toString().compareTo("admin") == 0) { - message_tv.setText("You cannot login as admin"); - return; - } - //hook target - message_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT)); - - } - }); - - } -} +```javascript +$ nano s1.js ``` -最终跑起来之后,效果就是这样。 - -![](pic/02.png) - -我们的目标就是在`kali`主机上"得到"输入框输入的内容,并且修改其输入的内容,并且"传输"给安卓机器,使其通过验证。也就是说,我们哪怕输入`admin`的账户和密码,也可以绕过本地校验,进行登陆的操作。 - -所以最终安卓端的`js`代码的逻辑就是,截取输入,传输给`kali`主机,暂停执行,得到`kali`主机传回的数据之后,继续执行。形成代码如下: - ```js -Java.perform(function () { - var tv_class = Java.use("android.widget.TextView"); - tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) { - var string_to_send = x.toString(); - var string_to_recv; - send(string_to_send); // 将数据发送给kali主机的python代码 - recv(function (received_json_object) { - string_to_recv = received_json_object.my_data - console.log("string_to_recv: " + string_to_recv); - }).wait(); //收到数据之后,再执行下去 - return this.setText(string_to_recv); - } -}); -``` - -`kali`主机端的流程就是,将接受到的`JSON`数据解析,提取出其中的密码部分,然后将用户名替换成`admin`,这样就实现了将`admin`和`pw`发送给"服务器"的结果。 - -```py -import time -import frida - -def my_message_handler(message, payload): - print message - print payload - if message["type"] == "send": - print message["payload"] - data = message["payload"].split(":")[1].strip() - print 'message:', message - data = data.decode("base64") - user, pw = data.split(":") - data = ("admin" + ":" + pw).encode("base64") - print "encoded data:", data - script.post({"my_data": data}) # 将JSON对象发送回去 - print "Modified data sent" - -device = frida.get_usb_device() -pid = device.spawn(["com.roysue.demo04"]) -device.resume(pid) -time.sleep(1) -session = device.attach(pid) -with open("s4.js") as f: - script = session.create_script(f.read()) -script.on("message", my_message_handler) # 注册消息处理函数 -script.load() -raw_input() -``` - -我们只要输入任意用户名(非admin)+密码,非admin的用户名可以绕过`compareTo`校验,然后`frida`会帮助我们将用户名改成`admin`,最终就是`admin:pw`的组合发送到服务器。 - -``` -$ python loader4.py -Script loaded successfully -{u'type': u'send', u'payload': u'Sending to the server :YWFhYTpiYmJi\n'} -None -Sending to the server :YWFhYTpiYmJi - -message: {u'type': u'send', u'payload': u'Sending to the server :YWFhYTpiYmJi\n'} -data: aaaa:bbbb -pw: bbbb -encoded data: YWRtaW46YmJiYg== - -Modified data sent -string_to_recv: YWRtaW46YmJiYg== -``` - -动态修改输入内容就这样实现了。 - -## 打算做个成套的教程、目录已经想好了 - -frida『葵花宝典』 - -第一章.各种环境安装(包括Win、Mac、Ubuntu、ARM机器下的各种环境安装) -第二章.基本案例上手(安卓、iOS、Win、Mac为对象的各种插桩方法) -第三章.frida-tools(frida原生提供的各种工具的使用) -第四章.frida-scripts(各种frida脚本的介绍、使用和总结) -第五章.frida高级应用(安卓hook参数模型的总结、SSL-unpinning模型、iOS应用重打包动态修改等等) -第六章.二次开发基础(frida-API基本使用方法、基于frida的二次开发模型) -第七章.二次开发案例(Fridump、r2frida、brida、Appmon等源码解析和解读) - -当然还在酝酿中,大家有想法可以跟我沟通,想要源码的也可以加我。微信&微博:r0ysue - -谢谢大家。 - -参考资料: - -- [frida](https://www.frida.re/) -- [dweinstein/awesome-frida](https://github.com/dweinstein/awesome-frida) -- [nluug-2015-frida-putting-the-open-back-into-closed-software](http://slides.com/oleavr/nluug-2015-frida-putting-the-open-back-into-closed-software#/) -- [Frida hooking android](https://11x256.github.io/Frida-hooking-android-part-1/) -- [brida](https://github.com/federicodotta/Brida) +console.log("Script loaded successfully "); +Java.perform(function x(){ +console.log("Inside java perform function"); +// Location class +var my +``` \ No newline at end of file diff --git a/FRIDA/B01/README.md b/FRIDA/B01/README.md index 0f0ac88..451f331 100644 --- a/FRIDA/B01/README.md +++ b/FRIDA/B01/README.md @@ -1,96 +1,96 @@ - - + + -* [FRIDA脚本系列(一)入门篇:在安卓`8.1`上dump蓝牙接口和实例](#frida脚本系列一入门篇在安卓81上dump蓝牙接口和实例) - * [0x01.FRIDA是啥?为啥这么火?](#0x01frida是啥为啥这么火) - * [0x02.FRIDA脚本的概念](#0x02frida脚本的概念) - * [0x03.简单脚本一:枚举所有的类](#0x03简单脚本一枚举所有的类) - * [0x04.简单脚本二:定位目标类并打印类的实例](#0x04简单脚本二定位目标类并打印类的实例) - * [0x05.简单脚本三:枚举所有方法并定位方法](#0x05简单脚本三枚举所有方法并定位方法) - * [0x06.综合案例:在安卓`8.1`上dump蓝牙接口和实例:](#0x06综合案例在安卓81上dump蓝牙接口和实例) +* [FRIDA Scripting Series (I) Primer: dump Bluetooth Interfaces and Instances on Android '8.1'] (Primer on #frida Scripting Series: dump Bluetooth Interfaces and Instances on Android 81) +* [What is 0x01.FRIDA? Why is it so popular?](#0x01frida What is it and why is it so popular) +* [concept of 0x02.FRIDA script] ( concept of #0x02frida script) +* [0x03. simple script one: enumerate all classes] (#0x03 simple script one enumerates all classes) +* [0x04. Simple Script II: Locate Target Class and Print Instances of Class] (#0x04 Simple Script II Locate Target Class and Print Instances of Class) +* [0x05. Simple Script III: Enumerate all methods and locate methods] (#0x05 Simple Script III enumerates all methods and locates methods) +* [0x06. Composite case: dump Bluetooth interface and instance on Android '8.1':] (#0x06 composite case dump Bluetooth interface and instance on Android 81) - + -## FRIDA脚本系列(一)入门篇:在安卓`8.1`上dump蓝牙接口和实例 +## FRIDA Script Series (I) Getting Started: dump Bluetooth Interfaces and Instances on Android '8.1' -### 0x01.FRIDA是啥?为啥这么火? +### What is 0x01.FRIDA? Why is it so hot? -`frida`目前非常火爆,该框架从`Java`层hook到`Native`层hook无所不能,虽然持久化还是要依靠`Xposed`和`hookzz`等开发框架,但是`frida`的动态和灵活对逆向以及自动化逆向的帮助非常巨大。 +'frida' is currently very popular and the framework is everything from 'Java' layer hook to 'Native' layer hook. Although persistence relies on development frameworks such as 'Xposed' and 'hookzz', the dynamic and flexible nature of 'frida' can be very helpful in reverse and in automating reverse. -`frida`是啥呢,github目录[Awesome Frida](https://github.com/dweinstein/awesome-frida)这样介绍`frida`的: +What is 'frida'? The github directory [Awesome Frida](https://github.com/dweinstein/awesome-frida) describes 'frida' in this way: >Frida is Greasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript into native apps that run on Windows, Mac, Linux, iOS and Android. Frida is an open source software. -`frida`是平台原生`app`的`Greasemonkey`,说的专业一点,就是一种动态插桩工具,可以插入一些代码到原生`app`的内存空间去,(动态地监视和修改其行为),这些原生平台可以是`Win`、`Mac`、`Linux`、`Android`或者`iOS`。而且`frida`还是开源的。 +'frida' is the 'Greasemonkey' of the native 'app' of the platform, and to be professional, it is a dynamic instrumentation tool that can insert some code into the memory space of the native 'app' (to dynamically monitor and modify its behavior), which native platforms can be 'Win', 'Mac', 'Linux', 'Android' or 'iOS'. And 'frida' is open source. -`Greasemonkey`可能大家不明白,它其实就是`firefox`的一套插件体系,使用它编写的脚本可以直接改变`firefox`对网页的编排方式,实现想要的任何功能。而且这套插件还是外挂的,非常灵活机动。 +'Greasemonkey' may not be understood as a set of plug-ins for 'firefox', and scripts written using it can directly alter the way 'firefox' organizes webpages to achieve any desired functionality. And the plug-in is external, very flexible. -`frida`也是一样的道理。那它为什么这么火爆呢? +'frida' makes the same sense. So why is it so popular? -动静态修改内存实现作弊一直是刚需,比如金山游侠,本质上`frida`做的跟它是一件事情。原则上是可以用`frida`把金山游侠,包括`CheatEngine`等"外挂"做出来的。 +Static and dynamic modification of memory cheating has always been a necessity, such as the Golden Mountain Ranger, essentially 'frida' does what it does. In principle, 'frida' can be used to "outboard" Jin Shan Rangers, including 'CheatEngine'. -当然,现在已经不是直接修改内存就可以高枕无忧的年代了。大家也不要这样做,做外挂可是违法行为。 +Of course, it's not the age when you can simply modify your memory to rest easy. Don't do that. It's illegal to do a plugging. -在逆向的工作上也是一样的道理,使用`frida`可以"看到"平时看不到的东西。出于编译型语言的特性,机器码在CPU和内存上执行的过程中,其内部数据的交互和跳转,对用户来讲是看不见的。当然如果手上有源码,甚至哪怕有带调试符号的可执行文件包,也可以使用`gbd`、`lldb`等调试器连上去看。 +The same is true in reverse work, where 'frida' is used to "see" what is not normally seen. Due to the characteristics of compiled language, machine code in the CPU and memory in the process of execution, its internal data interaction and jump, is invisible to the user. Of course, if you have source code on hand, even if you have a debuggeable executable package, you can use a debugger such as 'gbd', 'lldb', and so on. -那如果没有呢?如果是纯黑盒呢?又要对`app`进行逆向和动态调试、甚至自动化分析以及规模化收集信息的话,我们需要的是细粒度的流程控制和代码级的可定制体系,以及不断对调试进行动态纠正和可编程调试的框架,这就是`frida`。 +And if not? What if it's a black box? To reverse and dynamically debug the app, and even automate analysis and gather information on a large scale, we need fine-grained process control and code-level customizable systems, as well as a framework for constant dynamic correction of debugging and programmable debugging, which is 'frida'. -`frida`使用的是`python`、`JavaScript`等"胶水语言"也是它火爆的一个原因,可以迅速将逆向过程自动化,以及整合到现有的架构和体系中去,为你们发布"威胁情报"、"数据平台"甚至"AI风控"等产品打好基础。 +'frida' uses 'python', 'JavaScript' and other 'glue languages' as a reason for its popularity, quickly automating reverse processes and integrating them into existing architectures and systems to lay the groundwork for the release of products such as 'threat intelligence', 'data platforms' and even 'AI risk control'. ![](pic/01.png) -官宣屁屁踢甚至将其`敏捷开发`和`迅速适配到现有架构`的能力作为其核心卖点。 +Officials have even made their ability to 'agile development' and 'quickly adapt to existing architectures' their core selling points. -### 0x02.FRIDA脚本的概念 +### Concepts for 0x02.FRIDA scripts -`FRIDA脚本`就是利用`FRIDA`动态插桩框架,使用`FRIDA`导出的`API`和方法,对内存空间里的对象方法进行监视、修改或者替换的一段代码。`FRIDA`的`API`是使用`JavaScript`实现的,所以我们可以充分利用`JS`的匿名函数的优势、以及大量的`hook`和回调函数的API。 +'FRIDA script' is a piece of code that uses the 'FRIDA' dynamic instrumentation framework, 'FRIDA' exported 'APIs' and methods, to monitor, modify, or replace object methods in memory space. The 'API' of 'FRIDA' is implemented using 'JavaScript', so we can take full advantage of the advantages of the 'JS' anonymity function, as well as the numerous 'hook' and callback function APIs. -我们来举个最直观的例子:`hello-world.js` +Let's take the most intuitive example: 'hello-world.js' ```js setTimeout(function(){ - Java.perform(function(){ - console.log("hello world!"); - }); +Java.perform(function(){ +console.log("hello world!"); +}); }); ``` -这基本上就是一个`FRIDA`版本的"Hello World!",我们把一个匿名函数作为参数传给了`setTimeout()`函数,然而函数体中的`Java.perform()`这个函数本身又接受了一个匿名函数作为参数,该匿名函数中最终会调用`console.log()`函数来打印一个"Hello world!"字符串。我们需要调用`setTimeout()`方法因为该方法将我们的函数注册到`JavaScript`运行时中去,然后需要调用`Java.perform()`方法将函数注册到`Frida`的`Java`运行时中去,用来执行函数中的操作,当然这里只是打了一条`log`。 +This is essentially a 'FRIDA' version of "Hello World!", we pass an anonymous function as an argument to the 'setTimeout()' function, whereas the 'Java.perform()' function in the body of functions itself accepts an anonymous function as an argument, which eventually calls the 'console.log()' function to print a "Hello world!"String. We need to call the 'setTimeout()' method because it registers our function with the 'JavaScript' runtime, and then we need to call the 'Java.perform()' method to register the function with the 'Java' runtime of 'Frida', to perform the operations in the function, and of course there's just a 'log'. -然后我们在手机上将`frida-server`运行起来,在电脑上进行操作: +We then run 'frida-server' on our phones and operate it on our computers: `$ frida -U -l hello-world.js android.process.media` ![](pic/001.png) -然后可以看到`console.log()`执行成功,字符串打印了出来。 +You can then see that 'console.log()' was executed successfully and the string was printed. -### 0x03.简单脚本一:枚举所有的类 +### 0x03. Simple script one: enumerate all classes -我们现在来给这个`HelloWorld.js`稍微加一点功能,比如说枚举所有已经加载的类,这就用到了`Java`对象的`enumerateLoadedClasses`方法。代码如下: +We are now going to add a little bit of functionality to this 'HelloWorld.js', such as enumerating all the classes that have been loaded, which takes into account the 'enumerateLoadedClasses' method of the 'Java' object. The code is as follows: ```js -setTimeout(function (){ - Java.perform(function (){ - console.log("\n[*] enumerating classes..."); - Java.enumerateLoadedClasses({ - onMatch: function(_className){ +setTimeout(function(){ + Java.perform(function(){ + console.log("\n[*] enumerating classes..."); + Java.enumerateLoadedClasses({ + onMatch: function(_className){ console.log("[*] found instance of '"+_className+"'"); - }, - onComplete: function(){ + }, + onComplete: function(){ console.log("[*] class enuemration complete"); - } + } + }); }); - }); }); ``` -首先还是确保手机上的`frida-server`正在运行中,然后在电脑上操作: +First, make sure that 'frida-server' is running on your phone, and then on your PC: -``` +```bash $ frida -U -l enumerate_classes.js android.process.media ``` @@ -98,163 +98,91 @@ $ frida -U -l enumerate_classes.js android.process.media ![](pic/003.png) -### 0x04.简单脚本二:定位目标类并打印类的实例 +### 0x04. Simple Script II: Position the target class and print an instance of the class -现在我们已经找到目标进程中所有已经加载的类,比如说现在我们的目标是要查看其蓝牙相关的类,我们可以把代码修改成这样: +Now we have found all the loaded classes in the target process, for example, now our goal is to see its Bluetooth-related classes, we can modify the code to this: ```js Java.enumerateLoadedClasses({ onMatch: function(instance){ - if (instance.split(".")[1] == "bluetooth"){ + if(instance.split(".")[1] == "bluetooth"){ console.log("[->]\t"+instance); - } + } }, - onComplete: function() { - console.log("[*] class enuemration complete"); + onComplete: function(){ + console.log("[*] class enuemration complete"); } - }); + }); ``` -我们来看下效果: +Here's what it does: ![](pic/005.png) -可以找到上述这么多蓝牙相关的类。当然也可以使用字符串包含的方法,使用`JavaScript`字符串的`indexOf()`、`search()`或者`match()`方法,这个留给读者自己完成。 +So many Bluetooth-related classes can be found. It is also possible, of course, to use the method contained in the string, using the 'indexOf()', 'search()', or 'match()' method of the 'JavaScript' string, which is left to the reader. -定位到我们想要研究的类之后,就可以打印类的实例了,查看[`FRIDA的API手册`](https://www.frida.re/docs/javascript-api/#java)可以得知,此时应该使用`Java.choose()`函数,来选定某一个实例。 +Once you have located the class we want to study, you can print an instance of the class and look at the ['FRIDA's API Manual'](https://www.frida.re/docs/javascript-api/#java) to see that you should use the 'Java.choose()' function to select an instance. ![](pic/004.png) -我们增加下列几行选定`android.bluetooth.BluetoothDevice`类的实例的代码。 +We add the following lines to the code for the instance of the 'android.bluetooth.BluetoothDevice' class selected. ```js Java.choose("android.bluetooth.BluetoothDevice",{ - onMatch: function (instance){ - console.log("[*] "+" android.bluetooth.BluetoothDevice instance found"+" :=> '"+instance+"'"); - bluetoothDeviceInfo(instance); - }, - onComplete: function() { console.log("[*] -----");} +onMatch: function(instance){ +console.log("[*] "+" android.bluetooth.BluetoothDevice instance found"+" :=> '"+instance+"'"); +bluetoothDeviceInfo(instance); +}, +onComplete: function(){ console.log("[*] —");} }); ``` -在手机打开蓝牙,并且连接上我的漫步者蓝牙耳机,开始播放内容之后: +After your phone turns on Bluetooth and connects to my Walker Bluetooth headset to start playing content: ![](pic/006.jpeg) -在电脑上运行脚本: +To run a script on your PC: -``` +```js $ frida -U -l enumerate_classes_bluetooth_choose.js com.android.bluetooth ``` -可以看到正确检测到了我的蓝牙设备: +You can see that my Bluetooth device is correctly detected: ![](pic/007.png) -### 0x05.简单脚本三:枚举所有方法并定位方法 +### 0x05. Simple script III: Enumerate all methods and locate them -上文已经将类以及实例枚举出来,接下来我们来枚举所有方法,主要使用了`Java.use()`函数。 +We have enumerated the classes and instances above, and then we enumerate all the methods, mainly using the 'Java.use()' function. ![](pic/008.png) -`Java.use()`与`Java.choose()`最大的区别,就是在于前者会新建一个对象,后者会选择内存中已有的实例。 +'Java.use()' differs from 'Java.choose()' in that the former creates a new object and the latter selects an instance that already exists in memory. -对代码的增加如下: +Add code as follows: ```js function enumMethods(targetClass) { - var hook = Java.use(targetClass); - var ownMethods = hook.class.getDeclaredMethods(); - hook.$dispose; + var hook = Java.use(targetClass); + var ownMethods = hook.class.getDeclaredMethods(); + hook.$dispose; - return ownMethods; + return ownMethods; } ... ... - var a = enumMethods("android.bluetooth.BluetoothDevice") - a.forEach(function(s) { - console.log(s); - }); - -``` - -保持上一小节环境的情况下,在电脑上进行操作: - -```js -$ frida -U -l enumerate_classes_bluetooth_choose_allmethod.js com.android.bluetooth +var a = enumMethods("android.bluetooth.BluetoothDevice") +a.forEach(function(s){ +console.log(s); +}); ``` -最终效果如下,类的所有方法均被打印了出来。 - -![](pic/009.png) - -接下来如何"滥用"这些方法,拦截、修改参数、修改结果、等等,皆可悉听尊便,具体流程请参考 - -### 0x06.综合案例:在安卓`8.1`上dump蓝牙接口和实例: - -一个比较好的综合案例,就是作为上文案例的`dump`蓝牙信息的"加强版"——['BlueCrawl'](https://github.com/IOActive/BlueCrawl)。 +To operate on a PC while maintaining the previous section of the environment: ```js -VERSION="1.0.0" -setTimeout(function(){ - Java.perform(function(){ - - Java.enumerateLoadedClasses({ - onMatch: function(instance){ - if (instance.split(".")[1] == "bluetooth"){ - console.log("[->]\t"+lightBlueCursor()+instance+closeCursor()); - } - }, - onComplete: function() {} - }); - - Java.choose("android.bluetooth.BluetoothGattServer",{ - onMatch: function (instance){ - ... - onComplete: function() { console.log("[*] -----");} - }); - - Java.choose("android.bluetooth.BluetoothGattService",{ - onMatch: function (instance){ - ... - onComplete: function() { console.log("[*] -----");} - }); - - Java.choose("android.bluetooth.BluetoothSocket",{ - onMatch: function (instance){ - ... - onComplete: function() { console.log("[*] -----");} - }); - - Java.choose("android.bluetooth.BluetoothServerSocket",{ - onMatch: function (instance){ - ... - onComplete: function() { console.log("[*] -----");} - }); - - Java.choose("android.bluetooth.BluetoothDevice",{ - onMatch: function (instance){ - ... - onComplete: function() { console.log("[*] -----");} - }); - }); -},0); -``` - -该脚本首先枚举了很多蓝牙相关的类,然后`choose`了很多类,包括蓝牙接口信息以及蓝牙服务接口对象等,还加载了内存中已经分配好的蓝牙设备对象,也就是上文我们已经演示的信息。我们可以用这个脚本来"查看"`App`加载了哪些蓝牙的接口,`App`是否正在查找蓝牙设备、或者是否窃取蓝牙设备信息等。 - -在电脑上运行命令: - -``` -$ frida -U -l bluecrawl-1.0.0.js com.android.bluetooth -``` - -![](pic/010.png) - -可以看到该脚本在安卓8.1上运行良好,我们的接口和设备均被打印了出来。 - -我们今天的脚本入门就到这里,谢谢大家。 +$ frida -U -l enumerate_classes_bluetooth_choose_allmethod.js +``` \ No newline at end of file diff --git a/FRIDA/B02/README.md b/FRIDA/B02/README.md index 2262193..4c39b0a 100644 --- a/FRIDA/B02/README.md +++ b/FRIDA/B02/README.md @@ -1,174 +1,174 @@ -## FRIDA脚本系列(二)成长篇:动静态结合逆向WhatsApp +## FRIDA Script Series (2) Growth Chapter: Dynamic-Static Combination Reverse WhatsApp -在[FRIDA脚本系列(一)入门篇:在安卓8.1上dump蓝牙接口和实例](https://github.com/r0ysue/AndroidSecurityStudy)中,我们学到了枚举模块中所有的类、子类及其方法,以及找到其所有重载,最终还通过蓝牙接口来小小的实战了一下。这一篇我们倒着来,从`hook`所有重载开始,写一个可以动态观察所有模块、类、方法等接口数据的工具出来。 +In the [FRIDA Script Series(i) Getting Started: dump Bluetooth Interfaces and Instances on Android 8.1](https://github.com/r0ysue/AndroidSecurityStudy), we learned to enumerate all classes, subclasses, and methods in the module, find all its overloads, and finally, do a little battle over the Bluetooth interface. In this paper, we reverse, starting from 'hook' all overloads, write a tool that can dynamically observe all the modules, classes, methods and other interface data out. -### 0x01.hook方法的所有重载 +### All overloads of the 0x01.hook method -在[一篇文章带你领悟Frida的精髓(基于安卓8.1)](https://github.com/r0ysue/AndroidSecurityStudy)一文中,我们已经学会了对放的重载进行处理的方法,我们先回顾一下代码: +In the article [An article takes you through the essence of Frida (based on Android 8.1)](https://github.com/r0ysue/AndroidSecurityStudy), we've learned how to handle the reload of the playback, and let's review the code: ```js my_class.fun.overload("int" , "int").implementation = function(x,y){ my_class.fun.overload("java.lang.String").implementation = function(x){ ``` -也就是说我们需要构造一个重载的数组,并把每一个重载都打印出来。我们直接上代码: +That means we need to construct an array of overloads and print out every overload. Let's go straight to the code: ```js -//目标类 +// Target class var hook = Java.use(targetClass); -//重载次数 +// Number of overloads var overloadCount = hook[targetMethod].overloads.length; -//打印日志:追踪的方法有多少个重载 +// Print Log: How many overloads are tracked console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]"); -//每个重载都进入一次 -for (var i = 0; i < overloadCount; i++) { -//hook每一个重载 - hook[targetMethod].overloads[i].implementation = function() { - console.warn("\n*** entered " + targetClassMethod); - - //可以打印每个重载的调用栈,对调试有巨大的帮助,当然,信息也很多,尽量不要打印,除非分析陷入僵局 - Java.perform(function() { - var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()); - console.log("\nBacktrace:\n" + bt); - }); - - // 打印参数 - if (arguments.length) console.log(); - for (var j = 0; j < arguments.length; j++) { - console.log("arg[" + j + "]: " + arguments[j]); - } - - //打印返回值 - var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?) - console.log("\nretval: " + retval); - console.warn("\n*** exiting " + targetClassMethod); - return retval; - } +// Enter once for each overload +for(var i = 0; i < overloadCount; i++){ +//hook Each overload +hook[targetMethod].overloads[i].implementation = function(){ +console.warn("\n*** entered " + targetClassMethod); + +// Can print each overloaded call stack, which is very helpful for debugging, and of course, there is a lot of information, try not to print unless the analysis is deadlocked +Java.perform(function(){ +var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()); +console.log("\nBacktrace:\n" + bt); +}); + +// Print parameters +if(arguments.length)console.log(); +for(var j = 0; j < arguments.length; j++){ +console.log("arg[" + j + "]: " + arguments[j]); +} + +// print return value +var retval = this[targetMethod].apply(this, arguments); // rare crash(Frida bug?) +console.log("\nretval: " + retval); +console.warn("\n*** exiting " + targetClassMethod); +return retval; +} } ``` -这样我们对于方法的所有重载就处理好了,接下来是枚举所有方法。 +So we handle all the overloads of the methods, and then we enumerate all the methods. -### 0x02.hook类的所有方法 +### All methods of the 0x02.hook class -还是直接上代码: +Direct code: ```js function traceClass(targetClass) { - //Java.use是新建一个对象哈,大家还记得么? - var hook = Java.use(targetClass); - //利用反射的方式,拿到当前类的所有方法 - var methods = hook.class.getDeclaredMethods(); - //建完对象之后记得将对象释放掉哈 - hook.$dispose; - //将方法名保存到数组中 - var parsedMethods = []; - methods.forEach(function(method) { - parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]); - }); - //去掉一些重复的值 - var targets = uniqBy(parsedMethods, JSON.stringify); - //对数组中所有的方法进行hook,traceMethod也就是第一小节的内容 - targets.forEach(function(targetMethod) { - traceMethod(targetClass + "." + targetMethod); - }); + //Java.use is a new subject, remember? + var hook = Java.use(targetClass); + // Use reflection to get all the methods of the current class + var methods = hook.class.getDeclaredMethods(); + // Remember to release objects after they are built + hook.$dispose; + // Save method name to array + var parsedMethods = []; + methods.forEach(function(method){ + parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]); + }); + // Remove some duplicate values + var targets = uniqBy(parsedMethods, JSON.stringify); + // hook all methods in the logarithm group, traceMethod is the first section + targets.forEach(function(targetMethod){ + traceMethod(targetClass + "." + targetMethod); + }); } ``` -### 0x03.hook类的所有子类 +### All subclasses of the 0x03.hook class -还是上核心部分的代码: +Or code from the top core: ```js -//枚举所有已经加载的类 +// Enumerate all loaded classes Java.enumerateLoadedClasses({ - onMatch: function(aClass) { - //迭代和判断 - if (aClass.match(pattern)) { - //做一些更多的判断,适配更多的pattern - var className = aClass.match(/[L]?(.*);?/)[1].replace(/\//g, "."); - //进入到traceClass里去 - traceClass(className); - } - }, - onComplete: function() {} + onMatch: function(aClass){ + // Iteration and Judgment + if(aClass.match(pattern)){ + // Make more judgments and adapt to more pattern + var className = aClass.match(/[L]? (.*);?/)[1].replace(/\//g, "."); + // Go inside the traceClass + traceClass(className); + } + }, + onComplete: function(){} }); ``` -### 0x04.hook本地库的导出函数 +### Export function for 0x04.hook local library ```js -// 追踪本地库函数 +// Track local library functions function traceModule(impl, name) { - console.log("Tracing " + name); - //frida的Interceptor - Interceptor.attach(impl, { - onEnter: function(args) { - - console.warn("\n*** entered " + name); - //打印调用栈 - console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE) - .map(DebugSymbol.fromAddress).join("\n")); - }, - onLeave: function(retval) { - //打印返回值 - console.log("\nretval: " + retval); - console.warn("\n*** exiting " + name); - - } - }); + console.log("Tracing " + name); + //frida's Interceptor + Interceptor.attach(impl, { + onEnter: function(args){ + + console.warn("\n*** entered " + name); + // Print call stack + console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE) + .map(DebugSymbol.fromAddress).join("\n")); + }, + onLeave: function(retval){ + // print return value + console.log("\nretval: " + retval); + console.warn("\n*** exiting " + name); + + } + }); } ``` -### 0x05.动静态结合逆向WhatsApp +### 0x05. Dynamic and static combined inverse WhatsApp -终于到了实战的时候,把以上代码拼接起来,形成一个脚本,其实这个脚本[awesome-frida -](https://github.com/dweinstein/awesome-frida)里面也有介绍,代码在[这里](https://github.com/0xdea/frida-scripts/blob/master/raptor_frida_android_trace.js),就是有点小bug,经[葫芦娃](https://github.com/hookmaster/frida-all-in-one)修改好之后,终于可以用了。 +Finally, when it's time for real combat, splice the code together to form a script, which is actually awesome-frida. +https://github.com/dweinstein/awesome-frida The code is [here](https://github.com/0xdea/frida-scripts/blob/master/raptor_frida_android_trace.js), just a little bug. After [gourd-doll](https://github.com/hookmaster/frida-all-in-one) has been modified, it is finally ready to use. -我们来试下它的几个主要的功能,首先是本地库的导出函数。 +Let's try several of its main functions, first of all, the local library export function. ```js -setTimeout(function() { - Java.perform(function() { - trace("exports:*!open*"); - //trace("exports:*!write*"); - //trace("exports:*!malloc*"); - //trace("exports:*!free*"); - }); +setTimeout(function(){ +Java.perform(function(){ +trace("exports:*!open*"); +//trace("exports:*!write*"); +//trace("exports:*!malloc*"); +//trace("exports:*!free*"); +}); }, 0); ``` -我们`hook`的是`open()`函数,跑起来看下效果: +We 'hook' is the 'open()' function and run to see the effect: -``` -$ frida -U -f com.whatsapp -l raptor_frida_android_trace_fixed.js --no-pause +```bash +$ frida -U -f com.whatsapp -l raptor_frida_android_trace_fixed.js —no-pause ``` ![](pic/01.png) -如图所示`*!open*`根据正则匹配到了`openlog`、`open64`等导出函数,并hook了所有这些函数,打印出了其参数以及返回值。 +As shown in the figure, '*!open*' matches the derived functions 'openlog', 'open64', and so on according to the regularization, and hook all of them, printing out their parameters and their return values. -接下来想要看哪个部分,只要扔到[`jadx`](https://github.com/skylot/jadx)里,静态"分析"一番,自己随便翻翻,或者根据字符串搜一搜。 +Next you want to see which part, just throw it in ['jadx'](https://github.com/skylot/jadx), "analyze" it statically, flip it yourself, or search it based on a string. ![](pic/03.png) -比如说我们想要看上图中的`com.whatsapp.app.protocol`包里的内容,就可以设置`trace("com.whatsapp.app.protocol")`。 +For example, if we want to look at the contents of the 'com.whatsapp.app.protocol' package in the figure above, we can set the 'trace("com.whatsapp.app.protocol")'. ![](pic/04.png) ![](pic/05.png) -可以看到包内的函数、方法、包括重载、参数以及返回值全都打印了出来。这就是`frida`脚本的魅力。 +You can see that the functions, methods, including overloads, parameters, and return values within the package are all printed. This is the charm of the 'frida' script. -当然,脚本终归只是一个工具,你对`Java`、安卓App的理解,和你的创意才是至关重要的。 +Of course, scripts are ultimately just a tool, and your understanding of 'Java', Android apps, and your ideas are all that matters. -接下来可以搭配[Xposed module](https://repo.xposed.info/module-overview)看看别人都给`whatsapp`做了哪些模块,`hook`的哪些函数,实现了哪些功能,学习自己写一写。 +Next, you can go with [Xposed module](https://repo.xposed.info/module-overview) to see what modules have been made for 'whatsapp', what functions of 'hook', what functions have been implemented, and learn to write by yourself. ![](pic/06.png) -当然,再强调一句,做外挂是违法行为,千万不要制作和分发任何App的外挂,否则等待你的只会是法律的制裁。 +Of course, it's illegal to make a plug-in, and don't make and distribute any app's plug-ins, because you'll only be punished by law. diff --git a/FRIDA/B03/README.md b/FRIDA/B03/README.md index 098b46b..75a9a82 100644 --- a/FRIDA/B03/README.md +++ b/FRIDA/B03/README.md @@ -1,30 +1,30 @@ - + - + -* [FRIDA脚本系列(三)超神篇:百度AI"调教"抖音AI](#frida脚本系列三超神篇百度ai调教抖音ai) - * [0x01. 日本抖音美如画](#0x01-日本抖音美如画) - * [0x02. 如何让抖音AI更加"智能"](#0x02-如何让抖音ai更加智能) - * [点赞&关注&上滑&转发:模拟点击](#点赞关注上滑转发模拟点击) - * [评论:模拟点击&输入框](#评论模拟点击输入框) - * [0x03. 如何"训练"抖音AI](#0x03-如何训练抖音ai) - * [截图](#截图) - * [将图片传回`PC`](#将图片传回pc) - * [将图片上传到百度`AI`平台](#将图片上传到百度ai平台) - * [获取百度`AI`结果](#获取百度ai结果) - * [按照结果决定是否点赞](#按照结果决定是否点赞) - * [0x04. 百度AI效果演示](#0x04-百度ai效果演示) +* [FRIDA Script Series (3) Hypnotism: Baidu AI "Tuning" Douyin AI] (#frida Script Series 3 Hypnotism: Baidu ai Tuning Douyin ai) +* [0x01. Japanese TikTok Beauty Paintings] (#0x01 - Japanese TikTok Beauty Paintings) +* [0x02. How to Make TikTok AI Smarter] (#0x02- How to Make TikTok AI Smarter) +* [Like & Follow & Slide & Forward: Analog Tap] (#Like & Forward analog tap) +* [Comment: Impersonate Tap & Input Box] (#Comment Impersonate Tap Input Box) +* [0x03. How to "train" TikTok AI] (#0x03- How to train TikTokAi) +* [screenshot] (#screenshot) +* [Return picture to 'PC'] (#Return picture to pc) +* [Upload images to Baidu'AI' platform] (#Upload images to Baidu'ai platform) +* [Get Baidu'AI' results] (#Get Baidu ai results) +* [Like it or not according to the results] (#Like it or not according to the results) +* [0x04. Baidu AI Effects Demo] (#0x04-Baidu ai Effects Demo) - + -## FRIDA脚本系列(三)超神篇:百度AI"调教"抖音AI +## FRIDA Script Series (3) Superhuman: Baidu AI "Tuning" Douyin AI -### 0x01. 日本抖音美如画 +### 0x01. Japanese TikTok -在万恶的资本主义国家,女孩子普遍都吃不饱,每天也没啥事情干,只能在沙滩上晒晒太阳,拍点短视频填充自己空虚的灵魂,在穿着上也是怎么省怎么来,比如布料普遍不会太多,有时候甚至会用**几根线**来代替面料,资本主义实在是太可恶了。 +In the vile capitalist world, where girls are generally underfed and have little to do every day, they spend their days basking in the sun on the beach, shooting short videos to fill their empty souls, and dressing in such a way that the fabric is generally not too much, and sometimes even ** a few threads ** is used in place of the fabric, capitalism is so abhorrent. ![](pic/01.gif) @@ -34,177 +34,177 @@ ![](pic/04.gif) -为了培养社会主义的接班人,让更多的人跟我一起去批判资本主义,我在朋友圈发了一些上述的视频([样本地址](tiktok),为了大家批判的足够充分,我采集了足够多的`mp4`样本。) +To prepare my socialist heirs and bring more people with me to criticize capitalism, I have circulated some of these videos among my friends ([tiktok], and I have collected enough 'mp4' samples to be critical enough.) -大家纷纷表示此时此刻正需要这样的反面教材,有人甚至发私信问我,如何可以得到更多的这样的反面教材,他们已经通过一系列方法,成功的进入敌人的大本营,奈何不懂日语,刷出来的都是一些索然无味的视频,非常懊恼。 +Many people said that they needed such negative textbooks at this moment, some even sent personal messages asking me how to get more of them. They have successfully entered enemy strongholds through a series of methods, so much to the chagrin of those who don't understand Japanese and have painted some tasteless videos. -为了解决大家的刚性需求,建设富强民主文明和谐的程序员社区,我们来研究一下,如何使用最为前沿的`AI`技术,结合胶水框架——`frida`,提高大家刷出反面教材的几率,做到真正的造福程序猿,提高幸福感和获得感。 +To address your rigid needs and build a community of programmers that are strong, democratic, culturally civilized and harmonious, let's examine how the cutting-edge 'AI' technology, combined with the glue frame—'frida', can be used to increase your chances of painting negative textbooks that will truly benefit programmers and increase your sense of well being and accessibility. -(PS:文中大量使用黑产惯用技术,为避免效仿,仅介绍过时的工具,且不做调试) +(PS: This article uses the black production customary technology in large quantities, in order to avoid imitating, only introduces the outdated tools, and does not do the debugging) -### 0x02. 如何让抖音AI更加"智能" +### 0x02. How to Make TikTok AI Smarter -首先我们来看下日本抖音的界面,跟国内的版本并没有什么不同。 +First of all, let's look at the interface of Douyin, Japan, which is not very different from the domestic version. ![](pic/05.png) -那就其实很容易想到,只要看到自己喜欢的反面教材,尤其是泳装、沙滩、比基尼的内容,只要疯狂点赞就好了。 +It's easy to think of all the things you love about the negative, especially the swimsuits, the beach, the bikinis, and just the crazy likes. -做足全套的话,就是点赞、关注、评论加转发,这些是正向反馈。 +In full, it's like likes, concerns, comments and retweets, and these are positive feedback. -更有甚者,可以滑进某个喜欢的爱豆页面,将她发的每一个视频全都点赞一遍,这个正向反馈也足够大了,不过会模糊我们的目标——泳装比基尼,毕竟爱豆也不是每个短视频都比基尼。 +Or even swim to a favorite Love Beans page and give every video she posts a thumbs-up, a positive feedback that's big enough but blurs our goal — the swimsuit bikini, which isn't every short video anyway. -我们应该目标明确:就是要看比基尼!要不然潜入敌人内部的目的何在?国产小姐姐也足够迷人。 +We should aim for a bikini! Otherwise, what's the point of infiltrating the enemy? The homegrown sisters are charming enough. -如果不是比基尼呢?负面反馈好像真的很少,直接上滑跳过这则视频即可。 +What if it's not a bikini? Negative feedback seems to be really low, just slide up and skip the video. -#### 点赞&关注&上滑&转发:模拟点击 +#### Like & Follow & Slide & Forward: Analog Tap -在安卓手机上进行模拟点击的技术实在是太多了,常见的有按键精灵、触摸精灵,辅助功能、adb调试指令直接点击等等。 +There are too many analog click technology on the Android phone, the common key wizard, touch wizard, accessibility, adb debug instructions directly click and so on. -这个产业链也是非常成熟,大量黑灰从业者开发出大量的脚本工具,来实现"定制"的功能。 +This industrial chain is also very mature, a large number of black and gray practitioners developed a large number of script tools to achieve the "custom" function. ![](pic/07.jpg) -`frida-server`注入到目标进程之后,可以加载一些模拟点击的库,根据`frida`脚本及点击脚本的逻辑,有针对性地对指定内容进行点赞、关注、上滑和转发。这些操作的位置都是固定的,只要模拟用户点击上去即可实现。 +Once 'frida-server' has been injected into the target process, a library of mock clicks can be loaded to specifically like, follow, slide up, and forward the specified content according to the 'frida' script and the logic of the click script. The positions of these operations are fixed, and can be realized as long as the analog user clicks on them. ![](pic/08.jpg) -辅助功能是另外一个方法,而且这是一个完全合法的方法,毕竟这是安卓系统自带的一个功能,提供给残障人士去用的,`frida-server`注入到目标进程之后,可以直接加载一些辅助功能的代码库,进行操作。 +Accessibility is another method, and it's a completely legal method, after all, it's a feature of Android system, which is provided to people with disabilities, 'frida-server' injected into the target process, you can load some accessibility code base directly to operate. -另外安卓的调试命令`adb`也是可以直接在屏幕上进行点击的,格式如下。可以在`frida`的客户端直接用`python`的`os`库调用`adb`的相关命令,进行屏幕点击的相关操作,`adb`点击的相关研究也特别多,百度搜一下可以找到很多代码。 +In addition, Android's debug command 'adb' can also be clicked directly on the screen, in the following format. 'adb'-related commands can be invoked directly from the 'os' library of 'frida' on the client side of 'python' for screen-clicking related operations, 'adb'-clicking related research is particularly numerous, and Baidu can search for a lot of code. -``` +```bash adb shell shell@PRO6:/ $ input tap 125 521 shell@PRO6:/ $ ``` -最最`low`的方式就是在`Windows`模拟器里跑,有`Intel Houdini for arm-x86`在,也可以把抖音跑起来,但抖音肯定会做检测就对了。然后使用`python`的`pywin32`实现`Windows`窗口的点击,这样做有两个好处,一个是脱离真机,成本大大降低,还有就是做批量化很容易。缺点也是容易被检测到。 +The most 'low' way is to run in the 'Windows' simulator, with the 'Intel Houdini for arm-x86', or run Douyin up, but Douyin will definitely be tested. The 'pywin32' of 'python' is then used to implement the click of the 'Windows' window, which has two advantages, one is that it is out of touch with the real machine, the cost is greatly reduced, and the other is that it is easy to do batch quantization. Disadvantages are also easily detected. -当然,在`iOS`上,这类需求也是屡见不鲜的,除了上述触摸精灵本身也支持`iOS`端之外,`iOS`的模拟点击库也是层出不穷,`frida-server`可以加载这些`gadget`,调用`API`,实现模拟点击、双击、滑动等功能。 +Of course, such needs are also common on 'iOS' and, in addition to the touch wizards themselves supporting the 'iOS' end, there is an endless stream of 'iOS' analog click libraries, and 'frida-server' can load these 'gadget' and invoke 'APIs' for analog click, double click, slide and other functions. ![](pic/09.jpg) -#### 评论:模拟点击&输入框 +#### Comment: Analog Tap & Input Box -模拟点击的功能实现了,在输入框里输入评论更加小菜一碟了,使用`frida-server`来`hook`输入框那简直跟玩一样,属于入门技巧,这里是一段典型的`hook` `TextView`的代码。 +The function of analog clicking is realized, the input of comments in the input box is more small dish, using 'frida-server' to 'hook' input box is just like playing, it is a start skill, here is a typical 'hook' 'TextView' code. ```js -console.log("Script loaded successfully "); -Java.perform(function () { + console.log("Script loaded successfully "); + Java.perform(function(){ var tv_class = Java.use("android.widget.TextView"); - tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) { - var string_to_send = x.toString(); - var string_to_recv; - send(string_to_send); // send data to python code - recv(function (received_json_object) { - string_to_recv = received_json_object.my_data - }).wait(); //block execution till the message is received - return this.setText(string_to_recv); + tv_class.setText.overload("java.lang.CharSequence").implementation = function(x){ + var string_to_send = x.toString(); + var string_to_recv; + send(string_to_send); // send data to python code + recv(function(received_json_object){ + string_to_recv = received_json_object.my_data + }).wait(); //block execution till the message is received + return this.setText(string_to_recv); } }); ``` -需要注意的是,输入的字符串肯定不可以全都是一样的,这样也太明显了,最好用`sqlite`存一个`舔狗集合.db`,然后不断从里面调就行了。 +It is important to note that the input strings cannot all be the same, which is too obvious, and it is best to save a 'lick dog set .db' with 'sqlite' and then keep pulling it from the inside. -### 0x03. 如何"训练"抖音AI +### 0x03. How to "train" TikTok AI -#### 截图 +#### Screenshot -在`frida`的客户端,使用`python`调`os`包,执行`adb`调试命令,对正在运行中的手机进行截图操作,截图会保存在`sd`卡上。运行的频率可以是每两三秒运行一次之类的,具体看具体调试结果。 +On the client side of 'frida', using the 'python' tune 'os' package, execute the 'adb' debug command to capture a running mobile phone, which is saved on the 'sd' card. The frequency of running can be every two or three seconds, etc., depending on the specific debugging results. -``` +```bash $ adb shell screencap -p /sdcard/01.png ``` -#### 将图片传回`PC` +#### Return pictures to 'PC' -逻辑跟截图相同,取回`sd`卡里的照片。 +The logic is the same as the screenshot. Retrieve the 'sd' card photo. -``` +```bash $ adb pull /sdcard/01.png ``` -#### 将图片上传到百度`AI`平台 +#### Upload image to Baidu'AI' platform -百度`AI`平台的调用接口还是比较简单易用的,提供的功能也非常多,比如`GIF`色情识别,短视频审核,图像审核等等,将图片使用`base64`编码之后,`post`到百度`AI`的服务器即可,返回的`json`数据即为百度`AI`对图片的判定结果。 +The 'AI' platform of Baidu is still relatively easy to use and provides a wide range of functions such as 'GIF' pornographic recognition, short video review, image moderation, etc. After the pictures are encoded using 'base64', 'post' to Baidu'AI' servers can be used, and the 'json' data returned is the result of Baidu'AI' determination of the pictures. ![](pic/10.JPG) -#### 获取百度`AI`结果 +#### Get Baidu'AI' results -返回的`json`包也是简单粗暴,直接将不合规内容以及可能性全都打印出来,这样的格式化数据想要提取出来进行逻辑判断只能说是太容易了。 +The returned 'json' package is also simple and crude, printing out non-compliant content and possibilities directly, such formatted data would be too easy to extract for logical judgment. ```json { - "conclusion": "不合规", - "log_id": "15537510677705536", - "data": [ - { - "msg": "存在性感内容", - "probability": 0.999525, - "type": 2 - }, - { - "msg": "存在水印码内容", - "probability": 0.8558467, - "type": 5 - }, - { - "msg": "存在公众人物", - "stars": [ - { - "probability": 0.73777246, - "name": "孙淑媚" - } - ], - "type": 11 - } - ], - "conclusionType": 2 + "conclusion": "Non-compliant", + "log_id": "15537510677705536", + "data": [ + { + "msg": "sexy content", + "probability": 0.999525, + "type": 2 + }, + { + "msg": "Watermark Content Present", + "probability": 0.8558467, + "type": 5 + }, + { + "msg": "public figure exists", + "stars": [ + { + "probability": 0.73777246, + "name": "Sun Shu-may" + } + ], + "type": 11 + } + ], + "conclusionType": 2 } ``` -#### 按照结果决定是否点赞 +####Decide whether to like it or not based on the results -在`frida`客户端,将结果动态影响到`frida-server`是非常容易的,[前面的教程](https://github.com/r0ysue/AndroidSecurityStudy)中已经讲得非常丰富了。 +In the 'frida' client, it is very easy to dynamically affect the results to 'frida-server', which is already well covered in the [previous tutorial](https://github.com/r0ysue/AndroidSecurityStudy). -### 0x04. 百度AI效果演示 +### 0x04. Baidu AI Effects Demo -上面说了这么多,如果百度`AI`不能准确地将我们想要看的"反面教材"圈出来点赞,那岂不是都白干了。所以百度`AI`一定要准,对于"反面教材"的筛选要毫不留情,尽量不要有漏网之鱼。虽然哪怕即使有,使用百度`AI`"调戏"抖音`AI`这个事情也是有意义的,因为成功率已经相当高了。 +If Baidu's 'AI' doesn't exactly circle the 'negative material' we want to see, with all that said, it would be a waste of time. So Baidu's 'AI' must be accurate, and the screening of "negative material" should be unsparing, as far as possible. Although even if there is, it makes sense to use Baidu's 'AI' to play TikTok 'AI' because the success rate is already high. ![](pic/06.png) -我们选择`色情识别`功能,来验证一下,到底识别率怎么样。 +We've chosen the Porn Recognition feature to verify what the recognition rate is. ![](pic/11.JPG) -色情识别,存在性感内容,很好。 +Porn recognition. There's sexy content. Good. ![](pic/12.JPG) -色情识别,存在性感内容,很好。 +Porn recognition. There's sexy content. Good. ![](pic/13.JPG) -色情识别,存在性感内容,很好。 +Porn recognition. There's sexy content. Good. ![](pic/14.JPG) -色情识别,通过,很好。 +Porn recognition. Pass. Good. ![](pic/16.JPG) -色情识别,通过,很好。 +Porn recognition. Pass. Good. ![](pic/15.JPG) -色情识别,存在性感内容,很好。 +Porn recognition. There's sexy content. Good. -百度`AI`真棒!比基尼一张不落!全都识别了出来! +Baidu'AI' is awesome! The bikini is not falling! It'll all come out! -真好。 +That's great. -蒽,抖音看片,指日可待! +Anthracene, TikTok, you're almost there! diff --git a/FRIDA/B04/README.md b/FRIDA/B04/README.md index 35a91f2..59bd2b7 100644 --- a/FRIDA/B04/README.md +++ b/FRIDA/B04/README.md @@ -1,371 +1,206 @@ - + - + -- [FRIDA脚本系列(四)更新篇:几个主要机制的大更新](#frida脚本系列四更新篇几个主要机制的大更新) - - [进程创建机制大更新](#进程创建机制大更新) - - [存在的问题:无法为新进程准备参数和环境](#存在的问题无法为新进程准备参数和环境) - - [问题产生的原因(一):当初源码中就没有实现](#问题产生的原因一当初源码中就没有实现) - - [问题产生的原因(二):`spawn()`的历史遗留问题](#问题产生的原因二spawn的历史遗留问题) - - [进程创建机制更新(一):参数、目录、环境均可设置](#进程创建机制更新一参数-目录-环境均可设置) - - [进程创建机制更新(二):利用`aux`机制实现平台特定功能](#进程创建机制更新二利用aux机制实现平台特定功能) - - [子进程插装机制大更新](#子进程插装机制大更新) - - [存在的问题:子进程多线程机制混乱容易崩](#存在的问题子进程多线程机制混乱容易崩) - - [解决的方法:引入新的子进程控制`API`:`Child gating`](#解决的方法引入新的子进程控制apichild-gating) - - [退出(崩溃)消息机制大更新](#退出崩溃消息机制大更新) - - [存在的问题:程序崩溃时消息来不及发出](#存在的问题程序崩溃时消息来不及发出) - - [解决的方法:对各大平台的停止进程`API`进行插装](#解决的方法对各大平台的停止进程api进行插装) +- [FRIDA Script Series (IV) Updates: Major Updates of Several Major Mechanisms] (#frida Script Series IV Updates: Major Updates of Several Major Mechanisms) +- [Process Creation Mechanism Big Update] (#Process Creation Mechanism Big Update) +- [Existing Problem: Unable to prepare parameters and environment for new process] (#Existing Problem Unable to prepare parameters and environment for new process) +- [Cause of Problem (I): No Implementation in Original Source] (# Cause of Problem: No Implementation in Original Source) +- [Cause of the problem (ii): 'spawn()' Historical Legacy] (#Cause of the problem (ii) Historical Legacy of spawn) +- [process creation mechanism update (i): parameters, directories, environment can be set] (# process creation mechanism update a parameter - directories - environment can be set) +- [Process Creation Mechanism Update (II): Platform-specific functions implemented using 'aux' mechanism] (#Process Creation Mechanism Update II) Platform-specific functions implemented using 'aux' mechanism) +- [Subprocess Instrumentation Big Update] (#Subprocess Instrumentation Big Update) +- [Problem: Sub-process multithreading mechanism chaos easy to collapse] (#Problem: Sub-process multithreading mechanism chaos easy to collapse) +- [Resolution: Introduces new subprocess control 'API`:`Child gating'] (#Resolution Introduces new subprocess control apichild-gating) +- [exit (crash) message mechanism big update] (#exit crash message mechanism big update) +- [Problem: Message Not Ready When Program Crashes] (#ProblemMessage Not Ready When Program Crashes) +- [Solution: Interpolate 'APIs' of stop processes for major platforms] (#SolutionInterpolate stop process api for major platforms) - + -## FRIDA脚本系列(四)更新篇:几个主要机制的大更新 +## FRIDA Script Series (IV) Updates: Major Updates to Several Major Mechanisms -最近沉迷学习,无法自拔,还是有一些问题百思不得骑姐,把官网文档又翻了一遍,发现其实最近的几个主要版本,更新还是挺大的,遂花了点时间和功夫,消化吸收下,在这里跟大家分享。 +Recently indulged in learning, can't get rid of oneself, there are still some problems can't think of rode the sister, go through the official website document again, find out that several recent major versions, update is quite large, so spent a bit of time and effort, digestion, absorption, here to share with you. -### 进程创建机制大更新 +### Process Creation Mechanism Big Update -#### 存在的问题:无法为新进程准备参数和环境 +#### Problem: Unable to prepare parameters and environment for new process -当我们使用`Frida Python`的`binding`的时候,一般会这么写: +When we use the 'binding' of 'Frida Python', it is generally written as follows: -``` +```bash pid = device.spawn(["/bin/cat", "/etc/passwd"]) ``` -或者在iOS平台上,会这样写: +Or on iOS platforms, it would read: -``` +```bash pid = device.spawn(["com.apple.mobilesafari"]) ``` -目前来看貌似用这个`API`只能这么写,这么写其实是存在很多问题的,比如说没有考虑完整参数列表的问题,或者说新进程的环境是继承自绑定的`host`机环境还是设备`client`环境?再比如想要实现定制功能,比如以关闭`ASLR`模式打开`safari`,这些都没有考虑进去。 +At this point it seems that this 'API' can only be written in this way, and there are many problems in writing this, such as not considering the full list of parameters, or whether the environment of the new process is inherited from the bundled 'host' machine environment or the device 'client' environment? Nor does it take into account the desire to implement custom functions, such as turning 'safari' on with 'ASLR' mode turned off. -#### 问题产生的原因(一):当初源码中就没有实现 +#### Cause of the problem (1): It was not implemented in the original source code -我们先来看看这个`API`在`frida-core`里是如何实现的: +Let's start by looking at how this 'API' was achieved in 'frida-core': ```vala namespace Frida { - ... - public class Device : GLib.Object { - ... - public async uint spawn (string path, - string[] argv, string[] envp) - throws Frida.Error; - public uint spawn_sync (string path, - string[] argv, string[] envp) - throws Frida.Error; - } - ... + ... + public class Device : GLib.Object { + ... + public async uint spawn(string path, + string[] argv, string[] envp) + throws Frida.Error; + public uint spawn_sync(string path, + string[] argv, string[] envp) + throws Frida.Error; +} +... } ``` -这段代码是用`vala`语言写的,`frida-core`都是用`vala`写的,`vala`看起来跟`C#`很像,并且最终会被编译成`C`代码。从代码可以看出,第一个方法——`spawan`是异步的,调用者调用一遍就可以去干其他事情了,不用等待调用完成,而第二个方法——`spawn_sync`则需要等到调用完全结束并返回。 +The code is written in the language 'vala', 'frida-core' is written in 'vala', 'vala' looks like 'C#' and is eventually compiled into 'C' code. As you can see from the code, the first method—'spawan' is asynchronous, and the caller can do something else once it has been called, rather than waiting for the call to complete, while the second method—'spawn_sync' needs to wait until the call is completely completed and returned. -这两个方法会被编译成如下的C代码: +Both methods are compiled into C code as follows: ```c -void frida_device_spawn (FridaDevice * self, - const gchar * path, - gchar ** argv, int argv_length, - gchar ** envp, int envp_length, - GAsyncReadyCallback callback, gpointer user_data); -guint frida_device_spawn_finish (FridaDevice * self, - GAsyncResult * result, GError ** error); -guint frida_device_spawn_sync (FridaDevice * self, - const gchar * path, - gchar ** argv, int argv_length, - gchar ** envp, int envp_length, - GError ** error); +void frida_device_spawn(FridaDevice * self, +const gchar * path, +gchar ** argv, int argv_length, +gchar ** envp, int envp_length, +GAsyncReadyCallback callback, gpointer user_data); +guint frida_device_spawn_finish(FridaDevice * self, +GAsyncResult * result, GError ** error); +guint frida_device_spawn_sync(FridaDevice * self, +const gchar * path, +gchar ** argv, int argv_length, +gchar ** envp, int envp_length, +GError ** error); ``` -前两个函数组成了`spawn()`的过程,首先调用第一个获得一个回调,当获得回调之后就会调用第二个函数——`spawn_finish()`,将回调的返回值将会作为`GAsyncResult`的参数。最终的返回值就是`PID`,当然如果有`error`的话就会返回`error no`。 +The first two functions form the procedure 'spawn()', first calling the first to obtain a callback, and then calling the second function—'spawn_finish()' when the callback is obtained, the return value of the callback is used as an argument to 'GAsyncResult'. The final return value is 'PID' and of course 'error no' if there is 'error'. -第三个函数——`spawn_sync()`上面也解释了,是完全同步的,`Frida Python`用的其实就是这个。`Frida nodejs`用的其实是前两个,因为`nodejs`里的绑定默认就是异步的。当然以后其实应该也考虑将`Frida Python`的绑定迁移到异步的模式中来,利用`Python 3.5`版本引入的`async/await`机制。 +The third function—'spawn_sync()' also explains that it is completely synchronized, and 'Frida Python' uses this in fact. 'Frida nodejs' uses the first two, because the bindings in 'nodejs' are asynchronous by default. Of course, it should also be considered in the future to migrate the binding of 'Frida Python' to an asynchronous mode, using the 'async/await' mechanism introduced in the 'Python 3.5' version. -回到上一小节那两个例子,可以发现其实调用的格式跟我们写的`API`并不完全一致,仔细看源码就会发现,像`envp`字符串列表并没有暴露给上层`API`,如果查看`Frida Python`的绑定过程的话,就可以发现其实后来在绑定里是这样写的: +Going back to the two examples in the previous section, it turns out that the format of the call is not exactly the same as the 'API' that we wrote. A close look at the source code shows that a list of strings like 'envp' is not exposed to the upper 'API'. If you look at the binding process of 'Frida Python', you can see that it was actually written later in the binding as follows: -``` -envp = g_get_environ (); -envp_length = g_strv_length (envp); +```c +envp = g_get_environ(); +envp_length = g_strv_length(envp); ``` -也就是说最终我们传递给`spawn()`函数的是调用者的`Python`环境,这明显是不对的,`host`的`Python`环境跟`client`的`Python`肯定是不一样的,比如像`client`是`iOS`或`Android`的情况。 +That is to say, ultimately we pass on the 'spawn()' function to the caller's 'Python' environment, which is clearly incorrect, and the 'Python' environment of 'host' is certainly not the same as the 'Python' of 'client', as in the case where 'client' is 'iOS' or 'Android'. -当然我们在`frida-server`里做了设定,在`spawn()`安卓或者`iOS`的进程的时候,`envp`会被默认忽略掉,这或多或少减少了问题的产生。 +Of course, we set in 'frida-server' that 'envp' will be overlooked by default during the process of 'spawn()' 'Android or 'iOS', which more or less reduces the problem. -#### 问题产生的原因(二):`spawn()`的历史遗留问题 +#### The cause of the problem (ii): 'spawn()' The legacy of the problem -还有一个问题就是`spawn()`这个古老的`API`的定义——`string[] envp`,这个定义意味着不能为空(如果写成`string[]? envp`的话其实就可以为空了),也就是说其实无法从根本上区别"用默认的环境配置"和"不使用任何环境配置"。 +Another problem is the definition of 'spawn()', this ancient 'API' —'string[] envp', which means that it cannot be empty (if written as 'string[]? envp' can be virtually empty), meaning that there is no fundamental distinction between "default environment configuration" and "no environment configuration at all". -#### 进程创建机制更新(一):参数、目录、环境均可设置 +#### Process Creation Mechanism Update (I): Parameters, directories, environment can be set -既然决定要修这个`API`,那就干脆顺便把跟这个`API`相关的问题都来看下: +Since you have decided to fix this 'API', consider all the issues related to this 'API': -- 如何给命令提供一些额外的环境参数 -- 设置工作目录 -- 自定义标准输入流 -- 传入平台特定的参数 +- How to provide some additional environment parameters to the command +- Set working directory +- Custom Standard Input Stream +- Platform-specific parameters passed in -修正完以上`bug`之后,最终代码会变成下面这样: +After fixing the 'bug' above, the final code will look like this: ```vala namespace Frida { - ... - public class Device : GLib.Object { - ... - public async uint spawn (string program, - Frida.SpawnOptions? options = null) - throws Frida.Error; - public uint spawn_sync (string program, - Frida.SpawnOptions? options = null) - throws Frida.Error; - } - ... - public class SpawnOptions : GLib.Object { - public string[]? argv { get; set; } - public string[]? envp { get; set; } - public string[]? env { get; set; } - public string? cwd { get; set; } - public Frida.Stdio stdio { get; set; } - public GLib.VariantDict aux { get; } - - public SpawnOptions (); - } - ... + ... + public class Device : GLib.Object { + ... + public async uint spawn(string program, + Frida.SpawnOptions? options = null) + throws Frida.Error; + public uint spawn_sync(string program, + Frida.SpawnOptions? options = null) + throws Frida.Error; + } + ... + public class SpawnOptions : GLib.Object { + public string[]? argv { get; set; } + public string[]? envp { get; set; } + public string[]? env { get; set; } + public string? cwd { get; set; } + public Frida.Stdio stdio { get; set; } + public GLib.VariantDict aux { get; } + + public SpawnOptions(); + } + ... } ``` -最后,我们回到开头的那段示例代码,本来我们是这么写的: +Finally, we go back to the example code at the beginning, which we wrote: ```py device.spawn(["com.apple.mobilesafari"]) ``` -现在得这样写了: +Now it has to be: ```py device.spawn("com.apple.mobilesafari") ``` -第一个参数是要被`spawn`的命令,后面可以加上`argv`的字符串列表,`argv`就会被用来设定参数的命令,比如: +The first argument is the command to be 'spawn', followed by a list of strings for 'argv', and 'argv' is used to set the parameter's command, such as: ```py device.spawn("/bin/busybox", argv=["/bin/cat", "/etc/passwd"]) ``` -如果想要将默认环境替换成自己的设定的话: +If you want to replace the default environment with your own settings: ```py device.spawn("/bin/ls", envp={ "CLICOLOR": "1" }) ``` -只更改环境变量里的一个参数: +Change only one parameter in the environment variable: ```py device.spawn("/bin/ls", env={ "CLICOLOR": "1" }) ``` -更改命令的工作目录: +To change the working directory of a command: ```py device.spawn("/bin/ls", cwd="/etc") ``` -重定向标准输入流: +To redirect a standard input stream: ```py device.spawn("/bin/ls", stdio="pipe") ``` ->`stdin`默认的输入是`inherit`,加上`stdio="pipe"`这个选项之后,就变成管道了。 +>'stdin'The default input is 'inherit', plus the option 'stdio="pipe"', which makes it a pipe. -#### 进程创建机制更新(二):利用`aux`机制实现平台特定功能 +##### Process Creation Mechanism Update (II): 'aux' mechanism for platform-specific functionality -到这里我们几乎覆盖了`spawn()`的所有选项,还剩下最后一个选项——`aux`,该选项的本质是平台特定参数的一个字典。可以用`Python`绑定来设置这个参数,任何无法被前面参数捕获的键值对,都会直接放在命令行的最后面。 +Here we cover almost all of the options for 'spawn()', leaving the last option—'aux', which is essentially a dictionary of platform-specific parameters. This parameter can be set with a 'Python' binding, and any key-value pair that cannot be captured by the preceding parameter is placed directly at the end of the command line. -比如,打开`Safari`并且通知它去打开特定的`URL`: +For example, open 'Safari' and notify it to open a specific 'URL': ```py device.spawn("com.apple.mobilesafari", url="https://bbs.pediy.com") ``` -再比如以关闭`ASLR`的模式执行一个命令: +Another example would be to execute a command in the mode of turning off 'ASLR': ```py device.spawn("/bin/ls", aslr="disable") ``` -再比如用特定的`Activity`来打开一个安卓的`App`: +Or open an Android 'Activity' with a specific 'app': ```py spawn("com.android.settings", activity=".SecuritySettings") ``` -`aux`机制让命令行可以轻松定制,这可比为每个平台单独写代码方便多了。事实上,底层代码一行都没变 ^.< - -最后来看下这个`API`修改完成之后的效果,逗号后面的第二个参数就是带属性的对象,后面无法被是别的参数则全部进`aux`字典。 - -```js -const pid = await device.spawn('/bin/sh', { - argv: ['/bin/sh', '-c', 'ls /'], - env: { - 'BADGER': 'badger-badger-badger', - 'SNAKE': true, - 'MUSHROOM': 42, - }, - cwd: '/usr', - stdio: 'pipe', - aslr: 'auto' -}); -``` - -当然,修改完成之后,子进程的路径、参数和环境都可以置空了,这个置空已经可以区分"用默认的环境配置"和"不使用任何环境配置"了。 - -### 子进程插装机制大更新 - -#### 存在的问题:子进程多线程机制混乱容易崩 - -首先来回顾一下,传统的`fork()`函数本来的操作是这样婶儿的,它会克隆完整的父进程空间给子进程,这个过程通常开销不大,因为有着`copy-on-write`机制,然后将子进程的进程`ID`返回给父进程,将`0`返回给子进程。 - -而当涉及到多线程的时候,情况就会变得复杂起来,只有调用`fork()`函数的线程,可以"存活"到子进程里面,而如果其他线程碰巧有线程锁,这些锁在子进程里将永远不会被解开。 - -所以说`App`如果要同时进行多线程和`fork`操作的话,必须得非常谨慎,当然大多数`App`都在`fork`进程时都是使用的单线程设计,可是在注入我们的`frida-gum`之后,该进程就变成了多线程,所以程序经常会崩溃或失去响应。还有一种情况就是拥有共享属性的文件描述符,处理的时候也需要非常非常谨慎。 - -在这个版本中作者花了大力气,最终解决了这个问题。作者非常鸡冻的宣布,现在`FRIDA`可以检测到即将运行`fork()`函数,临时暂停`FRIDA`的线程,暂停通讯通道,并随着`fork()`的过程一起备份,备份完成之后恢复运行。也就是说在子进程开始运行之前,我们就把想要实施的插装操作应用到子进程上了。 - -当然不仅仅是`fork()`,还有`execve(), posix_spawn(), CreateProcess()`等系列子进程操作函数,这么说吧,只要是对进程实施的操作,不管是像`execve()`一样替换自身进程的,还是像`posix_spawn()`一样另起一个进程的,都会像`fork()`函数一样,由`FRIDA`先实施好插桩之后,再开始运行。 - -#### 解决的方法:引入新的子进程控制`API`:`Child gating` - -前两个问题主要就是由这个新引入的"子进程控制"的`API`来解决的,我们为拥有`create_script()`方法的`Session`对象全新加入了`enable_child_gating()`和`disable_child_gating()`这两个方法,在不显示调用新`API`的情况下,`Frida`的机制还是会跟从前一样,我们需要手动调用`enable_child_gating()`方法来切换到子进程控制的模式。 - -进入子进程控制模式之后,所有的子进程都会先暂停,等我们一顿操作完成之后,再对子进程的`PID`调用`resume()`来恢复子进程的运行。`Device`对象有一个叫做`delivered`的信号,我们可以在这个信号上装一个回调`callback`,这样有新的进程被产生出来的时候就会得到通知,得到通知之后立刻对新进程进行插桩等操作即可,然后调用`resume()`函数就可以恢复新进程的运行。`Device`对象还有一个新的`enumerate_pending_children()`的方法,用来列出即将产生的子进程列表,所有即将产生的子进程都会在这个表里,直到用户运行`resume()`函数恢复其运行,或者直接被**kill**掉。 - -理论讲完了,接下来实际操作一遍。下面是`host`端的`py`代码: - -```py -from __future__ import print_function -import frida -from frida.application import Reactor -import threading - -class Application(object): - def __init__(self): - self._stop_requested = threading.Event() - self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait()) - - self._device = frida.get_local_device() - self._sessions = set() - - self._device.on("delivered", lambda child: self._reactor.schedule(lambda: self._on_delivered(child))) - - def run(self): - self._reactor.schedule(lambda: self._start()) - self._reactor.run() - - def _start(self): - argv = ["/bin/sh", "-c", "cat /etc/hosts"] - print("✔ spawn(argv={})".format(argv)) - pid = self._device.spawn(argv) - self._instrument(pid) - - def _stop_if_idle(self): - if len(self._sessions) == 0: - self._stop_requested.set() - - def _instrument(self, pid): - print("✔ attach(pid={})".format(pid)) - session = self._device.attach(pid) - session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason))) - print("✔ enable_child_gating()") - session.enable_child_gating() - print("✔ create_script()") - script = session.create_script("""'use strict'; - -Interceptor.attach(Module.findExportByName(null, 'open'), { - onEnter: function (args) { - send({ - type: 'open', - path: Memory.readUtf8String(args[0]) - }); - } -}); -""") - script.on("message", lambda message, data: self._reactor.schedule(lambda: self._on_message(pid, message))) - print("✔ load()") - script.load() - print("✔ resume(pid={})".format(pid)) - self._device.resume(pid) - self._sessions.add(session) - - def _on_delivered(self, child): - print("⚡ delivered: {}".format(child)) - self._instrument(child.pid) - - def _on_detached(self, pid, session, reason): - print("⚡ detached: pid={}, reason='{}'".format(pid, reason)) - self._sessions.remove(session) - self._reactor.schedule(self._stop_if_idle, delay=0.5) - - def _on_message(self, pid, message): - print("⚡ message: pid={}, payload={}".format(pid, message["payload"])) - - -app = Application() -app.run() - -``` - -然后来运行这段代码: - -```sh -$ python3 example.py -✔ spawn(argv=['/bin/sh', '-c', 'cat /etc/hosts']) -✔ attach(pid=42401) -✔ enable_child_gating() -✔ create_script() -✔ load() -✔ resume(pid=42401) -⚡ message: pid=42401, -↪payload={'type': 'open', 'path': '/dev/tty'} -⚡ detached: pid=42401, reason='process-replaced' -⚡ delivered: Child(pid=42401, parent_pid=42401, -↪path="/bin/cat", argv=['cat', '/etc/hosts'], -↪envp=['SHELL=/bin/bash', 'TERM=xterm-256color', ...], -↪origin=exec) -✔ attach(pid=42401) -✔ enable_child_gating() -✔ create_script() -✔ load() -✔ resume(pid=42401) -⚡ message: pid=42401, -↪payload={'type': 'open', 'path': '/etc/hosts'} -⚡ detached: pid=42401, reason='process-terminated' -$ -``` - -我们重构了子进程的`hook`机制,也顺便重构了`Android App`的启动机制,移除了之前的`frida-loader-{32,64}.so`,全新的`Zygote`插桩机制会在后台承担所有的子进程控制工作,这也意味着可以对`Zygote`进行任意的插桩工作,当然得记好要调用`enable_child_gating()`来开启这这个功能,对于不需要进行插桩的子进程立即使用`resume()`来恢复其运行。 - -### 退出(崩溃)消息机制大更新 - -#### 存在的问题:程序崩溃时消息来不及发出 - -另外一个一直以来存在的问题就是,当进程快要意外崩溃的时候,进程传给`FRIDA`的`send()`的`API`的数据,可能会来不及发出去,虽然民间也有一种解决的办法就是可以`hook`一些`exit()`或`abort()`函数,然后在`hook`的语句里进行`send()`和`recv().wait()`的`client-host`结对操作,虽然不是很优雅,但针对特定平台也是有效的。 - -#### 解决的方法:对各大平台的停止进程`API`进行插装 - -针对程序意外崩溃的情况,`Frida`目前已经可以介入各大系统平台常用的停止进程的`API`,为用户做好进程崩溃时的清理工作,包含把数据发送出去。 - -有些脚本会把想要输出的数据在本地做个持久化然后定期通过`send()`传出去,这种情况下需要在进程即将崩溃的时候显式地将数据传输出去,我们为这种情况定制了一个`RPC`,导出名为`dispose`: - -```js -rpc.exports = { - dispose: function () { - send(bufferedData); - } -}; -``` - -几个大的机制的更新先介绍到这里,应该还会有下一篇,介绍一些小的但是刁钻的,或者是理念式的变化,不要小看这些变化,对于代码来讲,every line matters 。 \ No newline at end of file +The 'aux' mechanism makes command lines easily customizable, which is much easier than writing code separately for each platform. In fact, the underlying code hasn't changed in one line^.< diff --git a/README.md b/README.md index a8aeb49..006bff3 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,94 @@ # Android Application Security Study -安卓应用安全学习 +Android App Security Learning -# 进Frida&&FART群 +# Into Frida&&FART Group -加微信:r0ysue(备注:进FridaFart群) +Plus WeChat: r0ysue (Note: Into FridaFart Group) ---- +— -# 工具相关update +# Tools Related update -> 部分链接为知识星球、小鹅通等收费链接,不喜勿入 +> Some of the links are for charge links such as` Knowledge Planet`, `Little Goose Lane`, etc. Do not enter -|工具|更新| +|Tools|Update| |:-:|:-| -|Fart|- [《Fart8终极版》](https://aistk.xet.tech/s/1mMFrK):仅Nexus5x、Pixel1(XL)支持刷入
- [《Fart12脱壳王》](https://alq.xet.tech/s/1hGLBF):1. Fart10升级到Fart12,脱一二代壳依旧so easy ! 2. 无root,及对抗所有大厂的检测指纹 3. (todo)直接用的内核模块syscall hook,无视eBPF的高版本限制 4. (todo)内置Frida过检测方案
- jadx-gui定制版自动修复二代壳:报Fart脱壳王课程赠送| -|r0env|- [介绍文章:r0env:打造年轻人的第一套安卓逆向环境!](https://mp.weixin.qq.com/s/gBdcaAx8EInRXPUGeJ5ljQ)
- [r0env之两小时完整使用视频](https://www.bilibili.com/video/BV1qQ4y1R7wW)
- [r0env2011](https://t.zsxq.com/10LsaUoNU)(很多人跟我要r0env2021的分享,因为里面带DDMS、hyperpwn等等)
- [r0env2022](https://appli0n8byd8759.h5.xiaoeknow.com)(店铺首页轮播图第五张)
- todo:M芯片版r0env...现在不做主要因为还不成熟| -|Frida|- [《安卓Frida逆向与抓包实战》签名版](https://aistk.xet.tech/s/3DLQvC)
- [《Frida逆向与协议分析》签名版](https://aistk.xet.tech/s/14e51G)
- [todo:《安卓Frida应用SO逆向分析实战》签名版预售](https://aistk.xet.tech/s/2fdcLP)| -|KernelSU|- [Pixel3](https://t.zsxq.com/10boFCXni)
- [Pixel4](https://t.zsxq.com/10QjuMgHG)
- [Pixel4上的安卓13与KernelSU证书导入与抓包全套验证ok](https://t.zsxq.com/10OHT2S48)、[视频版](https://www.bilibili.com/video/BV1M8411Z7rC)
- [Pixel5](https://t.zsxq.com/10V5Ah2yK)
- [小米8:也一起解决了小米机器自编译boot后导致wifi无法使用的问题](https://t.zsxq.com/10Tjnbwz7)
- [Pixel5](https://t.zsxq.com/10V5Ah2yK)
- [一加7](https://t.zsxq.com/101F2paxn)| -|云手机|- [rock5b](https://t.zsxq.com/10BP2JODa)、[一开多安卓云手机 原生支持frida hook so](https://www.bilibili.com/video/BV1XY4y1m7NZ)
- [orangepi5:几百块的云手机套装](https://t.zsxq.com/107aZE2fL)| -|iOS|- 越狱:iOS12:[最高版本也能越狱的iPhone6](https://t.zsxq.com/10bB9dEvt)、[《两百元成本的iOS逆向环境搭建》](https://t.zsxq.com/10v9zKkvv)
- 抓包:[https://t.zsxq.com/10WVReMbd](https://t.zsxq.com/10WVReMbd)、https://t.zsxq.com/10lQkInqh
- 越狱:iOS14-16:iPhoneX| +|Fart|- ["Fart8 Ultimate"](https://aistk.xet.tech/s/1mMFrK): Only Nexus5x, Pixel1 (XL) support swiping
- ["Fart12 Shell"](https://alq.xet.tech/s/1hGLBF): 1. Fart10 Upgrade to Fart12, Off Generation 12 Still so easy! 2. No root, and detection fingerprint against all major manufacturers 3. (todo) Kernel module syscall hook for direct use, ignoring eBPF's high version limit 4. (todo) Built-in Frida over-detection scheme
- jadx-gui custom version Auto-repair second-generation shell: Report Fart Shell King Course Giveaway | +|r0env|- [Introduction: r0env: Creating the First Android Reverse Environment for Young People!](https://mp.weixin.qq.com/s/gBdcaAx8EInRXPUGeJ5ljQ)
- [Two Hours of Video on r0env](https://www.bilibili.com/video/BV1qQ4y1R7wW)
- [r0env2011](https://t.zsxq.com/10LsaUoNU) (Many people share with me r0env2021 because it has DDMS, hyperpwn, and so on)
- [r0env2022](https://appli0n8byd8759.h5.xiaoeknow.com)(Shop Home Page Rotation Title Five)
- todo:M Chipset r0env... Not Now Mostly Because It's Not Mature| +|Frida|- [ Android Frida Reverse and Packet Capture Signature Edition](https://aistk.xet.tech/s/3DLQvC)
- [ Frida Reverse and Protocol Analysis Signature Edition](https://aistk.xet.tech/s/14e51G)
- [todo: Android Frida Application SO Reverse Analysis Signature Edition Pre-Sold](https://aistk.xet.tech/s/2fdcLP)| +|KernelSU|- [Pixel3](https://t.zsxq.com/10boFCXni)
- [Pixel4](https://t.zsxq.com/10QjuMgHG)
- [Android 13 and KernelSU Certificate Import and Packet Capture Full Set of Validations on Pixel4 ok](https://t.zsxq.com/10OHT2S48), [Video Version](https://www.bilibili.com/video/BV1M8411Z7rC)
- [Pixel5](https://t.zsxq.com/10V5Ah2yK)
- [Mi8: Also addresses the issue of wifi not being available after Xiaomi self-compiles boot](https://t.zsxq.com/10Tjnbwz7)
- [Pixel5](https://t.zsxq.com/10V5Ah2yK)
- [One plus 7](https://t.zsxq.com/101F2paxn)| +| cloud phone |- [rock5b](https://t.zsxq.com/10BP2JODa), [OneDrive Android cloud phone native support frida hook so](https://www.bilibili.com/video/BV1XY4y1m7NZ)
- [orangepi5: A few hundred dollar cloud phone bundle](https://t.zsxq.com/107aZE2fL) | +|iOS|- Prison Break: iOS12:[The Most Capable iPhone6](https://t.zsxq.com/10bB9dEvt), [Two Hundred Dollar iOS Reverse Environment](https://t.zsxq.com/10v9zKkvv)
-Catch: [https://t.zsxq.com/10WVReMbd](https://t.zsxq.com/10WVReMbd), https://t.zsxq.com/10lQkInqh
- Prison Break:iOS14-16:iPhoneX| -PS:[以上手机、硬件、图书等下单地址(卖手机处点击查看更多)](https://appli0n8byd8759.h5.xiaoeknow.com) +PS: [The above mobile phone, hardware, books, and other places to order (click to see more at the seller)](https://appli0n8byd8759.h5.xiaoeknow.com) +— ---- +# FRIDA Series Articles -# 《FRIDA系列文章》 +## A. Environment Preparation and Getting Started -## A.环境准备和入门篇 +- [01.Android Environment Preparation: Google Original Mirror 8.1, TWRP, Magisk root](FRIDA/A01/README.md) +- [02. An article takes you to the heart of frida (based on Android 8.1)](FRIDA/A02/README.md) (code updated: 20200512, see folder compression package) -- [01.Android环境准备:谷歌原版镜像8.1、TWRP、Magisk root](FRIDA/A01/README.md) -- [02.一篇文章带你领悟frida的精髓(基于安卓8.1)](FRIDA/A02/README.md)(代码已更新:20200512,具体见文件夹压缩包) +## B.FRIDA script article -## B.FRIDA脚本篇 +- [01.FRIDA Script Series (I) Getting Started: dump Bluetooth Interfaces and Instances on Android 8.1](FRIDA/B01/README.md) +- [02.FRIDA Script Series (II) Growth: Dynamic and Static Combined Reverse WhatsApp](FRIDA/B02/README.md) +- [03.FRIDA Script Series (III) Psychic: Baidu AI "Tuning" TikTok AI](FRIDA/B03/README.md) +- [04.FRIDA Script Series (IV) Update: Major Updates to Several Major Mechanisms](FRIDA/B04/README.md) -- [01.FRIDA脚本系列(一)入门篇:在安卓8.1上dump蓝牙接口和实例](FRIDA/B01/README.md) -- [02.FRIDA脚本系列(二)成长篇:动静态结合逆向WhatsApp](FRIDA/B02/README.md) -- [03.FRIDA脚本系列(三)超神篇:百度AI"调教"抖音AI](FRIDA/B03/README.md) -- [04.FRIDA脚本系列(四)更新篇:几个主要机制的大更新](FRIDA/B04/README.md) +## C.FRIDA API -## C.FRIDA API篇 +- [01.FRIDA-API usages: rpc, Process, Module, Memory usage and examples](https://www.anquanke.com/post/id/195215) +- [02.FRIDA-API Usages: Java, Interceptor, NativePointer (Function/Callback) Usage and Examples](https://www.anquanke.com/post/id/195869) +- [03.Frida Java Hook Details (Android9): Code and Samples (top)](https://mp.weixin.qq.com/s/2BdX-rtAu8WZuzY3pK94NQ) +- [04.Frida Java Hook Details (Android9): Code and Examples (below)](https://mp.weixin.qq.com/s/heK_r0zXo_6_RoA37yPtGQ) -- [01.FRIDA-API使用篇:rpc、Process、Module、Memory使用方法及示例](https://www.anquanke.com/post/id/195215) -- [02.FRIDA-API使用篇:Java、Interceptor、NativePointer(Function/Callback)使用方法及示例](https://www.anquanke.com/post/id/195869) -- [03.Frida Java Hook 详解(安卓9):代码及示例(上)](https://mp.weixin.qq.com/s/2BdX-rtAu8WZuzY3pK94NQ) -- [04.Frida Java Hook 详解(安卓9):代码及示例(下)](https://mp.weixin.qq.com/s/heK_r0zXo_6_RoA37yPtGQ) +## D. Practical FRIDA -## D.实用FRIDA篇 +- [01. Functional FRIDA Advanced: Memory Roaming, 'hook anywhere', Packet Grab](https://www.anquanke.com/post/id/197657) +- [02. Practical FRIDA Advancements: Unpacking, Automating, Practical Issues Collection](https://www.anquanke.com/post/id/197670) +- 03. Practical FRIDA Advancements: Active Invocation, CipherStar -- [01.实用FRIDA进阶:内存漫游、`hook anywhere`、抓包](https://www.anquanke.com/post/id/197657) -- [02.实用FRIDA进阶:脱壳、自动化、实用问题集锦](https://www.anquanke.com/post/id/197670) -- 03.实用FRIDA进阶:主动调用,密码克星 +— ---- +# FART Series Articles -# 《FART系列文章》 +## Source Family -## 源码系列 +- [2020 Android Source Code Compilation Guide and 'FART' Off-Shell Google Full Device Image Launch](https://www.anquanke.com/post/id/199898) +- [FART source parsing and compilation mirroring support to Pixel2(xl)](https://www.anquanke.com/post/id/201896) (link:https://pan.baidu.com/s/1zAYliYbkagdUUsykww_L4g extract:vv5u) +- ['XPOSED'Change One: Get Features](FART/xposed1.md) -- [2020年安卓源码编译指南及`FART`脱壳机谷歌全设备镜像发布](https://www.anquanke.com/post/id/199898) -- [FART源码解析及编译镜像支持到Pixel2(xl)](https://www.anquanke.com/post/id/201896)(链接:https://pan.baidu.com/s/1zAYliYbkagdUUsykww_L4g 提取码:vv5u) -- [`XPOSED`魔改一:获取特征](FART/xposed1.md) +> 'Kali Linux' virtual machine download seed in 'FART/' folder -> `Kali Linux`虚拟机下载种子在`FART/`文件夹中 +## OPPOSRC: The Battle Series from Galway -## OPPOSRC:来自高纬的对抗系列 +- [Confrontation from High Latitude (1): Custom ART interpreter stripping all first and second generation shells](https://mp.weixin.qq.com/s/3tjY_03aLeluwXZGgl3ftw) ([Attachment](FART/H1/attachment)) +- [Confrontation from High Latitude (2): Magic Modified XPOSED Over Frame Detection (top)](https://mp.weixin.qq.com/s/c97zoTxRrEeYLvD8YwIUVQ) +- [Confrontation from High Latitude (3): Magic Alterations XPOSED Over Frame Detection (below)](https://mp.weixin.qq.com/s/YAMCrQSi0LFJGNIwB9qHDA) ([Annex 1](https://t.zsxq.com/eQR3fMf), [Annex 2] (https://t.zsxq.com/BqFAIEu)) +- [Confrontation from Galway (4): Custom Android Kernel Over Anti-Debug](https://mp.weixin.qq.com/s/CC40CwUS6jwNTc_by1zPlA)(Attachment: Link: https://pan.baidu.com/s/1zAYliYbkagdUUsykww_L4g Extract Code: vv5u]) +- [Confrontation from High Latitude (5): Replace the Android kernel and unpack Linux commands and environments](https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g) ([attachment](https://t.zsxq.com/jqNZrrr)) -- [来自高纬的对抗1:定制ART解释器脱所有一二代壳](https://mp.weixin.qq.com/s/3tjY_03aLeluwXZGgl3ftw) ([附件](FART/H1/attachment)) -- [来自高纬的对抗2:魔改XPOSED过框架检测(上)](https://mp.weixin.qq.com/s/c97zoTxRrEeYLvD8YwIUVQ) -- [来自高纬的对抗3:魔改XPOSED过框架检测(下)](https://mp.weixin.qq.com/s/YAMCrQSi0LFJGNIwB9qHDA)([附件1](https://t.zsxq.com/eQR3fMf)、[附件2](https://t.zsxq.com/BqFAIEu)) -- [来自高纬的对抗4:定制安卓内核过反调试](https://mp.weixin.qq.com/s/CC40CwUS6jwNTc_by1zPlA)(附件:链接:https://pan.baidu.com/s/1zAYliYbkagdUUsykww_L4g 提取码:vv5u]) -- [来自高纬的对抗5:替换安卓内核并解封Linux命令和环境](https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g)([附件](https://t.zsxq.com/jqNZrrr)) +## Hit Coder: Premium Serial -## 进击的Coder:精品连载 +- [Environment Configuration for Reverse Android App Lesson 1](https://mp.weixin.qq.com/s/YyDP_Lfk7kxOZf7F5SViLw) +- [Introduction to the Reverse Magic frida, Second Reverse Course on Android Apps](https://mp.weixin.qq.com/s/5LpaRY1O9br1ZnRNA-gH6Q) +- [Top Quality Plus Android App Reverse Course 3 frida Injection Okhttp Scratches Top](https://mp.weixin.qq.com/s/F_UGRoAsfDW4SAa7cXMKrg) +- [frida Injection into Okhttp Package Chapter 4 Reverse Course on Android Apps](https://mp.weixin.qq.com/s/PICqN6K_LFGHkjyiXkPzUw) +- [Quality Continuous Running Bank Android App Reverse Course 5 frida Injection Okhttp Packet Capture Next](https://mp.weixin.qq.com/s/SBEKXSO6LrFYsO5pOtfxJA) -- [安卓 App 逆向课程一之环境配置](https://mp.weixin.qq.com/s/YyDP_Lfk7kxOZf7F5SViLw) -- [精品连载丨安卓 App 逆向课程之二逆向神器 frida 的介绍](https://mp.weixin.qq.com/s/5LpaRY1O9br1ZnRNA-gH6Q) -- [精品连载丨安卓 App 逆向课程之三 frida 注入 Okhttp 抓包上篇](https://mp.weixin.qq.com/s/F_UGRoAsfDW4SAa7cXMKrg) -- [精品连载丨安卓 App 逆向课程之四 frida 注入 Okhttp 抓包中篇](https://mp.weixin.qq.com/s/PICqN6K_LFGHkjyiXkPzUw) -- [精品连载丨安卓 App 逆向课程之五 frida 注入 Okhttp 抓包下篇](https://mp.weixin.qq.com/s/SBEKXSO6LrFYsO5pOtfxJA) +## Collections: -## 各种合集: - -- [《常见Java层反调试技术之root检测方式总结》](Student/001) -- [《挑战不用macOS逆向iOS APP》之两百元成本的iOS逆向环境搭建](Student/002) -- [《Pixel1代平替:一加3手机刷入KaliNethunter解封完整Linux命令环境》](Student/003) -- [《Pixel4平替:OnePlus7刷NetHunter(android10) 指南》](Student/004) -- [《挑战不用macOS逆向iOS APP》之ObjC语法、iOS应用开发、及Objection自动化hook入门](Student/005) -- [《Frida前置知识:iOS/ObjC语法进阶及其ARM汇编实现》](Student/006) -- [《Frida hook/invoke iOS以及内存搜刮和黑盒调用》](Student/007) -- [《2023HW移动端武器库工具技巧分享》](Student/008) -- [《一道花指令标准算法SO还原题》](Student/009) -- [《Pixel4刷入KernelSU后Frida和VPN抓包配置》](Student/010) \ No newline at end of file +- ["Summary of Common Java Layer Anti-Debugging Techniques for root Detection"](Student/001) +- ["Challenging the 200ドル Cost of iOS Reverse Environment Without macOS Reverse iOS APP"](Student/002) +- ["Pixel1 Replacement: OnePlus3 Phone Swipe KaliNethunter to Unpack the Full Linux Command Environment"](Student/003) +- ["Pixel4 Parity: A Guide to OnePlus7 Brushing NetHunter (android10)"](Student/004) +- ["Challenging Reverse iOS APP without macOS" ObjC syntax, iOS application development, and Objection Automation hook Getting Started](Student/005) +- [ Frida Prerequisites: Advanced iOS/ObjC Syntax and Its ARM Assembly Implementation](Student/006) +- [ Frida hook/invoke iOS and Memory Scratches and Black Box Calls ](Student/007) +- [2023HW Mobile Arsenal Tools Tips Sharing](Student/008) +- [A Standard Algorithm SO Reduction Problem](Student/009) +- [ Frida and VPN Packet Capture Configuration after Pixel4 Flushes into KernelSU ](Student/010) \ No newline at end of file diff --git a/Student/001/README.md b/Student/001/README.md index 2e14711..e47d794 100644 --- a/Student/001/README.md +++ b/Student/001/README.md @@ -1,42 +1,42 @@ - + - + - [Intro](#intro) -- [《常见Java层反调试技术之root检测方式总结》](#常见java层反调试技术之root检测方式总结) - - [执行'which su'命令检测su](#执行which-su命令检测su) - - [检测在常用目录下是否存在非法的二进制文件](#检测在常用目录下是否存在非法的二进制文件) - - [判断SELinux是否开启,](#判断selinux是否开启) - - [检测ro.debuggable和ro.secure值](#检测rodebuggable和rosecure值) - - [检查特定路径是否有写权限](#检查特定路径是否有写权限) - - [检测test-keys](#检测test-keys) - - [检测非法应用](#检测非法应用) +- ["Summary of root Detection Methods for Common Java Layer Anti-Debugging Techniques"](#Summary of root Detection Methods for Common java Layer Anti-Debugging Techniques) +- [Execute 'which su' command detect su](#Execute which-su command detect su) +- [Detect the presence of illegal binaries in popular directories](# Detect the presence of illegal binaries in popular directories) +- [Determine if SELinux is open,](#Determine if selinux is open) +- [Detect ro.debuggable and ro.secure values](# Detect rodebuggable and rosecure values) +- [Check specific path for write permissions](#Check specific path for write permissions) +- [Detect test-keys](# Detect test-keys) +- [Detect Illegal Apps] (# Detect Illegal Apps) - + # Intro -该系列为学员优秀作品系列,附件apk、代码等位于我的项目中,大家可以自取: +This series is a good series of works for the students. The accessories, apk, code and so on, are in my project. You can choose from: -[https://github.com/r0ysue/AndroidSecurityStudy](https://github.com/r0ysue/AndroidSecurityStudy) +[https://github.com/r0ysue/AndroidSecurityStudy] (https://github.com/r0ysue/AndroidSecurityStudy) -# 《常见Java层反调试技术之root检测方式总结》 +# "Summary of Java Detection Methods for Common root Layer Anti-Debugging Techniques" -Android 系统的设计初衷为了保护用户设备的安全和稳定性,并控制对系统的访问权限。因此,Android 系统会对root这种非授权的操作进行限制,在渗透测试 Android 应用程序时候,大多数技术都需要 root 权限才能安装各种工具,从而危及应用程序的安全性,以下是笔者对几种Root检测方式的总结,抛砖引玉 +The Android system was designed to protect the security and stability of user equipment and control access to the system. Therefore, the Android system will limit the unauthorized operation of root. In the penetration test of Android applications, most technologies require root privileges to install a variety of tools, which endanger the security of the application. The following is a summary of the author on several Root detection methods, and some remarks -## 执行'which su'命令检测su -su命令可用于切换至其他用户,安卓手机出于安全考虑,系统内部一般是没有su可执行文件,所以基于检查系统内部是否存在su文件的方式可以判断该系统有没有被Root +## Execute 'which su' command to detect su +The su command can be used to switch to other users, Android phones for security reasons, the system generally does not have the su executable file, so based on the way to check whether the system exists su file can determine whether the system has been Root -以下命令测试的是被Root过的手机,可以看到存在su +The following command tests a phone that has been Root, and you can see that su exists ```bash bullhead:/ $ which su which su /system/bin/su ``` -而另一部华为手机没有被Root,没有检测到su +Another Huawei phone, however, was not Root and no su was detected ```bash D:\Users\wyz\Desktop\Fastboot>adb shell HWEVR:/ $ which su @@ -44,57 +44,57 @@ which su 1|HWEVR:/ $ ``` -用java实现检测是否存在su命令 +Detecting the presence of su commands with the java implementation ```java - package com.example.checksu; +package com.example.checksu; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import java.io.BufferedReader; import java.io.InputStreamReader; import android.widget.Toast; - public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); +@Override +protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); - if(checkSuExists()) - { - Toast.makeText(getApplicationContext(), "检测到su命令", - Toast.LENGTH_SHORT).show(); - } - else - { - Toast.makeText(getApplicationContext(),"没有检测到su命令", - Toast.LENGTH_SHORT).show(); - } + if(checkSuExists()) + { + Toast.makeText(getApplicationContext(), "su command detected", + Toast.LENGTH_SHORT).show(); } + else + { + Toast.makeText(getApplicationContext(), "su command not detected", + Toast.LENGTH_SHORT).show(); + } +} - public boolean checkSuExists() { - Process process = null; - try { - process = Runtime.getRuntime().exec(new String[] { "which", "su" }); - BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); - return in.readLine() != null; - } catch (Throwable t) { - return false; - } finally { - if (process != null) process.destroy(); - } - } +public boolean checkSuExists(){ + Process process = null; + try { + process = Runtime.getRuntime().exec(new String[] { "which", "su" }); + BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); + return in.readLine()!= null; + } catch(Throwable t){ + return false; + } finally { + if(process != null)process.destroy(); + } + } } ``` -没有root过的华为手机的检测结果 +Huawei Phones Without root ![](202303211.png) -root过的N5x的检测结果 +Detection of N5x Over root ![](202303212.png) -## 检测在常用目录下是否存在非法的二进制文件 +## Detects the presence of illegal binaries in common directories -笔者root过的N5x在一些环境变量路径里存在su和magisk +The N5x of the author root has su and magisk in some environment variable paths ```bash bullhead:/system/bin $ which magick which magick @@ -112,468 +112,83 @@ echo $PATH | grep /system/bin /product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin ``` - -由此可以用遍历系统PATH的手段检测一些非法二进制文件,java实现如下 +Therefore, we can use the means of traversing the system PATH to detect some illegal binary files. The implementation of java is as follows ```java package com.example.checkpath; import android.os.Bundle; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; -import java.io.File; +import java.io.Fi; import java.util.ArrayList; import java.util.Arrays; public class MainActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - checkForBinary("magisk"); - checkForBinary("su"); - checkForBinary("busybox"); - } +@Override +protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + checkForBinary("magisk"); + checkForBinary("su"); + checkForBinary("busybox"); +} - private static final String[] suPaths = { - "/data/local/", - "/data/local/bin/", - "/data/local/xbin/", - "/sbin/", - "/su/bin/", - "/system/bin/", - "/system/bin/.ext/", - "/system/bin/failsafe/", - "/system/sd/xbin/", - "/system/usr/we-need-root/", - "/system/xbin/", - "/cache/", - "/data/", - "/dev/" +private static final String[] suPaths = { + "/data/local/", + "/data/local/bin/", + "/data/local/xbin/", + "/sbin/", + "/su/bin/", + "/system/bin/", + "/system/bin/.ext/", + "/system/bin/failsafe/", + "/system/sd/xbin/", + "/system/usr/we-need-root/", + "/system/xbin/", + "/cache/", + "/data/", + "/dev/" }; - public void checkForBinary(String filename) { +public void checkForBinary(String filename){ - String[] pathsArray = this.getPaths(); + String[] pathsArray = this.getPaths(); - boolean flag = false; + boolean flag = false; - for (String path : pathsArray) { - String completePath = path + filename; - File f = new File(path, filename); - boolean fileExists = f.exists(); - if (fileExists) { - Toast.makeText(getApplicationContext(), "检测到非法的二进制文件: "+path+filename, Toast.LENGTH_LONG).show(); - } - } + for(String path : pathsArray){ + String completePath = path + filename; + File f = new File(path, filename); + boolean fileExists = f.exists(); + if(fileExists){ + Toast.makeText(getApplicationContext(), "Illegal binary detected: "+path+filename, Toast.LENGTH_LONG).show(); } - - private String[] getPaths() { - ArrayList paths = new ArrayList(Arrays.asList(suPaths)); - - String sysPaths = System.getenv("PATH"); - - // If we can't get the path variable just return the static paths - if (sysPaths == null || "".equals(sysPaths)) { - return paths.toArray(new String[0]); - } - - for (String path : sysPaths.split(":")) { - - if (!path.endsWith("/")) { - path = path + '/'; - } - - if (!paths.contains(path)) { - paths.add(path); - } - } - - return paths.toArray(new String[0]); } -} -``` - -N5x运行结果 -![](202303213.png) -![](202303214.png) - -## 判断SELinux是否开启, - ->**但此方法或已过时**,需要结合其他多种方法来判断手机是否root - - -用java反射获取ro.build.selinux值来判断 -```java -package com.example.checkselinuxenabled; - -import android.os.Bundle; -import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; -import java.lang.reflect.Method; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if(isSelinuxFlagInEnabled()) - { - Toast.makeText(getApplicationContext(), "SELinux开启", - Toast.LENGTH_SHORT).show(); - } - else - { - Toast.makeText(getApplicationContext(), "SELinux没有开启", - Toast.LENGTH_SHORT).show(); - } - } - - private boolean isSelinuxFlagInEnabled() { - try { - Class c = Class.forName("android.os.SystemProperties"); - Method get = c.getMethod("get", String.class); - String selinux = (String) get.invoke(c, "ro.build.selinux"); - return "1".equals(selinux); - } catch (Exception ignored) { - - } - return false; } -} -``` -## 检测ro.debuggable和ro.secure值 +private String[] getPaths(){ +ArrayList paths = new ArrayList(Arrays.asList(suPaths)); -"ro.debuggable" 是 Android 操作系统中的一个属性,它指示设备是否已启用 Android Debug Bridge(ADB)功能。当该属性设置为 "1" 时,设备将启用 ADB 功能,允许开发人员使用各种工具和命令在设备上进行调试和测试。在生产环境中,该属性通常应该被设置为 "0" 以增加设备的安全性。如果该属性设置为 "1",则任何人都可以使用 ADB 命令访问设备,从而可能会导致设备受到攻击或其他安全问题 + String sysPaths = System.getenv("PATH"); -"ro.secure" 是一个 Android 操作系统中的系统属性(System Property),它控制着该设备是否开启了安全性强化措施。如果该属性的值为 "1",则表示该设备已开启安全性强化措施。如果该属性的值为 "0",则表示该设备未开启安全性强化措施。 - -不过笔者root过的N5x的ro.secure值为1,不能盲目的相信ro.secure -```bash -bullhead:/ $ getprop ro.debuggable -getprop ro.debuggable -1 -bullhead:/ $ getprop ro.secure -getprop ro.secure -1 -``` -所以,检测的逻辑是检查ro.debuggable属性是否为true,为true的话APP所运行环境很可能是Root环境 -使用java代码实现 -```java -package com.example.checkdebuggableandsecure; - -import android.os.Bundle; -import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Scanner; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - checkForDangerousProps(); - } - - private void checkForDangerousProps() { - - final Map dangerousProps = new HashMap(); - dangerousProps.put("ro.debuggable", "1"); - - boolean result = false; - - String[] lines = propsReader(); - - if (lines == null){ - // Could not read, assume false; - return; - } - - for (String line : lines) { - for (String key : dangerousProps.keySet()) { - if (line.contains(key)) { - String badValue = dangerousProps.get(key); - badValue = "[" + badValue + "]"; - if (line.contains(badValue)) { - Toast.makeText(getApplicationContext(), "检测到危险值 "+key+": "+badValue, - Toast.LENGTH_LONG).show(); - } - } - } - } - } - - private String[] propsReader() { - try { - InputStream inputstream = Runtime.getRuntime().exec("getprop").getInputStream(); - if (inputstream == null) return null; - String propVal = new Scanner(inputstream).useDelimiter("\\A").next(); - return propVal.split("\n"); - } catch (IOException | NoSuchElementException e) { - return null; - } - } -} -``` - -N5x执行以上代码的结果 -![](202303215.png) - - -## 检查特定路径是否有写权限 -在 Linux 和 Android 的系统中,root 用户有着最高的权限,可以在系统中执行一些非常低级的操作,例如更改系统设置、系统文件、程序文件等。而其他常规用户则只能够在自己拥有的权限下进行操作,通过检查具体路径的读写权限,可以判断设备是否被root - -下列Java代码将使用mount命令检查这些路径的读写权限,如果可读写,说明设备可能被root了 -``` - /system - /system/bin - /system/sbin - /system/xbin - /vendor/bin - /sbin - /etc - /sys - /proc - /dev -``` - -```java -package com.example.checkrw; - -import android.os.Bundle; -import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; -import java.io.IOException; -import java.io.InputStream; -import java.util.NoSuchElementException; -import java.util.Scanner; - -public class MainActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.checkForRWPaths(); - } - - - private String[] pathsThatShouldNotBeWritable = { - "/system", - "/system/bin", - "/system/sbin", - "/system/xbin", - "/vendor/bin", - "/sbin", - "/etc", - "/sys", - "/proc", - "/dev" - }; - - private String[] mountReader() { - try { - InputStream inputstream = Runtime.getRuntime().exec("mount").getInputStream(); - if (inputstream == null) return null; - String propVal = new Scanner(inputstream).useDelimiter("\\A").next(); - return propVal.split("\n"); - } catch (IOException | NoSuchElementException e) { - Toast.makeText(getApplicationContext(), e.getMessage(), - Toast.LENGTH_LONG).show(); - return null; - } + // If we can't get the path variable just return the static paths + if(sysPaths == null || "".equals(sysPaths)){ + return paths.toArray(new String[0]); } - public void checkForRWPaths() { + for(String path : sysPaths.split(":")){ - //Run the command "mount" to retrieve all mounted directories - String[] lines = mountReader(); - - if (lines == null){ - return; + if(!path.endsWith("/")){ + path = path + '/'; } - int sdkVersion = android.os.Build.VERSION.SDK_INT; - - for (String line : lines) { - - // Split lines into parts - String[] args = line.split(" "); - - if ((sdkVersion <= android.os.Build.VERSION_CODES.M && args.length < 4) - || (sdkVersion> android.os.Build.VERSION_CODES.M && args.length < 6)) { - // If we don't have enough options per line, skip this and log an error - Toast.makeText(getApplicationContext(), "Error formatting mount line: "+line+line, - Toast.LENGTH_LONG).show(); - continue; - } - - String mountPoint; - String mountOptions; - - /** - * To check if the device is running Android version higher than Marshmallow or not - */ - if (sdkVersion> android.os.Build.VERSION_CODES.M) { - mountPoint = args[2]; - mountOptions = args[5]; - } else { - mountPoint = args[1]; - mountOptions = args[3]; - } - - for(String pathToCheck: this.pathsThatShouldNotBeWritable) { - if (mountPoint.equalsIgnoreCase(pathToCheck)) { - - /** - * If the device is running an Android version above Marshmallow, - * need to remove parentheses from options parameter; - */ - if (android.os.Build.VERSION.SDK_INT> android.os.Build.VERSION_CODES.M) { - mountOptions = mountOptions.replace("(", ""); - mountOptions = mountOptions.replace(")", ""); - - } - - // Split options out and compare against "rw" to avoid false positives - for (String option : mountOptions.split(",")){ - - if (option.equalsIgnoreCase("rw")){ - Toast.makeText(getApplicationContext(), pathToCheck+" 路径以rw权限挂载! "+line, - Toast.LENGTH_LONG).show(); - } - } - } - } + if(!paths.contains(path)){ + paths.add(path); } - } - - - } -``` - -N5x执行以上代码的结果 -![](202303216.png) - - -## 检测test-keys - -通过检测其系统属性中的"ro.build.tags"参数的值,如果其值为"test-keys",则说明该手机系统是使用测试版的数字签名密钥构建的,而这种密钥是开发人员常用的,容易被黑客攻击,因此有被root的风险。而正式发布的Android系统则使用的是"release-keys",这种密钥是由厂家发布的数字签名,更难被攻击,因此是相对安全的。因此,如果检测到一个Android设备的系统属性中的"ro.build.tags"为"test-keys",则可以判定该设备可能被root - -root过的N5x,打印出的ro.build.tags值为test-keys -```bash -bullhead:/ $ getprop ro.build.tags -getprop ro.build.tags -test-keys -``` - -没有root过的华为手机,打印出的ro.build.tags值为release-keys -```bash -HWEVR:/ $ getprop ro.build.tags -getprop ro.build.tags -release-keys -``` - -用java代码实现 -```java -package com.example.checkbuildtags; - -import android.os.Bundle; -import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if(detectTestKeys()) - Toast.makeText(getApplicationContext(), "Test Keys", - Toast.LENGTH_SHORT).show(); - else - Toast.makeText(getApplicationContext(), "Relese Keys", - Toast.LENGTH_SHORT).show(); - } - - public boolean detectTestKeys() { - String buildTags = android.os.Build.TAGS; - return buildTags != null && buildTags.contains("test-keys"); - } + return paths.toArray(new String[0]); } -``` - -root过的N5x执行结果 -![](202303217.png) - -没有root过的华为手机执行结果 -![](202303218.png) - - -## 检测非法应用 -通过获取系统安装的应用列表,从中过滤出非法应用 - -以下命令过滤出了非法应用magisk -```bash -PS D:\Users\wyz\Desktop\Fastboot> ./adb shell pm list packages | findstr magisk -package:com.topjohnwu.magisk -``` - -用java代码实现类似逻辑,读者可在knownRootAppsPackages中加入自己的过滤列表,不过这段代码过滤不到magisk,建议读者使用Runtime.getRuntime().exec来执行和上段类似的shell命令进行过滤magisk -```java -package com.example.checkapp; - -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - detectPotentiallyDangerousApps(); - } - - static final String[] knownRootAppsPackages = { - //add.... - }; - - public void detectPotentiallyDangerousApps() { - - // Create a list of package names to iterate over from constants any others provided - ArrayList packages = new ArrayList(); - packages.addAll(Arrays.asList(this.knownRootAppsPackages)); - isAnyPackageFromListInstalled(packages); - } - - private void isAnyPackageFromListInstalled(List packages){ - PackageManager pm = getApplicationContext().getPackageManager(); - - for (String packageName : packages) { - try { - // Root app detected - pm.getPackageInfo(packageName, 0); - Toast.makeText(getApplicationContext(), "检测到非法应用: "+packageName, - Toast.LENGTH_LONG).show(); - } catch (PackageManager.NameNotFoundException e) { - Toast.makeText(getApplicationContext(), "没有检测到非法应用: "+packageName, - Toast.LENGTH_LONG).show(); - } - } - } } -``` - -以上是笔者对各种root检测方式的一个粗略总结,应该把多种检测方式叠加,以确保检测成功率符合预期,不过这些都是初级的检测方式,不排除有大量的漏网之鱼 \ No newline at end of file +``` \ No newline at end of file diff --git a/Student/002/README.md b/Student/002/README.md index 53b90a0..70d8d24 100644 --- a/Student/002/README.md +++ b/Student/002/README.md @@ -1,112 +1,113 @@ - - - - -- [1.Windows中iPhone基本信息获取/软件安装/投屏](#1windows中iphone基本信息获取软件安装投屏) - - [(1)基本信息获取](#1基本信息获取) - - [(2)软件安装](#2软件安装) - - [(3)手机投屏](#3手机投屏) -- [2.越狱设备分析推荐/unC0ver流程详解](#2越狱设备分析推荐unc0ver流程详解) - - [(1)越狱设备的分析与推荐](#1越狱设备的分析与推荐) - - [(2)checkra1n越狱详细流程](#2checkra1n越狱详细流程) - - [(1)制作一个越狱u盘](#1制作一个越狱u盘) - - [(2)刷入手机实现越狱](#2刷入手机实现越狱) -- [3.越狱后手机常用工具:日志/网络/包管理/免签名](#3越狱后手机常用工具日志网络包管理免签名) - - [(1)cydia自带源工具安装](#1cydia自带源工具安装) - - [(2)三方源工具安装](#2三方源工具安装) -- [4.Frida/Objection/任意版本安装切换/动态分析](#4fridaobjection任意版本安装切换动态分析) - - [(1)安装最新frida](#1安装最新frida) - - [(2)安装objection](#2安装objection) -- [5.ios砸壳/IPA静态分析基本流程/IDAF5函数定位](#5ios砸壳ipa静态分析基本流程idaf5函数定位) - - [(1)ios砸壳](#1ios砸壳) - - [(2)IPA静态分析基本流程及IDAF5函数定位](#2ipa静态分析基本流程及idaf5函数定位) -- [6.linux中iphone手机信息获取:ID/名称/详情/签名/截屏/定位](#6linux中iphone手机信息获取id名称详情签名截屏定位) - - [(1)获取设备ID](#1获取设备id) - - [(2)获取设备名称](#2获取设备名称) - - [(3)获取屏幕截图](#3获取屏幕截图) - - [(4)设置虚拟定位](#4设置虚拟定位) - - [(5)获取设备详情](#5获取设备详情) -- [7.安装APP/IPA:安装/卸载/升级/备份/恢复APP](#7安装appipa安装卸载升级备份恢复app) - - [(1)安装软件](#1安装软件) - - [(2)卸载软件](#2卸载软件) - - [(3)查看已安装应用](#3查看已安装应用) - - [(4)其他](#4其他) -- [8.收发传输文件:usbmuxd OpenSSH=adb pull/push](#8收发传输文件usbmuxd-opensshadb-pullpush) - - [(1)基于usbmuxd实现adb pull/push](#1基于usbmuxd实现adb-pullpush) - - [(2)基于OpenSSH实现adb pull/push](#2基于openssh实现adb-pullpush) -- [9.远程文件管理:磁盘映射/电脑上浏览手机文件夹](#9远程文件管理磁盘映射电脑上浏览手机文件夹) - - [(1)挂载文件系统](#1挂载文件系统) - - [(2)取消挂载](#2取消挂载) -- [10.升降级和激活:恢复模式/固件升降级/手机激活](#10升降级和激活恢复模式固件升降级手机激活) - - + + + + +- [iPhone Profile Acquisition/Software Installation/Projection in 1.Windows](#iphone Profile Acquisition Software Installation Projection in 1windows) +- [(1) Profile Acquisition](#1 Profile Acquisition) +- [(2) Software Installation](#2 Software Installation) +- [(3) Phone Drop Screen] (#3 Phone Drop Screen) +- [2. Escape Device Analysis Recommendation/unC0ver Process Details] (#2 Escape Device Analysis Recommendation unc0ver Process Details) +- [(1) Analysis and recommendation of escape devices](#1 Analysis and recommendation of escape devices) +- [(2) checkra1n Escape Detail Procedure] (#2checkra1n Escape Detail Procedure) +- [(1) Make an escape u disk] (#1 Make an escape u disk) +- [(2) Escape by painting your phone] (#2 Escape by painting your phone) +- [3. Post-Escape Phone Utilities: Log/Network/Package Management/No Signature] (#3 Post-Escape Phone UtilitiesLog Network Package Management No Signature) +- [(1) cydia Native Source Tool Installation] (#1cydia Native Source Tool Installation) +- [(2) Tripartite Source Tool Installation] (#2 Tripartite Source Tool Installation) +- [4.Frida/Objection/Any Version Install Switch/Dynamic Analysis] (#4fridaobjection Any Version Install Switch Dynamic Analysis) +- [(1) Install the latest frida] (#1 Install the latest frida) +- [(2) Install objection] (#2 Install objection) +- [5.ios Shell Smashing/IPA Static Analysis Basic Flow/IDAF5 Function Positioning] (#5ios Shell Smashing ipa Static Analysis Basic Flow idaf5 Function Positioning) +- [(1)ios crush] (#1ios crush) +- [(2) IPA Static Analysis Basic Flow and IDAF5 Function Positioning] (#2ipa Static Analysis Basic Flow and idaf5 Function Positioning) +- [iphone Phone Info Acquisition in 6.linux: ID/Name/Details/Signature/Screenshot/Location] (#iphone Phone Info Acquisition in 6linux id Name Details Signature Screenshot Location) +- [(1) get device id] (#1 get device id) +- [(2) get device name] (#2 get device name) +- [(3) Get Screenshot] (#3 Get Screenshot) +- [(4) Set Virtual Location] (#4 Set Virtual Location) +- [(5) Get Device Details] (#5 Get Device Details) +- [7. Install APP/IPA: Install/Uninstall/Upgrade/Back-up/Restore APP] (#7 Install appipa Install Uninstall Upgrade Back-up Restore APP) +- [(1) Install Software] (#1 Install Software) +- [(2) Uninstall Software] (#2 Uninstall Software) +- [(3) View installed apps] (#3 View installed apps) +- [(4) Other] (#4 Other) +- [8. Send and receive transfer files: usbmuxd OpenSSH=adb pull/push] (#8 Send and receive transfer files usbmuxd-opensshadb-pullpush) +- [(1) adb pull/push over usbmuxd] (#1 adb-pullpush over usbmuxd) +- [(2) adb pull/push over OpenSSH] (#2 adb-pullpush over openssh) +- [9. Remote File Management: Disk Mapping/Browse Phone Folder on PC] (#9 Remote File Management Disk Mapping Browse Phone Folder on PC) +- [(1) Mount File System] (#1 Mount File System) +- [(2) Unmount] (#2 Unmount) +- [10. Elevations and Activations: Recovery Mode/Firmware Elevations/Phone Activation] (#10 Elevations and Activations Recovery Mode Firmware Elevations Phone Activation) + + # Intro -很多人在学习逆向iOS app的时候,都有两个痛点: +When many people learn reverse iOS app, they have two pain points: -1. iOS设备太贵 -2. 需要macOS环境 +1. iOS device too expensive +2. Requires macOS environment -前者需要小几千的iPhone,后者需要至少大几千的Macbook 。本系列文章就是为了解决这两个痛点,全部操作在一百多块钱的iPhone6上完成,且电脑端在r0env/Win上进行演示,让我们康康,这几乎无成本的一套环境,究竟可以让我们走多远,走多深! +The former requires a few thousand iPhone, while the latter requires at least a few thousand Macbook. This series is designed to address these two pain points, all done on a hundred dollar iPhone6 and demonstrated on a r0env/Win, giving us a healthy, cost-free set of environments that can drive us far and wide! -本篇文章是《挑战不用macOS逆向iOS APP》系列的第一课环境搭建主要为了实现在iOS APP逆向过程中一些环境方面的常规需求,具体实现目标如下: -- windows中iPhone基本信息获取/软件安装/投屏 -- 越狱设备分析推荐/工具推荐/unC0ver流程详解 -- 越狱后手机常用工具:日志/网络/包管理/免签名 -- Frida/Objection/任意版本安装切换/动态分析 -- ios砸壳/IPA静态分析基本流程/IDAF5函数定位 -- linux中iphone手机信息获取:ID/名称/详情/签名/截屏/定位 -- 安装APP/IPA:安装/卸载/升级/备份/回复APP -- 收发传输文件:usbmuxd OpenSSH=adb pull/push -- 远程文件管理:磁盘映射/电脑上浏览手机文件夹 -- 升降级和激活:恢复模式/固件升降级/手机激活 +This article is "Challenge Not Using macOS to Reverse iOS APP" series of the first course environment to build in order to achieve some of the iOS APP reverse process in the environment of the general needs, specific objectives are as follows: +- iPhone Essentials Acquisition/Software Installation/Screen Shot in windows +- Escape Device Analysis Recommendations / Tool Recommendations / unC0ver Process Details +- Post-escape mobile phone tools: Blog/Network/Package Management/Autograph-free +- Frida/Objection/Any Version Installation Switch/Dynamic Analysis +- ios shrapnel/ipa static analysis basic flow/IDAF5 function positioning +- iphone Phone Info Acquisition in linux: ID/Name/Details/Signature/Screenshot/Location +- Install APP/IPA: Install/Uninstall/Upgrade/Back-up/Restore APP +- Send and receive transfer files: usbmuxd OpenSSH=adb pull/push +- Remote File Management: Disk Mapping/Browse Phone Folders on PC +- Elevations and Activations: Recovery Mode/Firmware Elevations/Mobile Activation -# 1.Windows中iPhone基本信息获取/软件安装/投屏 -## (1)基本信息获取 -Windows中操作iPhone官方推荐方式是使用iTunes,但是后续我们会安装未经签名的应用,因此这里我们推荐使用爱思助手进行操作。直接去官网下载安装爱思助手,打开并在手机上信任此电脑 +# iPhone Basics Get/Software Install/Screen in 1.Windows +## (1)Access to basic information +The official recommendation for operating iPhone in Windows is to use iTunes, but we will install the unsigned app in the future, so here we recommend using the Love Assistant. Go to the website to download and install AIZ Assistant, turn on and trust this PC on your phone ![1](202304041.png) ![2](202304042.png) -## (2)软件安装 +## (2)Software Installation -软件安装:点开爱思助手的应用游戏可以直接安装,爱思上安装的软件有APP Store上架的也有未上架但有企业账号签名的,具体内容涉及IPA签名,会在后续文章解释。 +Software Installation: Click on the AIX Assistant app game to install directly, Aix installed software on the APP Store or not on the shelf but signed by the corporate account, specific content related to the IPA signature, will be explained in the follow-up article. ![3](202304043.png) -## (3)手机投屏 -直接使用爱思助手中的投屏,可以使用有线投屏,同一局域网下也可以使用无线投屏 +## (3)Cellular Projection +You can use a wired screen shot directly from the IZ Assistant, or you can use a wireless screen shot from the same LAN ![4](202304044.png) -手机端上滑点击屏幕镜像点击爱思投屏即可。 +The phone slides up, clicks on the screen image, clicks on the AIX screen. -# 2.越狱设备分析推荐/unC0ver流程详解 +# 2. Escape Equipment Analysis Recommendations / unC0ver Process Details -## (1)越狱设备的分析与推荐 +## (1)Analysis and recommendation of escape equipment -查看爱思助手的越狱方式可以看到 -可以支持ios版本最高的是unc0ver方式可以支持到iOS14.8,而又由于iPhone越狱失败后重启设备失败的话需要恢复出厂设置,此时会自动更新到当前设备支持的最新iOS版本,而iPhone6最高版本是12.5.4,iPhone6s和iPhone7最新版本均是iOS 15,因此我们选择使用iPhone6作为越狱设备,这样即使越狱失败最新版本也依然在越工具支持版本之下。 -越狱方式对比,这里主要分析两种方式unc0ver和Checkra1n -checkra1n:比较复杂还要制作u盘但是胜在稳定 -unc0ver:过程简单但是成功比较看运气,需尝试多次才能成功 +Check out how Assistant Ace escaped -## (2)checkra1n越狱详细流程 +Can Support ios The highest version is the unc0ver method which can support iOS14.8, and due to the failure to restart the device after the iPhone escape, the factory settings will need to be restored automatically to the latest iOS version supported by the current device, while the highest version of iPhone6 is 12.5.4, and the latest versions of iPhone6s and iPhone7 are both iOS 15, so we chose to use iPhone6 as the escape device so that the latest version will remain under the tool support even if the escape fails. +In this paper, the comparison of the escape modes is made. The analysis of the escape modes of unc0ver and Checkra1n is presented +checkra1n: It's more complicated, and you have to make a u-disk, but it's more stable +unc0ver: It's a simple process, but it's a lot of luck, and it's a lot of trying to be successful -### (1)制作一个越狱u盘 +## (2) checkra1n Escape Details + +### (1) Make an escape u disk ![5](202304045.png) ![6](202304046.jpg) ![7](202304047.jpg) -### (2)刷入手机实现越狱 +### (2) Escape by swiping his phone -进入电脑BIOS选择VendorCo ProductCode 从u盘启动 +Enter the computer BIOS Select VendorCo ProductCode to boot from the u disk ![8](202304048.jpg) -选择ALT+F2进入Checkra1n刷机系统 +Select ALT+F2 to Enter the Checkra1n Brush System ![9](202304049.jpg) -上下左右空格键控制start开始越狱 +Up, down, left, right spacebar controls start to begin escape ![10](2023040410.jpg) next ![11](2023040411.jpg) @@ -114,242 +115,103 @@ next ![12](2023040412.jpg) -进入刷机界面后根据提示操作 +Follow the prompts when you enter the brush interface ![13](2023040413.jpg) -1.点击start -2.同时摁住侧边键和home键 -3.摁住home键 -这个界面就是在刷入,ALL Done就是刷入成功了。 +1. Tap start +2. Press the side key and home key at the same time +3. Press home +This interface is a brush in, and ALL Done is a brush in. ![14](2023040414.jpg) -补充:中间可能会失败,没关系多来几次总能成功的,但是一定注意,手机不要买到带锁机,二手iphone卖家有时会隐藏ID锁,这种机子可以登录自己ID但是一旦越狱失败就寄了。。。 +Add: It doesn't matter how many times it's always successful, but be careful not to buy a locked phone. Second-hand iphone sellers sometimes hide ID locks, which can log in to their ID but send it if the escape fails. -# 3.越狱后手机常用工具:日志/网络/包管理/免签名 +# 3. Post-escape mobile phone common tools: blog/network/package management/no signature -手机越狱成功后,会在桌面显示checkra1n图标,点击安装cydia,cydia是一个需要越狱后使用的三方软件仓库。这里我们主要安装以下几个工具: -## (1)cydia自带源工具安装 -cydia自带源的工具可以直接搜索安装 +When the phone is released from prison, the checkra1n icon is displayed on the desktop and cydia is clicked to install. cydia is a three-way software warehouse that needs to be used after the escape. Here we are mainly installing the following tools: +##(1) cydia Native Source Tool Installation +cydia native tools can search for installations directly ![15](2023040415.png) -- oslog 日志查看工具,默认安装在/usr/bin目录下 -- OPENSSH 远程连接软件,作用是远程连接手机,默认账号密码为root/alpine -- Filza File Manager 是手机端的文件管理软件,作用是让我们更方便的操作手机上的文件。 -- Apple File Conduit "2" IOS上的一个插件工具,作用是帮助我们在电脑端操作手机上的文件 +- oslog log viewing tool, installed by default in /usr/bin +- OPENSSH Remote connection software, which allows you to connect to your phone remotely, with a default account password of root/alpine +- Filza File Manager It is the mobile phone file management software, the role is to let us more convenient operation of the mobile phone file. +- A plug-in tool on the Apple File Conduit "2" IOS that helps us manipulate files on our phones on the computer side -## (2)三方源工具安装 +## (2) Three-party source tool installation -- AppSync Unifield +- AppSync Unifield -AppSync Unifield是IOS上的插件工具,作用是帮助我们安装未经苹果签名的IPA,安装后可以安装未经苹果签名的软件,安装具体流程如下: -首先添加源:编辑->添加->输入cydia.angelxwind.net +The AppSync Unifield is a plug-in tool on IOS that helps us install an IPA that is not signed by Apple. After installation, the software that is not signed by Apple can be installed. The specific process is as follows: +Add source first: Edit->Add->Enter cydia.angelxwind.net ![16-2](20230404162.png) -然后进行插件安装:karen->插件->AppSync->安装->确认->重启 +Then install the plug-in: karen->Plug-ins->AppSync->Install->Confirm->Reboot + ![17](2023040417.png) - frida-server - 卸载历史版本 -``` -dpkg -l | grep frida 匹配已经安装的frida -dpkg -P re.frida.server卸载软件 +Uninstall Historical Version +```bash +dpkg -l | grep frida matches an already installed frida +dpkg -P re.frida.server uninstall software ``` -下载最新frida-server进行安装 -![18 (2)](2023040418.png) +Download the latest frida-server to install +![18(2)](2023040418.png) +```bash +dpkg -i frida_16.0.10_iphoneos-arm.deb ``` -dpkg -i frida_16.0.10_iphoneos-arm.deb -``` -# 4.Frida/Objection/任意版本安装切换/动态分析 +# 4.Frida/Objection/Any Version Installation Switch/Dynamic Analysis + +Here using r0env, with native pyenv supporting multi-version Frida/objection switching, here installing the latest version as a demo + +## (1) Install the latest frida -这里使用r0env,自带pyenv支持多版本Frida/objection切换,这里安装最新版本作为演示 -## (1)安装最新frida ``` pyenv install 3.9.5 pyenv global 3.9.5 pip install frida==16.0.5 ``` -这里安装的时候不挂代理会很慢,挂代理后遇到了一个错误 - ![19](2023040419.jpg) -错误原因是没有socks相关库 +It will be slow to install here without hanging up the agent. An error was encountered after hanging up the agent +![19](2023040419.jpg) +The error reason is that there are no socks related libraries ``` unset ALL_PROXY pip install pysocks ``` -再挂代理安装 +Remount Agent Installation ``` -export ALL_PROXY="socks://代理IP:port" +export ALL_PROXY="socks:// Proxy IP:port" pip install frida pip install frida-tools ``` -安装成功 -![20 (2)](2023040420.png) +Installation Successful -## (2)安装objection -``` +![20(2)](2023040420.png) + +## (2) Install objection +```bash pip install objection -objection -g 高考蜂背 explore -``` -![21 (2)](2023040421.png) -# 5.ios砸壳/IPA静态分析基本流程/IDAF5函数定位 -## (1)ios砸壳 -ios砸壳工具也有很多,这里我们推荐使用frida-ios-dump,下边是安装使用详细过程: +objection -g gaokao back explore ``` +![21(2)](2023040421.png) + +# 5.ios Shell Smashing / IPA Static Analysis Basic Process / IDAF5 Function Positioning +## (1)ios smashing shell +ios shell smashing tools are also available, here we recommend frida-ios-dump, below is the detailed installation and use process: + +```bash git clone https://github.com/AloneMonkey/frida-ios-dump cd frida-ios-dump/ apt-get install usbmuxd -pip install -r requirements.txt --upgrade +pip install -r requirements.txt —upgrade iproxy 2222 22 -./dump.py 高考蜂背 -``` -## (2)IPA静态分析基本流程及IDAF5函数定位 -因为本篇文章主题是环境搭建,因此这里仅演示简单流程 -``` -file 高考蜂背.ipa -unzip 高考蜂背.ipa -cd Payload/ -cd Gkfb.app/ -file * | grep -i mach -ida64 ./Gkfb -``` -经过frida-ios-dump砸壳后我们拿到了IPA包对其进行解压,并检索内部mach-o可执行文件,随后用ida分析文件。 -![22 (2)](2023040422.png) - -![checkfile](20230404checkfile.png) - -分析过程检索发现MD5格式函数,使用F5即可看到其伪代码 -![ida检测](20230404idacheck.png) - -![code](20230404code.png) - -# 6.linux中iphone手机信息获取:ID/名称/详情/签名/截屏/定位 -在linux中操作iPhone主要使用libimobiledevice库及依赖于它的一些开源工具,这里先下载安装该库 -``` -apt-get update -add-apt-repository ppa:pmcenery/ppa -apt-get install libimobiledevice-utils -``` -下面是该库的一些简单使用: -## (1)获取设备ID - -``` -idevice_id 获取当前连接设备UUID -``` -![UUID](20230404UUID.png) -## (2)获取设备名称 -``` -idevicename 查看当前连接设备名称 -``` -![name](20230404name.png) -## (3)获取屏幕截图 -``` -idevicescreenshot :从连接的设备获取屏幕截图 -``` -![截图](20230404screenshot.png) -## (4)设置虚拟定位 -``` -idevicesetlocation [OPTIONS] -- 根据经纬度模拟定位 -idevicesetlocation -- 35.10463 117.193626 山东枣庄 -``` -![虚拟定位](20230404position.png) -## (5)获取设备详情 -``` -ideviceinfo :查看手机设备详情列出全部相关信息 -``` -![详情](20230404detail.png) - -# 7.安装APP/IPA:安装/卸载/升级/备份/恢复APP -linux上对iPhone中软件操作这里是使用的依赖于libimobiledevice库的工具ideviceinstaller,在安装完libimobiledevice库后我们可以直接使用apt安装该工具 -``` -apt-get install ideviceinstaller -``` -## (1)安装软件 - -``` -ideviceinstaller -i xxx.ipa 安装软件 -``` -![安装](20230404install.png) -## (2)卸载软件 -``` -ideviceinstaller -U [bundleID]卸载应用 -``` -![卸载](20230404unload.png) -## (3)查看已安装应用 -``` -ideviceinstaller -l 查看安装软件 -``` - -![查看](20230404view.png) -## (4)其他 - -``` -ideviceinstaller -g [bundle_id] [path] 根据压缩包升级app -ideviceinstaller -o export -i [bundle_id] -o [PATH] 根据路径备份app -ideviceinstaller -r 从备份中恢复app -``` - -# 8.收发传输文件:usbmuxd OpenSSH=adb pull/push -usbmuxd是一个苹果提供的通信服务,用于建立通信通道,libimobiledevice库就是基于这个服务实现了许多功能,依赖该库的工具ifuse实现的文件系统挂载自然也是基于此服务,我们可以通过挂载文件系统实现手机端与电脑端的文件传输,从而实现android中adb push/pull的同样效果,我们也还可以通过OpenSSH服务实现文件传输,以下是二者具体实现: -## (1)基于usbmuxd实现adb pull/push -ifuse是依赖libimobiledevice库将iOS设备挂载到本地系统的开源工具,安装完库后直接使用apt安装 -``` -apt install ifuse -``` -``` -ifuse --root [挂载点]:越狱后将整个iphone文件系统挂载过来 -``` -![挂载1](20230404mount1.png) -将文件系统挂载过来后我们就可以进行自由的文件传输 - -## (2)基于OpenSSH实现adb pull/push -OpenSSH在手机中已经安装过,安装后自动开启ssh服务,我们就可以使用linux的scp命令进行文件传输 -从本地push到手机 -``` -scp -P 22 1.txt root@192.168.1.108:/ 这里写-P是为了使用一些非标准端口时指定端口 -``` -![push](20230404push.png) -从手机pull到本地 - -``` -scp -P 22 root@192.168.1.108:/2.txt ./ -``` -![pull](20230404pull.png) -# 9.远程文件管理:磁盘映射/电脑上浏览手机文件夹 -ifuse是依赖libimobiledevice库将iOS设备挂载到本地系统的开源工具,安装完库后直接使用apt安装 -``` -apt install ifuse -``` -## (1)挂载文件系统 -``` -ifuse --root [挂载点]:越狱后将整个iphone文件系统挂载过来 -``` - ![挂载2](20230404mount2.png) -## (2)取消挂载 -``` -fusermount -u [挂载点]:卸载挂载点 -``` -![取消挂载](20230404Unmount.png) -# 10.升降级和激活:恢复模式/固件升降级/手机激活 -(1)进入恢复模式 -``` -ideviceenterrecovery 让设备启动到恢复模式,iOS设备的恢复模式允许用户刷写、升级或还原设备的固件。 -``` -(2)固件升降级 - -``` -idevicerestore [OPTIONS] PATH 将PATH路径上的固件包安装到ios设备 - -u 指定设备ID - -l 使用最新可用固件 -``` -(3)手机激活 - -``` -ideviceactivation activate +./dump.py Gaokao Bei ``` -其中,activation_record_path是包含激活记录的文件路径。这个文件可以从其它iPhone上备份,或者从开发人员手中获得。运行后,libimobiledevice将会将激活记录写入到手机中,从而完成激活过程。激活成功后,你的iPhone将可以正常使用。 -《挑战不用macOS逆向iOS APP》系列的第一课环境搭建内容到这里就结束了,两百元成本的iOS APP逆向的基本环境已经搭建成功,后续会继续更新iOS App逆向的内容,需要购买设备和全套学习课程的可以私信vx:r0ysue 来下单。 +## (2) IPA static \ No newline at end of file diff --git a/Student/003/README.md b/Student/003/README.md index b74a736..05e9f10 100644 --- a/Student/003/README.md +++ b/Student/003/README.md @@ -1,20 +1,20 @@ [toc] -# Pixel1代平替:一加3手机刷入KaliNethunter解封完整Linux命令环境 +# Pixel1 Replacement: OnePlus 3 phones swipe into KaliNethunter to unpack full Linux command environment -# 意义 +# Meaning -大家知道在Linux上,我们想要对数据封包进行统计、定位、可视化、分析和转储等,有一系列的工具和软件。这些工具为我们进行网络数据封包的分析和处理提供了无与伦比的支持,可以说有这些工具的存在,没有抓不到的包,如果再结合hook技术,可以说没有解不开的协议。 +As you know, on the Linux, we want to do statistics, positioning, visualization, analysis and dump of data packets, there is a series of tools and software. These tools for our network data packet analysis and processing provides unparalleled support, we can say that there are these tools, there is no packet can not be caught, if combined with hook technology, there is no protocol that can not be unlocked. -工具虽好,但是它们的运行都需要一个完整的Linux环境,比如像jnettop/htop/nethogs等需要root权限,像iwlist/aircrack/HID等需要内核驱动支持、监听模式的打开等,Wireshark/Charles/BP会需要桌面环境的支持,在GUI中进行可视化分析。 +Although the tool is good, but they need a complete Linux environment to run, such as jnettop/htop/nethogs and so on need root authority, such as iwlist/aircrack/HID and so on need kernel driver support, monitoring mode open, Wireshark/Charles/BP will need the support of desktop environment, visual analysis in the GUI. -具体的内容在这篇文章中进行了详细的解释:[《来自高纬的对抗:替换安卓内核并解封Linux命令和环境》](https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g) +The details are explained in detail in this article: ["Confrontation from High Altitude: Replacing the Android Kernel and Unsealing Linux Commands and Environments"](https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g) -随着时间的推移,Nexus设备逐渐退出历史舞台,而Kali Nethunter却迟迟不能支持到Pixel设备;此时我们只能退而求其次,选择一加手机,来延续手机上刷入Kali Nethunter解封完整Linux环境的传统。 +Over time, the Nexus device gradually dropped out of the historical scene, while the Kali Nethunter was slow to support the Pixel device; now we have to settle for the next best option, to continue the tradition of swiping the phone with Kali Nethunter to unseal the entire Linux environment. -本篇基于一加3(t)手机,在性能上相当于Pixel一代手机。运行环境是安卓10。本文中用到的附件等位于百度盘中:链接:https://pan.baidu.com/s/1c01pB6JIY6a-25P-855dAA 提取码:e2gn +Based on the OnePlus 3(t) phone, this one is the performance equivalent of the Pixel generation. The operating environment is Android 10. The accessories used in this paper are located in Baidu Disk:Link:https://pan.baidu.com/s/1c01pB6JIY6a-25P-855dAAExtraction code:e2gn -该系列为学员优秀作品系列,附件apk、代码等位于我的项目中,大家可以自取: +This series is a good series of works for the students. The accessories, apk, code and so on, are in my project. You can choose from: [https://github.com/r0ysue/AndroidSecurityStudy](20230403https://github.com/r0ysue/AndroidSecurityStudy) @@ -22,113 +22,135 @@ # Intro -1. 为什么要搭建nethunter的环境 - + 工欲善其事,必先利其器.nethunter强大的工具支持,使得工作变得轻松 -2. 为什么使用oneplus3T - + oneplus3T这部机型,lineage,nethunter,twrp都有很好的支持 - + oneplus3T现在的价格很低 - + oneplus3T性能还不错,比nexus 5x的性能强悍很多 -3. 为什么写这个刷机文章 - 不仅仅是因为这个nethunter的强大和oneplus3t的性价比,更因为是刷机总是出现这样那样的问题,导致失败.经过反复几天测试,得出了一种比较轻松的方式.后面提供了这个刷机过程中用到的所有的工具包 -# 刷机前设置 -需要开启开发者模式,bootloader解锁,usb调试 -如果手机满足这些条件,可以直接进入刷入lineage -## 开启开发者模式 -系统版本号(点击多次),一般进入方式都是手机里面,设置->关于手机->版本号 +1. Why nethunter environments ++ You need to get ahead of your work when you're ready. With nethunter's powerful tools, work is easy +2. Why oneplus3T ++ oneplus3T This model, lineage, nethunter, twrp, is well supported ++ oneplus3T is now very cheap ++ oneplus3T performance is good, much better than the nexus 5x +3. Why write this brush article +Not only because of the power of the nethunter and oneplus3t price performance, but also because the brush always has such a problem, leading to failure. After several days of testing, a more relaxed approach was found. All toolkits used during this swipe are provided later + +# Pre-Press Set-up +Developer mode needs to be turned on, bootloader unlock, usb debug +If the phone meets these conditions, it can go straight into the lineage + +## Turn on developer mode +The system version number (clicked multiple times), the general way to access is inside the phone, set->about the phone->version number ![1](202304061.jpg) ![2](202304062.jpg) -## 开启usb调试和oem解锁许可 -+ 设置->系统->开发者选项 + +## Turn on usb debug and oem unlock licenses ++ Settings->System->Developer Options ![3](202304063.jpg) + ![4](202304064.jpg) -## 解锁OEM -1. 重启到bootloader -+ 使用adb命令 -``` - adb reboot bootloader +## Unlock OEM +1. Reboot to bootloader ++ Use the adb command + +```bash +adb reboot bootloader ``` -+ 手机关机,然后开机键+手机音量下键 ++ Turn on the phone, then On + Phone Volume Down ![5](202304065.png) ![6](202304066.jpg) -1. 解锁 -``` + +1. Unlock + +```bash fastboot oem unlock ``` + ![7](202304067.png) ![8](202304068.jpg) -手机出现这个界面以后,选择UNLOCK THE BOOTLOADER(音量上下键可以切换选项,电源键做选择) -等待解锁后自动重启 -注意: 解锁重启之后,完成基本设置之后,再次启动开发者模式和usb调试 -# 刷入lineage -nethunter官方提供的系统是android10的镜像包,需要输入一个android10的底包,这里选择lineage的系统 -## 刷入twrp -1. 重启手机,刷入twrp.img -``` + +Once the phone has this interface, choose UNLOCK THE BOOTLOADER +Automatically restart after waiting to unlock +Note: After unlocking the reboot, start developer mode and usb debugging again after you have completed basic set-up +# flush lineage +The system provided by nethunter is the mirror package of android10. We need to input a bottom package of android10. Here we choose the system of lineage +## Flush twrp +1. Restart your phone, swipe into twrp.img +```bash adb reboot bootloader -fastboot flash recovery recovery.img +fastboot flash recovery recovery.img ``` ![9](202304069.png) -1. 手机音量上下键,选择recovrey mode进入twrp的系统 +1. Phone volume up and down, select recovrey mode to enter the twrp system + ![11](2023040611.jpg) -## 清理数据 -1. 在twrp中,Wipe->Format Data->输入'yes'->成功之后,back到可以选择Advanced Wipe地方 + +## Clean up data +1. In twrp, Wipe->Format Data->Enter 'yes'->After success, back to a place where you can select Advanced Wipe ![12](2023040612.jpg) ![10](2023040610.jpg) -1. Advanced Wipe->勾选全部 + +1. Advanced Wipe->Check All + ![15](2023040615.jpg) -## 输入防护墙的包 +## Inbound Wall Bag + 1. Advanced->ADB Sideload ![17](2023040617.jpg) -2. 输入命令 -``` -adb sideload oxygenos-9.0.6-bl-km-5.0.8-firmware-3.zip +2. Enter commands + +```bash +adb sideload oxygenos-9.0.6-bl-km-5.0.8-firmware-3.zip ``` ![40](2023040640.png) ![41](2023040641.jpg) -## 刷入lineage的包 +## Package flushed into lineage 1. Advanced->ADB Sideload -2. 输入如下命令 -``` -adb sideload lineage-17.1-20210215-nightly-oneplus3-signed.zip +2. Enter the following command + +```bash +adb sideload lineage-17.1-20210215-nightly-oneplus3-signed.zip ``` ![42](2023040642.png) ![43](2023040643.jpg) -## 刷入magisk +## Flush magisk 1. Advanced->ADB Sideload -2. 输入下面命令 -``` +2. Enter the following command +```bash adb sideload app-release.zip ``` + ![44](2023040644.png) ![45](2023040645.jpg) -3. 重启 -4. 开启开发者模式,usb调试 -5. 使用下面命令安装magisk的app -``` -adb install app-release.apk + +3. Restart +4. Turn on developer mode, usb debugging +5. Use the following command to install the magisk app +" +```bash +adb install app-release.apk ``` + ![46](2023040646.png) ![47](2023040647.jpg) -6. 重启,安装magisk之后,第一次需要重启,让配置生效 +6. Reboot, After installing magisk, reboot is required for configuration to take effect for the first time ![48](2023040648.jpg) -7. magisk如上已经安装成功 +7. magisk has been successfully installed as above -# 使用Magisk刷入nethunter +# Flush nethunter using Magisk -1. 传入nethunter的包 -``` +1. Package Incoming to nethunter + +```bash adb push kalihunter.zip /sdcard/Download ``` ![28](2023040628.png) -2. 选择 模块->本地,选择,nethunter的包,等待结束,重启 +2. Select Module -> Local, Select, Package for nethunter, Wait to Finish, Restart ![29](2023040629.jpg) -# nethunter初始化设置 -1. 重启后,连wifi,输入命令,设置时间 -``` +# nethunter initialization settings +1. After reboot, connect to wifi, enter command, set time + +```bash settings put global captive_portal_http_url https://www.google.cn/generate_204 settings put global captive_portal_https_url https://www.google.cn/generate_204 settings put global ntp_server 1.hk.pool.ntp.org @@ -136,12 +158,13 @@ reboot ``` ![30](2023040630.png) -2. 初始化,点击Kali Chroot Manager开始初始化 +2. Initialize, click Kali Chroot Manager to start initializing + ![31](2023040631.jpg) ![32](2023040632.jpg) -3. 命令窗口可以使用,成功 +3. The command window is available, successful ![33](2023040633.jpg) -# 刷入成功! +# Flush successful! -可以开启的逆向之旅了!后续还会更新OnePlus系列的Kali Nethunter刷入教程,敬请期待! 想要直接购买刷好的设备,直接私信肉丝即可,vx:r0ysue 。卖设备还卖教程噢!助您升职加薪! \ No newline at end of file +It's a reverse journey that can be started! Look forward to updating the OnePlus Family's Kali Nethunter swipe-through tutorial as well! vx:r0ysue. Selling equipment and tutorials. Help you get promoted and get a raise! \ No newline at end of file diff --git a/Student/004/README.md b/Student/004/README.md index 9a9b2ce..3da865d 100644 --- a/Student/004/README.md +++ b/Student/004/README.md @@ -1,168 +1,173 @@ [toc] -# Pixel4平替:一加7刷NetHunter(android10) 指南 +# Pixel4 Tile: OnePlus 7-brush NetHunter (android10) guide ## Intro -## 为什么要用OnePlus7这个设备 -1. Kali Nethunter官方支持一加7 -2. OnePlus7是属于一加前几代机型,价格不高,性能上相当于Pixel4代 -3. OnePlus7拥有高通9008的救砖模式,这个机器不管怎么折腾都可以恢复. ->> 救砖模式是高通CPU提供的特性 -4. OnePlus7在xda上面拥有众多的三方rom包,随便玩 +## Why use the OnePlus7 device +1. Kali Nethunter officially supports OnePlus 7 +2. The OnePlus7 is an OnePlus previous generation model that is inexpensive and performs as well as the Pixel4 generation +3. The OnePlus7 has the Qualcomm 9008 brick-rescue model, and the machine can be restored no matter how messy. +>> Brick Rescue mode is a feature provided by Qualcomm CPU +4. OnePlus7 has numerous tripartite rom packages on xda and plays with it -本篇基于一加7手机,在性能上相当于Pixel四代手机。运行环境是安卓10。本文中用到的附件等位于百度盘中:链接:https://pan.baidu.com/s/1gtmCfqfQmEX5JvMZtvUH7w 提取码:euuo +Based on the OnePlus 7, this is the performance equivalent of four generations of Pixel phones. The operating environment is Android 10. The accessories used in this paper are located in Baidu Disk:Link:https://pan.baidu.com/s/1gtmCfqfQmEX5JvMZtvUH7wExtraction code:euuo -该系列为学员优秀作品系列,附件apk、代码等位于我的项目中,大家可以自取: +This series is a good series of works for the students. The accessories, apk, code and so on, are in my project. You can choose from: [https://github.com/r0ysue/AndroidSecurityStudy](20230403https://github.com/r0ysue/AndroidSecurityStudy) -## 刷机是玄学,怎么做才科学 -1. 刷机需要测试各个版本的工具,很多时候不是你步骤错了,可能仅仅是使用的工具包版本不对 -方案:提供下面步骤用到的所有工具包,在附件里面 -2. 系统版本,OnePlus的系统版本众多,会影响结果,可能bootloader都解锁不了(这里有个伤心的故事) -方案:提供刷机固件包,从头开始,这样肯定行 +## It's a myth how to make a brush. It's a science +1. The brush requires testing the various versions of the tool, and it is not always that you have taken the wrong steps, it may simply be that the wrong version of the kit is being used +Scenario: Provide all toolkits used in the following steps, inside the enclosure +2. System Version, There are so many system versions of OnePlus that it will affect the results that the bootloader may not be unlocked (here's a sad story) +Scenario: Provide a Brush Firmware Pack, Start Over, This Is Sure -## 开始刷机 -### 下载安装高通驱动 +## Start Swiping +### Download and Install Qualcomm Driver -1. 安装高通9008驱动,驱动名称:"QDLoader_HS-USB_Driver_64bit_Setup.exe" -+ 安装 +1. Install the Qualcomm 9008 driver named "QDLoader_HS-USB_Driver_64bit_Setup.exe" ++ Install ![11](2023041811.png) ![12](2023041812.png) ![13](2023041813.png) ![14](2023041814.png) ![15](2023041815.png) -+ 用管理员权限打开cmd,输入命令开启测试模式,然后重启电脑 -``` ++ Open cmd with administrator privileges, enter the command to turn on test mode, and then restart the computer +```powershell bcdedit /set testsigning on ``` -+ 检查测试模式是否打开,重启后电脑桌面右下角出现下图所示就是成功,可继续下一步 ++ Check if the test mode is turned on and the following image in the lower right corner of the PC's desktop after reboot indicates success, to continue to the next step ![19](2023041819.png) -### 刷入固件包 - -1. 电脑解压固件包guacamoleb_14_P.32_210127.zip, - ![20](2023041820.png) -2. 启动软件MsmDownloadTool V4.0.exe - ![21](2023041821.png) -3. 点击start,等待手机进入edl模式 - ![22](2023041822.png) -4. 手机进入edl模式,MsmDownloadTool会自动输入android 10系统 -+ 方式1:手机关机,数据线连接电脑,然后同时按住音量上键和下键 -+ 方式2: 如果手机还可以开机,还可以连接adb -``` +### Flash in the firmware package + +1. The PC unpacks the firmware package guacamoleb_14_P.32_210127.zip. +![20](2023041820.png) +2. Boot the software MsmDownloadTool V4.0.exe +![21](2023041821.png) +3. Tap start and wait for your phone to enter edl mode +![22](2023041822.png) +4. The phone enters edl mode, and MsmDownloadTool automatically enters the android 10 system ++ Mode 1: Turn the phone on, connect the cable to the PC, and press and hold the volume up and down keys at the same time ++ Mode 2: If your phone is still on, you can also connect to adb + +```powershell adb reboot edl ``` + ![23](2023041823.png) -5. 等待系统刷入,进入简单设置下 + +5. Wait for the system to flush and enter the Simple Set-up ![24](2023041824.jpg) -### 解锁手机bootloader +### Unlock your phone bootloader -1. 打开手机开发者模式, 开启adb调试 +1. Turn on Phone Developer Mode and turn on adb debugging ![2](202304182.jpg) -2. 开启oem解锁 +2. Open oem unlock ![1](202304181.jpg) -3. 使用命令,进入fastboot模式 -``` +3. Enter fastboot mode using the command +```powershell adb reboot bootloader ``` -4. 使用命令解锁 -``` +4. Unlock with command +```powershell fastboot oem unlock ``` - ![24](2023041824.png) +![24](2023041824.png) -5. 选择"UNLOCK THE BOOTLOADER",等待重启,进入,重新设置usb调试和开发者模式 +5. Select "UNLOCK THE BOOTLOADER", wait for reboot, enter, reset usb debug and developer mode - ![18](2023041818.jpg) +![18](2023041818.jpg) -### 刷入twrp +### Flush twrp -1. 重启手机到fastboot模式 -``` +1. Restart your phone to fastboot mode +```powershell adb reboot bootloader ``` -2. 刷入twrp -``` +2. Flush twrp +```powershell fastboot boot twrp.img ``` ![25](2023041825.png) -3. 这里安装完就直接进入revovery模式,twrp系统中, -4. 操作界面选择 Advanced->Flash Current TWRP +3. Once installed, go straight to revovery mode, twrp. +4. Operational Interface Select Advanced->Flash Current TWRP ![26](2023041826.jpg) -4. 操作界面选择 Wipe->Format Data->输入yes +4. Operational Interface Select Wipe->Format Data->Enter yes ![27](2023041827.jpg) -5. 这时候不要重启 +5. Do not restart at this time -### 刷入Magisk +### Flush Magisk 1. Advanced->ADB Sideload->Swipe to Start Sideload ![28](2023041828.jpg) ![29](2023041829.jpg) ![30](2023041830.jpg) -2. 输入命令,刷入Magisk.zip +2. Enter the command to flush the Magisk.zip ![32](2023041832.png) ![31](2023041831.jpg) -### 刷入Disable_Dm-Verity_ForceEncrypt_11.02.2020.zip +### Flush Disable_Dm-Verity_ForceEncrypt_11.02.2020.zip 1. Advanced->ADB Sideload->Swipe to Start Sideload -2. 刷入Disable_Dm-Verity_ForceEncrypt_11.02.2020.zip,出现Select Option的时候,按音量+键 +2. Press the Volume + key when the Select Option appears by swiping the Disable_Dm-Verity_ForceEncrypt_11.02.2020.zip ![33](2023041833.png) ![34](2023041834.png) -3. 重启,进入设置,其中有个设置选"无" +3. Restart and enter the settings, one of which is selected as None ![35](2023041835.jpg) -### 刷入nethunter +### Flush nethunter -1. 重启到fastboot模式,然后音量键-进入recovery模式,重新进入twrp +1. Reboot to fastboot mode, then volume key - enter recovery mode, re-enter twrp ![36](2023041836.jpg) 2. Advanced->ADB Sideload->Swipe to Start Sideload -3. 输入命令,刷入"kernel-nethunter-2021.3-oneplus7-oos-ten.zip" -``` -adb sideload kernel-nethunter-2021.3-oneplus7-oos-ten.zip +3. Enter the command to flush the "kernel-nethunter-2021.3-oneplus7-oos-ten.zip" + +```powershell +adb sideload kernel-nethunter-2021.3-oneplus7-oos-ten.zip ``` 4. Advanced->ADB Sideload->Swipe to Start Sideload -5. 输入命令,输入"nethunter-2022.1-oneplus7-oos-ten-kalifs-full.zip " -``` -adb sideload nethunter-2022.1-oneplus7-oos-ten-kalifs-full.zip +5. Enter the command, enter "nethunter-2022.1-oneplus7-oos-ten-kalifs-full.zip" +```powershell +adb sideload nethunter-2022.1-oneplus7-oos-ten-kalifs-full.zip ``` ![38](2023041838.png) ![39](2023041839.jpg) ![40](2023041840.jpg) ![41](2023041841.jpg) -### 安装一个magisk -1. 输入命令安装magisk.app -``` +### Install a magisk +1. Enter the command to install the magisk.app +```powershell adb install magisk.app ``` ![44](2023041844.png) ![45](2023041845.jpg) -### 初始化Nethunter -1. 联wifi,修改时间 -2. 打开图中红圈得nethunter - ![46](2023041846.jpg) -3. 点击kali Chroot Manager - ![47](2023041847.jpg) -4. 点击START KALI CHROOT - ![48](2023041848.jpg) -5. 成功 - ![49](2023041849.jpg) -6. 验证,打开命令行窗口,输入命令 - ``` - apt update - ``` - ![50](2023041850.jpg) +### Initialize Nethunter +1. wifi, Modified +2. Open the red circle in the figure to get nethunter +![46](2023041846.jpg) +3. Tap kali Chroot Manager +![47](2023041847.jpg) +4. Tap START KALI CHROOT +![48](2023041848.jpg) +5. Success +![49](2023041849.jpg) +6. Verify, open a command line window, and enter a command + +```bash +apt update +``` +![50](2023041850.jpg) \ No newline at end of file diff --git a/Student/005/README.md b/Student/005/README.md index 03b5e4b..cb02539 100644 --- a/Student/005/README.md +++ b/Student/005/README.md @@ -1,101 +1,101 @@ [toc] -# 《ObjC语法、iOS应用开发、及Objection自动化hook入门》 +# ObjC Syntax, iOS Application Development, and Getting Started with Objection Automation hook # Intro -本篇文章是《挑战不用macOS逆向iOS APP》系列的第二课iOS逆向基础知识主要为了了解在iOS APP逆向过程中一些常见知识,具体内容如下: +This article is the second lesson of the Challenge Reverse iOS APP without macOS series, iOS Reverse Basics, which is intended to learn some common knowledge about the reverse process of iOS APP. -- ObjC基础语法与消息传递 -- iOS开发简单了解 -- iOS签名相关了解 - - 为什么企业签名可以卖钱? - - 可以白嫖签名吗?不花钱那种? - - 网上下载的IPA能长期无痛使用吗? - - 第三方软件商店能用吗?感受如何? - - AltStore第三方软件商店体验实操 -- Objection自动化逆向与hook -- Objection破解简单CrackMe +- ObjC underlying syntax and messaging +- iOS Development Simple Understanding +- iOS Signature Related Understanding +- Why do business signatures sell? +- Can I sign with my client? Without paying for that? +- Can IPAs downloaded online be used painlessly for a long time? +- Is third-party software store available? How you feeling? +- AltStore 3rd party software store experience +- Objection Automation Converse and hook +- Objection Break Simple CrackMe -该系列为学员优秀作品系列,附件apk、代码等位于我的项目中,大家可以自取: +This series is a good series of works for the students. The accessories, apk, code and so on, are in my project. You can choose from: [https://github.com/r0ysue/AndroidSecurityStudy](https://github.com/r0ysue/AndroidSecurityStudy) -# 1.Objective-C 基础语法 与消息传递 +# 1.Objective-C Base Syntax and Messaging -我们通过最简单的"Hello World!"源码来学习ObjC的基础语法知识。 +We've got the simplest Hello World!source code to learn the basic grammar of ObjC. -## (1)类的声明与实现 +## (1) class declaration and implementation ![1](./image/1.png) ```ObjectiveC -声明:所有的类都继承自NSObject类 +Declaration: All classes inherit from the NSObject class @interface test : NSObject { } @end -实现: +Implementation: #import "test.h" @implementation test @end ``` -## (2)类方法和实例方法的声明与实现 +## (2) Declaration and Implementation of Class and Instance Methods ![2](./image/2.png) -### 1类方法 +### (1) Class method ```ObjectiveC -声明: -+(void) class_method; +Declaration: ++(void)class_method; -实现: -+(void) class_method; +Implementation: ++(void)class_method; { - NSLog(@"This is class_method"); +NSLog(@"This is class_method"); } ``` -### 2实例方法 +### (2) Instance method ```ObjectiveC -声明: --(void) instance_method; +Declaration: +-(void)instance_method; -实现: --(void) instance_method; +Implementation: +-(void)instance_method; { - NSLog(@"This is instance_method"); - test *test1 = [test new]; +NSLog(@"This is instance_method"); +test *test1 = [test new]; } ``` -## (3)变量与属性 +## (3) Variables and Attributes -Objective- C类中的变量默认是private权限的,对象无法直接访问,否则会报错,属性则是使用@property声明,声明的属性可以选择是否自动生成getter() 和setter()方法 +Variables in the Objective- C class default to private permissions, and objects are not directly accessible, or an error is reported, and attributes are declared using @property, with the option to automatically generate the getter() and setter() methods ```ObjectiveC -声明: +Declaration: { - int num1; - @public - int num2; +int num1; +@public +int num2; } -@property(assign) int num1;//(assign) 括号内是用来写属性特性 -@property(assign) int num2; +@property(assign)int num1;//(assign) Property attributes are written in parentheses +@property(assign)int num2; -实现: +Implementation: @synthesize num1; @synthesize num2; ``` -## (4)消息传递 +## (4) Messaging -C++里类别与方法的关系严格清楚,一个方法必定属于一个类别,而且在编译时(compile time)就已经紧密绑定,不可能调用一个不存在类别里的方法。但在Objective-C,类别与消息的关系比较松散,调用方法视为对对象发送消息,所有方法都被视为对消息的回应。所有消息处理直到运行时(runtime)才会动态决定,并交由类别自行决定如何处理收到的消息。 +The relation between class and method in C++ is very clear. A method must belong to a class, and it is bound tightly at compile time. It is impossible to call a method that does not exist in class. However, in Objective-C, the relationship between the class and the message is loose, the calling method is considered to send the message to the object, and all methods are considered to respond to the message. All message processing is not dynamically determined until run time, and is left to the category to decide how to handle incoming messages. ```ObjectiveC [test class_method]; @@ -103,128 +103,128 @@ test *test1 = [test new]; [test1 instance_method]; ``` -完整类代码 +Full Class Code ```ObjectiveC -类声明: +Class declaration: @interface test : NSObject{ int num1; @public int num2; } -@property(assign) int num1; -@property(assign) int num2; -+(void) class_method; --(void) instance_method; +@property(assign)int num1; +@property(assign)int num2; ++(void)class_method; +-(void)instance_method; @end -类实现: -#import "test.h" -@implementation test -@synthesize num1; -@synthesize num2; -+(void) class_method; -{ - NSLog(@"This is class_method"); -} --(void) instance_method; -{ - NSLog(@"This is instance_method"); - test *test1 = [test new]; +Class implementation: + #import "test.h" + @implementation test + @synthesize num1; + @synthesize num2; + +(void)class_method; + { + NSLog(@"This is class_method"); + } + -(void)instance_method; + { + NSLog(@"This is instance_method"); + test *test1 = [test new]; } @end -类实例化及方法调用: -#import -#import "test.h" -int main(int argc, const char * argv[]) { - @autoreleasepool { - // insert code here... - NSLog(@"Hello, World!"); - [test class_method]; - - test *test1 = [test new]; - [test1 instance_method]; - - } - return 0; +Class instantiation and method invocation: + #import + #import "test.h" + int main(int argc, const char * argv[]){ + @autoreleasepool { + // insert code here... + NSLog(@"Hello, World!"); + [test class_method]; + + test *test1 = [test new]; + [test1 instance_method]; + + } + return 0; } ``` -# 2.iOS开发简述 +# A Brief Description of 2.iOS Development -## (1)ios架构 +## (1)ios architecture ![3](./image/3.png) -- Application Layer(应用层)、 -- Cocoa Touch Layer(触摸层)、 -- Media Layer (媒体层)、 -- Core Services Layer(核心服务层)、 -- Core OS Layer (核心系统操作层)、 -- The Kernel and Device Drivers layer(内核和驱动层) +- Application Layer, +- Cocoa Touch Layer, +- Media Layer, +- Core Services Layer, +- Core OS Layer, +- The Kernel and Device Drivers layer (kernel and driver layer) -## (2)系统架构各层功能模块视图: +## (2) System architecture of each layer function module view: ![4](./image/4.png) -## (3)使用Xcode建立简单MVC程序 +##(3) Use Xcode to establish a simple MVC program -- Xcode中的MVC简述 +- MVC Brief in Xcode - - 模型是应用程序的数据。 - - 视图是用户看见的界面,包含在Interface Builder 中构建的各种UI组件。 - - 控制器是将模型和视图元素连接在一起的逻辑单元,处理用户输入和UI交互。 +- The model is the application's data. +- The view is the user-visible interface containing the various UI components built in Interface Builder. +- Controllers are logical units that connect model and view elements together, handling user input and UI interaction. -- Interface Builder 简述: +- Interface Builder Brief: - Interface Builder是一个控件工具箱,开发者只需要从工具箱中简单地向窗口或菜单中拖曳控件即可完成界面的设计。然后,用连线将 控件可以提供的"动作"(Action)、 控件对象分别和应用程序代码中对象"方法"(Method)、对象"接 口"( Outlet)连接起来,就完成了整个创建工作 。 +Interface Builder is a control toolbox, developers only need to drag the controls from the toolbox to the window or menu to complete the interface design. Then, we connect the "action" and the object of the control with the "method" and the "interface" of the object in the application code. -### 1打开xcode新建项目,这里xcode版本为14 +### (1) Open xcode New Project, where xcode version 14 ![5](./image/5.png) -### 2选择iOS APP 模版 +### (2) Select iOS APP Template ![6](./image/6.png) -### 3填入项目相关信息 +### (3) Fill in the project-related information ![7](./image/7.png) -### 4保存后生成项目可以看到自动生成的项目模版文件 +### (4) Build Project After Save You Can See Automatically Generated Project Template Files ![8](./image/8.png) -- AppDelegate: AppDelegate 文件是应用程序的委托,负责响应系统和应用程序事件,处理应用程序生命周期及对应用程序级别的配置与操作。 -- SceneDelegate: SceneDelegate文件是iOS13及以上版本新增的文件,负责管理应用程序中的场景,每个场景都代表着应用程序中的一个窗口和对应视图层次。 -- ViewController: MVC模式中的Controller部分用于管理应用程序的界面和处理用户的交互操作,对应ViewController类继承自UIViewController类是UIKit框架的一部分,是构建iOS应用程序的核心组件和类。 -- storyboard:故事板文件其中LaunchScreen是应用程序启动画面,我们不用管他,Main是我们的主要故事板,包含所有UI元素及视图控制器。 -- Assets.xcassets:资源文件夹。 -- Info.plist:应用程序配置文件。 +- AppDelegate: The AppDelegate file is the delegation of applications to respond to system and application events, handle the application lifecycle, and configure and operate at the application level. +- SceneDelegate: The SceneDelegate file is a new file from iOS13 and above that manages scenes in the application, each representing a window and corresponding view hierarchy in the application. +- ViewController: The Controller part of the MVC pattern is used to manage the interface of the application program and deal with the interaction of the user. The corresponding ViewController class inherited from the UIViewController class is part of the UIKit framework, is the core component and class of building iOS application program. +- storyboard: Storyboard File Where LaunchScreen is the application launch screen, we don't mind him, Main is our main storyboard, containing all the UI elements and the view controller. +- Assets.xcassets: Resource folder. +- Info.plist: The application configuration file. -### 5设计界面 +### (5) Design Interface -打开Main.storyboard故事板,箭头指向为主视图文件中前两个对象分别是用户输入区域(文本框)和输出(标签), 而第 3个对象(按钮)触发代码 中的操作,以便将标签的内容设置为文本框的内容 +Opening the Main.storyboard storyboard, the arrow points to the first two objects in the main view file, the user input area (text box) and the output (label), while the third object (button) triggers the action in the code to set the content of the label to that of the text box ![9](./image/9.png) -### 6使用控件 +### (6) Use controls -使用Interface Builder编辑器(快捷键Shift+command+L)直接拉取控件到页面上,这里我们拉取一个文本框(UITextField),一个标签(UILabel),一个按钮(UIButton)共三个组件对象,其中前两个对象分别是用户输入区域(文本框)和输出(标签), 而第 3个对象(按钮)触发代码 中的操作,以便将标签的内容设置为文本框的内容。设置完组件后设置view对应class为ViewController +Using the Interface Builder editor (shortcut key Shift+command+L), we pull the control directly to the page. Here we pull a text box (UITextField), a label (UILabel), and a button (UIButton). There are three component objects, the first two objects are the user input area (text box) and the output (label), and the third object (button) triggers the operation in the code, so that the content of the label is set to the content of the text box. Set the view corresponding class to ViewController when the component is set ![10](./image/10.png) -### 7创建并连接输出口和操作 +### (7) Create and connect outlets and operations -IBAction与IBOutlet简介: +Introduction to IBAction and IBOutlet: -- IBAction:从返回值角度看,作用相当于void,只有返回值声明为IBAction的方法,才能跟storyboard中控件连线 -- IBOutlet:只有声明为IBOutlet的属性,才能跟storyboard中的控件进行连线 +- IBAction: From a return value perspective, acts like void, and only methods whose return value is declared IBAction can be connected to controls in storyboard +- IBOutlet: Only properties declared as IBOutlet can be connected to controls in storyboard -在ViewController类中声明两个IBOutlet属性对应UILabel和UITextField两个输入输出对象,声明一个返回值为IBAction类型的方法对应按钮点击事件,同时在ViewController文件中实现,可以看到,当对象和方法声明后就出现三个圆圈等待与storyboard绑定 +In the ViewController class declare two IBOutlet properties corresponding to UILabel and UITextField two input and output objects, declare a method with return value of IBAction type corresponding to the button click event, and at the same time in the ViewController file implementation, you can see, when the object and method declared three circles waiting to be bound with storyboard ![11](./image/11.png) @@ -232,8 +232,8 @@ IBAction与IBOutlet简介: #import @interface ViewController : UIViewController -@property (strong, nonatomic )IBOutlet UILabel *userOutput; -@property (strong, nonatomic )IBOutlet UITextField *userInput; +@property(strong, nonatomic)IBOutlet UILabel *userOutput; +@property(strong, nonatomic)IBOutlet UITextField *userInput; -(IBAction)setOutput:(id)sender; @end @@ -241,418 +241,33 @@ IBAction与IBOutlet简介: @implementation ViewController @synthesize userOutput; @synthesize userInput; -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. - self.userOutput.text=self.userInput.text; +-(void)viewDidLoad { +[super viewDidLoad]; +// Do any additional setup after loading the view. +self.userOutput.text=self.userInput.text; } @end ``` -### 8绑定 +### (8) Binding -直接拖拉进行空间与属性和方法的绑定 +Drag directly to bind space to properties and methods ![11](./image/11.png) -### 9设置启动视图 +### (9) Set Start-up View ![12](./image/12.png) -启动项目发现成功运行 +Boot Project Discovery ran successfully ![13](./image/13.png) -### 10项目启动流程简单分析: +### (10) Simple analysis of project initiation process: -main.m ->找到AppDelegate文件 +main.m -> Locate the AppDelegate file ![14](./image/14.png) -AppDelegate类根据打包根据配置文件找到启动故事板 - -![15](./image/15.png) - -在故事板找到箭头指向的视图控制器->加载视图控制器内的视图view展示 - -![16](./image/16.png) - -## (4)demo打包签名生成IPA - -Xcode中打包签名需要生成一个开发者证书,开发者证书需要开发正账号生成,以下为开发者账号的从无到有申请过程: - -1打开[官网](appleid.apple.com)注册Apple ID - -![17](./image/17.png) - -2填写相关信息 - -![18](./image/18.png) - -3输入验证码 - -![19](./image/19.png) - -![20](./image/20.png) - -4同意条款 - -![21](./image/21.png) - -5Apple ID 注册成功 - -![22](./image/22.png) - -到这里Apple ID 注册成功了但是我们还需要将其申请为开发者,这里申请开发者对于iOS版本有要求,而且不同机型需求还不一样,第一次使用iphone6iOS版本太低需要14以上,第二次使用iphone SE 要求版本在15以上。 - -6申请开发者账号 - -a.登录apple id 打开双重验证 - -![23](./image/23.png) - -b.在App store 下载Apple Developer进行注册 - -![24](./image/24.png) - -c.激活触控ID或打开密码 - -![24](./image/24.png) - -d.换了几次设备才可以填写信息进行申请 - -![26](./image/26.png) - -这里申请的话看网上说是要等一个月才可以审核通过。 - -7打包生成IPA(这里因为开发者账号没有审核通过无法生成证书,也没办法添加设备,所以先欠着后续补) - -### (5)IPA文件结构分析 - -查看文件格式可以发现其实ipa也是zip格式的压缩包 - -``` -file bilibili.ipa -``` - -![27](./image/27.png) - -对其进行解压发现以下目录,其中payload就是存放主要代码的目录,里边的app后缀文件夹就是我们主要代码目录,而且如果想要对一个ipa进行重打包的话就可以将其app文件夹抠出来进行压缩,然后进行签名 - -``` -unzip bilibili.ipa -``` - -![28](./image/28.png) - -``` -ls allit 查看app文件夹内文件 -``` - -查看app文件夹内文件可以发现许多资源文件,框架文件及.plist后缀的配置文件.pem后缀的证书文件等 - -![29](./image/29.png) - -这里我们主要把其中的二进制执行文件找出来,mac中可执行文件格式为Mac-O 类似于windows中的PE,linux中的elf,Android中的dex是整个程序的运行代码内容编译生成的二进制文件. - -``` -file * |grep -i mach -``` - -![33](./image/33.png) - -同时二进制文件肯定也是最大的,因此我们也可以通过文件大小寻找可执行文件 - -查看这个文件有多大 - -``` -du -h bilibili-universal -``` - -![30](./image/30.png) - -查看当前目录下最大的是个目录或文件 - -``` -du -a |sort -n -r |head -n 10 -``` - -![31](./image/31.png) - -找到代码文件后我们可以通过mac自带的otool命令查看文件信息,通过其crypt id参数判断其是否加壳值为1为加壳 - -``` -otool -l bili-universal|grep -i crypt -``` - -![32](./image/32.png) - -# 3.iOS签名相关了解 - -## (1).iOS签名原理 - -![55](./image/55.png) - -1、在你的 Mac 开发机器生成一对公私钥,这里称为公钥L,私钥L。L:Local -2、苹果自己有固定的一对公私钥,跟上面 AppStore 例子一样,私钥在苹果后台,公钥在每个 iOS 设备上。这里称为公钥A,私钥A。A:Apple -3、把公钥 L 传到苹果后台,用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名,把这份数据称为证书。 -4、在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第三步得到的证书一起打包进 APP 里,安装到手机上。 -5、在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证证书的数字签名是否正确。 -6、验证证书后确保了公钥 L 是苹果认证过的,再用公钥 L 去验证 APP 的签名,这里就间接验证了这个 APP 安装行为是否经过苹果官方允许。(这里只验证安装行为,不验证APP 是否被改动,因为开发阶段 APP 内容总是不断变化的,苹果不需要管。) - -## (2).不同账号区别 - -![56](./image/56.png) - -1这张图我们可以清晰的看到不同账号的用途及权限,根据这里不同账号生成的不同证书在打包时能够选择不同的打包方式,这里由于申请的开发者账号没有审核完成,后续补充证书生成及项目打包方式选择。 - -2查看应用签名 这里使用命令查看时要注意不是直接查看ipa文件而是其解压后payload目录下的app后缀文件夹 - -``` -codesign -vv -d example.app -``` - -![57](./image/57.png) - -## (3).签名相关问题解释 - -- 为什么企业签名可以卖钱? - -企业签名是有限的,是为了方便企业在没有通过App Store 审核的情况下在企业内部分发应用,属于企业的资源,而且申请时也是需要钱的,因此可以卖钱。 - -- 可以白嫖签名吗?不花钱那种? - -可以可以一直使用个人账号免费的,但有效期只有七天,而且设备数量及应用包数量等都有限制。 - -- 网上下载的IPA能长期无痛使用吗? - -可以,我么可以通过工具AltStore 进行个人签每七天签一次延长时间。 - -- 第三方软件商店能用吗?感受如何? - -可以正常使用,而且其实爱思应用商店的应用有些就是正版应用 - -- 这里补充一点,越狱后手机安装了AppSync Unified插件其实就绕过了app应用安装签名检查了 - -## (4)AltStore工具实操 - -1下载[iTunes](https://www.apple.com/itunes/download/win64),[iCloud](https://updates.cdn-apple.com/2020/windows/001-39935-20200911-1A70AA56-F448-11EA-8CC0-99D41950005E/iCloudSetup.exe),[Altstore](https://cdn.altstore.io/file/altstore/altinstaller.zip)并安装 - -![34](./image/34.png) - -2打开iTunes点击设备,如果没出现就登陆apple Id - -![35](./image/35.png) - -3打开wifi同步同时保证手机及电脑在同一网段,安装AltStore - -![36](./image/36.png) - -4输入你要设置的签名ID,这里我的apple id 其实还没有申请开发者账号不过也没事就是后边无法签名,而且这里要注意如果你的apple id 是手机号的话记得在前面加86 - -![37](./image/37.png) - -5信任你的Apple ID - -![38](./image/38.png) - -6使用AltStore安装ipa并对其签名 - -![39](./image/39.png) - -# 4.Objection自动化逆向与hook简述 - -1查看包路径,缓存路径,文件路径,lib路径 - -``` -env -``` - -![41](./image/41.png) - -``` -evaluate 自己写一段js代码加载进来 -``` - -2查看当前注入的包的路径 - -``` -pwd 当前注入的包的路径 -``` - -![42](./image/42.png) - -3列出内存中模块 - -我们只关心自己的app(containners)中的模块 其他是系统或组件模块,而且这里也可以直接看到我们应用的基址 - -``` -memory list modules -``` - -![43](./image/43.png) - -4列出导出表 - - 这里可以找到 do_it地址 拿这个地址去减去基址(包模块地址)得到函数相对地址 ida64 UnCrackable1.ipa 用ida看一下发现do_it函数是基址+相对地址 - -``` -memory list exports [包名] -``` - -![44](./image/44.png) - -5获取当前应用存储的cookies - -``` -ios cookies get --json -``` - -6在堆上找一个类的实例化内存对象把内存地址打印出来 - -``` -iOS heap search instances UILabel -``` - -![45](./image/45.png) - -7打印域中变量值 这里可以得到 结果 - -``` -ios heap execute 地址 text --return-string 打印域中变量值 这里可以得到 结果 -``` - -![45](./image/46.png) - -``` -ios heap evaluate --inline 自己写OC代码 -``` - -8查看二进制文件信息,查看是否加密判断是否加壳 - -``` -ios info binary -``` - -![47](./image/47.png) - -9dump存储的密码 - -``` -ios keychain -``` - -10hook 一些加解密算法的库 - -``` -ios monitor crypto -``` - -11查看是否有凭据存储 - -``` -ios nsurlcredentialstorage -``` - -12查看用户的一些信息 - -``` -ios nsuserdefaults 用户的一些信息 -``` - -![48](./image/48.png) - -hook证书绑定相关方法 - -``` -ios sslpinning -``` - -![49](./image/49.png) - -``` -ios ui screenshot 123.png 做个截图 -``` - -![50](./image/50.png) - -13hook相关: - -列出所有类 - -``` -ios hook list classes -``` - -![51](./image/51.png) - -列出相关类的所有方法 - -``` -ios hooking list class_methods ViewController -``` - -![52](./image/52.png) - -hook这个类的所有方法 hook后去点击查看调用 - -``` -ios hooking watch class ViewController -``` - -![53](./image/53.png) - - hook 这个buttonclick方法 这里要注意OC中的方法签名要记得加":"完整方法签名是:-[ViewController buttonClick:] - -``` -ios hooking watch method "-[ViewController buttonClick:]" --dump-args --dump-backtrace --dump-return -``` - -![54](./image/54.png) - -# 5.Objection破解简单CrackMe - -(1)首先对CrackMe进行分析 - -![58](./image/58.png) - -1找到点击事件响应方法buttonClick发现其对输入字符串及本地字符串比对 - -2发现本地字符串存放在类属性中,值由do_it()函数生成 - -3查看属性类型是UILabel,也就是说是隐藏起来的文本显示控件类对象 - -(2)进行破解 - -首先注入该CrackMe进程 - -``` -frida-ps -U -objection -g PID explore -``` - -1直接dump当前ui界面所有对象,可以看到设置为隐藏的UILabel对象及其text值 - -``` -ios ui dump dump -``` - -![59](./image/59.png) - -2直接找到所有UILabel类对象,对其进行打印,打印其域内属性及对象 - -``` -ios heap search instances UILabel -``` - -![61](./image/61.png) - -``` -ios heap print ivars 0x101311270 --to-utf8 -``` - -![60](./image/60.png) - - -《挑战不用macOS逆向iOS APP》系列的第二课基础知识讲解内容到这就结束了,这里的内容可能并不全面,后续会根据需要进行补充,同时也会继续更新iOS App逆向学习内容,有需要的可以联系r0ysue师傅报名课程,共同学习进步。 +AppD \ No newline at end of file diff --git a/Student/006/README.md b/Student/006/README.md index fca8e33..d4a8e62 100644 --- a/Student/006/README.md +++ b/Student/006/README.md @@ -1,95 +1,95 @@ - - - - -- [Frida前置知识:iOS/ObjC语法进阶及其ARM汇编实现](#frida前置知识iosobjc语法进阶及其arm汇编实现) -- [1.ObjC类与方法的底层实现逻辑](#1objc类与方法的底层实现逻辑) - - [(1)基本概念](#1基本概念) - - [(2)引用关系](#2引用关系) -- [2.ObjC运行时类的结构与消息传递](#2objc运行时类的结构与消息传递) - - [(1)运行时类结构:](#1运行时类结构) - - [(2)ObjC中的消息传递:](#2objc中的消息传递) - - [1SEL](#1sel) - - [2IMP](#2imp) - - [3消息机制](#3消息机制) -- [3.ObjC runtime的"反射"->KVC](#3objc-runtime的反射-kvc) - - [(1)概念](#1概念) - - [(2)使用](#2使用) - - [(3)原理概述](#3原理概述) -- [4.ObjC使用AssociatedObject动态为对象添加属性](#4objc使用associatedobject动态为对象添加属性) - - [(1)概念](#1概念-1) - - [(2)简单使用](#2简单使用) -- [5.ObjC使用Method Swizzling 进行方法绑定](#5objc使用method-swizzling-进行方法绑定) - - [(1)概念](#1概念-2) - - [(2)简单使用](#2简单使用-1) -- [6.ARM架构/指令集/寄存器/编码](#6arm架构指令集寄存器编码) - - [(1)ARM架构:](#1arm架构) - - [(2)寄存器:](#2寄存器) - - [1通用寄存器:](#1通用寄存器) - - [2特殊寄存器:](#2特殊寄存器) - - [3处理器状态:](#3处理器状态) - - [(3)指令集编码:](#3指令集编码) -- [7.ARM64算术/传输/逻辑/地址/移位指令](#7arm64算术传输逻辑地址移位指令) -- [8.栈和方法在ARM64指令集上的实现细节](#8栈和方法在arm64指令集上的实现细节) - - [(1)栈基础介绍](#1栈基础介绍) - - [(2).栈相关寄存器](#2栈相关寄存器) - - [(3).栈帧结构](#3栈帧结构) -- [9.函数调用/参数传递/入栈出栈完整流程](#9函数调用参数传递入栈出栈完整流程) - - [(1)函数调用逻辑及参数传递](#1函数调用逻辑及参数传递) - - [(2)OC中的消息机制的汇编代码](#2oc中的消息机制的汇编代码) -- [10.ObjC汇编静态分析与CrackMe动态调试](#10objc汇编静态分析与crackme动态调试) -- [总结](#总结) -- [参考文献](#参考文献) - - - -# Frida前置知识:iOS/ObjC语法进阶及其ARM汇编实现 - -学习这些语言和汇编的特性,有助于我们后续理解Frida在hook的时候,特定寄存器代表特定的值。比如hook函数的时候,为什么x0代表对象本身,x1代表selector方法名,因为这是由调用约定决定的;再或者我们为什么不用ObjC.implemt去hook,而是Interceptor.attach,因为ObjC其本身就是一个C++的运行时,所以可以采用相同的hook地址的方式。前置的语言学习是学习Frida hook ObjC的理论基础,希望大家可以掌握。 - -Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时能够更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。本篇文章包含以下知识点,大家学完后可以掌握: - ->Frida前置知识:iOS/ObjC语法进阶 ->- ObjC类与方法的底层实现逻辑 ->- ObjC运行时类的结构与消息传递 ->- ObjC runtime的"反射"->KVC获取与设置类属性 ->- ObjC使用AssociatedObject动态为对象添加属性 ->- ObjC使用Method Swizzling 进行方法绑定 - ->ARM汇编动手实操学习 ->- ARM架构/指令集/寄存器/编码 ->- ARM64算术/传输/逻辑/地址/移位指令 ->- 栈和方法在ARM64指令集上的实现细节 ->- 函数调用/参数传递/入栈出栈完整流程 ->- ObjC汇编静态分析与CrackMe动态调试 - - -# 1.ObjC类与方法的底层实现逻辑 - -## (1)基本概念 - -- 根类:在OC中几乎所有类都继承自NSObject,NSObject类就是根类,根类的父类为nil -- 元类:在我们平时开发中会用到类方法和实例方法,但是在底层的实现中并没有这种区分,实际上都是通过实例方法去查找的,底层为了区分这两种方法,引入元类的概念,然后将实例方法存储在类中,类方法存储在元类中。类的isa指向元类。(所有的类本身就是一个对象) -- 根元类:即为根类NSObject的isa指向的类 - -## (2)引用关系 + + + + +- [Frida pre-knowledge:iOS/ObjC syntax advanced and its ARM assembly implementation] (#frida pre-knowledge iosobjc syntax advanced and its arm assembly implementation) +- [Underlying Implementation Logic for 1.ObjC Classes and Methods] (#Underlying Implementation Logic for 1objc Classes and Methods) +- [(1) Basic Concepts] (#1 Basic Concepts) +- [(2) Reference Relationship] (#2 Reference Relationship) +- [Structure and Messaging of 2.ObjC Runtime Classes] (#2objc Runtime Class's Structure and Messaging) +- [(1) Runtime class structure:] (#1 Runtime class structure) +- [(2) Messaging in ObjC:] ( Messaging in #2objc) +- [(1)SEL](#1sel) +- [(2)IMP](#2imp) +- [(3) Message Mechanism] (#3 Message Mechanism) +- [Reflection of 3.ObjC runtime ->KVC] (#3objc-runtime's reflection-kvc) +- [(1) Concepts] (#1 Concepts) +- [(2) Use] (#2 Use) +- [(3) Principle Overview] (#3 Principle Overview) +- [4.ObjC dynamically adds properties to objects using AssociatedObject] (#4objc dynamically adds properties to objects using associatedobject) +- [(1) Concept] (#1 Concept-1) +- [(2) Simple Use] (#2 Simple Use) +- [5.ObjC uses Method Swizzling for method binding] (#5objc uses method-swizzling- for method binding) +- [(1) Concept] (#1 Concept-2) +- [(2) Simple Use] (#2 Simple Use -1) +- [6.ARM schema/instruction set/register/number] (#6arm schema instruction set register number) +- [(1) ARM Schema:] (#1arm Schema) +- [(2) Register:] (#2 Register) +- [(1) Universal Register:] (#1 Universal Register) +- [(2) Special Register:] (#2 Special Register) +- [(3) Processor Status:] (#3 Processor Status) +- [(3) Script Number:] (#3 Script Number) +- [7.ARM64 arithmetic/transport/logical/address/shift instruction] (#7arm64 arithmetic transport logical address shift instruction) +- [8. Implementation details of heap and method on ARM64 instruction set] (#8 Implementation details of heap and method on arm64 instruction set) +- [(1) Heap Foundation Introduction] (#1 Heap Foundation Introduction) +- [(2). Heap Related Registers] (#2 Heap Related Registers) +- [(3).Stack Frame Structure] (#3 Stack Frame Structure) +- [9. Function Call/Parameter Pass/Inbound Heap Full Flow] (#9 Function Call Parameter Pass Inbound Heap Full Flow) +- [(1) function call logic and parameter pass] (#1 function call logic and parameter pass) +- [(2) Assembly code for the message mechanism in OC] ( Assembly code for the message mechanism in #2oc) +- [10.ObjC Assembly Static Analysis and CrackMe Dynamic Debugging] (#10objc Assembly Static Analysis and crackme Dynamic Debugging) +- [Summary] (#Summary) +- [Ref.] (#Ref.) + + + +# Frida Preknowledge: Advanced iOS/ObjC Syntax and Its Implementation in ARM Assembly + +Learning the properties of these languages and assemblages helps us to understand the Frida later in the hook when the specific register represents a specific value. For example, the hook function, why x0 for the object itself, x1 for the selector method name, because this is determined by the calling convention, or why we do not use ObjC.implemt to hook, but Interceptor.attach, because ObjC itself is a runtime of C++, so we can use the same hook address. The pre-language learning is the theoretical basis of learning Frida hook ObjC, I hope you can master. + +Objective-C language is a dynamic language, it puts a lot of static language in the period of compiling and linking to the runtime to deal with. The advantage of this dynamic language is that we can write code more flexibly, such as we can forward the message to the object we want, or exchange the implementation of a method at will. This feature means that Objective-C requires not only a compiler, but also a runtime system to execute the compiled code. For Objective-C, this runtime system is like an operating system: it allows all the work to run properly. This runtime system is Objc Runtime. Objc Runtime is actually a Runtime library, it is basically written with C and Sinks, the library makes C language has object-oriented capabilities. This article contains the following knowledge points that you can master when you are finished: + +>Frida Prerequisites: Advanced iOS/ObjC Syntax +>- Underlying Implementation Logic for ObjC Classes and Methods +>- Structure and messaging of ObjC runtime classes +>- Reflection of ObjC runtime->KVC get and set class properties +>- ObjC dynamically adds attributes to objects using AssociatedObject +>- ObjC uses Method Swizzling for method binding + +> ARM Assembly Hands-on Learning +>- ARM Architecture/Instruction Set/Register/Encoding +>- ARM64 arithmetic/transport/logic/address/shift instructions +>- Implementation Details of Heap and Method on ARM64 Instruction Set +>- Function Call/Parameter Pass/Inbound Heap Heap Full Flow +>- ObjC assembly static analysis and CrackMe dynamic debugging + + +# The underlying implementation logic of 1.ObjC classes and methods + +## (1) Basic Concepts + +- Root class: Almost all classes in OC inherit from NSObject, the NSObject class is the root class, and the parent class of the root class is nil +- Meta-class: In our usual development will use the class method and the instance method, but in the underlying implementation there is no such distinction, in fact, all through the instance method to look for, in order to distinguish between the two methods, the underlying introduction of the concept of meta-class, and then the instance method is stored in the class, class method is stored in the meta-class. The class's isa points to a metaclass. (All classes are themselves an object) +- Root metaclass: The class to which isa of the root class NSObject points + +## (2)Reference Relationship ![1](image/1.png) -(3)类的底层实现: +(3) The underlying implementation of the class: -首先写下边的代码然后转为c +First write the code below and then turn to c. -``` +```ObjectiveC #import @interface MyClass : NSObject @property NSString *myProperty; @end -@implementation MyClass --(void) myMethod{ +@implementation MyClass +-(void)myMethod{ NSLog(@"my method"); } +(void)myClassMethod{ @@ -99,891 +99,109 @@ Objective-C语言是一门动态语言,它将很多静态语言在编译和链 @end int main(int argc,char *argv[]){ @autoreleasepool { - [MyClass myClassMethod]; - return 0; - } + [MyClass myClassMethod]; + return 0; +} } ``` -使用如下命令将其转为C -``` +Convert it to C using the following command + +```bash xcrun -sdk iphoneos clang -rewrite-objc -F UIKit -fobjc-arc -arch arm64 ClassAndMethod.m ``` -使用VS打开C文件进行分析 +Open the C file for analysis using VS -搜索我们写的MyClass分别发现其声明及实现 +Search for the MyClass we wrote to find its declaration and its implementation ![2](image/2.png) ![3](image/3.png) -发现其实现中存在NSObject_IMPL属性,进行搜索 +Discover the NSObject_IMPL attribute in its implementation, search ![4](image/4.png) -可以看到内部其实是一个Class指针,查看其声明发现其是objc_class结构体。 +You can see that inside is actually a Class pointer, and look at its declaration and see that it's a objc_class structure. -- 第一个属性:isa指针(继承自根类) -- 第二个属性:父类指针 +- First property: isa pointer (inherited from root class) +- Second property: parent class pointer -- 第三个属性:用于缓存最近使用的方法。 -- 第四个属性:类中实例方法、属性、协议的存储 +- Third property: Used to cache the most recently used methods. +- Fourth property: storage of instance methods, properties, protocols in class -到这里我们可以看出OC中类结构的基本信息,而objc_class结构体是定义在NSObject.h头文件中它继承自定义在runtime.h头文件中的`_class_t`结构体,下边我们在看一下`_class_t`结构体。 +Here we can see the basic information of the class structure in OC, and the objc_class structure is defined in the NSObject.h header file, it inherits the '_class_t' structure defined in the runtime.h header file, the next we look at the '_class_t' structure. -接着我们将代码拉到最后,可以看到定义的类写到了以下section中 +Then we pull the code to the end and you can see that the defined classes are written in the following section ![7](image/7.png) -并在`OBJC_CLASS_SETUP_$_MyClass`方法中对其进行了初始化,这里可以看出,进行初始化时其实是分为元类及类的 +And it is initialized in the 'OBJC_CLASS_SETUP_$_MyClass' method, we can see that the initialization is divided into meta-class and class ![8](image/8.png) -检索`OBJC_CLASS_$_MyClass`查看其类型发现是`_class_t`结构体 +Retrieve 'OBJC_CLASS_$_MyClass' View its type Discovery Yes '_class_t' Structure ![9](image/9.png) -结构体定义如下 +The structure is defined as follows ![10](image/10.png) -可以看到结构体内属性存在一个`_class_ro_t` 结构体,检索发现其存放及定义内容 +You can see that there is a '_class_ro_t' structure for attributes in the structure body, and the search finds its storage and definition ![11](image/11.png) ![12](image/12.png) -然后分别检索`OBJC_METACLASS_$_MyClass`以及`OBJC_METACLASS_$_MyClass`可以看到以下实现 +Then retrieve 'OBJC_METACLASS_$_MyClass' and 'OBJC_METACLASS_$_MyClass' separately to see the following implementations ![14](image/14.png) -这里的两个RO属性都是readonly只读的在编辑器中确定,继续检索这两个变量查看定义 +Both RO properties here are readonly read-only determined in the editor, continue to retrieve both variables to view the definition ![15](image/15.png) -这里已经可以看到方法属性对象方法及类方法的区别,分别保存在元类及类中 +Here we can already see the difference between the method attribute object method and the class method, which are saved in the meta-class and the class -继续检索`OBJC_$_INSTANCE_METHODS_MyClass` 及`OBJC_$_CLASS_METHODS_MyClass`看类方法及对象方法 +Continue retrieving 'OBJC_$_INSTANCE_METHODS_MyClass' and 'OBJC_$_CLASS_METHODS_MyClass' look-at methods and object methods ![16](image/16.png) -可以看到我们自己写的对象方法myMethod及类方法myClassMethod这里可以知道 `_class_t`是类结构,内部包含有方法及属性结构体`_class_ro_t`属性,在实际的实现过程中对一个类分别实现了基于`_class_t`的`OBJC_METACLASS_$_MyClass`以及`OBJC_METACLASS_$_MyClass`分别为类及元类,二者内部又都有基于`_class_ro_t` 结构体的实现`OBJC_$_INSTANCE_METHODS_MyClass` 及`OBJC_$_CLASS_METHODS_MyClass`存放方法及属性。 +We can see the object method myMethod and the class method myClassMethod which we write by ourselves. We know that '_class_t' is the class structure, the interior contains the method and the attribute structure body '_class_ro_t' attribute. In the actual realization process, we realize that the 'OBJC_METACLASS_$_MyClass' and 'OBJC_METACLASS_$_MyClass' based on '_class_t' are the class and the meta-class respectively for a class, and both of them have the realization 'OBJC_$_INSTANCE_METHODS_MyClass' and 'OBJC_$_CLASS_METHODS_MyClass' based on '_class_ro_t' structure body. -关于类的继承可总结如下: +Inheritance of classes can be summarized as follows: -isa指向: +isa points to: -- 实例变量的`isa`指向对应的类objc_ -- 类的`isa`指向对应的元类 -- 元类的`isa`指向根元类 -- 根元类的`isa`指向自身 +- The 'isa' of the instance variable points to the corresponding class objc_ +- 'isa' of the class points to the corresponding meta-class +- 'isa' of a metaclasses points to the root metaclasses +- 'isa' of the root metaclass points to itself -类的继承: +Inheritance of class: -- 类的`superclass`指向父类 -- 父类的`superclass`指向根类 -- 根类的`superclass`指向`nil` +- Class's 'superclass' points to parent class +- Parent's 'superclass' points to root class +- 'superclass' of the root class points to 'nil' -元类的继承: +Inheritance of meta classes: -- 元类的`superclass`指向对应类的父类的元类 -- 父类的元类的`superclass`指向根元类 -- 根元类的`superclass`指向根类 -- 根类的`superclass`指向`nil` +- The 'superclass' of a meta class points to the meta class of the parent class of the corresponding class +- The 'superclass' of the parent's metaclass points to the root metaclass +- 'superclass' of the root metaclass points to the root class +- 'superclass' of the root class points to 'nil' -这里我们的分析过程是Class -->`_class_t`(类结构结构体)-->`_class_ro_t`(类结构内属性方法结构体)-->`_methood_list_t`(类的方法列表)-->`_objc_method`(类内方法对应的结构体,包含名称方法名hash SEL 及方法实际地址IMP) +Here our analysis is Class —>'_class_t' (class structure body)—>'_class_ro_t' (properties within a class structure method structure)—>'_methood_list_t' (method list of classes)—>'_objc_method' (structure body for a method within a class, including the name method name hash SEL and the method actual address IMP) -# 2.ObjC运行时类的结构与消息传递 +# Structure and Messaging of 2.ObjC Runtime Classes -## (1)运行时类结构: +## (1) Runtime class structure: ![48](image/48.png) -在上边我们已经分析了OC中类的结构,但是其中的class_ro_t却是一个只读结构体,为了实现OC语言的动态性,因此在运行时给类加了一个中间层,下面我们借用AloneMonkey巨佬书的书中的[Demo案例](https://github.com/AloneMonkey/iOSREBook/tree/master/chapter-4/4.3%20%E7%B1%BB%E4%B8%8E%E6%96%B9%E6%B3%95)查看运行时类的结构 +We have analyzed the structure of the class in OC, but class_ro_t is a read-only structure. In order to realize the dynamic of OC language, we add an intermediate layer to the class at runtime. We borrow [Demo case] from the book of AloneMonkey's books (https://github.com/AloneMonkey/iOSREBook/tree/master/chapter-4/4.3%20%E7%B1%BB%E4%B8%8E%E6%96%B9%E6%B3%95) to view the structure of the class at runtime ![47](image/47.png) -可以看到类结构中多了一部分名为classReadWrite的结构,这就是增加的可读可写的中间层,原本只读的class_ro_t结构变成了可读可写的class_rw_t的一部分,正是利用这个中间层,实现OC语言的动态性,可以在运行时增加类方法及属性。 - ---- - -## (2)ObjC中的消息传递: - -首先了解一些基础属性 - -### 1SEL - -SEL又叫选择器,是表示一个方法的`selector`的指针,其定义如下: - -![17](image/17.png) - -Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(`Int`类型的地址),这个标识就是`SEL`,两个类之间,不管它们是父类与子类的关系,还是之间没有这种关系,只要方法名相同,那么方法的SEL就是一样的。每一个方法都对应着一个`SEL`。所以在Objective-C同一个类(及类的继承体系)中,不能存在2个同名的方法,即使参数类型不同也不行。相同的方法只能对应一个`SEL`。这也就导致Objective-C在处理相同方法名且参数个数相同但类型不同的方法方面的能力很差。 - -### 2IMP - -`IMP`实际上是一个函数指针,指向方法实现的首地址,其定义在Runtime 中如下 - -![18](image/18.png) - -这个函数使用当前`CPU`架构实现的标准的C调用约定。第一个参数是指向`self`的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针),第二个参数是方法选择器(`selector`),接下来是方法的实际参数列表。 - -通过以上两个变量我们已经明白SEL存放方法名hash过的字符串,而IMP又存放方法的具体地址,当使用Runtime中方法调用方法时又根据self判断其是类方法还是实例方法。那又是如何将二者联系在一起呢? - -在代码中检索发现_objc_method 定义内含方法名及地址 - -![19](image/19.png) - -再结合我们上边查看过的方法列表 - -![16](image/16.png) - -到这里我们大致了解了方法的存储方式,那实际的方法调用,消息机制是如何实现的呢? - -### 3消息机制 - -直接查看我们编译的代码检索main 函数查看其中方法调用 - -![20](image/20.png) - -这里可以看到,实际上OC中的方法调用会转化为消息函数objc_msgSend的调用。这个函数将消息接收者和方法名作为其基础参数,如以下所示: - -objc_msgSend(receiver, selector, arg1, arg2, ...) - -objc_msgSend确定调用方法是进行一个动态查找,具体过程如下: - -1.在相应对象的缓存方法列表中查看是否有调用方法(objc_class 的cache,这里可以加快查找速度) - -2.如果没找到,在相应对象的方法列表中查找 - -3.如果还没找到,就在根类指针指向的对象中执行1,2两步 - -4.如果直到根类中都没有,就进行消息转发,给自己保留处理找不到方法这一状况的机会 - -以上四步可以归纳为如下图所示 - -![21](image/21.png) - -当以上方法都查询不到调用方法时进入消息转发机制,消息转发分为三步:动态方法解析,备用接收者,完整转发 - -5.动态方法解析(其实就是动态换个方法selector) - -![24](image/24.png) - -动态添加类有什么好处?当一个类中的方法非常多且有些方法不常用的时候如果直接写了方法,那么这些方法会直接加载到内存,于是内存就很大了,所以我们使用runtime的动态添加方法就不会出现这个问题,只有在运行时才会添加到内存,使用的是class_addMethod方法,具体代码如下: - -``` -#import - -#import "Person.h" -#import - -@implementation Person :NSObject -- (void) run { - NSLog(@"I am run"); - NSLog(@"%s", __func__); -} - -+ (BOOL)resolveInstanceMethod:(SEL)sel -{ - // 动态的添加方法实现 - if (sel == @selector(test)) { - // 获取其他方法 指向method_t的指针 - Method otherMethod = class_getInstanceMethod(self, @selector(run)); - - // 动态添加test方法的实现 - class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod)); - - // 返回YES表示有动态添加方法 - return YES; - } - - NSLog(@"%s", __func__); - return [super resolveInstanceMethod:sel]; -} - -@end - - - -#import -#import "test.h" -#import "Person.h" -int main(int argc, const char * argv[]) { - @autoreleasepool { - Person *person = [[Person alloc] init]; - [person performSelector:@selector(test)]; - } - return 0; -} -``` - -从代码中可以看到Person类中是没有test方法的,但是我们仍然可以调用,这是因为OC的消息转发机制在类及父类中均未找到方法是会进行动态方法解析,会自动调用类的`resolveInstanceMethod`:(或resolveClassMethod:)方法进行动态查找,所以我们可以在resolveInstanceMethod:方法内部使用class_addMethod动态的添加方法实现。 - -![22](image/22.png) - -方法参数简单介绍: -``` -class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) - -@cls : 给哪个类对象添加方法 -@name : SEL类型,给哪个方法名添加方法实现 -@imp : IMP类型的,要把哪个方法实现添加给给定的方法名 -@types : 就是表示返回值和参数类型的字符串 -``` - -6.备用接收者(其实就是换个执行对象) - -![25](image/25.png) - -``` -#import - -#import "Person.h" -#import -#import "Car.h" -@implementation Person :NSObject -- (void) run { - NSLog(@"I am run"); - NSLog(@"%s", __func__); -} - -+ (BOOL)resolveInstanceMethod:(SEL)sel -{ - // 动态的添加方法实现 - if (sel == @selector(test)) { - // 获取其他方法 指向method_t的指针 - Method otherMethod = class_getInstanceMethod(self, @selector(run)); - - // 动态添加test方法的实现 - class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod)); - - // 返回YES表示有动态添加方法 - return YES; - } - - NSLog(@"%s", __func__); - return [super resolveInstanceMethod:sel]; -} -- (id)forwardingTargetForSelector:(SEL)aSelector { - //返回能够处理消息的对象 - if (aSelector == @selector(drive)) { - return [[Car alloc] init]; - } - return [super forwardingTargetForSelector:aSelector]; -} - -@end - - - -#import -#import "Car.h" - -@implementation Car : NSObject --(void)drive{ - NSLog(@"我可以开车"); -} - -@end - - - -#import -#import "test.h" -#import "Person.h" -int main(int argc, const char * argv[]) { - @autoreleasepool { - Person *person = [[Person alloc] init]; - [person performSelector:@selector(test)]; - [person drive]; - } - return 0; -} -``` - -这里我们在Person类中对drive方法只声明不实现,然后写forwardingTargetForSelector方法实现对象的转换,调用有drive方法的Car类,这就是OC消息转发机制中的备用接收者。 - -![23](image/23.png) - -7.完整转发(我个人理解其实就是在这一步自己决定执行对象和执行方法) - -![26](image/26.png) - -写一个`- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector`方法只要他返回一个方法签名就会调用你写的另一个函数`- (void)forwardInvocation:(NSInvocation *)anInvocation`,然后你可以自己在这个函数内生成对象,使用这个对象去调用你的这个方法签名代码如下: -``` -#import - -#import "Person.h" -#import -#import "Car.h" -@implementation Person :NSObject -- (void) run { - NSLog(@"I am run"); - NSLog(@"%s", __func__); -} - -+ (BOOL)resolveInstanceMethod:(SEL)sel -{ - // 动态的添加方法实现 - if (sel == @selector(test)) { - // 获取其他方法 指向method_t的指针 - Method otherMethod = class_getInstanceMethod(self, @selector(run)); - - // 动态添加test方法的实现 - class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod)); - - // 返回YES表示有动态添加方法 - return YES; - } - - NSLog(@"%s", __func__); - return [super resolveInstanceMethod:sel]; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector -{ - if (aSelector == @selector(drive)) { - // return [NSMethodSignature signatureWithObjCTypes: "v@:"]; - // return [NSMethodSignature signatureWithObjCTypes: "v16@0:8"]; - // 也可以通过调用Car的methodSignatureForSelector方法得到方法签名,这种方式需要car对象有aSelector方法 - return [[[Car alloc] init] methodSignatureForSelector: aSelector]; - - } - return [super methodSignatureForSelector:aSelector]; -} - -//NSInvocation 封装了一个方法调用,包括:方法调用者,方法,方法的参数 -// anInvocation.target 方法调用者 -// anInvocation.selector 方法名 -// [anInvocation getArgument: NULL atIndex: 0]; 获得参数 -- (void)forwardInvocation:(NSInvocation *)anInvocation -{ - // anInvocation中封装了methodSignatureForSelector函数中返回的方法。 - // 此时anInvocation.target 还是person对象,我们需要修改target为可以执行方法的方法调用者。 - // anInvocation.target = [[Car alloc] init]; - // [anInvocation invoke]; - [anInvocation invokeWithTarget: [[Car alloc] init]]; -} - -@end -``` - -![28](image/28.png) - -总结: - -OC中的对象方法调用都是采用消息发送,而所谓消息发送其实是SEL-IMP的查找过程,当我们在类中进行前四步都没找到,那就要进行消息转发,在消息转发中OC提供了三条补救措施,分别是动态方法解析,备用接收者,完整转发。完整流程如下图: - -![27](image/27.png) - -# 3.ObjC runtime的"反射"->KVC - -## (1)概念 - -KVC是Key-Value-Coding缩写,意思是键值编码,作用时通过名称访问对象属性,操作方法是由NSObject实现的NSKeyValueCoding协议实现,因此几乎所有对象都支持。 - -## (2)使用 - -``` -设置值 -- (void)setValue:(id)value forKey:(NSString *)key; -- (void)setValue:(id)value forKeyPath:(NSString *)keyPath; -获取值 -- (id)valueForKey:(NSString *)key; -- (id)valueForKeyPath:(NSString *)keyPath; -``` - -案例如下: - -``` -@interface Dog : NSObject -@property (nonatomic, copy) NSString *name; -@end - -#import "dog.h"@implementation Dog - -@end - - -#import "dog.h" -@interface Person2 : NSObject -@property (nonatomic, assign) NSString *name; -@property (nonatomic, assign) NSInteger age; -@property (nonatomic, strong) Dog *dog; -@end - -#import "person2.h" -@implementation Person2 : NSObject -@end - -#import -#import "test.h" -#import "Person.h" -#import "person2.h" -int main(int argc, const char * argv[]) { - @autoreleasepool { - - Person2 *person2 = [Person2 new]; - [person2 setValue:@(18) forKey:@"age"]; - NSLog(@"age: %@", [person2 valueForKey:@"age"]); - - person2.dog = [Dog new]; - [person2 setValue:@"xiaoHuang" forKeyPath:@"dog.name"]; - NSLog(@"name: %@", [person2 valueForKeyPath:@"dog.name"]); } - return 0; -} -``` - - ![60](image/60.png) - -## (3)原理概述 - -设置值: - -![50](image/50.png) - -查看文档可以看出设置值分为三步 - -1有没有set 或者_set 这些方法,如果有的话,优先调用他们,就不再按自己的值设置了的 - -2检查accesssInstanceVariableDirectly()函数返回值,该函数默认返回值是YES,当然我们可以重写,如果为YES 往下,如果重写了就跳过第二部,直接去第三步,为YES时会检查(`_`),`_is`,is 这几个名字的变量,如果有就给他们赋值顺序第一个,如果没有到第三步 - -3执行setValue:forUndefinedKey:.这个函数,如果我们不重写那就抛异常结束了,我们可以对其重写 - -获取值: - -取值调用 - -![57](image/57.png) - -1先看对象内函数,用函数返回值充当值,如果没有就为null - -![51](image/51.png) - -![54](image/54.png) - -2第二步和第三步是关于数组和集合的 - -![58](image/58.png) - -![52](image/52.png) - -4同样会进行函数检查,如果函数返回yes 也会从这些**成员变量**中找值 - -![53](image/53.png) - -![55](image/55.png) - -# 4.ObjC使用AssociatedObject动态为对象添加属性 - -## (1)概念 - -一般情况下,对象在实例化后是不能动态添加属性的,但是在OC中可以通过关联对象(Associated Object)实现我们的需求,具体来说就是新建一个类把两个类关联起来,关联后就可以随时获取该对象 - -runtime提供了給我们3个API以管理关联对象(存储、获取、移除): - -``` -//关联对象 -void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) -//获取关联的对象 -id objc_getAssociatedObject(id object, const void *key) -//移除关联的对象 -void objc_removeAssociatedObjects(id object) -``` - -其中的参数 - -- id object:被关联的对象 -- const void *key:关联的key,要求唯一 -- id value:关联的对象 -- objc_AssociationPolicy policy:内存管理的策略 - -其中内存管理策略是一个枚举,取值如下 - -``` -typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { - OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */ - OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. - * The association is not made atomically. */ - OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied. - * The association is not made atomically. */ - OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object. - * The association is made atomically. */ - OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied. - * The association is made atomically. */ -}; -``` - -不同的内存管理策略对应了不同的属性修饰符。 - -## (2)简单使用 - -``` -#import "MyClass.h" -@interface MyClass () -{ - NSString * _property; -} -@end - -@implementation MyClass - -- (instancetype)init -{ - self = [super init]; - if (self) { - _property = @"AloneMonkey"; - } - return self; -} - -- (void)myMethod{ - NSLog(@"my method"); -} - -+ (void)classMethod{ - NSLog(@"class method"); -} - -@end - -#import "ViewController.h" -#import "MyClass.h" -#import - -static const void *kAssociatedKey = &kAssociatedKey; - -static void *kExampleDoubleKey; - -@interface ViewController () - -@end - -@implementation ViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. - - MyClass *myClass = [[MyClass alloc] init]; - - //KVC - NSString* property = [myClass valueForKey:@"_property"]; - NSLog(@"property: %@", property); - - Ivar ivar = class_getInstanceVariable(objc_getClass("MyClass"), "_property"); - if(ivar){ - NSString* ivarProperty = (__bridge NSString *)(*(void**)((__bridge void*)myClass + ivar_getOffset(ivar))); - NSLog(@"ivarProperty: %@", ivarProperty); - } - - //AssociatedObject - - objc_setAssociatedObject(myClass, kAssociatedKey, @"AssociatedObject1", OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - NSString* associatedString = objc_getAssociatedObject(myClass, kAssociatedKey); - - NSLog(@"associatedString: %@", associatedString); - - objc_setAssociatedObject(myClass, &kExampleDoubleKey, @"AssociatedObject2", OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - associatedString = objc_getAssociatedObject(myClass, &kExampleDoubleKey); - - NSLog(@"associatedString: %@", associatedString); - - objc_setAssociatedObject(myClass, @selector(myProperty), @"AssociatedObject3", OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - associatedString = objc_getAssociatedObject(myClass, @selector(myProperty)); - - NSLog(@"associatedString: %@", associatedString); -} - - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - - -@end -``` - -![61](image/61.png) - - - -可以看到,我们通过objc_setAssociatedObject方法为MyClass对象关联一个字符串,再通过id objc_getAssociatedObject方法获取其值打印输出,实现为一个对象添加属性 - -# 5.ObjC使用Method Swizzling 进行方法绑定 - -## (1)概念 - -我们在前面了解类结构的时候已经知道了方法对象是由SEL与IMP组成 SEL为方法名称IMP则是方法的具体实现,在OC语言中,Runtime提供了修改IMP的方法和交换两个IMP实现的方法。通过交换两个selector的实现,可以达到在调用A方法时实际调用了B方法,在B方法中可以继续调用A方法的效果,通常把这中操作称为Method Swizzling. - -![59](image/59.png) - -## (2)简单使用 - -这里先补充一点:当一个类被引用进项目时会在main函数执行前先执行load函数 - -``` -@interface MethodSwizzling : NSObject - --(void)test1; --(void)test2; - -@end - -#import -#import "MethodSwizzling.h" -#import - -@implementation MethodSwizzling - -+ (void)load { - // 获取test1、test2方法 - Method method_test1 = class_getInstanceMethod(self, @selector(test1)); - Method method_test2 = class_getInstanceMethod(self, @selector(test2)); - // 交换两个方法的实现 - method_exchangeImplementations(method_test1, method_test2); -} - --(void)test1 { - NSLog(@"test1"); -} - --(void)test2 { - NSLog(@"test2"); -} - -@end - - -#import -#import "test.h" -#import "Person.h" -#import "person2.h" -#import "MethodSwizzling.h" -int main(int argc, const char * argv[]) { - @autoreleasepool { - - MethodSwizzling *obj = [[MethodSwizzling alloc] init]; - [obj test1]; - } - return 0; -} -``` - -![60](image/60.png) - -可以看到方法交换后调用test1方法,实际执行函数是test2。 - - - -# 6.ARM架构/指令集/寄存器/编码 - -## (1)ARM架构: - -iOS设备和安卓设备的区别在于,android设备在n5x之后建议使用64位架构,而iOS设备则是在18年iOS111时就强制使用64位架构,禁止32位应用上架,因此我们这里只用了解64位架构即可. - -体系结构: - -A:Applicationtion 体系,就是咱们学习的应用相关。 - -R:Real-time体系 嵌入式相关。 - -M:Microcontroller体系,嵌入式相关。 - -## (2)寄存器: - -### 1通用寄存器: - -R0~R30是31个通用寄存器,每个寄存器又有两种访问方式 - -- 64位通用寄存器名为X0~X30 -- 32位通用寄存器名为W0~W30 - -两种访问方式对应关系为Wn表示Xn的低32位,具体如下图所示: - -![35](image/35.png) - -通用寄存器X30又用于程序调用的的link register.是一个特殊的寄存器,用于保存函数调用完成时的返回地址。 - -### 2特殊寄存器: - -SP:64位堆栈指针寄存器,可以通过寄存器名WSP 访问堆栈指针最低有效32位 - -PC:保存当前指令地址的64位程序计数器,程序中不能直接修改PC,只能在分支,异常条目或异常返回时更新。 - -V0~V31:主要用于浮点数运算,但我们暂时可能也用不到,这里也不展开。 - -### 3处理器状态: - -AArch64通过PSTATE的标志位来保存处理器状态,PSTATE也不是寄存器是进程状态信息的抽象,处理器执行指令时可以读取与设置这些标志位,以这些标志位为依据。以下为PSTATE可以在EL0级别访问的常见标志位 - -![36](image/36.png) - - - -## (3)指令集编码: - -计算机中存储的都是二进制数据,而我们学习的ARM64指令集肯定也是要转化为二进制数据,比如我们最常见的mov x1 x2 对应的肯定也是要转化为二进制数据存储,所谓的指令集编码就是指令到二进制数据的对应,但其实我们知道我们无论是使用IDA还是lldb进行调试看到最底层也是汇编了,所以这里就不再展开。 - -# 7.ARM64算术/传输/逻辑/地址/移位指令 - -ARM64相关指令很多我们可以去 [官网](https://developer.arm.com/documentation/#q=ARMv8-A%20Reference%20Manual&f[navigationhierarchiescontenttype]=Architecture%20Document&cf[navigationhierarchiesproducts]=Architectures)下载官方文档,这里只简单了解一些常见指令。 - -(1)算术指令: - -![29](image/29.png) - -(2)传输指令: - -![30](image/30.png) - -(3)逻辑指令: - -![31](image/31.png) - -(4)地址指令: - -![32](image/32.png) - -(5)移位指令: - -![33](image/33.png) - -# 8.栈和方法在ARM64指令集上的实现细节 - -## (1)栈基础介绍 - -这里的栈是进程中的一块特殊内存区域,因为我们知道一个进程在运行时都会有属于自己的内存空间,栈就是进程内存空间内一块连续的区域,由编译器自动分配释放,因此一般用来保存一些临时数据,比如局部变量和上下文环境,在操作上类似于数据结构上的栈的操作,有栈顶与栈底,只能在一端操作。 - -根据栈的增长方向和栈顶指针指向的位置,可以将其分为以下4种类型 - -- 向高地址方向生长,称为递增堆栈。 -- 向低地址方向生长,称为递减堆栈。 -- 堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈 -- 堆栈指针指向下一个要放入的空位置,成为空堆栈 - -ARM的堆栈具有后进先出和满递减的特点,如下图所示,将其想象为一个函数栈,有如下特点 - -![37](image/37.png) - -- 栈中元素按ABCD顺序入栈,按DCBA顺序出栈。 -- 栈是向低地址方向生长的 -- SP指向栈顶元素,其他元素通过SP+offset 偏移获取 - -## (2).栈相关寄存器 - -函数在调用的时候会开辟栈帧,函数参数的传递是通过x0-x7传递的,以下是部分与函数栈帧相关寄存器。 - -PC寄存器:记录当前执行代码地址 - -SP寄存器:指向栈帧的指针,在内存操作指令中通过x31寄存器访问。 - -LR寄存器:指向返回地址,对应寄存器x30 - -FP寄存器:指向栈帧底部,对应寄存器x29 - -## (3).栈帧结构 - -一个栈帧包括以下部分 - -- 参数区(parameter area):存放调用函数传递的参数。 -- 连接区(linkage area):存放调用者(caller)的下一条指令 -- 栈帧指针存放区(frame pointer):存放调用函数的栈帧底部 -- 寄存器存储区(saved registers area):被调用函数(callee)返回需要恢复的寄存器内容 -- 局部存储区(local storage area):用于存放被调用函数(callee)的局部变量 - -![38](image/38.png) - -# 9.函数调用/参数传递/入栈出栈完整流程 - -Xcode导入[ArmDemo项目](https://github.com/AloneMonkey/iOSREBook/tree/master/chapter-6/6.3-ARM%E6%B1%87%E7%BC%96/ArmDemo),这里要注意需要有开发者账号将手机设为调试设备,不然无法attach进程,xcode中的模拟器是X86_64,调试出来的汇编代码不是ARM。另外调试过程中使用xia0LLDB调试工具,安装命令及[介绍地址](https://github.com/4ch12dy/xia0LLDB)如下: - -``` -git clone https://github.com/4ch12dy/xia0LLDB.git && cd xia0LLDB && ./install.sh -``` - -1.先看main函数 - -info -f main 查看main函数地址 - -dfuc addr查看汇编指令 - -## (1)函数调用逻辑及参数传递 - -``` -info -f main 查看函数地址及信息 -``` - -![39](image/39.png) - -``` -dfuc 查看该地址函数对应汇编代码 -``` - -![40](image/40.png) - -从上述汇编指令可以看到add1函数的调用的参数的传递,add1方法调用共需11个参数,其中前八个参数都是先mov保存到了w0-w7寄存器中然后读取到栈中,而后三个参数则直接使用w8作为中转直接放入栈中,说明函数调用时是用x0-x7传递参数的,但是参数过多时超出个数就直接存放到栈中了 - -同样的方式查看一个简单函数add2的汇编指令 - -![40](image/40.png) - -通过分析可以发现方法的调用会开辟新的栈帧空间并对原来栈指针进行保存,方便执行完毕返回原样,现场保存完毕后就是参数的传递及执行完后栈帧空间销毁,跳回原样,具体过程如下: - -(1)函数调用前: - -1. 开辟新的栈帧空间 -2. 保存FP和LR寄存器,以便找到上一个栈帧和返回地址 -3. 设置新的FP寄存器 -4. 保存子函数会用到的寄存器 -5. 保存局部变量或参数 - -(2)函数调用结束 - -1. 还原FP和LR寄存器 -2. 释放栈帧空间 -3. 跳到LR子程序返回 - -通过上边add2方法的汇编指令我们可以看到,其在传递参数时,不是使用的我们上边所说的x0-x7来传递的参数,这是因为小数是通过d0,d1来传递的参数。 - -## (2)OC中的消息机制的汇编代码 - -我们在上边已经了解了OC语言中方法调用时利用消息机制将其转化为objc_msgSend方法的调用该方法调用格式为objc_msgSend(receiver, selector, arg1, arg2, ...),接下来我们看一看汇编中的实现 - -![41](image/41.png) - -调试CrackMe,当代码执行到函数调用地址时,我们打印x0,x1寄存器,可以发现x0寄存器存放的是类名,x1寄存器存放方法名,其他参数存储在x2-x7或堆栈中。 - -# 10.ObjC汇编静态分析与CrackMe动态调试 - -接下来我们对案例进行简单的汇编分析 - -先看案例代码如下 - -![43](image/43.png) - -类方法名下断点 - -![34](image/34.png) - -进行调试,发现isEqualToString方法 - -![44](image/44.png) - -继续调试发现theTextField对象,但是其实可以发现,对于这种业务代码,牵扯到库函数的汇编指令会变得格外多,需要分析地方,并不是一个简单的方式,对于初学者来说并不友好,因此这里推荐IDA F5查看伪代码,或者学习之后会更新的frida调试更为方便一些。 - -![45](image/45.png) - -![46](image/46.png) - -# 总结 - -学到这里我们明白了ObjC类与方法的底层逻辑,运行时的消息传递原理和过程,动态语言的反射、动态修改等特性,并对调用过程的汇编实现、参数返回值传递等进行了分析,为后续开展frida这款hook框架的编码学习打下了良好的基础。 - -# 参考文献 - -- 本文中大部分内容来源于:刘培庆:《iOS应用逆向与安全》 https://github.com/AloneMonkey/iOSREBook +You can see that there is more in the class structure \ No newline at end of file diff --git a/Student/007/README.md b/Student/007/README.md index b758ee2..611e9ee 100644 --- a/Student/007/README.md +++ b/Student/007/README.md @@ -1,744 +1,154 @@ - + - + -- [前言](#前言) -- [1.ios hooking search 源码解析](#1ios-hooking-search-源码解析) -- [2.ApiResolver 搜刮内存中所有符号](#2apiresolver-搜刮内存中所有符号) -- [3.枚举搜索所有类/所有方法/所有重载](#3枚举搜索所有类所有方法所有重载) -- [4.hook所有类/所有方法/所有重载](#4hook所有类所有方法所有重载) -- [5.输出(修改)解析参数/调用栈/返回值](#5输出修改解析参数调用栈返回值) - - [1修改返回值实现目的](#1修改返回值实现目的) - - [2修改参数实现目的](#2修改参数实现目的) - - [3查看调用栈](#3查看调用栈) - - [4替换函数](#4替换函数) -- [6.枚举内存中所有模块/符号/地址](#6枚举内存中所有模块符号地址) -- [7.无脑自动化hook 应用包下所有函数](#7无脑自动化hook-应用包下所有函数) -- [8.Objection 内存漫游搜刮所有对象](#8objection-内存漫游搜刮所有对象) -- [9.ObjC.choose枚举所有类输出属性](#9objcchoose枚举所有类输出属性) -- [10.主动调用对象方法获取算法执行结果](#10主动调用对象方法获取算法执行结果) -- [11配置RPC黑盒调用算法远程批量脱机](#11配置rpc黑盒调用算法远程批量脱机) +- [Preface] (#Preface) +- [1.ios hooking search Source Resolution] (#1ios-hooking-search-Source Resolution) +- [2.ApiResolver scraps all symbols in memory] (#2apiresolver - scraps all symbols in memory) +- [3. Enumeration Search All Classes/All Methods/All Overloads] (#3 Enumeration Search All Classes All Methods All Overloads) +- [4.hook all classes/all methods/all overloads] (#4hook all classes all methods all overloads) +- [5. Output (modify) parse parameters/call stack/return value] (#5 Output modify parse parameters call stack return value) +- [(1) modify return value implementation purpose] (#1 modify return value implementation purpose) +- [(2) Modify Parameter Implementation Purpose] (#2 Modify Parameter Implementation Purpose) +- [(3) View Call Stack] (#3 View Call Stack) +- [(4) Replacement Function] (#4 Replacement Function) +- [6. Enumerate all modules/symbols/addresses in memory] (#6 Enumerate all module symbol addresses in memory) +- [7. Brainless Automation hook All Functions Under App Package] (#7 Brainless Automation hook - All Functions Under App Package) +- [8.Objection Memory Roaming Scratches All Objects] (#8objection- Memory Roaming Scratches All Objects) +- [9.ObjC.choose enumerates all class output properties] (#9objcchoose enumerates all class output properties) +- [10. Actively Invoking Object Methods to Obtain Algorithm Execution Results] (#10 Actively Invoking Object Methods to Obtain Algorithm Execution Results) +- [11 Configure RPC Black Box Calling Algorithm Remote Bulk Offline] (#11 Configure RPC Black Box Calling Algorithm Remote Bulk Offline) - + -# 前言 +# Preface -终于学到了Frida的部分,在本篇中我们将从objection这款自动化hook框架的源码出发,学习使用Frida内置的ApiResolver搜刮器来枚举内存中的所有符号,包括所有类/所有方法/所有重载,再对其进行hook后输出参数调用栈返回值,并且对参数与返回值进行修改,以实现修改函数逻辑的目的。 +Finally we learned the part of Frida, in this paper we will learn to use the ApiResolver scraper built in Frida to enumerate all symbols in memory, including all classes/all methods/all overloads, from the source code of objection automatic hook framework. Then we will output the return value of the parameter call stack after hook, and modify the parameters and return value in order to achieve the purpose of modifying the function logic. -一次性hook一个函数并不是最终目的,批量自动化一次性hook成百上千个函数才是真的牛逼,本文中我们还介绍一种搜刮App的所有类并全部hook上的方法,并且使用Frida遍历内存的特性搜刮ObjC的对象,直接调用方法,获取算法的执行结果,最终配置RPC进行远程批量脱机调用,实现团队成员在即使不知道算法实现细节的情况下,依旧可以获取算法执行结果的目的。 +One-time hook function is not the final purpose, batch automation Hundreds of one-time hook function is really the bully, in this paper we also introduce a method to scrape all the classes of App and all the hook, and use the traversal memory property of Frida to scrape the ObjC object, directly call the method to get the algorithm's execution results, and finally configure RPC to make remote batch offline calls to achieve the purpose that team members can still get the algorithm's execution results even if they don't know the details of the algorithm. -附件APP、代码等位于我的项目中,大家可以自取: +Accessories APP, code, etc. are in my project, you can take it from: [https://github.com/r0ysue/AndroidSecurityStudy](https://github.com/r0ysue/AndroidSecurityStudy) -# 1.ios hooking search 源码解析 +# 1.ios hooking search Source Parsing -这里我们直接去github[下载](https://github.com/sensepost/objection)objection的源码,查看其对ios的hook是如何写的 +Here we go straight to github[download](https://github.com/sensepost/objection)objection source code to see how its hook for ios is written -objection 中ios相关hook代码在agent-->src-->ios-->hooking.ts文件上,这里我们主要查看其search 及watch如何实现。 +ios-related hook code in objection is in the agent—>src—>ios—>hooking.ts file, here we mainly look at how its search and watch implementation. -``` -const objcEnumerate = (pattern: string): ApiResolverMatch[] => { - return new ApiResolver('objc').enumerateMatches(pattern); +```swift +const objcEnumerate =(pattern: string): ApiResolverMatch[] => { + return new ApiResolver('objc').enumerateMatches(pattern); }; -export const search = (patternOrClass: string): ApiResolverMatch[] => { +export const search =(patternOrClass: string): ApiResolverMatch[] => { - // if we didnt get a pattern, make one assuming its meant to be a class - if (!patternOrClass.includes('[')) - patternOrClass = `*[*${patternOrClass}* *]`; + // if we didnt get a pattern, make one assuming its meant to be a class + if(!patternOrClass.includes('[')) + patternOrClass = `*[*${patternOrClass}* *]`; - return objcEnumerate(patternOrClass); + return objcEnumerate(patternOrClass); }; ``` -这里首先查看search源码,可以看到我们传入的参数作为patternOrclass参数,首先检验是否为相应格式,如果不是相应格式则进行补全,之后使用ApiResolverMatch进行内存中的符号搜索,以找到要hook方法的地址,ios的hook与android中so层的hook都是直接对地址进行hook,再看watch方法 +Here, we first look at the search source code, we can see that the parameters we passed in as the patternOrclass parameters, first check whether it is the corresponding format, if not the corresponding format, then complete it, and then use ApiResolverMatch to search the symbols in memory to find the address of the hook method, the hook of ios and the hook of so layer in android are all directly to hook the address, and then look at the watch method -``` -export const watch = (patternOrClass: string, dargs: boolean = false, dbt: boolean = false, - dret: boolean = false, watchParents: boolean = false): void => { - - // Add the job - // We init a new job here as the child watch* calls will be grouped in a single job. - // mostly commandline fluff - const job: IJob = { - identifier: jobs.identifier(), - invocations: [], - type: `ios-watch for: ${patternOrClass}`, - }; - jobs.add(job); - - const isPattern = patternOrClass.includes('['); - - // if we have a patterm we'll loop the methods, hook and push a listener to the job - if (isPattern === true) { - const matches = objcEnumerate(patternOrClass); - matches.forEach((match: ApiResolverMatch) => { - watchMethod(match.name, job, dargs, dbt, dret); - }); +```swift +export const watch =(patternOrClass: string, dargs: boolean = false, dbt: boolean = false, +dret: boolean = false, watchParents: boolean = false): void => { + + // Add the job + // We init a new job here as the child watch* calls will be grouped in a single job. + // mostly commandline fluff + const job: IJob = { + identifier: jobs.identifier(), + invocations: [], + type: `ios-watch for: ${patternOrClass}`, + }; + jobs.add(job); + + const isPattern = patternOrClass.includes('['); + + // if we have a patterm we'll loop the methods, hook and push a listener to the job + if(isPattern === true){ + const matches = objcEnumerate(patternOrClass); + matches.forEach((match: ApiResolverMatch)=> { + watchMethod(match.name, job, dargs, dbt, dret); + }); return; - } +} - watchClass(patternOrClass, job, dargs, dbt, dret, watchParents); +watchClass(patternOrClass, job, dargs, dbt, dret, watchParents); }; ``` -当使用watch参数时会将其添加进jobs列表,同时调用watchClass方法 +When the watch parameter is used, it is added to the jobs list and the watchClass method is called -``` -const watchClass = (clazz: string, job: IJob, dargs: boolean = false, dbt: boolean = false, - dret: boolean = false, parents: boolean = false): void => { +```ObjectiveC +const watchClass =(clazz: string, job: IJob, dargs: boolean = false, dbt: boolean = false, +dret: boolean = false, parents: boolean = false): void => { - const target = ObjC.classes[clazz]; +const target = ObjC.classes[clazz]; - if (!target) { - send(`${c.red(`Error!`)} Unable to find class ${c.redBright(clazz)}!`); - return; - } +if(!target){ +send(`${c.red(`Error!`)} Unable to find class ${c.redBright(clazz)}!`); +return; +} - // with parents as true, include methods from a parent class, - // otherwise simply hook the target class' own methods - (parents ? target.$methods : target.$ownMethods).forEach((method) => { - // filter and make sure we have a type and name. Looks like some methods can - // have '' as name... am expecting something like "- isJailBroken" - const fullMethodName = `${method[0]}[${clazz} ${method.substring(2)}]`; - watchMethod(fullMethodName, job, dargs, dbt, dret); - }); +// with parents as true, include methods from a parent class, +// otherwise simply hook the target class' own methods +(parents ? target.$methods : target.$ownMethods).forEach((method)=> { +// filter and make sure we have a type and name. Looks like some methods can +// have "as name... am expecting something like "- isJailBroken" +const fullMethodName = `${method[0]}[${clazz} ${method.substring(2)}]`; +watchMethod(fullMethodName, job, dargs, dbt, dret); +}); }; ``` -可以看到watchClass方法中首先通过传入参数clazz当作参数使用api ObjC.classes[clazz]获取该类对象,然后遍历该类方法分别调用watchMethod方法进行方法hook. +You can see that the watchClass method first gets the class object by passing the parameter clazz as a parameter using api ObjC.classes[clazz], and then traverses the class method to call the watchMethod method to the method hook. -``` -const watchMethod = (selector: string, job: IJob, dargs: boolean, dbt: boolean, - dret: boolean): void => { - - const resolver = new ApiResolver("objc"); - let matchedMethod = { - address: undefined, - name: undefined, - }; - - // handle the resolvers error it may throw if the selector format is off. - try { - // select the first match - const resolved = resolver.enumerateMatches(selector); - if (resolved.length <= 0) { - send(`${c.red(`Error:`)} No matches for selector ${c.redBright(`${selector}`)}. ` + +```ObjectiveC + const watchMethod =(selector: string, job: IJob, dargs: boolean, dbt: boolean, + dret: boolean): void => { + + const resolver = new ApiResolver("objc"); + let matchedMethod = { + address: undefined, + name: undefined, + }; + + // handle the resolvers error it may throw if the selector format is off. + try { + // select the first match + const resolved = resolver.enumerateMatches(selector); + if(resolved.length <= 0){ + send(`${c.red(`Error:`)} No matches for selector ${c.redBright(`${selector}`)}. ` + `Double check the name, or try "ios hooking list class_methods" first.`); - return; + return; } // not sure if this will ever be the case... but lets log it // anyways - if (resolved.length> 1) { - send(`${c.yellow(`Warning:`)} More than one result for selector ${c.redBright(`${selector}`)}!`); + if(resolved.length> 1){ + send(`${c.yellow(`Warning:`)} More than one result for selector ${c.redBright(`${selector}`)}!`); } matchedMethod = resolved[0]; - } catch (error) { + } catch(error){ send( - `${c.red(`Error:`)} Unable to find address for selector ${c.redBright(`${selector}`)}! ` + - `The error was:\n` + c.red((error as Error).message), - ); - return; - } - - // Attach to the discovered match - // TODO: loop correctly when globbing - send(`Found selector at ${c.green(matchedMethod.address.toString())} as ${c.green(matchedMethod.name)}`); - - const watchInvocation: InvocationListener = Interceptor.attach(matchedMethod.address, { - // tslint:disable-next-line:object-literal-shorthand - onEnter: function (args) { - // how many arguments do we have in this selector? - const argumentCount: number = (selector.match(/:/g) || []).length; - const receiver = new ObjC.Object(args[0]); - send( - c.blackBright(`[${job.identifier}] `) + - `Called: ${c.green(`${selector}`)} ${c.blue(`${argumentCount}`)} arguments` + - `(Kind: ${c.cyan(receiver.$kind)}) (Super: ${c.cyan(receiver.$superClass.$className)})`, - ); - - // if we should include a backtrace to here, do that. - if (dbt) {1 - - send( - c.blackBright(`[${job.identifier}] `) + - `${c.green(`${selector}`)} Backtrace:\n\t` + - Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t"), + `${c.red(`Error:`)} Unable to find address for selector ${c.redBright(`${selector}`)}! ' + + `The error was:\n` + c.red((error as Error).message), ); - } - - if (dargs && argumentCount> 0) { - const methodSplit = ObjC.selectorAsString(args[1]).split(":").filter((val) => val); - const r = methodSplit.map((argName, position) => { - // As this is an ObjectiveC method, the arguments are as follows: - // 0. 'self' - // 1. The selector (object.name:) - // 2. The first arg - // - // For this reason do we shift it by 2 positions to get an 'instance' for - // the argument value. - const t = new ObjC.Object(args[position + 2]); - return `${argName}: ${c.greenBright(`${t}`)}`; - }); - - send(c.blackBright(`[${job.identifier}] `) + - `Argument dump: [${c.green(receiver.$className)} ${r.join(" ")}]`); - } - }, - onLeave: (retval) => { - // do nothing if we are not expected to dump return values - if (!dret) { return; } - send(c.blackBright(`[${job.identifier}] `) + `Return Value: ${c.red(retval.toString())}`); - }, - }); - - job.invocations.push(watchInvocation); -}; -``` - -观察代码可以发现,这里使用frida 的api Interceptor.attach 进行hook同时根据传入的参数dargs,dbt判断是否打印出参数及调用栈 - -# 2.ApiResolver 搜刮内存中所有符号 - -首先去官网查看该api的介绍 - -![6](pic/6.png) - -可以看到该api支持module模块及objc并且返回值为包含api名称及地址的对象数组,下边我们使用该api编写hook代码 - -补充:这里我们可以看到ObjC的hook代码在编写时没有Java.perform()原因是Frida注入时,会自动创建一个被注入程序的运行时环境,OC的Runtime java 的jvm 但是由于jvm是一个独立的"库" 也就是libart.so,因此frida注入后会hook这个art.so使用其内置的API进行java hook,因此frida注入后创建的是自己的jvm虚拟机进程,但是我们想操作应用的类或方法时就要进入他的jvm - -``` -setImmediate(() => { - const resolver = new ApiResolver('objc'); - const matches = resolver.enumerateMatches('*[ViewController *]'); - matches.forEach((match)=>{ - console.log(JSON.stringify(match)) - }) -}) -``` - -![7](pic/7.png) - -# 3.枚举搜索所有类/所有方法/所有重载 - -其实搜索所有类及所有方法和重载仍然使用ApiResolver 即可只需要修改枚举匹配的字串 - -``` -setImmediate(() => { - const resolver = new ApiResolver('objc'); - const matches = resolver.enumerateMatches('*[* *]'); - matches.forEach((match)=>{ - console.log(JSON.stringify(match)) - }) -}) -``` - -![8](pic/8.png) - -这里可以看到已经将所有的api及属性名称及地址全部枚举打印出来了 - -# 4.hook所有类/所有方法/所有重载 - -首先我们先用objection去hook以下ViewController类 - -``` -frida-ps -U 查看应用名 -objection -g UnCrackable1 explore objection 注入应用进程 -ios hooking watch class ViewController hook ViewController类 -``` - -![9](pic/9.png) - -``` -ios hooking list class_methods ViewController 列出所有方法签名 - --include-parents是否包含父类方法 -``` - -![10](pic/10.png) - -``` -ios hooking watch method "*[ViewController buttonClick:]" --dump-args --dump-backtrace --dump-return -hook 相应方法 -``` - -![11](pic/11.png) - -到了这里我们想要去查看objection中的代码具体位置可以复制hook时打印出的提示字符串Found selector去objection中搜索查看如何实现 - -![12](pic/12.png) - -搜索发现提示字符串是在方法watchMethod中打印出来的,具体hook实现逻辑上边也说过,其实是watchClass-->watchMethod,然后在watchMethod中对方法地址使用Interceptor.attach 这个api进行hook,我们去官网看一下这个api的简单使用 - -![13](pic/13.png) - -``` - const resolver = new ApiResolver('objc'); - const matches = resolver.enumerateMatches('*[* isEqualToString:*]'); - matches.forEach((match) => { - console.log(JSON.stringify(match)) - Interceptor.attach(match.address,{ - onEnter:function(args){ - - },onLeave:function(ret){ - - } - }) - }) -``` - -查看代码可以看到我们使用ApiResolver 枚举字符并且使用了JSON.stringify(match)将便利的方法转为字符串打印出来,这里转化的原因是在上边也说过,因为得到的是对象数组,这个对象包含方法名及地址属性,然后使用 Interceptor.attach其match.address进行hook,onEnter是原方法执行前,onLeave是方法执行后,args是方法执行参数,ret是返回值都可以自己命名 - -# 5.输出(修改)解析参数/调用栈/返回值 - -## 1修改返回值实现目的 - -这里我们查看demo源码可以看到其通过判断输入字符串与隐藏字符串进行比较得到结果 - -![14](pic/14.png) - -因此我们这里直接hook isEqualToString 方法的返回值使其返回正确 - -``` -setImmediate(() => { - console.log("hello world!r0ysue! objc =>", ObjC.available) - const resolver = new ApiResolver('objc'); - const matches = resolver.enumerateMatches('*[* isEqualToString:*]'); - matches.forEach((match) => { - console.log(JSON.stringify(match)) - Interceptor.attach(match.address,{ - onEnter:function(args){ - this.change = false; - if(receiver.toString().indexOf("aaaabbbb")>= 0){ - this.change = true; - console.log("need change"); - - } - },onLeave:function(ret){ - console.log("ret=>",ret) - if(this.change){ - ret.replace(new NativePointer(0x1)) - } - } - }) - }) - -}) -``` - -这里我们在代码中直接修改了返回值为true就可以使返回结果正确这里要注意两点: - -(1)ret是引用对象,如果我们想修改其值,需要使用官方提供的replace方法,如上所示。 - -(2)Interceptor.attach 中参数和返回值都是指针,即便是数值类型也需要我们使用ptr()将其转为指针类型赋值 - -## 2修改参数实现目的 - -``` -setImmediate(() => { - // console.log("hello world!r0ysue! objc =>", ObjC.available) - const resolver = new ApiResolver('objc'); - const matches = resolver.enumerateMatches('*[* isEqualToString:*]'); - matches.forEach((match) => { - console.log(JSON.stringify(match)) - Interceptor.attach(match.address,{ - onEnter:function(args){ - const receiver = new ObjC.Object(args[0]) - console.log("receiver is =>",receiver.$className, " =>",receiver.toString()); - if(receiver.toString().indexOf("aaabbb")>= 0){ - - const { NSString } = ObjC.classes; - var newString = NSString.stringWithString_("aaabbb"); - args[2] = newString; - } - },onLeave:function(ret){ - - } - }) - }) - -``` - -仍旧是分析demo案例修改isEqualToString 方法的参数,使其判断为true。这里要注意以下两点: - -(1)我们在前边ObjC基础语法说过在OC语言中消息机制会转化为objc_msgSend(receiver, selector, arg1, arg2, ...)进行调用,也就是说在方法调用时第一个参数为调用者本身,第二个参数为selector即方法的名字,第三个参数及之后的参数才是其真实参数,这里我们可以看到我们通过 const receiver = new ObjC.Object(args[0])获取了调用者本身并将其转化为OC对象,将其转化为OC对象后我们可以调用frida的api,如官网所示 - -![15](pic/15.png) - -我们可以根据这个对象使用一系列api获取其类名,方法等 - -(2)我们要对参数赋值指针,因此我们新建NSString将其赋值这也是在官网找到的实现 - -![16](pic/16.png) - -## 3查看调用栈 - -打印调用栈依然是直接使用官网提供的api - -``` - console.log('CCCryptorCreate called from:\n' + - Thread.backtrace(this.context, Backtracer.ACCURATE) - .map(DebugSymbol.fromAddress).join('\n') + '\n'); -``` - -![17](pic/17.png) - -这里还是用了几个解析api: - -jsonStringfy :js中将js对象转为字符串 - -tostring :对像转为字符串 - -objc.object:转化为OC对象 - -## 4替换函数 - -上边的修改参数返回值都是建立在我们想让函数执行的情况下,但是有时我们可能并不想让方法执行,那么我们就可以使用下边的方法直接替换其执行 - -``` -setImmediate(() => { - const ViewController = ObjC.classes.NSString; - const buttonClick = ViewController['- buttonClick:']; - const oldImpl = buttonClick.implementation; - buttonClick.implementation = ObjC.implement(buttonClick,(handle, selector,args)=>{ - console.log("handle selector args =>",handle,selector,args) - console.log(Thread.backtrace(this.AudioContext,Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n\t')) - oldImpl(handle,selector,args); - }) -}) -``` - -可以看到上述代码提前将方法地址保存在oldImpl然后使用ObjC.implement 进行方法的替换,是否在内执行原方法可以自己去实现 - -# 6.枚举内存中所有模块/符号/地址 - -``` -frida-ps -Ua 查看运行的应用 -objection -g UnCrackable1 explore objection注入应用进程 -memory list modules 列举内存中加载的模块 -``` - -![18](pic/18.png) - -``` -memory list exports UnCrackable\ Level\ 1 列出模块中的导出符号 -memory list exports Foundation -``` - -![19](pic/19.png) - -我们去看看objection是如何实现的枚举模块及枚举模块内导出符号,代码在agent-->generic-->memory.ts - -``` -export const listModules = (): Module[] => { - return Process.enumerateModules(); -}; - -export const listExports = (name: string): ModuleExportDetails[] | null => { - const mod: Module[] = Process.enumerateModules().filter((m) => m.name === name); - if (mod.length <= 0) { - return null; - } - return mod[0].enumerateExports(); -}; -``` - -可以看到其实现逻辑其实很简单,就是使用了frida的api Process.enumerateModules()得到模块对象数组,然后根据模块对象调用enumerateExports()得到导出符号,与安卓中c相关的符号地址枚举其实是一样的,自己实现代码如下: - -``` - function listExports(name) { - //1可以在枚举模块是保存模块的name属性作为参数然后作为参数给Module.enumerateExports(name) - // var exporttArr = Module.enumerateExports(name); - //2也可以在枚举模块是保存模块对象当作参数,然后用模块对象调用name.enumerateExports() - var exporttArr = name.enumerateExports(); - // var exporttArr = name.enumerateSymbols(); - console.log("枚举模块==》",name.name) - for(var i = 0;i",modules[i].name,"--address:",JSON.stringify(modules[i].base)) - console.log("=======================================") - console.log("模块名称:",JSON.stringify(modules[i].name)); - console.log("模块地址:",JSON.stringify(modules[i].base)); - console.log("大小:",JSON.stringify(modules[i].size)); - console.log("文件系统路径",JSON.stringify(modules[i].path)); - } - return modules[0] - } - function main(){ - var modulename = listModules(); - console.log(modulename) - listExports(modulename) - } - setImmediate(main) -``` - -# 7.无脑自动化hook 应用包下所有函数 - -hook应用包下所有函数,我们可以去网上搜索[相关案例](https://codeshare.frida.re/@interference-security/ios-app-all-classes-methods-hooks/)查看如何编写 - -``` -function get_timestamp() -{ - var today = new Date(); - var timestamp = today.getFullYear() + '-' + (today.getMonth()+1) + '-' + today.getDate() + ' ' + today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds() + ":" + today.getMilliseconds(); - return timestamp; -} - -function hook_class_method(class_name, method_name) -{ - var hook = eval('ObjC.classes.'+class_name+'["'+method_name+'"]'); - Interceptor.attach(hook.implementation, { - onEnter: function(args) { - console.log("[*] [" + get_timestamp() + " ] Detected call to: " + class_name + " -> " + method_name); - } - }); -} - -function run_hook_all_methods_of_classes_app_only() -{ - console.log("[*] Started: Hook all methods of all app only classes"); - var free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer']) - var copyClassNamesForImage = new NativeFunction(Module.findExportByName(null, 'objc_copyClassNamesForImage'), 'pointer', ['pointer', 'pointer']) - var p = Memory.alloc(Process.pointerSize) - Memory.writeUInt(p, 0) - var path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String() - var pPath = Memory.allocUtf8String(path) - var pClasses = copyClassNamesForImage(pPath, p) - var count = Memory.readUInt(p) - var classesArray = new Array(count) - for (var i = 0; i < count; i++) - { - var pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize)) - classesArray[i] = Memory.readUtf8String(pClassName) - var className = classesArray[i] - if (ObjC.classes.hasOwnProperty(className)) - { - //console.log("[+] Class: " + className); - //var methods = ObjC.classes[className].$methods; - var methods = ObjC.classes[className].$ownMethods; - for (var j = 0; j < methods.length; j++) - { - try - { - var className2 = className; - var funcName2 = methods[j]; - //console.log("[-] Method: " + methods[j]); - hook_class_method(className2, funcName2); - //console.log("[*] [" + get_timestamp() + "] Hooking successful: " + className2 + " -> " + funcName2); - } - catch(err) - { - console.log("[*] [" + get_timestamp() + "] Hooking Error: " + err.message); - } - } - } - } - free(pClasses) - console.log("[*] Completed: Hook all methods of all app only classes"); -} - -function hook_all_methods_of_classes_app_only() -{ - setImmediate(run_hook_all_methods_of_classes_app_only) + return; } - -hook_all_methods_of_classes_app_only() -``` - -可以看到该脚本主要实现逻辑大体如下: - -1在run_hook_all_methods_of_classes_app_only方法中获取所有类,具体获取方法是使用copyClassNamesForImage这一 Objective-C 运行时中的一个私有 API去 获取当前 App 中定义的所有类名,并用内存数组保存 - -2然后遍历类方法,并使用hasOwnProperty这一js的api判断类是否属于当前app,如果是就遍历类中方法 - -3使用hook_class_method 方法进行每个方法的实际hook - -![20](pic/20.png) - -# 8.Objection 内存漫游搜刮所有对象 - -先使用objection搜刮ViewController对象 - -``` -ios heap search instances ViewController -``` - -![21](pic/21.png) - -然后我们去objection代码中查看其具体实现,位置在agent-->src-->ios-->heap.ts - -``` -const enumerateInstances = (clazz: string): ObjC.Object[] => { - if (!ObjC.classes.hasOwnProperty(clazz)) { - c.log(`Unknown Objective-C class: ${c.redBright(clazz)}`); - return []; - } - - const specifier: ObjC.DetailedChooseSpecifier = { - class: ObjC.classes[clazz], - subclasses: true, // don't skip subclasses - }; - - return ObjC.chooseSync(specifier); -}; - -export const getInstances = (clazz: string): IHeapObject[] => { - c.log(`${c.blackBright(`Enumerating live instances of`)} ${c.greenBright(clazz)}...`); - - return enumerateInstances(clazz).map((instance): IHeapObject => { - try { - return { - className: instance.$className, - handle: instance.handle.toString(), - ivars: instance.$ivars, - kind: instance.$kind, - methods: instance.$ownMethods, - superClass: instance.$superClass.$className, - }; - } catch (err) { - c.log(`Warning: ${c.yellowBright((err as Error).message)}`); - } - }); -}; -``` - -可以看到其实最终其实是调用了ObjC.chooseSync这一frida提供的api来进行对象的搜刮 - -![22](pic/22.png) - -这里之所以使用ObjC.chooseSync是因为`ObjC.choose` 是一个异步的 API而`ObjC.chooseSync` 是一个同步的 API,如果我们需要迅速地枚举大量对象,并且可以接受可能漏掉一些对象,那么我们可以使用 `ObjC.choose`。但是如果我们需要保证枚举所有对象,并且可以接受较慢的速度,则调用 `ObjC.chooseSync` 更为适合。 - -# 9.ObjC.choose枚举所有类输出属性 - -当我们使用ObjC.choose枚举到实际的instance时就可以直接打印这个实例的属性frida官网也提供了这些方法![23](pic/23.png) - -实现代码如下: - -``` -setImmediate(() => { - if(ObjC.available){ - const specifier = { - class: ObjC.classes['ViewController'], - subclasses: true, - }; - ObjC.choose(specifier,{ - onMatch:function(ins){ - console.log("found ins =>",ins) - console.log("ivars =>",ins.$ivars["_theLabel"].toString()) - console.log("methods =>",ins.$ownMethods) - },onComplete(){ - console.log("Search Completed") - } - }) - } -}) -``` - -可以看到我们枚举到对象ins后直接调用其$ivars可以得到属性对象,使用toString()转化为字符串后即可打印输出 - -# 10.主动调用对象方法获取算法执行结果 - -现在objection中查看如何主动调用 - -``` -ios heap search instances ViewController -ios heap print methods 0x15dd08220 -ios heap execute 0x15dd08220 theLabel --return-string -``` - -![24](pic/24.png) - -可以看到这些方法都是一个对象,但是在demo中do_it方法是一个用c实现的函数,是没有对象的此时调用代码如下: - -``` - function callcmethod(name){ - var doit = new NativeFunction(Module.findExportByName(null, name),'pointer', []) - console.log("doit result => ",doit().readCString()) - - } - function main(){ - // var modulename = listModules(); - // console.log(modulename) - // listExports(modulename) - callcmethod('do_it') - - } - setImmediate(main) -``` - -具体可以描述为以下步骤: - -1调用之前写的枚举模块及模块内到处字符列表查看方法导出字符 - -2调用Module.findExportByName(null, name)导出字符为第二个参数,第一个参数为模块名得到该函数地址 - -3调用frida 的api NativeFunction()该api接受三个参数生成一个js的函数对象三个参数分别为函数地址,原生函数返回值类型,原生函数参数类型数组 - -4执行上边生成的js函数对象即调用了原函数 - -# 11配置RPC黑盒调用算法远程批量脱机 - -js代码如下: - -``` - function callSecretFunc(){ - return new NativeFunction(Module.findExportByName(null, 'do_it'),'pointer', [])().readCString() - } - - rpc.exports={ - callSecretFunction:callSecretFunc - } -``` - -手动hook注入进程调用测试 - -![25](pic/25.png) - -执行成功,说明导出成功 - -py代码如下: - -``` -import time -import frida - -def my_message_handler(message, payload): - print(message) - print(payload) - - -device = frida.get_usb_device() -#frida.get_usb_device() 相当于手工注入式的 -U参数 通过USB获取设备 -#device.get_frontmost_application() 相当于-F 获取当前页面进程 -#创建一个session对象准备注入 -session = device.attach(device.get_frontmost_application().pid) -#读取js文件并通过session开始注入 -with open("ios2.js") as f : - script = session.create_script(f.read()) -script.on("message", my_message_handler) -script.load() - -command = "" -while 1 ==1 : - command = input("Enter Command:") - if command == "1": - break - elif command == "2": - print(script.exports.callSecretFunctionon()) ``` -![26](pic/26.png) +// Attach to the discovered match -以上就是本篇文章全部内容,下一篇文章会继续学习frida为r0tracer增加ios的trace功能。 \ No newline at end of file +// TODO: loop correctly when globbing +send \ No newline at end of file diff --git a/Student/008/README.md b/Student/008/README.md index 78b8775..e55d097 100644 --- a/Student/008/README.md +++ b/Student/008/README.md @@ -1,69 +1,67 @@ -## 2023HW移动端武器库工具技巧分享 +## 2023HW Mobile Arsenal Tools Tips Sharing -(PS:文章中包含大量付费的链接、工具和商品,不喜勿读) +(PS: Articles with expensive links, tools, and merchandise to read) ### Intro -HW也开展有些年头了,2023的HW也如火如荼进行当中,今年HW的感受每个人都深有体会,这里不再赘述,笔者只结合自身的特长与实际任务中的经验,分享一下HW中移动端的分析打点与常见工具(AKA武器)及其应用的场景。 +HW has been around for some years, and HW 2023 is in full swing. What everyone feels at HW this year will no longer need to discuss. Combining my own expertise with real-world tasks, I'll share some of HW's mobile-side insights with common tools (AKA weapons) and their applications. -很多大型企业集团的OA等系统提供了移动端的登录和使用方式,这些系统由于相对封闭、使用群体固定且缺少测试存着诸多安全问题,很多(存在漏洞的)接口只存在于APP或者小程序,必须通过移动端逆向工程来找到这些接口。 +Many large-scale enterprise group OA systems provide mobile terminal login and use, these systems due to relatively closed, use groups fixed and lack of testing there are many security issues, many (loopholes) interfaces exist only in the APP or small programs, must be through the mobile reverse engineering to find these interfaces. -移动端的逆向,不外乎环境搭建、抓包分析和算法调用这三个主要方面。这里笔者分享一下各个方向领域的当前最为前沿、实用和高效的工具,帮助大家提高效率,事半功倍。以下所有工具的综合使用案例在MobileCTF项目中均有完整体现,欢迎大家踊跃尝试练手升级:https://github.com/r0ysue/MobileCTF/ +The reverse of the mobile terminal does not involve the environment building, packet capture analysis and algorithm call. Here I share some of the most cutting-edge, practical and efficient tools in all directions to help you become more efficient and do more with less. All of the following tools are fully featured in the MobileCTF engagement and you are welcome to try your hand at the upgrade: https://github.com/r0ysue/MobileCTF/ -### 渗透测试武器库环境搭建 +### Environmental Construction of Penetration Test Weapon Depot -逆向与正向不同,逆向没有官方的质保,一切都靠摸索与尝试。一套好的逆向环境就已经成功了一半,有时候还需要不断的切换环境,不同的环境都会有不同的报错和不同的结果。 +Converse is different from forward. Converse has no official guarantee. Everything depends on fumbling and trying. A good set of reverse environment has been half successful, sometimes also need to constantly switch environment, different environment will have different error reporting and different results. -分享一下我的环境: +Share my environment: -机型:Pixel 1代 sailfish;选择理由:谷歌亲儿子,可刷官方原版安卓7-10,刷Fart8终极版脱壳机,运行Frida12-14版本老代码一把梭,基于Frida的脱壳机dexdump,抓包工具r0capture,追踪器r0tracer,稳定高效bug少。 +Models: Pixel 1G sailfish; Reasons for choice: Google Pro Son, swipe the official original Android 7-10, swipe the Fart8 Ultimate Shell, run the Frida12-14 version of the old code as a shuttle, Frida-based shell remover dexdump, grab tool r0capture, tracker r0tracer, stable and efficient with few bugs. -机型:一加7pro;选择理由:国行手机双卡双待,刷好安卓10搭配Kali Nethunter移动渗透工具箱,解锁内核全功能,内置Wireshark网卡抓包、从内核对文件/网络/系统调用进行监控 详细文章:https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g。 +Model: OnePlus 7pro; Reason for selection: CBC mobile phone dual-card dual-standby, brush Android 10 with Kali Nethunter mobile penetration toolbox, unlock the full functionality of the kernel, built-in Wireshark network card to capture packets, from the kernel file / network / system calls for monitoring detailed article: https://mp.weixin.qq.com/s/PIiGZKW6oQnOAwlCqvcU0g. -机型:Pixel 6代;选择理由:谷歌亲儿子,安卓12-13,运行最新Frida16,主打全功能eBPF从上帝视角俯视App的所有行为,分析绕过root/frida反调试事半功倍。更强的性能,扛得住批量trace,一键hook成千上万个类与方法,暴力定位收发包函数、参数的生成位置,从抓包到算法一条龙 +Models: Pixel 6G; Why: Google Sons, Android 12-13, running the latest Frida16, featuring the full-featured eBPF God-sees all the App's behavior, and analyzes how to get around the root/frida counter-debugging with less effort. Stronger performance, can withstand the bulk of trace, one-button hook thousands of classes and methods, brute-force positioning packet function, parameter generation location, from the packet capture to the algorithm a dragon -机型:云手机:rock5b或orangepi;选择理由:NVMe SSD硬盘,GPU加速,运行安卓流畅不卡顿,速度有保障,一套板卡虚拟出十台手机,免去了电池鼓包网络抖动卡顿的烦恼。预装好frida/lsposed发货,主要场景是免分析还原算法而进行黑盒算法调用,直接跑发包所需的签名算法或加解密算法,并且可以做成HTTP接口供团队成员使用 +Models: Cloud Phone: rock5b or orangepi; Reasons for choice: NVMe SSD hard drive, GPU acceleration, smooth and unstuck Android, guaranteed speed, 10 phones virtually out of one board without the hassle of battery-packed network jitters. Pre-installed frida/lsposed shipping, the main scene is analysis-free restore algorithm and black-box algorithm call, directly run the required signature algorithm or encryption and decryption algorithm, and can be made into a HTTP interface for team members to use -机型:iPhone X;选择理由:iOS低版本14和高版本16各一台,分别跑Frida14与Frida16,分别使用checkra1n与palera1越狱,iOS的App分析没有各种企业壳与反调试,简单许多,难度主要在ObjC语言上。语言可以通过学习掌握,企业壳和反调试真不是人能过的。笔者现在以分析移动端的iOS版本为主,速度快、效率高 +Models: iPhone X; Reasons for selection: iOS One for Low 14 and One for High 16 running Frida14 and Frida16 respectively, using checkra1n and palera1 respectively to escape, iOS's App analysis has no variety of enterprise shells and anti-debugging, much simpler and more difficult mainly on the ObjC language. Language can be mastered by learning, and business shells and anti-debugging are not human. The author is now to analyze the mobile version of iOS-based, fast, efficient -其他软硬件还有收费版脱壳机脱企业壳Fart12,集成好多版本Frida/r0cap/r0tracer、IDA/charles/jadx/jeb动静态调试环境的开箱即用的r0env,具体介绍及获取地址看这里:https://github.com/r0ysue/AndroidSecurityStudy +Other hardware and software are a charging version of the shell removal machine Fart12, integrated with many versions of Frida/r0cap/r0tracer, IDA/charles/jadx/jeb dynamic and static debugging environment out of the box for r0env, specific introduction and access to address here: https://github.com/r0ysue/AndroidSecurityStudy -### 抓包环境:一个都少不了! +###Catch environment: Not one less! -包都抓不到,逆向分析无从谈起。 +I can't get a bag. Reverse analysis is impossible. -更不要小看抓包,小小的抓包,可以深入到系统底层、内核,上帝视角抓APP所有流量包;也可以从应用层进行防护,手段那是千变万化,你从内核抓也无济于事,只能从应用层解! +Not to mention small grab, small grab, can go deep into the system floor, the kernel, God perspective grab all APP traffic packets; can also be protected from the application layer, the means is ever-changing, you grab from the kernel is not helpful, can only understand from the application layer! -绝大部分应用标配的是HTTP/SSL默认的防护手段,也就是客户端校验服务器证书,只需要配置Postern/Charles的VPN抓包即可抓取,具体可以看我的最新视频:https://www.bilibili.com/video/BV1M8411Z7rC ,支持到安卓13和KernerlSU,向下到安卓7、8都是通用的。在iOS上的搭配是shadowrocket + Charles,配置方法见:https://t.zsxq.com/11lQkInqh +Most of the applications are standard with the default HTTP/SSL protection means, that is, the client verify server certificate, only need to configure Postern/Charles VPN packet capture can be retrieved, specific can see my latest video:https://www.bilibili.com/video/BV1M8411Z7rC, support to Android 13 and KernerlSU, down to Android 7, 8 are generic. On iOS, shadowrocket + Charles, configured at https://t.zsxq.com/11lQkInqh -HTTP/SSL还有一种防护模式,类似于以前的银行U盾,也就是服务器校验客户端证书,Charles典型的报错是:400 No required SSL certificate was sent,需要使用r0capture从APP中导出客户端证书,导入到charles中去,这样服务器才认可来自charles的连接,具体方法和流程我的MobileCTF项目中有案例,从判定到解决方法和操作流程都有。使用这种防护技术的已经非常少了。 +HTTP/SSL also has a kind of defending mode, similar to the former bank U shield, that is, the server checks the client certificate, Charles typical error report is: 400 No required SSL certificate was sent, need to use r0capture to export the client certificate from the APP, import into charles, so that the server will recognize the connection from charles, concrete method and process I have a case in MobileCTF project, from judgment to resolution and operation process. The use of this kind of protection technology has been very rare. -更多是用的框架提供的SSL pinning功能,在APP代码里对收到的服务器证书算哈希,与一个特定值做比对,不对则断开连接,Charles典型的报错是:Client closed the connection before a request was made. Possibly the SSL certificate was rejected. 此时需要用frida去hook框架的代码,置空其SSL pinning逻辑使其返回通过,如果框架没有混淆,笔者用的是通用方案:https://github.com/WooyunDota/DroidSSLUnpinning ;如果框架被混淆成a.b.c.d了,那么只能r0tracer批量hook文件打开函数或者摘要函数,调用栈里找线索,找到pinner函数hook置空即可绕过,MobileCTF项目中也有相关案例。 +More is the use of the framework provided by the SSL pinning function, in the APP code on the received server certificate hash, and a specific value to be compared, if not, then disconnect, Charles typical error reporting is: Client closed the connection before a request was made. Possibly the SSL certificate was rejected. Now the need to use frida to hook framework code, null its SSL pinning logic to return through, if the framework is not confused, the author used a common scheme: https://github.com/WooyunDota/DroidSSLUnpinning, if the framework is confused with a.b.c.d, then only the r0tracer bulk hook file open function or summary function, call stack for clues, find the pinner function hook null can be bypassed, MobileCTF project also has a related case. -以上所有收发包的模式,使用的是系统原生自带的网络通信库,因此可以直接从系统本身出发导出收发包流量,比如r0capture、eCapture就可以实现SSL库的SSL_write和SSL_read收发包导出,忽略所有协议、框架、证书绑定、加固等等应用层的各种复杂变量;r0capture结合了Wireshark可以有更好和直观地展示流量,还提供了栈回溯、客户端证书导出等功能,辅助定位收发包及算法位置,功能更为强大。 +All the above modes of sending and receiving packets use the network communication library which is native to the system, so it can directly export the sending and receiving packet flow from the system itself, such as r0capture, eCapture can realize the SSL_write and SSL_read sending and receiving packet export of the SSL library, ignoring all the protocols, framework, certificate binding, reinforcement and other complex variables of the application layer; r0capture combined with Wireshark can have a better and intuitive display of traffic, and also provides the functions of heap backtracking, client certificate export and other functions, auxiliary positioning of sending and receiving packets and algorithm location, more powerful. -有些开发能力极强的厂商,已经不屑于使用系统自带的网络库,比如浏览器、小程序、游戏、跨平台超级应用如抖音等,一般都会有自己的网络通信SDK,这种一般情况下,Postern+Charles的VPN抓包是可以解决的,如小程序等。如果加了额外的防护,比如SSL pinning,就需要分析通信库框架本身的SSL pinning是如何实现的,比如抖音的通信库证书绑定的实现细节分析:https://bbs.kanxue.com/thread-277996.htm ,然后hook特定函数来绕过,这种情况也是最难的,考验综合逆向能力,https://t.zsxq.com/11U2cH4vi,https://t.zsxq.com/11Pfh1PyU。 +Some manufacturers with strong development ability have already been dismissive of using the system's own network library, such as browsers, small programs, games, cross-platform super applications such as Douyin, etc., generally will have their own network communication SDK, in this general case, Postern+Charles VPN packet capture can be solved, such as small programs. If you add additional protection, such as SSL pinning, you need to analyze how the communication library framework itself SSL pinning is implemented, such as Douyin communication library certificate binding implementation details analysis: https://bbs.kanxue.com/thread-277996.htm, then hook specific function to bypass, this situation is the hardest, test the comprehensive converse ability, https://t.zsxq.com/11U2cH4vi, https://t.zsxq.com/11Pfh1PyU. -当然还有些银行会使用自己特殊的金融通道,从证书体系到传输体系都是独立开发的,与标准都不一样,那么此时所有的基于标准HTTPS建立的抓包方式都会失效,这也是业界最强的防护,得完全从零开始逆他们开发的这个通道。还有应用会使用特殊的二进制序列化网络库,比如protobuf、HTTP2,比如:https://bbs.kanxue.com/thread-262207.htm ,这些都要对通信库有非常深入的理解和功底,也就是说需要综合逆向,才能明白抓到的包是什么,后续如何利用。 +Of course, some banks will use their own special financial channel, from the certificate system to the transmission system is developed independently, and standards are not the same, then all the standard HTTPS based on packet capture will fail, this is the industry's strongest protection, have to completely reverse the channel they developed from scratch. There are also applications that use special binary serialized network libraries, such as protobuf, HTTP2, such as: https://bbs.kanxue.com/thread-262207.htm, all of which have a very deep understanding of the communication library, that is to say, the need for a comprehensive reverse to understand what the packet is, and how to use it. -小小的抓包,深究起来,深不见底,核心的思路还是见招拆招,通过逆向研究APP是怎么搭起来的,顺着开发的思路去拆。越是通用的大家研究的多的东西,拆起来越容易;越是专用系统大家研究越少的东西,拆起来越难。 +The key idea is to do some reverse research on how to build the APP, and then do some reverse research on how to build the APP. The more generic things that people study, the easier it is to dismantle; the more specialized systems that people study, the less hard it is to dismantle. -### 算法还原与黑盒调用 +### Algorithm Restore and Black Box Call -安卓APP的话首先得脱壳,壳都脱不了,静态分析无从谈起。iOS的话是砸壳。 +Android APPs have to be shelved first, and neither can be shelved, and static analysis is impossible. IOS is crashing. -iOS砸壳没有难度,frida-ios-dump秒砸,百试百灵;如果遇到frida反调试就用CrackerXI+,也大多数可以成功。安卓脱壳的话,讲究就可太多了,一二三代壳,每种脱法都不同,还有各种强混淆和反调试,特别恶心人。这里介绍几个常用的,一般免费壳用fart8终极版、frida-dexdump、frida_fart即可秒脱;二代函数抽取壳用youpk(现在已经被大多数企业壳anti),fart12收费版即可。三代壳dexvmp、dex2c这些只能靠jnitrace来分析Java层逻辑,然后手动还原算法,没有全自动的方案。 +iOS is easy to smash shell, frida-ios-dump smashed, Hundred Try Bailing, and if you encounter frida anti-debugging, use CrackerXI+, most of them can succeed. There's so much to be said for Android to be unhulled, one or two or three generations of hulls, each of which is different, and all kinds of obfuscations and debugs, especially disgusting. This paper introduces some common free shells, which can be separated in seconds by fart8 Ultimate, frida-dexdump, frida_fart, and the second generation function extraction shell by youpk (now used by most enterprise shell anti), which can be charged by fart12. Three generation shell dexvmp, dex2c, these can only rely on jnitrace to analyze the Java layer logic, and then manually restore the algorithm, there is no fully automatic program. -脱壳后紧接的问题是定位算法位置,位于哪个包、哪个类的哪个方法,是收发包里的那个最终的加解密函数或者生成签名的位置。我比较喜欢简单粗暴,可以从最极限的场景出发,进行全量hook。使用r0tracer可以直接hook包下所有的函数,一键hook整个包下所有的类方法重载,输出参数、调用栈和返回值,输出一份日志,在日志中直接搜抓包里的那串数值,即可定位到哪个函数生成了那串数值,调用栈保存了完整的调用逻辑,根据调用栈去追脱壳出来的源码进行阅读,判断确认参数是计算转化拼接生成的。当然全量hook只是理想情况,大部分情况只能根据关键字批量hook几百几千个方法,机器才扛得住。PS:兼容安卓的Java层和iOS的ObjC层,不可用于安卓的C(++)层或iOS的C与Swift层。 +The immediate problem after the discarding is the location of the algorithm, which package, which class method is located, which is the final encryption and decryption function in the transceiver package or the location of the signature generation. I prefer to be simple and rude, and start with the most extreme scenes, full hook. Using r0tracer can directly hook all the functions under the package, one key hook all the class methods under the package overload, output parameters, call stack and return value, output a log, in the log directly search the string of values in the package, you can locate which function generated the string of values, call stack to save the complete call logic, according to the call stack to chase off the shell out of the source code to read, determine that the confirmation parameters are generated by the calculation of conversion splicing. Of course, full-scale hook is only ideal, most cases can only be based on the keyword batch hook hundreds of thousands of methods, the machine is able to withstand. PS: The Android-compatible Java layer and the iOS ObjC layer are not available for the Android C(++) layer or the iOS C and Swift layers. -如果故事到这里就结束了,那么你算是幸运的boy,因为2023年了,稍微注重一丝丝安全性的APP,都会把核心逻辑也就是算法用C写到Native层,这样批量hook就实现不了了;而且也不会用标准的算法,这样很容易被其他语言重写算法做成脱机,比如python/go重写万物,尽量自己手写一个算法;如果非用标准的那么尽可能对标准算法稍微修魔改,比如md5算法改个码表,对称加解密改个S盒或者初始化算法的加减号,提高逆向难度;加个强混淆如ollvm、armvmp,把逆向的难度提高到地狱级别,这样你的算法几乎没有被还原的可能,这时候想要调用算法,只剩下模拟执行工具如frida/xposed的rpc、云手机或者unidbg。当然一般为了APP的运行效率和执行速度,会把混淆强度开低一些,这样任然有概率被手工逆向还原出来,熊掌与鱼不可兼得。 +If the story ends here, then you're lucky boy, because in 2023, APPs that pay a little bit of attention to security will write the core logic, the algorithm, in C to the Native layer, so that batch hook will not be able to achieve, and they won't use standard algorithms, so it's easy to be taken offline by other language rewriting algorithms, like python/go rewriting everything and trying to write an algorithm by yourself, and if not standard then try to slightly modify the standard algorithm, like md5 changing the codetable, Symmetric encryption and decryption to change the S-box or initialization algorithm plus or minus sign, improve the difficulty of the reverse, add strong confusion such as ollvm, armvmp, the difficulty of the reverse to hell level, so that your algorithm almost can not be restored, at this time you want to call the algorithm, only to simulate the execution tools such as frida/xposed rpc, cloud phone or unidbg. Of course, in general, for the APP running efficiency and execution speed, will be a little lower confusion intensity, so that there is a chance of manual reverse restore, bear palm and fish can not have both. -混淆到面目全非的逻辑,实在还原不出来算法的话,只能进行黑盒调用。xposed/lsposed和frida都是内存注入工具,都可以主动调用APP的算法并返回结果,也都可以做成HTTP接口供团队成员调用,区别是*posed只能工作于Java层,frida是Java/C层均可以。云手机则是天生适合主动调用,没有屏幕、没有电池、有线网络,硬件也更加稳定可靠,还可以多开甚至改机,主动调用暴露接口的话用真机不大合适。Unidbg是一款在x86硬件上跑ARM架构SO的模拟器,它主要提供各种精简的安卓库环境包括CPU、文件、JNI、进程、网络等等,让SO误以为自己运行在安卓手机上,此时再对其中的算法进行调用,其只能模拟执行C函数,且Unidbg可以结合SpringBoot跑在x86的服务器上进行批量部署:https://github.com/cxapython/unidbg-server ,难点是补环境费事费脑并不简单。 +If we can't get the algorithm back, we can only make black-box call. xposed/lsposed and frida are both memory injection tools, you can actively call the APP algorithm and return the results, you can also make a HTTP interface for team members to call, the difference is *posed can only work in the Java layer, frida is Java/C layer can. Cloud mobile phone is inherently suitable for active call, no screen, no battery, wired network, hardware is more stable and reliable, you can open more or even change the machine, active call exposed to the interface of real machine is not suitable. Unidbg is a simulator running on x86 hardware ARM architecture SO, it mainly provides a variety of streamlined Android library environment, including CPU, files, JNI, processes, networks, and so on, let SO mistakenly think that they are running on the Android phone, then call the algorithm in it at this time, it can only simulate the implementation of C function, and Unidbg can run on x86 server with SpringBoot for batch deployment:https://github.com/cxapython/unidbg-server, the difficulty is that the environment is laborious. -## 总结 +## Summary -对抗永无止境,一款框架的流行,就会意味着反制迟早会出现,之前用frida去批量trace有多快有多猛有多爽,现在frida反调试就有多流行。同理,想要实现最强的加固,那就意味着更强的手动编码能力,自己写独一份的SO动态加载、hook框架、OLLVM pass、反F5花指令、白盒秘钥、加解密哈希算法等等,原创度越高,逆向分析破解难度越大。 +The perpetual rivalry means that the backlash will come sooner or later, as the frida used to bulk up trace how fast and furious it was, and now the frida counter-debugging is as popular. Similarly, if you want to achieve the strongest reinforcement, it means a stronger manual coding ability, write your own unique SO dynamic load, hook framework, OLLVM pass, anti-F5 flower instructions, white-box key, encryption and decryption hash algorithm, etc., the higher the originality, the greater the difficulty of reverse analysis. -各种权限和资源的判断校验上务必放在服务器端,客户端只做MVC模型中的视图端呈现给用户,坚持客户端零信任原则,部署设备指纹和服务器风控策略,基于这些原则打造的APP,哪怕客户端毫无防护也无所谓,那个"账号"才是核心,比如大部分的海外APP,还有国内的微信、抖音等,都是这样的操作。 - -对于HW中移动客户端的分析,常见的环境、抓包、算法的分析方式和工具武器就是上述这些,使用场景及限制条件都进行了言简意赅的介绍,最后祝大家百步穿杨、钢板日穿! \ No newline at end of file +All kinds of authority and resources must be checked on the server side, the client only do the MVC model of the view side presented to the user, adhere to the client zero trust principle, deployment of equipment fingerprint and server risk control strategy, based on these principles of the APP, even if the client without any protection diff --git a/Student/009/README.md b/Student/009/README.md index 562f99f..db3cca3 100644 --- a/Student/009/README.md +++ b/Student/009/README.md @@ -1,33 +1,33 @@ # EasySoCrackMe -一道只有花指令的标准算法还原题。 +a standard algorithm with only flower commands. -### 要点 📌 +### Key Points📌 -1. 分析就根据已有的知识点大胆猜测,小心验证 -2. 这里 `v5` `v6` 没有提示 aes 相关算法字眼的话,也可以利用 Findcrypt 进行辅助分析, +1. Analysis Based on the existing knowledge points to make bold guesses, careful verification +2. Here 'v5' 'v6' can be used as a secondary analysis by Findcrypt if there is no hint of an aes-dependent algorithm. - ![Untitled](pic/Untitled%201.png) +![Untitled](pic/Untitled%201.png) -最终效果: +End effect: -![Untitled](pic/Untitled.png) +![Untitled] (pic/Untitled.png) -附件:https://github.com/r0ysue/MobileCTF/tree/main/AndroidAlgorithm/easyso +Annex: https://github.com/r0ysue/MobileCTF/tree/main/AndroidAlgorithm/easyso ---- +— -# 解题流程: +# Problem solving process: -- 拿到应用先看是否有加固(因为是 CrackMe 这里就不用验证应用是否能正常使用了) +- Get the app first to see if it's rugged (because CrackMe doesn't have to verify it works here) - ![Untitled](pic/Untitled%202.png) +![Untitled](pic/Untitled%202.png) ## 1. install -- 这里直接安装会失败,这里的错误提示 **INSTALL_FAILED_TEST_ONLY** 表示是测试应用,所以这里安装需要指定一些参数才能安装 +- Direct installation fails here, the error prompt here **INSTALL_FAILED_TEST_ONLY** indicates that it is a test application, so the installation here requires some parameters to be specified to install ```bash ~\Desktop> adb install app-debug.apk @@ -40,138 +40,138 @@ Success ``` -## 2. 分析思路 +## 2. Analytical thoughts -### 2.1 Java 分析 +### 2.1 Java Analysis -- 应用装上后随便输入点东西进行 Verify,提示如下 +- Enter anything you want to Verify after the app is installed, and the tips are as follows - ![Untitled](pic/Untitled%203.png) +![Untitled](pic/Untitled%203.png) -- 接下来就是 Jadx 上场 **Jadx app-debug.apk**,因为是 CrackMe,所以这里的类少,直接定位 MainActivity +- Next up is Jadx**Jadx app-debug.apk**, because it's CrackMe, so there's less class here, straight to MainActivity - ![Untitled](pic/Untitled%204.png) +![Untitled](pic/Untitled%204.png) -- 那么现在就是验证是否是执行的 method01 的流程,判断我们找到的位置是否正确 -- 📍小技巧:右键方法可直接复制 hook 该方法的 frida 片段,Jadx 需要一定的版本才能支持(我记得是1.4.3+ +- So now is the process of verifying that the method01 was executed, and determining if we found the correct location +- 📍 Tip: The right-click method can directly copy the frida fragment of the hook method, and Jadx requires a version to support it (I remember 1.4.3+ - ![Untitled](pic/Untitled%205.png) +![Untitled](pic/Untitled%205.png) ### 2.2 Java hook -- 使用 `Java.perform` 方法创建一个新的插桩会话,然后启动 frida-serve,触发 Verify,执行流程验证 +- Use the 'Java.perform' method to create a new instrumentation session, then start frida-serve, trigger Verify, and perform process validation ```js -Java.perform(() => { - let MainActivity = Java.use("com.roysue.easyso1.MainActivity"); - MainActivity["method01"].implementation = function (str) { - console.log('method01 is called' + ', ' + 'str: ' + str); - let ret = this.method01(str); - console.log('method01 ret value is ' + ret); - return ret; - }; +Java.perform(()=> { +let MainActivity = Java.use("com.roysue.easyso1.MainActivity"); +MainActivity["method01"].implementation = function(str){ +console.log('method01 is called' + ', ' + 'str: ' + str); +let ret = this.method01(str); +console.log('method01 ret value is ' + ret); +return ret; +}; }) ``` ```bash -# hook 信息 +# hook Information method01 is called, str: aa method01 ret value is ac33f2780262122a22a1f1c30aaeeae2 ``` -- 有 hook 信息输出,可以确定是执行我们找到的代码流程,那么接下来就是分析 So 层代码 +- With the hook information output, we can confirm that the code process we found was executed, and then the next step is to analyze the So layer code ```js public static native String method01(String str); ``` -### 2.3 So 分析 +### 2.3 So Analysis -- 这里只有 **armeabi-v7a** 也就是 32 位的程序,32 位的程序注意点就是 **hook 时地址需要 +1** +- There is only **armeabi-v7a**, or 32-bit program, and the 32-bit program caveat is that the address needs +1** for **hook - ![Untitled](pic/Untitled%206.png) +![Untitled](pic/Untitled%206.png) -- 把 **libroysue.so** 拖进 32 位 IDA 进行分析,期间一切弹出提示都不用管 -- 在导出窗口搜索关键词 method01,如果搜索不到,代表该函数并不是静态注册(什么是动静态注册看肉师傅的 NDK 基础去!) +- Drag **libroysue.so** into the 32-bit IDA for analysis and leave any pop-up prompts out +- Search the export window for the keyword method01, and if not, the function is not a static registration (what is a dynamic and static registration look at Meat Master's NDK basis!) - ![Untitled](pic/Untitled%207.png) +![Untitled](pic/Untitled%207.png) -### 2.4 So 函数注册 +### 2.4 So Function Registration -- 静态注册函数 - - 必须遵循一定的命名规则,一般是 **Java_包名_类名_方法名** - - 系统会通过 dlopen 加载对应的so,通过 dlsym 来获取指定名字的函数地址,然后调用 - - 静态注册的 JNI 函数,必然在导出表里 +- Statically register functions +- Certain naming rules must be followed, typically **Java_ package_class_method_name** +- The system loads the corresponding so through dlopen, gets the function address of the given name through dlsym, and then calls +- Statically registered JNI functions, must be in the export table ```c -// Java层加载so库 -// native 关键字代表JNI方法 +// Java layer loading so library +// The native keyword represents a JNI method public native void callBackShowToast(); -// 掐头去尾,加载支持库 文件名libhello.so +// End-of-head, load support library file name libhello.so static{ - System.loadLibrary("hello"); +`System.loadLibrary("hello"); } -// C.C++代码 +// C.C++ code extern "C" JNIEXPORT jstring JNICALL -Java_com_kk_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {} +Java_com_kk_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */){} -extern "C" // 以C的方式编译 不然就是以C++方式编译 自动加上符号修饰(函数名变化) - // 实现 C++ 代码调用 C 语言代码,该代码将按照 C 语言的方式进行编译链接。 +extern "C" // Compile as C Otherwise, compile as C++ Auto-Notation Decoration (function name change) +// Implementation C++ code calls C language code, the code will be compiled in accordance with the C language link. -JNIEXPORT // 导出函数 JNI导出 IDA里面查看的话就是出现在导出表 -jstring // 返回值类型 -JNICALL // 暂时没用到 -Java_com_kk_jnidemo_MainActivity_stringFromJNI // 固定命名规则 Java_包名_类名_方法名 +JNIEXPORT // Export functions JNI Export IDA If viewed in, it appears in the export table +jstring // return value type +JNICALL // not used for a while +Java_com_kk_jnidemo_MainActivity_stringFromJNI // fixed naming rules Java_package_class_method_name -// 参数 -JNIEnv *env // jni环境 -jobject // 指代调用该函数的Java对象 -jclass // 静态函数 public static native -``` +// Parameters +JNIEnv *env // jni environment +jobject // Refers to the Java object that calls the function +jclass // static functions public static native +" -- 动态注册函数 JNI_OnLoad - - 同一个Java函数可注册多个Native函数,以最后注册为准 +- dynamically registering functions JNI_OnLoad +- The same Java function can register more than one Native function, whichever is last registered -```cpp -// Java层 +"cpp +// Java Layer Log.d("MainActivity", "onCreate: " + stringFromJNI2(1, new byte[]{2}, "3")); public native String stringFromJNI2(int a, byte[] b, String c); ---------------------------------------------------------------------------------------------------- +— -// 动态注册: so -jstring encodeFromC(JNIEnv *env, jobject obj, jint a, jbyteArray b, jstring c) { - return env->NewStringUTF("encodeFromC"); +// Dynamic Registration: so +jstring encodeFromC(JNIEnv *env, jobject obj, jint a, jbyteArray b, jstring c){ +return env->NewStringUTF("encodeFromC"); } // JNI_OnLoad -JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { - JNIEnv *env = nullptr; - // 从java虚拟机中获取java env - if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { - LOGD("GetEnv failed"); - return -1; - } - - // 注册信息 - JNINativeMethod methods[] = { - // public native String stringFromJNI2(int a, byte[] b, String c); - {"stringFromJNI2", "(I[BLjava/lang/String;)Ljava/lang/String;", (void *) encodeFromC}, - {"stringFromJNI2", "(I[BLjava/lang/String;)Ljava/lang/String;", (void *) encodeFromC1}, - // 可以给同一个Java函数注册多个native函数,以最后一次为准 - }; - // RegisterNatives第二个参数为结构体 结构体参数:Java方法名 方法签名 函数指针 - jclass MainActivityClazz = env->FindClass("com/kk/myapplication/MainActivity"); - env->RegisterNatives(MainActivityClazz, methods, sizeof(methods) / sizeof(JNINativeMethod)); // 通过该函数注册 - return JNI_VERSION_1_6; // 只能返回JNI版本 不能返回其他jint值 +JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){ +JNIEnv *env = nullptr; +// Get java env from java virtual machine +if(vm->GetEnv((void **)&env, JNI_VERSION_1_6)!= JNI_OK){ + LOGD-java("GetEnv failed"); + return -1; +} + +// Registration Information +JNINativeMethod methods[] = { + // public native String stringFromJNI2(int a, byte[] b, String c); + {"stringFromJNI2", "(I[BLjava/lang/String;)Ljava/lang/String;",(void *)encodeFromC}, + {"stringFromJNI2", "(I[BLjava/lang/String;)Ljava/lang/String;",(void *)encodeFromC1}, + // Multiple native functions can be registered for the same Java function, whichever is last +}; +// RegisterNatives The second argument is a struct body parameter:Java method name method signature function pointer +jclass MainActivityClazz = env->FindClass("com/kk/myapplication/MainActivity"); +env->RegisterNatives(MainActivityClazz, methods, sizeof(methods) / sizeof(JNINativeMethod)); // Register with this function +return JNI_VERSION_1_6; // can only return JNI versions cannot return other jint values } -// 结构体 +// Structure // typedef struct { // const char* name; // const char* signature; @@ -180,143 +180,26 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { ``` -### 2.5 继续 So 分析 +### 2.5 Continue So Analysis -- 进入到这个解密按 F5 看伪代码,根据 So 函数注册规则,这里对 `a1` 的参数类型进行修改,`a3` 是我们的入参 +- Enter this decrypted pseudo-code by F5, where the parameter type of 'a1' is modified according to the So function registration rules, and 'a3' is our input - ![Untitled](pic/Untitled%208.png) +![Untitled](pic/Untitled%208.png) - ![Untitled](pic/Untitled%209.png) +![Untitled](pic/Untitled%209.png) -- 快捷键 `Y` 修改参数类型,快捷键 `N` 修改命名 +- Shortcut 'Y' modifies parameter type, shortcut 'N' modifies naming - ![1.gif](pic/1.gif) +![1.gif] (pic/1.gif) -- 接下来是不是清晰多了,根据入参进行分析 `a3` +- Is it much clearer next? Analysis 'a3' based on input ```c int __fastcall Java_com_roysue_easyso1_MainActivity_method01(JNIEnv *env, int a2, int a3) { - int v4; // [sp+8h] [bp-20h] - void *ptr; // [sp+Ch] [bp-1Ch] - const char *v6; // [sp+10h] [bp-18h] - // 0. a3 在这里引用 - v6 = (*env)->GetStringUTFChars(env, a3, 0); // 1.这里的 a3 是 jstring 需要转成 cstring 才能使用 - ptr = (void *)j_ll11l1l1ll(v6); // 2.传入 v6 也就是我们的字符串 - (*env)->ReleaseStringUTFChars(env, (jstring)a3, v6); // 3.资源释放 - v4 = j_o0OoOOOO(env, ptr); // 4.没进函数暂时不知道干嘛的 | 5.⭐根据返回值, 在回过头来分析, 既然返回 v4, ptr 又是通过函数 j_ll11l1l1ll 得到的值, 所以这里可以确定 ptr 通过一些操作赋值给了 v4 - free(ptr); - return v4; // 5.返回 v4 -} -``` - -- 所以这里我们的目的就是分析 `j_ll11l1l1ll` 函数逻辑 - -### 2.6 So 分析 - j_ll11l1l1ll - -- 这里分析完得到的点就是 `v6` `v5` 以及 `s` 是怎么得到的, - -```c -// // 0.根据a1入参引用的地方进行分析 -int __fastcall ll11l1l1ll(int a1) -{ - int v2; // [sp+8h] [bp-30h] - void *v3; // [sp+Ch] [bp-2Ch] - size_t size; // [sp+10h] [bp-28h] - void *v5; // [sp+18h] [bp-20h] - void *v6; // [sp+1Ch] [bp-1Ch] - char *s; // [sp+2Ch] [bp-Ch] - - v6 = (void *)sub_2658(); // 7.没有入参 直接返回 那么这里就需要考虑的是这里的返回时是否为随机值 - v5 = (void *)sub_2C44(); // 8.没有入参 直接返回 那么这里就需要考虑的是这里的返回时是否为随机值 - s = (char *)sub_24A4(a1); // 1.传入 a1 得到 s - size = strlen(s); // 2. 判断 s 的长度 - v3 = malloc(size); // 3.申请 s 长度大小的内存空间 - j_qpppqp(v3, s, size, v6, v5); // 4.传入一系列参数, 这里根据返回值进行判断, 可以猜测 v3 基本上可以确实是返回结果, C 里都喜欢这么用, 传入指针, 修改指针地址里面的值当返回值用 - v2 = j_bbddbbdbb(v3, size); // 5.通过一系列处理得到 v2 - free(s); - free(v3); - free(v6); - free(v5); - return v2; // 6.返回v2 -} -``` - -### 2.7 So hook - -- 写一个主动调用,方便操作(为什么这里主动调用要 `rpc.exports` 的写法,我也不知道为什么!!反正 frida15 就是这样写才能用❗) - -```js -rpc.exports = { - call: function call() { - Java.perform(() => { - let MainActivity = Java.use("com.roysue.easyso1.MainActivity"); - let ret = MainActivity["method01"]('aa'); - console.log(`[ * ]\t${ret}`); - // ac33f2780262122a22a1f1c30aaeeae2 - - }); - } -} - -let base = Module.findBaseAddress('libroysue.so') -console.log(`[ * ]\tlibroysue.so.base :=> ${base}`); - -// 分析入参跟返回值 -let funcAddr = Module.findExportByName('libroysue.so', 'll11l1l1ll') -Interceptor.attach(funcAddr, { - onEnter: function (args) { - console.log(`[ * ]\tll11l1l1ll.args[${0}] onEnter :=> ${args[0].readCString()}`) - }, - onLeave: function (retval) { - console.log(`[ * ]\tll11l1l1ll.retval onLeave :=> ${retval.readCString()}`) - console.error("------------------------------------------------------------\n"); - } -}) - -// 分析 v6 返回值 -let sub_2658 = base.add(0x2658 + 1) -Interceptor.attach(sub_2658, { - onLeave: function (retval) { - console.log(`[ * ]\tsub_2658.retval onLeave :=> ${retval.readCString()}`) - console.error("------------------------------------------------------------\n"); - } -}) - -// 分析 v5 返回值 -let sub_2C44 = base.add(0x2C44 + 1) -Interceptor.attach(sub_2C44, { - onLeave: function (retval) { - console.log(`[ * ]\tsub_2C44.retval onLeave :=> ${retval.readCString()}`) - console.error("------------------------------------------------------------\n"); - } -}) - -// 输出信息 - -[ * ] libroysue.so.base :=> 0xd67ed000 -[Pixel 3::easyso1 ]-> rpc.exports.call() -[ * ] ll11l1l1ll.args[0] onEnter :=> aa -[ * ] sub_2658.retval onLeave :=> goodl-aes-key123 ------------------------------------------------------------- - -[ * ] sub_2C44.retval onLeave :=> goodl-aes-iv1234 ------------------------------------------------------------- - -[ * ] ll11l1l1ll.retval onLeave :=> ac33f2780262122a22a1f1c30aaeeae2 ------------------------------------------------------------- - -[ * ] ac33f2780262122a22a1f1c30aaeeae2 -``` - - -### 2.8 验证输出信息 CyberChef - -- 根据这里的输出信息我可以得到大概是 aes 算法,还贴心的标注了 key 与 iv,但是这里也不能完全相信,这里打开逆向挚友 [AES Encrypt - CyberChef](https://gchq.github.io/CyberChef/#recipe=AES_Encrypt(%7B'option':'UTF8','string':'goodl-aes-key123'%7D,%7B'option':'UTF8','string':'goodl-aes-iv1234'%7D,'CBC','Raw','Hex',%7B'option':'Hex','string':''%7D)&input=YWE),可以发现结果能对上 - - ![Untitled](pic/Untitled%2010.png) - -- 那么代表算法是没有魔改的,如果需要得到 flag,这里拿 Jadx 反编译看见进行对比的 Hex 字符串解密即可 [AES Decrypt, AES Encrypt - CyberChef](https://gchq.github.io/CyberChef/#recipe=AES_Decrypt(%7B'option':'UTF8','string':'goodl-aes-key123'%7D,%7B'option':'UTF8','string':'goodl-aes-iv1234'%7D,'CBC','Hex','Raw',%7B'option':'Hex','string':''%7D,%7B'option':'Hex','string':''%7D)AES_Encrypt(%7B'option':'UTF8','string':'goodl-aes-key123'%7D,%7B'option':'UTF8','string':'goodl-aes-iv1234'%7D,'CBC','Raw','Hex',%7B'option':'Hex','string':''%7D/disabled)&input=ODFkNDRiYjA0MmQ1ZGU5YTdkYjJhNWE4NTZhMjliNWE) - - ![Untitled](pic/Untitled%2011.png) \ No newline at end of file +int v4; // [sp+8h] [bp-20h] +void *ptr; // [sp+Ch] [bp-1Ch] +const char *v6; // [sp+10h] [bp-18h] +// 0. a3 quoted here +``` \ No newline at end of file diff --git a/Student/010/README.md b/Student/010/README.md index 27a14af..9acf2d4 100644 --- a/Student/010/README.md +++ b/Student/010/README.md @@ -1,176 +1,175 @@ -# Pixel4刷入KernelSU后Frida和VPN抓包配置 +# Frida and VPN Packet Capture Configuration After Pixel4 Flushes Into KernelSU -手把手教你在Pixel4上刷入KernelSU,并且配置好frida和VPN抓包环境。 +Hands teach you to brush KernelSU on Pixel4 and configure your frida and VPN capture environments. -## KernelSu 环境 +## KernelSu environment -- https://github.com/msnx/KernelSU-Pixel4XL 环境 - - 基于 KernelSU from weishu:[https://github.com/tiann/KernelSU](https://github.com/tiann/KernelSU) - - 设备名称:Pixel 4xl - - Android版本:13 - - 基带版本:g8150 - 00123 - 220708 b - 8810441 - - 内核版本:4.14.276-g8ae7b4ca8564-ab8715030 - - 构建数字:TP1A.220624.014 -- 这里如果使用 r0env 刷机需要添加环境变量,否者无法使用 `adb` `fastboot` +- https://github.com/msnx/KernelSU-Pixel4XL environment +- Based on KernelSU from weishu:[https://github.com/tiann/KernelSU](https://github.com/tiann/KernelSU) +- Device Name: Pixel 4xl +- Android Version: 13 +- Baseband version: g8150 - 00123 - 220708 b - 8810441 +- Kernel Version: 4.14.276-g8ae7b4ca8564-ab8715030 +- Build Numbers: TP1A.220624.014 +- Environment variables need to be added here if you are using a r0env machine, no one can use 'adb' 'fastboot' - ```yaml - PATH=$PATH:/root/Android/Sdk/platform-tools;export PATH; - source ~/.bashrc - ``` +```yaml +PATH=$PATH:/root/Android/Sdk/platform-tools;export PATH; +source ~/.bashrc +``` -### 官方镜像刷机 +### Official mirror printer -1. 刷入官方固件包恢复出厂设置 +1. Restore factory settings by swiping in the official firmware package - ```bash - adb reboot bootloader # **这里需要手机连接到虚拟机, 否者就电源键+音量键进入** - 7z x adb flame-tp1a.220624.014-factory-7b8f6f73.zip - cd flame-tp1a.220624.014 - ./flash-all.sh # 等待刷入即可 - ``` +```bash +adb reboot bootloader # ** Phone to VM is required here, No Power + Volume key to enter ** +7z x adb flame-tp1a.220624.014-factory-7b8f6f73.zip +cd flame-tp1a.220624.014 +./flash-all.sh # Just wait to flush +``` - ![Untitled](pic/Untitled.jpeg) +![Untitled](pic/Untitled.jpeg) - ![Untitled](pic/Untitled%201.jpeg) +![Untitled](pic/Untitled%201.jpeg) -2. 这里刷完再次进入到 bootloader 模式(长按电源键 + 音量键)刷入 Android13 镜像 +2. Brush here Enter bootloader mode again (press and hold the Power key + Volume key) Brush in the Android13 image - ```bash - fastboot flash boot pixel4xl_android13_4.14.276_v057.img - ``` +```bash +fastboot flash boot pixel4xl_android13_4.14.276_v057.img +``` -3. 安装 KernelSu +3. Install KernelSu - ```bash - # 进入手机设置 -> 关于手机 -> 连点七下版本号, 启用开发者模式 - # 系统 -> 高级 -> 开发者选项 -> - # 1.关闭系统自动更新 - # 2.USB调试开关启用, 如果弹出信任弹窗需要信任 +# Go to Phone Settings -> About Phones -> Tap the release number seven times to enable developer mode +#System->Advanced->Developer Options-> +# 1. Turn off automatic system updates +# The 2.USB debug switch is enabled if the pop-up trust flyout requires trust - # 使用 adb 安装 KernelSU - adb install KernelSU_v0.5.7_10866-release.apk +# Use adb to install KernelSU +adb install KernelSU_v0.5.7_10866-release.apk - # 手机投屏 - scrcpy - ``` +# Screen shot +scrcpy +" - ![Untitled](pic/Untitled%202.jpeg) +![Untitled](pic/Untitled%202.jpeg) -## 抓包环境 +## Catch Environment -### 系统证书安装 +### System Certificate Installation -1. 保存 Charles 证书,导入到手机进行安装 `adb push 123.pem /sdcard/` +1. Save the Charles certificate and import it to your phone to install 'adb push 123.pem /sdcard/' - ![Untitled](pic/Untitled.png) +![Untitled](pic/Untitled.png) -2. 手机安装证书:设置 → 安全 → 更多安全设置 → 加密与凭据 → 安装证书 → CA 证书 +2. Mobile Phone Install Certificate: Settings → Security → More Security → Encryption and Credentials → Install Certificate → CA Certificate - ![1.gif](pic/1.gif) +![1.gif](pic/1.gif) -3. 安装完成后会出现在用户目录 +3. Appears in the user directory after installation completes - ![Untitled](pic/Untitled%201.png) +![Untitled](pic/Untitled%201.png) -4. 这里需要将证书移动到根目录才行,将证书模块导入到手机使用 KernelSU 进行安装并重启手机 +4. Here you need to move the certificate to the root directory, import the certificate module to the phone, use KernelSU to install and restart the phone - ```bash - adb push Move_Certificates-v1.9.zip - ``` +```bash +adb push Move_Certificates-v1.9.zip +``` -5. 手机重启完成后可以在系统处看见 Charles 的证书 **XK72 Ltd** +5. Charles's certificate can be seen at the system after the phone restarts **XK72 Ltd** - ![Untitled](pic/Untitled%202.png) +![Untitled](pic/Untitled%202.png) - ![Untitled](pic/Untitled%203.png) +![Untitled](pic/Untitled%203.png) ### Charles -### SSL 代理 +### SSL Proxy ![Untitled](pic/Untitled%204.png) -### 端口设置 +### Port Settings ![Untitled](pic/Untitled%205.png) -### 访问控制 +### access control ![Untitled](pic/Untitled%206.png) -### 环境验证 +### Environment Validation -- 安装代理工具与酷安应用 +- Install Agent Tools and Cool Apps - ```bash - adb install Postern_3.1.3_Apkpure.apk - adb install coolapk.apk - ``` +```bash +adb install Postern_3.1.3_Apkpure.apk +adb install coolapk.apk +``` -- 手机连接局域网 WI-FI 提示感叹号,执行以下命令去除(其实不去除也行,只是提示而已,网络能正常使用的) +- Mobile phone to LAN WI-FI hint exclamation point, execute the following command to remove (not to remove, just hint, network works) - ```bash - settings put global captive_portal_http_url https://www.google.cn/generate_204 - settings put global captive_portal_https_url https://www.google.cn/generate_204 - settings put global ntp_server 1.hk.pool.ntp.org - ``` +```bash +settings put global captive_portal_http_url https://www.google.cn/generate_204 +settings put global captive_portal_https_url https://www.google.cn/generate_204 +settings put global ntp_server 1.hk.pool.ntp.org +``` - ![Untitled](pic/Untitled%207.png) +![Untitled](pic/Untitled%207.png) -- 代理配置:实际是根据 PC IP 进行填写,这里别照搬 +- Agent Configuration: Actually based on PC IP, don't copy here - ![Untitled](pic/Untitled%208.png) +![Untitled](pic/Untitled%208.png) -- 启用 VPN,打开酷安进行登陆,如果看到以下信息证明抓包环境是没有问题的 +- Enable VPN, turn on CoolAnn to log on, and if you see the following information to prove that the capture environment is OK - ![Untitled](pic/Untitled%209.png) +![Untitled](pic/Untitled%209.png) ## frida & Objection -- 执行该命令会直接安装最新版 frida、firda-tools、obj +- Executing this command will install the latest version of frida, firda-tools, obj directly - ```bash - pip install objection -i https://mirrors.aliyun.com/pypi/simple +```bash +pip install objection -i https://mirrors.aliyun.com/pypi/simple - objection version # 输出版本号检测是否安装成功 - frida --version - ``` +objection version # Output Version Number Detect if installation succeeded +frida —version +``` -- 从 https://github.com/frida/frida/releases 中获取对应安装版本的 frida-server +- Get the frida-server for the installed version from https://github.com/frida/frida/releases - ```bash - # 先查看设备平台,然后在官网下载对应 server 版本 - adb shell getprop ro.product.cpu.abi # 查看设备CPU - adb push [电脑端frida-server路径] /data/local/tmp/fs - adb shell - su - cd /data/local/tmp - chmod 777 fs # 文件权限修改 - ./fs # 启动frida-server - ./fs -l 0.0.0.0:8888 # 监听端口就这样启动 - ``` +```bash +# Check the device platform first, then download the corresponding server version on the website +adb shell getprop ro.product.cpu.abi # View Device CPUs +adb push [computer-side frida-server path] /data/local/tmp/fs +adb shell +su +cd /data/local/tmp +chmod 777 fs # File Permission Modification +./fs # Launch frida-server +./fs -l 0.0.0.0:8888 # The listening port starts like this +``` -### 小案例 - 禁止截屏 +### Small Cases - No Screenshots -- 应用下载 [MobileCTF/AndroidNetwork/MZT at main · r0ysue/MobileCTF](https://github.com/r0ysue/MobileCTF/tree/main/AndroidNetwork/MZT) +- App download [MobileCTF/AndroidNetwork/MZT at main · r0ysue/MobileCTF](https://github.com/r0ysue/MobileCTF/tree/main/AndroidNetwork/MZT) - ```bash - Android API 禁止截屏 - activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - ``` +```bash +Android API No Screenshots +activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); +activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); +``` -- 这里就不在记录怎么详细过检测了,当做作业进行练手操作 +- There's no record of detailed testing, as homework and hand-training -## 引用资料和大附件: +## References and large attachments: -- [Pixel4 刷入 KernelSU 后的 VPN 抓包和 frida 配置_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1M8411Z7rC/?spm_id_from=333.999.0.0&vd_source=bfb9720533da6436a36b48fddb3d128a) -- 官方镜像:[Nexus 和 Pixel 设备的出厂映像 | Google Play services | Google for Developers](https://developers.google.cn/android/images?hl=zh-cn#coral) -- ****Pixel4**** 官方镜像、KernelSU、Postern(手机流量转发工具)、Move_Certificates(自动移动证书到根目录) - - [https://github.com/r0ysue/MobileCTF/tree/main/AndroidEnvironment/Pixel4KernelSU/attachment](https://github.com/r0ysue/MobileCTF/tree/main/AndroidEnvironment/Pixel4KernelSU/attachment) +- [VPN Capture and frida Configuration_Bilibili_bilibili after Pixel4 Flushes into KernelSU](https://www.bilibili.com/video/BV1M8411Z7rC/?spm_id_from=333.999.0.0&vd_source=bfb9720533da6436a36b48fddb3d128a) +- Official Image: [Factory image of Nexus and Pixel devices | Google Play services | Google for Developers](https://developers.google.cn/android/images?hl=zh-cn#coral) +- ****Pixel4**** Official Mirroring, KernelSU, Postern, Move_Certificates +- [https://github.com/r0ysue/MobileCTF/tree/main/AndroidEnvironment/Pixel4KernelSU/attachment](https://github.com/r0ysue/MobileCTF/tree/main/AndroidEnvironment/Pixel4KernelSU/attachment)

AltStyle によって変換されたページ (->オリジナル) /