mirror of
https://github.com/feicong/rom-course.git
synced 2025-05-05 10:06:53 +00:00
第11章内容优化
This commit is contained in:
parent
cb0acdd625
commit
d029dcfa87
@ -7,17 +7,24 @@
|
||||
|
||||
## 11.1 反调试常见手段
|
||||
|
||||
在`Android`逆向分析中,最常见的情况就是攻防的对抗,攻击者通过对样本进行静态分析,以及动态调试等手段,获取想要的信息。而保护方则通过对混淆,以及多种加固方式,来对自己的重要信息进行保护。例如使用加固的手段来干扰攻击者的静态分析,通过检测环境来对抗攻击者注入`hook`函数,添加各种检测调试来阻止攻击者动态分析。
|
||||
在Android逆向分析中,攻防对抗是最常见的情况。攻击者通过对样本进行静态分析和动态调试等手段来获取他们所需的信息。而保护方则通过混淆和其他多种加固方式来保护自己的重要信息。
|
||||
|
||||
例如,保护方可以使用加固技术干扰攻击者的静态分析。这些技术包括代码混淆、反编译难度增加和变量名修改等,以使源代码更难以理解和还原。
|
||||
|
||||
此外,保护方还可以通过检测运行环境来对抗攻击者注入hook函数,并添加各种调试检测机制来阻止攻击者进行动态分析。
|
||||
|
||||
这些方法都旨在提高应用程序的安全性,降低被恶意用户逆向工程或篡改的风险。
|
||||
|
||||
|
||||
### 11.1.1 检测调试标志
|
||||
|
||||
调试软件离不开调试器,而调试器又高度依赖`ptrace`。`ptrace`是`Linux`操作系统提供的一个系统调用,它允许一个进程监控另一个进程的执行,并能够在运行时修改它的寄存器和内存等资源。`ptrace`通常被用于调试应用程序、分析破解软件以及实现进程间沙盒隔离等场景。
|
||||
调试软件离不开调试器,而调试器又高度依赖于`ptrace`。`ptrace`是Linux操作系统提供的一个系统调用,它允许一个进程监控另一个进程的执行,并能够在运行时修改其寄存器和内存等资源。`ptrace`通常被用于调试应用程序、分析破解软件以及实现进程间沙盒隔离等场景。
|
||||
|
||||
使用`ptrace`来监控目标进程时,需要以`tracer`(追踪者)的身份启动一个新的进程,然后通过`ptrace`函数来请求操作系统将目标进程挂起并转交给`tracer`进程。一旦目标进程被挂起,`tracer`进程就可以读写其虚拟地址空间中的数据、修改寄存器值、单步执行指令等操作。当`tracer`完成了对目标进程的调试操作后,可以通过`ptrace`函数将控制权还原到目标进程上,使其继续执行。
|
||||
使用`ptrace`来监控目标进程时,需要以"tracer"(追踪者)的身份启动一个新的进程,并通过`ptrace()`函数请求操作系统将目标进程挂起并转交给"tracer"进程。一旦目标进程被挂起,"tracer"就可以读写其虚拟地址空间中的数据、修改寄存器值、单步执行指令等操作。当“tracer”完成对目标进程的调试操作后,可以通过`ptrace()`函数将控制权还原到目标进程上,使其继续执行。
|
||||
|
||||
由于`ptrace`功能的强大,它也被广泛应用于破解软件、恶意攻击等场景。因此,在一些安全敏感的场合,为了防止恶意攻击者使用`ptrace`来监控和修改进程的行为,需要采取一些反调试的手段来加强保护。
|
||||
由于`ptrace()`功能强大且灵活,在一些安全敏感场合中也常被恶意攻击者广泛应用于破解软件或进行恶意攻击。因此,在这些情况下为了防止恶意攻击者使用`ptrace()`来监控和修改程序行为,需要采取一些反调试的手段来加强保护。
|
||||
|
||||
通过在被保护程序中定期检测其父进程是否为指定的`tracer`进程,可以避免恶意攻击者使用`ptrace`跟踪程序的执行流程。
|
||||
通过在被保护程序中定期检测其父进程是否为指定的“tracer”进程,可以避免恶意攻击者使用 `ptrace()`跟踪程序的执行流程。这种方式能够帮助防止非授权调试器对目标进程进行操控,并增强应用程序的安全性。
|
||||
|
||||
接下来写一个简单的实例来进行测试。`Android Studio`创建`native c++`的项目。修改函数如下。
|
||||
|
||||
@ -89,19 +96,19 @@ su
|
||||
./android_server64
|
||||
```
|
||||
|
||||
接下来打开`ida`,选择`Debugger->Attach->Remote Arm linux/android debugger`,在`hostname`选项中填本地回环地址`127.0.0.1`,如下图。
|
||||
接下来打开`ida`,选择`Debugger->Attach->Remote Arm linux/android debugger`,在`hostname`选项中填本地回环地址`127.0.0.1`,如下图。
|
||||
|
||||

|
||||
|
||||
点击`ok`后,则会展示所有`Android`中的进程,在其中进行过滤,找到目标进程。如下图
|
||||
点击`ok`后,则会展示所有`Android`中的进程,在其中进行过滤,找到目标进程。如下图
|
||||
|
||||

|
||||
|
||||
成功挂起调试后,检查日志中的 `ppid`,发现并没有任何变化,依然是`zygote`作为父进程。
|
||||
成功挂起调试后,检查日志中的 `ppid`,发现并没有任何变化,依然是`zygote`作为父进程。
|
||||
|
||||
当使用 `IDA` 进行调试时,`IDA` 会创建一个调试器进程,并将其作为目标进程的父进程。但是,由于目标进程最初是由 `zygote`进程`fork`出来的,因此在查询其父进程`id`时,仍然会返回`zygote`进程的`id`。这并不意味着调试器进程没有被正确设置为目标进程的父进程。实际上,在`IDA`调试过程中,目标进程的执行状态确实是由调试器进程所控制的。因此,即使查询到的父进程`id`不正确,也不会影响`IDA`对目标进程的控制和调试操作。
|
||||
当使用`IDA`进行调试时,`IDA`会创建一个调试器进程,并将其作为目标进程的父进程。但是,由于目标进程最初是由 `zygote`进程`fork`出来的,因此在查询其父进程`id`时,仍然会返回`zygote`进程的`id`。这并不意味着调试器进程没有被正确设置为目标进程的父进程。实际上,在`IDA`调试过程中,目标进程的执行状态确实是由调试器进程所控制的。因此,即使查询到的父进程`id`不正确,也不会影响`IDA`对目标进程的控制和调试操作。
|
||||
|
||||
尽管查询`ppid`无法判断出进程被调试了,但是依然有其他地方会出现被调试的信息,例如`/proc/<pid>/status`文件中的字段`TracerPid`,就能看到调试进程的`id`。下面查看该文件。
|
||||
尽管查询`ppid`无法判断出进程被调试了,但是依然有其他地方会出现被调试的信息,例如`/proc/<pid>/status`文件中的字段`TracerPid`,就能看到调试进程的`id`。下面查看该文件。
|
||||
|
||||
```
|
||||
// 没有调试时的文件内容
|
||||
@ -135,7 +142,7 @@ ps -e|grep 7525
|
||||
root 7525 7523 10803524 33392 0 0 S android_server64
|
||||
```
|
||||
|
||||
除了`status`文件外,`/proc/<pid>/wchan`文件同样可以用来检测。下面是调试附加前,和附加后的对比。
|
||||
除了`status`文件外,`/proc/<pid>/wchan`文件同样可以用来检测。下面是调试附加前,和附加后的对比。
|
||||
|
||||
```
|
||||
// 附加前
|
||||
@ -145,7 +152,7 @@ SyS_epoll_wait
|
||||
ptrace_stop
|
||||
```
|
||||
|
||||
文件`/proc/<pid>/stat`也可以用来检测,当进程被中断等待时,内容将会由`S`变成`t`。对比如下。
|
||||
文件`/proc/<pid>/stat`也可以用来检测,当进程被中断等待时,内容将会由`S`变成`t`。对比如下。
|
||||
|
||||
```
|
||||
// 附加前
|
||||
@ -155,9 +162,16 @@ ptrace_stop
|
||||
t 1027 1027 0 0 -1 1077952832 29405 4835 0 0 81 9 0 0 20 0 19 0 424763 15088168960 24987 18446744073709551615 1 1 0 0 0 0 4612 1 1073775864 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
```
|
||||
|
||||
|
||||
### 11.1.2 禁止调试器附加
|
||||
|
||||
由于动态调试基本都是依赖`ptrace`对进程追踪,那么可以通过了解`ptrace`的使用特性,来针对性的检查自身是否被调试了。由于`ptrace`附加进程时,目标进程同时只能被一个进程附加,第二次附加就会失败,那么通过对自身进行`ptrace`处理,如果发现对自己进行附加失败,说明已经被调试了。同时对自身附加后,也能阻止其他进程再对其进行附加调试。下面看实现代码。
|
||||
由于动态调试基本上都依赖于`ptrace`对进程的追踪,我们可以通过了解`ptrace`的使用特性来有针对性地检查自身是否被调试。当一个进程已经被附加时,它同时只能被一个进程进行附加,第二次附加将会失败。因此,我们可以在自身上执行`ptrace()`操作,并观察是否成功来判断是否正在被调试。
|
||||
|
||||
当我们尝试对自身进行`ptrace()`处理时,如果发现无法成功附加到自己,则说明已经处于调试状态。同时,在成功对自身进行附加后,也可以阻止其他进程再次对其进行附加调试。
|
||||
|
||||
这种方式能够帮助我们及时发现并防止恶意攻击者使用动态调试工具来监控和修改程序行为。通过利用`ptrace()`的特性来检测和阻止不受信任的进程对目标程序的调试操作,提高应用程序的安全性和可靠性。
|
||||
|
||||
下面看实现代码。
|
||||
|
||||
```c++
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
@ -178,7 +192,7 @@ Java_cn_mik_nativedemo_MainActivity_stringFromJNI(
|
||||
}
|
||||
```
|
||||
|
||||
在`AOSP12`中,为了增强`Android`系统的安全性,`Google`限制了应用程序使用`ptrace`对自身进行调试。在当前进程中调用`ptrace(PTRACE_TRACEME)`函数将始终返回-1。但是我们可以创建一个子进程,来进行测试。下面是调整后的代码。
|
||||
在`AOSP12`中,为了增强`Android`系统的安全性,`Google`限制了应用程序使用`ptrace`对自身进行调试。在当前进程中调用`ptrace(PTRACE_TRACEME)`函数将始终返回-1。但是我们可以创建一个子进程,来进行测试。下面是调整后的代码。
|
||||
|
||||
```c++
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
@ -210,10 +224,11 @@ Java_cn_mik_nativedemo_MainActivity_stringFromJNI(
|
||||
}
|
||||
```
|
||||
|
||||
然后使用`ida`尝试对子进程进行调试,发现无法正常附加该进程了,错误如下。
|
||||
然后使用`ida`尝试对子进程进行调试,发现无法正常附加该进程了,错误如下。
|
||||
|
||||

|
||||
|
||||
|
||||
### 11.1.4 检测跟踪工具
|
||||
|
||||
除了常规调试器,一些安全分析工具都具备调试跟踪功能。比如Frida,可以向目标程序中注入Javascript脚本,来Hook跟踪与修改程序的执行状态。在执行反调试检测时,这类工具也是需要重点关注的对象。
|
||||
@ -224,7 +239,7 @@ Java_cn_mik_nativedemo_MainActivity_stringFromJNI(
|
||||
$ adb shell pidof com.android.settings
|
||||
$ frida -U -p 27507
|
||||
____
|
||||
/ _ | Frida 16.0.12 - A world-class dynamic instrumentation toolkit
|
||||
/ _ | Frida 16.1.1 - A world-class dynamic instrumentation toolkit
|
||||
| (_| |
|
||||
> _ | Commands:
|
||||
/_/ |_| help -> Displays the help system
|
||||
@ -254,42 +269,43 @@ $ frida -U -p 27507
|
||||
|
||||
### 11.1.4 系统调试检测接口
|
||||
|
||||
除了以上这两种比较常见的检测方式外,还有很多种方式进行检测,这些检测大多都是围绕着调试过程会产生的特征来进行检测,在真实的保护场景下,开发者会结合多种方案检测来防止被攻击者动态调试。以下是其他检测方案的介绍。
|
||||
除了上述提到的两种常见的检测方式之外,还有许多其他方法可以进行调试检测。这些检测方法主要围绕着调试过程产生的特征展开,在真实保护场景中,开发者通常会结合多种方案来防止被攻击者进行动态调试。以下是其他一些检测方案的介绍:
|
||||
|
||||
* `Android`本身提供的`api`判断是否被调试中,`android.os.Debug.isDebuggerConnected()`,这样的检测方法非常容易被`Hook`修改替换。
|
||||
* 调试器默认端口检测,例如`ida`默认使用的`23946`,以及调试进程名检测,例如前文中看到的`android_server`进程名称,这种检测方式同样很容易被处理,攻击者会修改默认端口,以及进程名称。
|
||||
* 运行效率检测,在函数执行过程计算执行消耗的时间,正常情况下执行效率是非常快的,如果时间较长,说明很有可能被人单步调试中。这种方式属于后知后觉,并不能根本性的阻止对方调试。
|
||||
* 断点指令检测,调试器在调试时,会在`so`的代码部分插入`breakpoint`指令,可以通过获取目标`so`的可执行部分,搜索其中是否存在断点的指令。
|
||||
* `ro.debuggable`是一个系统级属性,当在调试模式时,该值为1,否则是0,所以有时也会被拿来检测是否被调试中。
|
||||
- 使用Android本身提供的API来判断是否处于调试状态,例如`android.os.Debug.isDebuggerConnected()`。然而,这种检测方法很容易被Hook修改或替换。
|
||||
- 检查默认端口和进程名称:比如IDA使用的默认端口23946以及前文中提到的android_server进程名称等。但是这种方式同样容易受到处理,攻击者可能会修改默认端口和进程名称。
|
||||
- 运行效率检测:在函数执行过程中计算执行消耗的时间。正常情况下,执行效率应该非常快速。如果时间较长,则说明很有可能正在进行单步调试。然而,这种方式属于后知后觉,并不能根本性地阻止对方进行调试。
|
||||
- 断点指令检测:在调试器进行调试时,在目标代码部分插入断点指令(breakpoint)。可以通过获取目标可执行文件(so)并搜索其中是否存在断点指令来判断是否被调试。
|
||||
- `ro.debuggable`是一个系统级属性,当处于调试模式时,该值为1,否则为0。因此有时也会用它来检测是否正在进行调试。
|
||||
|
||||
除了一些常规的检测反调试,还有一些措施是针对反反调试的,例如通常情况下,检测`/proc/<pid>/status`中的`TracerPid`来判断是否被调试了,而开发者同时也知道,攻击者会选择将`status`文件重定向,或者采取其他方式,让`TracerPid`固定返回0,而这种情况,可以先检测,是否有攻击者将`status`文件进行的特殊出合理,例如先对自己的进程使用`ptrace`,然后检测`status`中的`TracerPid`是否有变更,如果结果为0,说明是被攻击者使用某种手段篡改了该值。
|
||||
需要注意的是,在实际应用中,开发者通常会结合多种方法和技术来增加检测的准确性和可靠性,并确保应用程序对动态调试的防护更加健壮。
|
||||
|
||||
由于大多数情况下,反调试手段会被攻击者使用各种`Hook`的方式进行替换处理,所以有些开发者会采用非常规的手段来获取,用来判断是否为调试状态的信息。例如内联汇编通过`svc`来执行对应的系统调用。
|
||||
除了一些常规的检测反调试,还有一些措施是针对反反调试的,例如通常情况下,检测`/proc/<pid>/status`中的`TracerPid`来判断是否被调试了,而开发者同时也知道,攻击者会选择将`status`文件重定向,或者采取其他方式,让`TracerPid`固定返回0,而这种情况,可以先检测,是否有攻击者将`status`文件进行的特殊出合理,例如先对自己的进程使用`ptrace`,然后检测`status`中的`TracerPid`是否有变更,如果结果为0,说明是被攻击者使用某种手段篡改了该值。
|
||||
|
||||
由于大多数情况下,反调试手段会被攻击者使用各种`Hook`的方式进行替换处理,所以有些开发者会采用非常规的手段来获取,用来判断是否为调试状态的信息。例如内联汇编通过`svc`来执行对应的系统调用。
|
||||
|
||||
|
||||
## 11.2 常见反调试绕过方案
|
||||
|
||||
围绕常见的反调试技术,都有相应的反反调试,也就是反调试绕过技术方案。
|
||||
常见的反调试技术,都有相应的反反调试,也就是反调试绕过技术方案。
|
||||
|
||||
1. `Hook`
|
||||
`Hook`是一种常见的反调试绕过方案。它可以修改目标进程的内存数据与代码,绕过应用程序的反调试技术。`Hook`技术在程序运行时替换函数的实现,从而绕过应用程序的反调试技术。例如,使用`Frida`注入程序,然后修改进程的调试标志读取接口,修改返回的内容,即可完成自身的调试标志检测。
|
||||
1. `Hook`技术。
|
||||
`Hook`是一种常见的反调试绕过方案。它可以修改目标进程的内存数据与代码,从而绕过应用程序的反调试技术。使用`Hook`技术时,在程序运行时替换函数的实现,以此来绕过应用程序中针对调试检测所采取的措施。例如,通过使用注入工具如Frida,并修改进程中与调试标志相关联的接口内容来完成自身是否被进行了调试标志检测。
|
||||
|
||||
2. 内存修改
|
||||
内存修改技术是一种常见的反调试绕过方案,它可以让黑客修改应用程序的内存,从而绕过应用程序的反调试技术。例如,可以使用内存修改工具来修改应用程序的内存,从而绕过应用程序的反调试技术。
|
||||
2. 内存修改。
|
||||
内存修改技术是另一种常见的反调试绕过方案,黑客可以利用它来修改应用程序在内存中保存的数据从而达到躲避应用程序设置好的防止被调式捉住功能。举例来说,黑客可以借助内存编辑工具直接更改应用程序在内存中储存在关键位置上需要被阻止观察或者捉住所需信息, 进而实现规避该等安全机制.
|
||||
|
||||
3. 反编译
|
||||
反编译技术是一种常见的反调试绕过方案,通过分析`App`程序的代码,反编译修改掉代码中的反调试检测逻辑部分,从而绕过应用程序的反调试技术。这种技术需要对目标程序进行大量的分析工作,结合多个工具执行反编译与重打包,在安卓安全技术相对不成熟的时期,这种方案被大量使用。目前,使用`Hook`方案与系统级别反反调试技术的场景会更加普遍。
|
||||
3. 反编译修改。
|
||||
另一个普遍采取之方法为使用反编译手段去找出App源代码里面负责执行检测应用是否被调试的部分逻辑,并对其进行修改以绕过反调试技术。此种手段需要对目标程序进行大量的分析研究,通常结合多个工具来执行反编译与重新打包等操作。在安卓安全领域尚未成熟时期,这类方案得到了广泛采纳。但如今,使用`Hook`方案和系统级别的反反调试技术更为普遍。
|
||||
|
||||
4. 系统级别反反调试
|
||||
系统级别反反调试技术是一种底层的反调试绕过方案,它通过修改系统反调试相关的代码逻辑,让系统输出程序为非调试状态。这种绕过应用程序的反调试技术比较稳定,且不易被检测,是一种常用的反反调试技术。
|
||||
4. 系统级反反调试。
|
||||
系统级别的反反调试技术是一种底层的方法,通过修改系统中与防止被调式相关联的代码逻辑,使得整个系统认定该程序处于非调式状态。这样做可以稳定地绕过应用程序设置好的防止被调式机制,并且不容易被检测到。因此,在实际应用中较为常见且广泛采用。
|
||||
|
||||
|
||||
## 11.3 系统层面解决反调试
|
||||
## 11.3 系统级反反调试
|
||||
|
||||
了解常见的反调试检测后,就可以对其进行修改,这些修改并不会完美解决反调试的所有问题,主要是处理掉一些常规的检测办法。来尽量减少分析成本。下面开始简单的对几种检测方式进行修改处理。
|
||||
了解常见的反调试检测后,就可以对其进行修改,这些修改并不会完美解决反调试的所有问题,主要是处理掉一些常规的检测办法。来尽量减少分析成本。下面开始简单的对几种检测方式进行修改处理。
|
||||
|
||||
|
||||
修改内核文件`fs/proc/array.c`,修改如下。
|
||||
修改内核文件`fs/proc/array.c`,修改如下。
|
||||
|
||||
```c++
|
||||
static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
|
||||
@ -316,9 +332,9 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
|
||||
}
|
||||
```
|
||||
|
||||
在这里的`tpid`就是前文中`status`中的`TracerPid`。被调试时,该值将是调试进程`id`,但是考虑到刚刚说的反反调试检测的情况,不能直接固定将文件中的调试特征去掉,而是添加控制,当我们需要调试时,才让其调试的特征不要被检测。这里可以通过应用层和内核层交互,传递参数过来,当该参数的值为1时,就修改其过滤掉调试特征。这里就不详细展开了,继续看下一个特征的修改。
|
||||
在这里的`tpid`就是前文中`status`中的`TracerPid`。被调试时,该值将是调试进程`id`,但是考虑到刚刚说的反反调试检测的情况,不能直接固定将文件中的调试特征去掉,而是添加控制,当我们需要调试时,才让其调试的特征不要被检测。这里可以通过应用层和内核层交互,传递参数过来,当该参数的值为1时,就修改其过滤掉调试特征。这里就不详细展开了,继续看下一个特征的修改。
|
||||
|
||||
同样是在这个文件中,修改函数`get_task_state`,这里同样可以优化成,由值来控制是否使用新的数组,修改内容如下。
|
||||
同样是在这个文件中,修改函数`get_task_state`,这里同样可以优化成,由值来控制是否使用新的数组,修改内容如下。
|
||||
|
||||
```c++
|
||||
static const char * const task_state_array[] = {
|
||||
@ -359,7 +375,7 @@ static inline const char *get_task_state(struct task_struct *tsk)
|
||||
}
|
||||
```
|
||||
|
||||
最后处理`wchan`的对应代码,修改内核文件`fs/proc/base.c`,相关修改如下。
|
||||
最后处理`wchan`的对应代码,修改内核文件`fs/proc/base.c`,相关修改如下。
|
||||
|
||||
```c++
|
||||
static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
|
||||
@ -387,7 +403,13 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
|
||||
|
||||
## 11.4 集成反反调试功能
|
||||
|
||||
所有这些对系统的修改,都是针对不同场景反调试而产生的对应解决方案,需要重新编译系统代码。涉及内核代码的部分,需要重新编译内核,涉及`framework`的部分编译生成`ROM`。整个过程可以编写自动化操作脚本,将重复性的工作做简化处理。
|
||||
所有这些对系统的修改,都是针对不同场景反调试而产生的对应解决方案,需要重新编译系统代码。涉及内核代码的部分,需要重新编译内核,涉及`framework`的部分编译生成`ROM`。整个过程可以编写自动化操作脚本,将重复性的工作做简化处理。
|
||||
|
||||
在实践过程中,调试与反调试技术都是随时攻防的不断升级实时变化的,例如,有一些软件壳会对系统状态与接口作检测,这个时候,这里介绍的一些公开的方法可能就失效了。这种情况下,需要结合实际,使用安全分析技术,对目标程序做进一步的分析,确定其使用的反调试技术,重新调整系统文件修改点,然后编译打包测试效果。
|
||||
在实践过程中,调试与反调试技术都是随时攻防的不断升级实时变化的,例如,有一些软件壳会对系统状态与接口作检测,这个时候,这里介绍的一些公开的方法可能就失效了。这种情况下,需要结合实际,使用安全分析技术,对目标程序做进一步的分析,确定其使用的反调试技术,重新调整系统文件修改点,然后编译打包测试效果。
|
||||
|
||||
|
||||
## 11.5 本章小结
|
||||
|
||||
本章主要介绍了在软件安全对抗领域的反调试与反反调试技术。以及简要介绍了不同反调试方法的原理,最后,讲解了一些系统级绕过反调试的方法。
|
||||
|
||||
除了公开的反调试技术外,还有一些鲜为人知,不被公开的反调试方法在商业与恶意软件中得到了应用。安全攻防技术的公开一般由社区安全研究人员实践并公开,隐藏的反调试方法会让防守方在一定的时间线内较得相对较高的安全防护能力。一个对抗方法的对外公开,意味着该方法失去了强有力的防护能力。因此,在安全对抗白热化的今天,安全对抗技术公开讨论的也越来越少了,一些新的思路与方法,需要研究人员自行探索与挖掘。
|
||||
|
@ -29,22 +29,22 @@ pip install jnitrace
|
||||
由于该工具是基于`frida`实现的,需要在手机中运行`frida-server`,在地址`https://github.com/frida/frida/releases`中下载`frida-server`,开发环境是`AOSP12`的情况直接下载`16`任意版本即可。然后将其推送到手机的`/data/local/tmp`目录中,并运行。具体命令如下。
|
||||
|
||||
```
|
||||
adb push ./frida-server-16.0.11-android-arm64 /data/local/tmp
|
||||
adb push ./frida-server-16.1.1-android-arm64 /data/local/tmp
|
||||
|
||||
adb forward tcp:27042 tcp:27042
|
||||
|
||||
adb shell
|
||||
adb shell
|
||||
|
||||
su
|
||||
|
||||
cd /data/local/tmp
|
||||
|
||||
chmod +x ./frida-server-16.0.11-android-arm64
|
||||
chmod +x ./frida-server-16.1.1-android-arm64
|
||||
|
||||
// 为防止出现错误,先将selinux关闭
|
||||
setenforce 0
|
||||
|
||||
./frida-server-16.0.10-android-arm64
|
||||
./frida-server-16.1.1-android-arm64
|
||||
```
|
||||
|
||||
`JniTrace`的启动环境准备就绪后,接下来准备测试的案例,案例实现如下。
|
||||
@ -101,7 +101,7 @@ jnitrace -l libnativedemo.so cn.mik.nativedemo
|
||||
|
||||
```
|
||||
/* TID 6996 */
|
||||
|
||||
|
||||
309 ms [+] JNIEnv->FindClass // 调用的JNI函数
|
||||
309 ms |- JNIEnv* : 0x7d3892f610 // 参数1的类型和值
|
||||
309 ms |- char* : 0x7c011aaf00 // 参数2的类型和值
|
||||
@ -371,7 +371,7 @@ DexFile_initConfig(JNIEnv* env, jobject ,jobject item) {
|
||||
|
||||
citem.isRegisterNativePrint = env->GetBooleanField(item, jIsRegisterNativePrint);
|
||||
citem.isJNIMethodPrint = env->GetBooleanField(item, jIsJNIMethodPrint);
|
||||
|
||||
|
||||
// 配置存储到全局
|
||||
runtime->SetConfigItem(citem);
|
||||
}
|
||||
@ -756,7 +756,7 @@ static const char* GetStringUTFChars(JNIEnv* env, jstring java_string, jboolean*
|
||||
313 ms |- jstring : 0x85
|
||||
313 ms |- jboolean* : 0x0
|
||||
313 ms |= char* : 0x7c8893f330
|
||||
|
||||
|
||||
313 ms ------------------------------------------------Backtrace------------------------------------------------
|
||||
313 ms |-> 0x7c01191b4c: _ZN7_JNIEnv17GetStringUTFCharsEP8_jstringPh+0x34 (libnativedemo.so:0x7c01183000)
|
||||
313 ms |-> 0x7c01191b4c: _ZN7_JNIEnv17GetStringUTFCharsEP8_jstringPh+0x34 (libnativedemo.so:0x7c01183000)
|
||||
@ -1077,7 +1077,7 @@ static void sample_signal_register(void) {
|
||||
struct sigaction act;
|
||||
// 清空act
|
||||
memset(&act, 0, sizeof(act));
|
||||
|
||||
|
||||
// 填充所有信号
|
||||
sigfillset(&act.sa_mask);
|
||||
// 删除SIGSEGV信号
|
||||
@ -1126,7 +1126,7 @@ static void sample_test(int solution, jboolean remote_unwind, jboolean with_cont
|
||||
g_remote_unwind = (JNI_TRUE == remote_unwind ? true : false);
|
||||
g_with_context = (JNI_TRUE == with_context ? true : false);
|
||||
g_signal_interrupted = (JNI_TRUE == signal_interrupted ? true : false);
|
||||
|
||||
|
||||
// 原子请求,为了保证获取到的g_frames_sz没问题
|
||||
__atomic_store_n(&g_frames_sz, 0, __ATOMIC_SEQ_CST);
|
||||
// 触发SIGABRT信号
|
||||
|
Loading…
x
Reference in New Issue
Block a user