GG修改器破解版下载地址:https://ghb2023zs.bj.bcebos.com/gg/xgq/ggxgq?GGXGQ
大家好,今天小编为大家分享关于gg修改器改游戏有用吗_gg修改器只能改游戏吗的内容,赶快来一起来看看吧。
Linux 内核热补丁可以修复正在运行的 linux 内核,是一种维持线上稳定性不可缺少的措施,现在比较常见的比如 kpatch 和 livepatch。内核热补丁可以修复内核中正在运行的函数,用已修复的函数替换掉内核中存在问题的函数从而达到修复目的。
函数替换的思想比较简单,就是在执行旧函数时绕开它的执行逻辑而跳转到新的函数中,有一种比较简单粗暴的方式,就是将原函数的第一条指令修改为“ jump 目标函数”指令,即直接跳转到新的函数以达到替换目的。
那么,问题来了,这么做靠谱吗?直接将原函数的第一条指令修改为 jump 指令,会破坏掉原函数和它的调用者之间的寄存器上下文关系,存在安全隐患!本文会针对该问题进行探索和验证。
安全性冲击:问题呈现
对于函数调用,假设存在这样两个函数 funA 和 funB,其中 funA 调用 funB 函数,这里称 funA 为 caller(调用者),funB 为 callee(被调用者),funA 和 funB 都使用了相同的寄存器 R,如下所示:
图1 funA 和 funB 都使用了寄存器 R,funA 再次使用 R 时已经被 funB 修改
因此,当 funA 再次使用到 R 的数据已经是错误的数据了。如果 funA 在调用 funB 前保存寄存器 R 中的数据,funB 返回后再将数据恢复到 R 中,或者 funB 先保存 R 中原有的数据,然后在返回前恢复,就可以解决这类问题。
唯一的调用约定
那寄存器该由 caller 还是 callee 来保存?这就需要遵循函数的调用约定(call convention),不同的 ABI 和不同的平台,函数的调用约定是不一样的,对于 Linux 来说,它遵循的是 System V ABI 的 call convention,x86_64 平台下函数调用约定有且只有一种,调用者 caller 和被调用者 callee 需要对相应的寄存器进行保存和恢复操作:
调用约定,gcc 它遵守了吗?
设问:当函数实现很简单,只用到了少量寄存器,那没使用到的还需要保存吗?
答案:it depends。根据编译选项决定。
众所周知,GCC 编译器有 -O0、-O1、-O2 和 -Ox 等编译优化选项,优化范围和深度随 x 增大而增大(-O0是不优化,其中隐含的意思是,它会严格遵循 ABI 中的调用约定,对所有使用的寄存器进行保存和恢复)。
Linux 内核选用的都是 -O2 优化。GCC 会选择性的不遵守调用约定,也就是设问里提到的,不需要保存没使用到的寄存器。
当【运行时替换】撞见【调用约定】
GCC 之所以可以做这个优化,是因为 GCC 高屋建瓴,了解程序的执行流。当它知道 callee,caller 的寄存器分配情况,就会大胆且安全地做各种优化。
但是,运行时替换破坏了这个假设,GCC 所掌握的 callee 信息,极有可能是错误的。那么这些优化可能会引发严重问题。这里以一个具体的实例进行详细说明,这是一个用户态的例子( x86_64 平台):
//test.c 文件
//编译命令:gcc test.c -o test -O2 (kernel 采用的是 O2 优化选项)
//执行过程:./test
//输入参数:4
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#define noinline __attribute__ ((noinline)) //禁止内联
static noinline int c(int x)
{
return x * x * x;
}
static noinline int b(int x)
{
return x;
}
static noinline int newb(int x)
{
return c(x * 2) * x;
}
static noinline int a(int x)
{
int volatile tmp = b(x); // tmp = 8 ** 3 * 4
return x + tmp; // return 4(not 8) + tmp
}
int main(void)
{
int x;
scanf("%d", &x);
if (mprotect((void*)(((unsigned long)&b) & (~0xFFFF)), 15,
PROT_WRITE | PROT_EXEC | PROT_READ)) {
perror("mprotect");
return 1;
}
/* 利用 jump 指令将函数 b 替换为 newb 函数 */
((char*)b)[0] = 0xe9;
*(long*)((unsigned long)b + 1) = (unsigned long)&newb
- (unsigned long)&b - 5;
printf("%d", a(x));
return 0;
}
该例子说明,直接使用 jump 指令替换函数在 -O2 的编译优化下,会出现问题,安全性受到了质疑和冲击!!!
安全性冲击:分析问题
上述例子中,我们将函数 b 用 jump 指令替换为 newb 函数,在 -O2 的编译优化下出现了计算错误的结果,因此,我们需要对函数的调用执行过程进行仔细分析,挖掘问题所在。首先,我们先来查看一下该程序的反汇编(指令:objdump -d test),并重点关注 a、b 和 newb 函数:
图2 -O2 编译优化的反汇编结果
汇编解释:
main:
-> 将参数 4 存放到 edi 寄存器中
-> 调用 a 函数:
-> 调用 b 函数,直接跳转到 newb 函数:
-> 将 edi 寄存器中的值存放到 edx 寄存器
-> edi 寄存器与自身相加后结果放入 edi
-> 调用 c 函数:
-> 将 edi 寄存器中的值存到 eax 寄存器
-> edi 乘以 eax 后结果放入 eax
-> edi 乘以 eax 后结果放入 eax
-> 返回到 newb 函数
-> 将 edx 与 eax 相乘后结果放入 eax
-> 返回到 a 函数
-> 将 edi 与 eax 相加后结果放入 eax
-> 返回 main 函数
(注意:b 函数中没有对 edi 寄存器进行写操作,而且它的代码段被修改为 jump 指令跳转到 newb 函数)
数据出错的原因在于,在函数 newb 中,使用到了 a 函数中使用的 edi 寄存器,edi 寄存器中的值在 newb 函数中被修改为 8,当 newb 函数返回后,edi 的值仍然是 8,a 函数继续使用了该值,因此,计算过程变为:8^3 * 4 + 8 = 2056,而正确的计算结果应该是 8^3 * 4 + 4 = 2052。
接下来不进行编译优化(-O0),其输出结果是正确的 2052,反汇编如下所示:
图3 不进行编译优化的反汇编
从反汇编中可以看到,函数 a 在调用 b 函数前,将 edi 寄存器的值存在了栈上,调用之后,将栈上的数据再取出,最后进行相加。这就说明,-O2 优化选项将 edi 寄存器的保存和恢复操作优化掉了,而在调用约定中,edi 寄存器本就该属于 caller 进行保存/恢复的。至于为什么编译器会进行优化,我们此刻的猜想是:
a 函数本来调用的是 b 函数,而且编译器知道 b 函数中没有使用到 edi 寄存器,因此调用者 a 函数没有对该寄存器进行保存和恢复操作。但是编译器不知道的是,在程序运行时,b 函数的代码段被动态修改,利用 jump 指令替换为 newb 函数,而在 newb 函数中对 edi 寄存器进行了数据读写操作,于是出现了错误。
这是一个典型的没有保存 caller-save 寄存器导致数据出错的场景。而编译内核采用的也是 -O2 选项。如果将该场景应用到内核函数热替换是否会出现这类问题呢?于是,我们带着问题继续探索。
安全性冲击:探索问题
不再观察到 bug
我们构造了一个内核函数热替换的实例,将上面的用户态的例子移植到我们构造的场景中,通过内核模块修改原函数的代码段,用 jump 指令直接替换原来的 b 函数。然而加载模块后,结果是正确的 2052,经过反汇编我们发现,内核中 a 函数对 edi 寄存器进行了保存操作:
图4 内核中 a 函数的反汇编
内核和模块编译时采用的是 -O2 优化选项,而此处 a 函数并没有被优化,仍然保存了 edi 寄存器。
此时我们预测:对于内核函数的热替换来说,使用 jump 做函数替换是安全的。
神奇的 -pg 选项
我们猜想是否是内核编译时使用其它的编译选项导致问题不能复现。果不其然,经过探索我们发现内核编译使用的 -pg 选项导致问题不再复现。
通过翻阅 GCC 手册得知,-pg 选项是为了支持 GNU 的 gprop 性能分析工具所引入的,它能在函数中增加一条 call mount 指令,去做一些分析工作。
在内核中,如果开启了 CONFIG_FUNCTION_TRACER,则会使能 -pg 选项。
图5 开启 CONFIG_FUNCTION_TRACER 使能 -pg 选项
FUNCTION_TRACE 即我们常说的 ftrace 功能,ftrace 大大提升了内核的运行时调试能力。ftrace 功能除了 -pg 选项,还要求打开 -mfentry 选项,后者的作用是将函数对 mcount 的调用放到函数的第一条指令处,然后通scripts/recordmcount.pl 脚本将该条 call 指令修改为 nop 指令。但 -mfentry 与本文主题没有关联,不再细说。
为了验证这个结论,我们回到上一节的用户态例子,并且增加了 -pg 编译选项:“gcc test.c -o test -O2 -pg”,此时运行结果果然正确了。查看其反汇编:
图6 增加 -pg 选项后的汇编
可以看到,每个函数都有 call mcount 指令,而且 a 函数中将 edi 寄存器保存到 ebx 中,在 newb 函数中又保存 ebx 寄存器。为什么在增加了 call mount 指令后,会做寄存器的保存操作?我们猜想,会不会是因为,由于 call mount 操作相当于调用了一个未知的函数( mcount 没有定义在同一个文件中),因此,GCC 认为这样未知的操作可能会污染了寄存器的数据,所以它才进行了保存现场的操作。
于是我们去掉了 -pg 选项,手动增加了 call mount 的行为进行验证:在另一个源文件 mcount.c 中增加一个函数 void mcount() { asm(“nop
“); },在 test.c 文件中增加对 mcount 函数的声明,a 函数中增加对该函数的调用:
extern void mcount(); //声明 mcount 函数
static noinline int a(int x){
int volatile tmp = b(x); // tmp = 8 ** 3 * 4
mcount();
return x + tmp; // return 4(not 8) + tmp
}
以上就是关于gg修改器改游戏有用吗_gg修改器只能改游戏吗的全部内容,希望对大家有帮助。
幻想小勇士GG修改器最新版,幻想小勇士GG修改器:让你的游戏更加畅快无比 大小:19.40MB4,501人安装 现在的游戏市场越来越多,各种类型的游戏层出不穷,而玩家们更是追求不断,他们对游……
下载gg修改器最新中文,探秘GG修改器最新中文版 大小:3.24MB4,527人安装 相信大家在游戏中都曾遇到过一些烦恼,比如想要一个更好的角色属性、更多的游戏币、……
下载gg游戏修改器最好用什么框架,为什么说gg游戏修改器是最好用的? 大小:14.87MB4,131人安装 在众多的游戏辅助软件中,gg游戏修改器可以说是最为出色的一款。它采用了最新的技术……
下载gg修改器最新版怎么下载,GG修改器最新版的下载和优点 大小:19.86MB4,405人安装 GG修改器最新版是一款非常强大的修改工具,可以帮助用户修改游戏中的一些参数和特性……
下载GG修改器最新版2022,gg修改器最新版下载2022 大小:6.93MB5,956人安装 GG修改器最新版2022能够让玩家感受瞬间秒杀全场的畅爽操作,该软件可以修改游戏中的……
下载给gg修改器root权限_gg修改器如何root权限 大小:10.43MB5,242人安装 大家好,今天小编为大家分享关于给gg修改器root权限_gg修改器如何root权限的内容,……
下载gg游戏修改器的文本,小资讨论:gg游戏修改器 – 改变游戏体验 大小:17.55MB4,625人安装 在游戏中,我们常常会面临着各种挑战和困难,而直面这些挑战往往需要很多耐心和时间……
下载GG修改器最新天天飞车,GG修改器带来的天天飞车全新体验 大小:17.24MB4,251人安装 天天飞车作为一款非常受欢迎的赛车游戏,一直受到玩家们的喜爱。但是,如果你已经厌……
下载root用gg修改器闪退怎么办_gg修改器怎么避免闪退 大小:6.96MB5,463人安装 大家好,今天小编为大家分享关于root用gg修改器闪退怎么办_gg修改器怎么避免闪退的……
下载gg修改器中文版在哪下载_正版gg修改器下载中文在哪下载 大小:13.47MB5,400人安装 大家好,今天小编为大家分享关于gg修改器中文版在哪下载_正版gg修改器下载中文在哪……
下载