所谓的外挂程序就是在某个进程执行的过程中,其他进程可以动态的修改进程中的数据或代码,从而影响程序的执行路径,并最终影响程序的运行结果。在windows上我们有系统库函数writeprocess()可以直接完成该项功能。而在linux上没有相同功能的函数可以使用,不过使用ptrace也可以完成类似的功能。
首先,Ptrace的使用形式如下:
#include <sys/ptrace.h>
int ptrace(int request, int pid, int addr, int data);
Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image的方式。它主要用于实现断点调试。一个被跟踪的进程在运行中,发生一个信号的时候进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/reg.h>
const int int_size = sizeof(int);
void putdata(pid_t child, int addr, //向进程号为child的进程的addr这个地址空间写入str
char *str, int len)
{
char *laddr;
int i, j;
union u {
int val;
char chars[int_size];
}data;
i = 0;
j = len / int_size;
laddr = str;
while(i < j) {
memcpy(data.chars, laddr, int_size);
ptrace(PTRACE_POKEDATA, child, //每次写入长度为int_size的数据
addr + i * 4, data.val);
++i;
laddr += int_size;
}
j = len % int_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);
}
}
int main()
{
pid_t child;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL); //设置自进程由父进程监控
execl("./test", "test", NULL); //由父进程启动自进程
}
else
{
int orig_eax;
int params[3];
int status;
char *str, *laddr;
int toggle = 0;
while(1) {
wait(&status); //阻塞自己,等待子进程信号
if(WIFEXITED(status)) // 子进程结束
break;
orig_eax = ptrace(PTRACE_PEEKUSER, //获取寄存器信息
child, 4 * ORIG_EAX,
NULL);
if(orig_eax == SYS_write) { //如果是写系统调用,修改细节具体参见系统write调用
params[0] = ptrace(PTRACE_PEEKUSER,
child, 4 * EBX,
NULL);
params[1] = ptrace(PTRACE_PEEKUSER,
child, 4 * ECX,
NULL);
params[2] = ptrace(PTRACE_PEEKUSER,
child, 4 * EDX,
NULL);
char* writedata = "The winner of 100 billion dollars is:gagaga\n";
putdata(child, params[1],writedata,
params[2]);
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL); //设置子进程系统调用或者终止时暂停
}
}
return 0;
}
子进程代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand((unsigned)(time(NULL)));
int i = 0;
for(i=0;i<120;i++)
{
int choose = rand()%4;
char* name="gagaga";
switch(choose)
{
case 0: name="hahaha";break;
case 1: name="papapa";break;
case 2: name="lalala";break;
}
printf("The winner of 100 billion dollars is:%s\n",name);
}
return 0;
}
这样在子进程系统调用write的时候,父进程就可以修改write的参数值,并进一步影响了信息的输出。