Android反调试——从源码入手
# 绕过TracePid反调试二之前花了很多时间在发帖这件事情上,觉得太浪费时间了就先发先知了。所以现在作为转贴,看看发帖问题解决没有,也把我花了很多时间写的文章分享给大家。如果大家觉得文章有什么不足的或者遇到与文章相关的问题, 可以问问我,我们可以一起研究一下。
第一篇文章是直接修改二进制文件[尝试绕过TracerPID反调试](https://www.52pojie.cn/thread-917096-1-1.html)
首发先知
## 前言
接受了评论的建议, 但是因为之前手机还没好加上没试过直接修改kernel的源码, 所以花了很多时间(都是环境惹的祸)。还有因为这个接触了shell code, 真的是一言难尽。事先说明, 下面的环境准备都是在国外的服务器上直接运行的, 难免有一些命令是需要翻墙的, 所以你实际上要用的命令可能跟我的有点不同(如果可以直接用代理之类的, 应该没多大影响)。
## 开发环境
Ubuntu 18.10(建议用Ubuntu 16.04, 至少2MB内存)
Android 6.0.1
Nexus 5
## Ubuntu环境搭建
### Java环境准备
下文Java环境搭建都是基于Ubuntu 18.10的, 如果你尝试过不能在自己的Ubuntu环境下使用, 可以到google上找找看, 应该能找到你想要的。如果不是为了之后Android源码调试, 只是为了修改kernel文件可以先不搭建Java环境。
#### 下载JDK
1. 为了下载最新的JDK, 可以现在Ubuntu的命令行里面先输入`javac`, 会显示下面的内容, 按照它提供的命令即可下载最新的JDK。
![](https://i.imgur.com/hDyztXW.png)
2. 很不快乐的是Java 6和Java 7需要有Oracle的账号, 所以只要去Orcle注册一个账号, 就可以下载(https://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html)和(https://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase6-419409.html)了(Java 7是压缩包, Java 6是一个二进制文件)。**文章末尾附有两个jdk文件的链接**。
#### 安装JDK
因为先安装了Java 8在路径`/usr/lib/jvm`目录下, 所以将文件文件`jdk-6u45-linux-x64.bin`和`jdk-7u80-linux-x64.tar.gz`都用mv命令移到上述目录下。
```
root@vultr:~/# mv jdk-6u45-linux-x64.bin /usr/lib/jvm/
root@vultr:~/# mv jdk-7u80-linux-x64.tar.gz /usr/lib/jvm/
```
解压jdk 6, 进入到`/usr/lib/jvm`目录下, 先给该文件读写的权限, 之后运行该二进制文件就会在当前目录下生成一个新的文件夹。
```
root@vultr:~/# cd /usr/lib/jvm
root@vultr:/usr/lib/jvm# chmod +x jdk-6u45-linux-x64.bin
root@vultr:/usr/lib/jvm# ./jdk-6u45-linux-x64.bin
```
解压jdk 7
```
root@vultr:/usr/lib/jvm# tar -zxvf jdk-7u80-linux-x64.tar.gz
```
为了我们能够在Ubuntu里面自由自在地切换Java版本, 我们可以先写个脚本将jdk-6和jdk-7添加到候选项中。先输入命令`vim alternativeJava.sh`, 并将下面的内容直接复制到alternativsjava.sh文件里。
```
#!/bin/sh
JAVAHOME=$1
if [ -d $JAVAHOME ];then
sudo update-alternatives --install /usr/bin/java java $JAVAHOME/bin/java 300
sudo update-alternatives --install /usr/bin/javac javac $JAVAHOME/bin/javac 300
sudo update-alternatives --install /usr/bin/jar jar $JAVAHOME/bin/jar 300
sudo update-alternatives --install /usr/bin/javah javah $JAVAHOME/bin/javah 300
sudo update-alternatives --install /usr/bin/javap javap $JAVAHOME/bin/javap 300
else
echo "Wrong input"
exit 0
fi
```
用命令`chmod+x
alternativsjava.sh`, 给脚本添加权限, 否则脚本会不能运行。输入命令`./alternativsjava.sh /usr/lib/jvm/jdk1.7.0_80`之后(脚本后面添加的路径是你jdk解压后的文件路径),用`sudo update-alternatives --config java`(切换java版本命令)进行检验。
![](https://i.imgur.com/WgvHJPd.png)
### 准备Android源码运行环境
以下内容仅编译内核, 并假设你还没有下载整个 AOSP源。因为我要编译的内核版本过旧, 所以用的都是旧的教程, 如果有要编译新的内核的要求的话, 可以看看这两篇文章, (https://github.com/nathanchance/android-kernel-clang#how-to-compile-the-kernel-with-clang-standalone)和[编译内核](https://source.android.google.cn/setup/build/building-kernels)。
#### 安装所需的软件包
在Ubuntu 14.04中如果下载git出问题, 可以看看这篇文章(https://www.digitalocean.com/community/tutorials/how-to-install-git-on-ubuntu-14-04)。
输入下述命令。
```
$ sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev libgl1-mesa-dev libxml2-utils xsltproc unzip
```
#### 下载源码
在下载之前先获取手机的内核版本, 从下面的信息可知道手机内核的`git short commit id`为`cf10b7e`。
![](https://i.imgur.com/r0l0Gz2.png)
因为内核版本比较旧, 所以按照[旧版的官方内核](https://source.android.com/setup/build/building-kernels-deprecated)编译手册来, 而不是按照[新版的内核编译手册](https://source.android.com/setup/build/building-kernels)来。如果内核比较新的, 还是直接用repo吧!接下来可以从官方手册上看到, 我需要的kernel源代码位于哪个branch, 然后从github上clone下来。
![](https://i.imgur.com/qZ8Dllc.png)
输入命令, 先将msm这个项目clone下来。(这一步花的时间可能会有一点点长)
`$ git clone https://android.googlesource.com/kernel/msm.git`
因为我用的是国外的服务器, 所以可以直接从google服务器下下来。如果是自己搭建的机器且觉得开代理太麻烦的话, 可以换成下面的命令。
`$ git clone https://aosp.tuna.tsinghua.edu.cn/kernel/msm.git`
将msm从github上clone下来之后, 会发现里面是个空的, 只有一个`.git`仓库。进入`msm`目录下, 用`git branch -a`查看分支。(我的文件路径跟图片下的不符, 实际上应该是`/AndroidKernel/msm`)
![](https://i.imgur.com/czFhbta.png)
现在就要用到我们之前获取的short commit id(显示的是实际的commit id的前7位)了, 直接检出我们需要的代码的分支。
`git branch -r --contains <your short commit id>`
从上面的图片我们可以知道, 本地实际上只有`master`这一个分支, 这时候我们需要做的事就是在远程分支的基础上再分一个本地分支。
```
$ git checkout -b android-msm-hammerhead-3.4-marshmallow-mr3 origin/android-msm-hammerhead-3.4-marshmallow-mr3
```
![](https://i.imgur.com/3J4jtIc.png)
#### 安装GCC交叉编译器
之前不是很能理解为什么官方网站没说要下载这个东西, 之后在(https://appuals.com/how-to-build-a-custom-android-kernel/)这篇文章里面看到。因为一般我们需要编译的kernel源代码都是基于arm架构编译运行的, 所以直接放在我们64位的Ubuntu里面是不合适的。也可以跟官方一样直接通过USB连接手机直接进行调试。
在`~/AndroidKernel`执行如下命令(下载现在Linux环境下的arm编译接口)。这个编译接口尽量别尝试arm-eabi-4.8以上的, 因为旧的内核和交叉编译器不匹配会出现很多麻烦, 例如现在Google已经弃用了gcc, 在最新的交叉编译器里面只能用clang, 即使`make`操作加了参数`CC=clang`也会在出现很多很麻烦的报错。所以我这里为了匹配, 用的是旧的交叉编译器。
```
$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6
```
在这里尝试了一下清华的AOSP源, 也是可以直接用的。参考贴出来的google的url, 直接将里面的`https://android.googlesource.com/`全部改成`https://aosp.tuna.tsinghua.edu.cn/`即可。详情可参考(https://mirror.tuna.tsinghua.edu.cn/help/AOSP/)。
```
$ git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6
```
#### 添加环境变量
添加环境变量总共有两种方法, 一种是短期的, 开机重启之后就会失效, 一种是长期的。
第一种:在`~/AndroidKernel`目录下执行以下命令。
```
$ export PATH=~/AndroidKernel/arm-eabi-4.6/bin:$PATH
```
第二种:在`~/.bashrc`中添加环境变量
`$ vim ~/.bashrc`
之后在文件末尾添加`export PATH=<交叉编译API存放的文件根目录>/arm-linux-androideabi-4.9/bin:$PATH`
为了让这个配置立马生效, 我们可以用下面的命令
`$ source ~/.bashrc`
## 修改源码
### 修改前准备+修改源码
如果觉得不想知道为什么要修改`base.c`和`array.c`文件, 可以跳过现在这一段, 直接从下一段“修改`msm/fs/proc/base.c`文件”开始看就好了。
我将`msm/fs/proc`目录下的文件都下载到本地(都是先修改完成的), 安装了(https://bbs.pediy.com/thread-215669.htm)来分析源码。(http://man7.org/linux/man-pages/man5/proc.5.html), 是以文件系统的方式为访问系统内核的操作提供接口, 动态从系统内核中读出所需信息的。这也就说明, 我们想要修改的TracePid也是通过这个文件中获取到的。
我们想获取进程信息的时候, 一般会输出下述内容。
```
>cat /proc/self/status
Name: cat
State:R (running)
Tgid: 5452
Pid: 5452
PPid: 743
TracerPid: 0 (2.4)
Uid: 501 501 501 501
Gid: 100 100 100 100
FDSize: 256
Groups: 100 14 16
VmPeak: 5004 kB
VmSize: 5004 kB
VmLck: 0 kB
VmHWM: 476 kB
VmRSS: 476 kB
VmData: 156 kB
VmStk: 88 kB
VmExe: 68 kB
VmLib: 1412 kB
VmPTE: 20 kb
VmSwap: 0 kB
Threads: 1
SigQ: 0/28578
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 00000000fffffeff
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
Seccomp: 0
voluntary_ctxt_switches: 0
nonvoluntary_ctxt_switches: 1
```
在上述Status信息中我们需要关注的两个部分, 一个是`State`字段, 一个是`TracePid`字段。因为这两个字段都可反映出进程是否被监测。详情可参考[ proc.txt ](https://android.googlesource.com/kernel/msm/+/android-wear-5.1.1_r0.6/Documentation/filesystems/proc.txt?autodive=0%2F%2F%2F)的`line 209`和`line 215`。
在(http://man7.org/linux/man-pages/man5/proc.5.html)查找`/proc//stat`, 我们可以知道Status是在`fs/proc/array.c`定义的, 我们就先从`array.c`入手。
先打开查看调用关系的窗口, `View->Panels->Relation Windows`。
在`array.c`文件中搜索`status`, 找到函数`proc_pid_status`, 之后查看该函数调用与被调用的信息。
![](https://i.imgur.com/jyMavtL.png)
在`Relation Window`中双击`get_task_state`函数, 就找到了我们想找的`TracePid`。这个就是我们要修改的第一处了。
![](https://i.imgur.com/rMp60mL.png)
TracePid 通常都是对父进程 pid 进行检测, 这里将 ppid 改为 0, 这样不管是否为调试状态, TracePid 都无法检测出。修改的结果如下
```
180 seq_printf(m,
181 "State:\t%s\n"
182 "Tgid:\t%d\n"
183 "Pid:\t%d\n"
184 "PPid:\t%d\n"
185 "TracerPid:\t%d\n"
186 "Uid:\t%d\t%d\t%d\t%d\n"
187 "Gid:\t%d\t%d\t%d\t%d\n",
188 get_task_state(p),
189 task_tgid_nr_ns(p, ns),
190 pid_nr_ns(pid, ns),
//修改部分
191 ppid, 0,
//修改结束
192 cred->uid, cred->euid, cred->suid, cred->fsuid,
193 cred->gid, cred->egid, cred->sgid, cred->fsgid);
```
上面的代码段中的`get_task_state()`函数引起了我的注意, 这个函数应该是获取state的函数。用鼠标选中该函数之后, 右手边的`Relation Window`会显示该函数所在的位置, 在该窗口双击之后跳转。
![](https://i.imgur.com/MAbWXuR.png)
在上图中, 看到了明显用来存放状态的数组`task_state_array`, 选中该数组之后, 同样的在`Relation Window`中双击跳转。
![](https://i.imgur.com/O70xlsH.png)
将原来状态表中的`T`和`t`都修改为`S`这样就避免了该状态位反映出被监测的状态。
```
RRunning
SSleeping in an interruptible wait
DWaiting in uninterruptible disk sleep
ZZombie
TStopped (on a signal) or (before Linux 2.6.33)
trace stopped
tTracing stop (Linux 2.6.33 onward)
WPaging (only before Linux 2.6.0)
XDead (from Linux 2.6.0 onward
xDead (Linux 2.6.33 to 3.13 only)
KWakekill (Linux 2.6.33 to 3.13 only)
WWaking (Linux 2.6.33 to 3.13 only)
PParked (Linux 3.9 to 3.13 only)
```
`array.c`我们已经修改完毕了, 这时候我们就要修改其他部分了。在[导入Project](https://blog.csdn.net/MyLinChi/article/details/78459373)之后, 我们在整个proc文件中搜索关键词`trace`。先按照下图打开`Project Search Bar`, 并在其中输入`trace`。
![](https://i.imgur.com/AF3qdP7.png)
我们会发现搜索的结果都是在`base.c`文件中(下图出现的第一个包含`trace`关键词的函数是我已经修改过的)。
![](https://i.imgur.com/qdR3XWj.png)
在检查完有`trace`关键词的代码没发现有用的, 就在`base.c`文件中搜索关键词`status`。
`Ctrl+F`输入关键词之后没找到, 就通过下图的向下搜索的功能一个个定位, 前面的部分都没找到自己想要找的函数段。
![](https://i.imgur.com/XOlk7fq.png)
直到找到了关键的部分, 选中函数`proc_pid_status`, 在右边`Relation Window`中继续找我们想要的关键函数。
![](https://i.imgur.com/VV1xxyc.png)
但是很遗憾, 在`proc_pid_status`函数中跟了很多相关的函数仍然没找到我们想要的。那我们就回到我们最开始的地方。这部分最上面的标识是`pid_entry`。顺着这个部分往下看, 我们就找到了`proc_tid_stat`函数, 选中该函数之后我们可以找到`do_task_stat`函数。
![](https://i.imgur.com/akjELQ8.png)
接下来, 我们就好好看看这个函数里面有什么。在右边的`Relation Window`中关注到一个有`state`关键词的函数, 双击之后跳转到该函数调用的位置。
![](https://i.imgur.com/Z6XIxPk.png)
定位到上图那一行之后, 分别跟了`state`关键词和`get_task_state`函数, 都没有发现什么(`base.c`是进程运行之前要做的准备工作, 从`get_task_state`函数可直接回到之前修改的`array.c`文件。但因为已修改完成, 所以就留在`base.c`文件中没有继续定位了)。
现在看到这段函数之中大部分都用到了变量`task`, 所以只好将`task`作为关键词用笨办法来一个一个定位。最后找到了`wchan`, 真的眼泪都掉下来。(因为事先知道要改这个部分)
看了(https://gtoad.github.io/2017/06/25/Android-Anti-Debug/)这篇文章才知道为什么要修改带有`wchan`关键词的函数。因为`/proc/pid/wchan` 和 `/proc/pid/task/pid/wchan`在调试状态下,里面内容为`ptrace_stop`, 非调试的状态下为ep_poll。所以也可能会泄露正在被调试的信息, 所以我们直接在Project中查找`wchan`关键词, 就定位到函数`proc_pid_wchan`
![](https://i.imgur.com/naZExKg.png)
定位结束之后我们进行如下修改, 到这里我们的修改就彻底结束了。
![](https://i.imgur.com/dU8GVIZ.png)
### 修改`msm/fs/proc/base.c`文件
在Ubuntu中编辑文件`vim msm/fs/proc/base.c`, 定位函数`proc_pid_wchan`(大概在268行左右)
```
267 static int proc_pid_wchan(struct task_struct *task, char *buffer)
268 {
269 unsigned long wchan;
270 char symname;
271
272 wchan = get_wchan(task);
273
274 if (lookup_symbol_name(wchan, symname) < 0)
275 if (!ptrace_may_access(task, PTRACE_MODE_READ))
276 return 0;
277 else
278 return sprintf(buffer, "%lu", wchan);
279 else
280 return sprintf(buffer, "%s", symname);
281 }
```
改成下面的内容
```
static int proc_pid_wchan(struct task_struct *task, char *buffer)
{
unsigned long wchan;
char symname;
wchan = get_wchan(task);
if (lookup_symbol_name(wchan, symname) < 0)
if (!ptrace_may_access(task, PTRACE_MODE_READ))
return 0;
else
return sprintf(buffer, "%lu", wchan);
else
{ // 更改的内容
if(strstr(symname,"trace"))
return sprintf(buffer, "%s", "sys_epoll_wait");
return sprintf(buffer, "%s", symname);
}
}
```
### 修改`msm/fs/proc/array.c`文件
用vim对`msm/fs/proc/array.c`进行编辑, 先修改第一处
```
134 static const char * const task_state_array[] 135 = {
136 "R (running)", /* 0 */
137 "S (sleeping)", /* 1 */
138 "D (disk sleep)", /* 2 */
139 "T (stopped)", /* 4 */
140 "t (tracing stop)", /* 8 */
141 "Z (zombie)", /*16 */
142 "X (dead)", /*32 */
143 "x (dead)", /*64 */
144 "K (wakekill)", /* 128 */
145 "W (waking)", /* 256 */
146 };
```
修改之后的结果如下
```
134 static const char * const task_state_array[] 135 = {
136 "R (running)", /* 0 */
137 "S (sleeping)", /* 1 */
138 "D (disk sleep)", /* 2 */
//修改的部分
139 "S (sleeping)", /* 4 */
140 "S (sleeping)", /* 8 */
141 "Z (zombie)", /*16 */
142 "X (dead)", /*32 */
143 "x (dead)", /*64 */
144 "K (wakekill)", /* 128 */
145 "W (waking)", /* 256 */
146 };
```
修改`array.c`的第二处
```
180 seq_printf(m,
181 "State:\t%s\n"
182 "Tgid:\t%d\n"
183 "Pid:\t%d\n"
184 "PPid:\t%d\n"
185 "TracerPid:\t%d\n"
186 "Uid:\t%d\t%d\t%d\t%d\n"
187 "Gid:\t%d\t%d\t%d\t%d\n",
188 get_task_state(p),
189 task_tgid_nr_ns(p, ns),
190 pid_nr_ns(pid, ns),
191 ppid, tpid,
192 cred->uid, cred->euid, cred->suid, cred->fsuid,
193 cred->gid, cred->egid, cred->sgid, cred->fsgid);
```
修改的结果为
```
180 seq_printf(m,
181 "State:\t%s\n"
182 "Tgid:\t%d\n"
183 "Pid:\t%d\n"
184 "PPid:\t%d\n"
185 "TracerPid:\t%d\n"
186 "Uid:\t%d\t%d\t%d\t%d\n"
187 "Gid:\t%d\t%d\t%d\t%d\n",
188 get_task_state(p),
189 task_tgid_nr_ns(p, ns),
190 pid_nr_ns(pid, ns),
//修改部分
191 ppid, 0,
192 cred->uid, cred->euid, cred->suid, cred->fsuid,
193 cred->gid, cred->egid, cred->sgid, cred->fsgid);
```
## 源码的编译运行
在编译运行之前, 我们需要先用`echo $PATH`确认交叉编译器在PATH中。
![](https://i.imgur.com/VfALUlJ.png)
按照下面来进行配置
```
$ export ARCH=arm #指明目标体系架构,arm、x86、arm64
$ export SUBARCH=arm
$ cd msm #进入内核所在目录
$ make hammerhead_defconfig # 设备名_defconfig
#指定使用的交叉编译器的前缀
$ make ARCH=arm CROSS_COMPILE=arm-eabi- -j4 ##如果没有gcc的环境, 就增加了CC=clang
```
可以从[编译内核](https://source.android.com/source/building-kernels.html)这篇文章中找到相应的设备名。
在编译的过程中, 遇到了下面的报错。
![](https://i.imgur.com/WrDEZh8.png)
这时候需要修改`kernel/timeconst.pl`文件, 用`vim kernel/timeconst.pl`编辑该文件, 定位到下述代码。
```
372 @val = @{$canned_values{$hz}};
373 if (!defined(@val)) {
374 @val = compute_values($hz);
375 }
376 output($hz, @val);
```
将`if (!defined(@val))`改为`if (!@val)`, 再编译一次就可以了。
![](https://i.imgur.com/sTmrBvr.png)
接下来, 就按照上图提示进入目录`arch/arm/boot`。
![](https://i.imgur.com/ytyQvDO.png)
### 重打包boot.img
为了防止发生不可挽回的刷砖错误, 在刷机之前, 一定要按照[尝试绕过TracePid反调试](https://www.52pojie.cn/thread-917096-1-1.html)将boot.img进行备份。
#### 准备好bootimg-tools工具
因为我之前Windows环境是准备好了的, 就直接在本地解决下面的任务。
在Ubuntu环境中, 输入下面命令就准备完成了
```
$ git clone https://github.com/pbatard/bootimg-tools.git
$ make
$ cd mkbootimg
```
Windows环境下进入`]\MinGW\msys\1.0`目录下, 双击`msys.bat`。
把[提取出来的boot.img](https://www.52pojie.cn/thread-917096-1-1.html)放到mkbootimg文件夹下, 之后的步骤不管是哪个环境下都是相同的。
#### 用unmkbootimg解包
在MinGW输入命令`./unmkbootimg -i boot.img`, 如果是Ubuntu, 直接去掉前面的`./`执行命令。
我们获得了rebuild需要输入的指令, 之后要rebuild的时候要修改一下才能用。
```
To rebuild this boot image, you can use the command:
mkbootimg --base 0 --pagesize 2048 --kernel_offset 0x00008000 --ramdisk_offset 0x02900000 --second_offset 0x00f00000 --tags_offset 0x02700000 --cmdline 'console=ttyHSL0,115200,n8 androidboot.hardware=hammerhead user_debug=31 maxcpus=2 msm_watchdog_v2.enable=1' --kernel kernel --ramdisk ramdisk.cpio.gz -o boot.img
```
#### 替换kernel重新打包
![](https://i.imgur.com/EAnFtOB.png)
#### 刷入bootnew.img
在手机开机的情况下, 进入bootnew.img存放的目录输入下述命令。
```
$ adb reboot bootloader
$ astboot flash boot bootnew.img
$ fastboot reboot
```
### 测试
现在到了见证奇迹的时刻了
![](https://i.imgur.com/pkdj3P3.png)
## 参考文章或其他链接
(http://www.cnblogs.com/a2211009/p/4265225.html)
[在Ubuntu中通过update-alternatives切换java版本](https://blog.csdn.net/zhandoushi1982/article/details/50807282)
[编译Android 9.0内核源码并刷入手机](https://blog.csdn.net/XXOOYC/article/details/85679143)
(https://blog.csdn.net/u012417380/article/details/73353670)
[搭建编译环境](https://source.android.com/setup/build/initializing)
(https://appuals.com/how-to-build-a-custom-android-kernel/)
(https://blog.csdn.net/fly_hps/article/details/86172698)
(https://pan.baidu.com/s/1K5LU-h8npuczj4JgjKkaWw)提取码:ma3i badou0332 发表于 2019-4-26 12:03
楼主你这个是不是 可以去某软件的VIP教程?
.....这个只是分析Android内核的源代码,绕过Android监测是否手机是否在被调试,没有对APP的源代码进行修改,不能去软件的VIP的。 Se8s0n 发表于 2019-4-26 12:24
.....这个只是分析Android内核的源代码,绕过Android监测是否手机是否在被调试,没有对APP的源代码进行修 ...
我想问下 学习哪里才能破解软件的VIP? 楼主你这个是不是 可以去某软件的VIP教程? 很详细啊…辛苦了
用这个能减少准备环境的工作量:
https://github.com/tiann/docker-aosp junkboy 发表于 2019-4-26 12:55
很详细啊…辛苦了
用这个能减少准备环境的工作量:
我也是完成这个文章才想起用docker的.....谢谢了 反调试最大的问题在于如果别人使用xposed进行dump,怎么办? 感谢分享,需要理解才能学懂啊 膜拜大神,想问一下新手要先从哪方面学起呢,有没有好的教程推荐?
页:
[1]
2