一、概念
具体的描述大家可以在网上找,在这里我就不再罗嗦了,大致意思为:通过调用程序自身的一些函数达到某种目的。
二、背景
虽然前面的HOOK已经可以实现一些特殊功能,但是对于一个线上的软件,只在本地做了修改是没有用的,因为服务器的数据并没有改掉,因此出现了CALL。
三、实现
仍以前面的Game为例进行CALL的说明,如下“弄死我自己”。
1、CALL代码构建
根据已有的逆向分析结果,该游戏中角色类中有一个成员方法——被攻击的函数beAct,其有两个int类型参数,分别为:伤害damage和攻击者在数组中的下标index。
首先还是需要在Game中分配一块内存空间写入CALL代码,如下:
// 还是瞎写的哈 。
#define PTR_THIS 0xaaaaaaaa
#define ACT_CALL 0xbbbbbbbb
char call_data[]
{
0x51, // push ecx 因为后面使用了两个寄存器,要先将原来的值保存起来
0x50, // push eax
0x6A,0x02, // push 攻击者在数组中的索引
0x68,0x00,0x00,0x00,0x00, // push 伤害,4字节,后面用指针打进来
0xB9,0x00,0x00,0x00,0x00, // mov ecx, 被攻击者的指针,即保存this指针
0xB8,0x00,0x00,0x00,0x00, // mov eax, 被攻击函数的地址,使用寄存器是为了不计算
0xFF,0xD0, // call eax 调用被攻击函数
0x58, // pop eax 恢复这两个寄存器的值
0x59, // pop ecx
0xC3 // return 函数要返回,否则崩溃
};
int* _damage = (int*)(call_data + 5);
_damage[0] = 99999;
int* _beActer = (int*)(call_data + 10);
_beActer[0] = PTR_THIS;
int* _actCall = (int*)(call_data + 15);
_actCall[0] = ACT_CALL;
关于call_data的说明:
- 开始的两次压栈,是因为后面需要用到这两个寄存器,所以需要先将其值保存起来,而后对应结尾的两次出栈。其中ecx保存被攻击者的指针是根据这是一个类确定的,因为ecx大多数都是保存this;而使用eax保存被攻击函数即beAct的地址是因为可以省去跳转的计算
- 结尾的0xC3,是为了返回用的,首先函数都有返回(虽然我们有时可以不写),其次这是分配出的一块内存空间,我们只是在这里写下了功能代码,当这些代码执行完之后,如果没有返回,CPU则会继续执行后面地址的其他指令,而后面是什么是不确定的!!!
2、如何调用CALL代码
虽然代码写完了,但是怎么调用,即怎么让CPU执行到这里?
我们都知道,进程是没有执行权限的而线程是有的,所以当进程创建时都会有一个主线程进行CPU的执行,进程为线程提供了一些环境以及部分的管理工作,所以问题就变成了获得线程执行权限。
方法有两个:1)劫持线程;2)创建远程线程。
本例以创建远程线程说明,因为劫持线程有一部分是HOOK。
调用API,如下:
DWORD Tid;
CreateRemoteThread(
hGameProcess, // 目标进程句柄
NULL, // 为NULL表示获得默认的安全描述符,并且不能继承句柄
0, // 初始堆栈大小 为0表示使用默认大小
(LPTHREAD_START_ROUTINE)call_kill, // 线程执行的起始地址 (函数指针)
0, // 线程函数的参数,为0表示没有参数
0, // 创建线程后 立即执行
&Tid // 返回线程id
);
其中call_kill是你创建的线程从哪里开始执行,即CALL代码写在内存中的地址。
当在对话框内勾选“杀死我自己”时,即完成了自杀。
四、总结
虽然HOOK和CALL都简单实现了一下,但是这都是靠汇编完成的,毕竟汇编是给机器看的,所以能不能不写汇编?
1、内联汇编
unsigned index = 2;
unsigned damage = 99999;
unsigned beActer = PTR_THIS;
unsigned dCall = ACT_CALL;
__asm
{
push index
push damage
mov ecx, beActer
call dCall
}
虽然还不是纯C++代码,但最起码看起来舒服多了,简明扼要的表达了我要做什么:传参、保存被攻击者的this指针、调用被攻击函数
2、纯C++代码
具体代码就不提供了,大致说下思路。
/*
* 该函数为CreateRemoteThread函数中参数函数指针可以指向的
* 根据已有的逆向分析,被攻击是一个类的成员函数,参数两个,均为int,分别为伤害和攻击者的序号
* 理想情况下,希望通过player->beAct(int damage, int index)的形式完成此功能
* 其中player为角色类的一个对象,此处代表人物角色
* 因为无法分析出完整的被攻击函数的实现内容,但是又已经知道了该函数的返回类型和参数,所以采用类的成员函数的函数指针来做
*/
剩下的大家自由发挥~