基本概念

1. 什么是函数重排和基本块重排

基本块重排是指在函数内部,重新排列程序的基本块顺序。基本块是程序中由顺序执行的一组指令组成的片段,且在执行的过程中不会发生跳转(除了最后一条指令可能是跳转指令)。重排的目的是使 高频执行的基本块(热路径) 彼此相邻,以减少分支跳转,增加代码的局部性。

1
2
3
4
5
6
if (condition) {
// Basic Block A (frequently executed)
} else {
// Basic Block B (rarely executed)
}
// Basic Block C (frequently executed)

基本块重排:对函数内部的基本块重新排序,优化局部性,减少跳转和缓存失效。
函数重排:对整个程序中函数的顺序进行调整,以便高频调用的函数相邻,优化函数调用的性能。

2. 获得函数地址后为什么要进算数混淆再还原使用

在 C/C++ 中,函数指针 是一个保存函数入口地址的变量。即通过该指针,你可以调用函数。当你将函数指针强制转换成整数时,本质上是获取了函数在内存中的地址(机器地址)。

• 安全性:混淆函数指针地址可以避免简单的内存攻击。比如,有时为了防止攻击者轻易找到和调用某个函数,通过将指针与常量相加/相减或进行位运算等操作,攻击者即使获得了内存内容,也无法轻易推导出真正的函数地址。这是一种基础的混淆技术,特别在一些保护机制不足的环境下有用。
• 防反编译或逆向工程:通过混淆函数指针和地址,程序员可以使得反编译器或逆向工程工具更难还原程序的真实行为,增加破解和篡改的难度。
• 动态计算地址:有时程序可能在不同的上下文中需要计算函数指针,而不是在编译时固定地址。将函数指针转化为整数后进行算术运算,可以动态地生成函数调用的地址,尤其是在编写系统底层代码时。

举个例子

在一个游戏中,如果存在管理角色真实充值数额的函数,并且没有对该函数指针或逻辑进行混淆或保护,逆向工程人员可以通过分析程序的二进制文件、运行时内存或使用调试工具,相对容易地找到这个函数的地址,并进行破解或篡改。

什么是堆栈上的返回地址

堆栈上的返回地址 是指当函数调用时,CPU 将调用方的返回地址(即函数执行完后的继续执行位置)保存到堆栈中,确保函数执行完后能正确返回。

这个返回地址实际上是一个指向代码的指针,它指向调用函数中的某条指令,因此也可以称作“代码指针”。

通过保存和恢复这个返回地址(如通过 setjmp/longjmp),程序可以控制执行流程的跳转位置。

核心思想