【ptrace】linux下ptrace注入器的实现,(x86和x86_64)

2 篇文章 0 订阅
1 篇文章 0 订阅

KKPtrace是我写的ptrace注入器类的名称,注入器将会实现进程附加、内存读、内存写、寄存器读写等操作。


目前已实现功能:

  • 进程
    • 进程附加
    • 取消附加
    • 继续运行
  • 内存读
    • int32
    • byteList(字节数组,Vector<uint8>)
  • 内存写
    • int8
    • int16
    • int32
    • int64
    • byteList
  • 寄存器
    • 获取
    • 设置
    • 打印
  • call
    • 计算远程libc中函数地址
    • 批量call
    • 得到call的返回值
  • inject



远程CALL流程图

 


附C++代码:

#include "iostream"
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <memory.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <dlfcn.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
#include <vector>
#include <sstream>

using namespace std;

#if __WORDSIZE == 64
#define ax rax
#define cx rcx
#define dx rdx
#define bx rbx
#define sp rsp
#define bp rbp
#define si rsi
#define di rdi
#define ip rip
#endif

#if __WORDSIZE == 32
#define ax eax
#define cx ecx
#define dx edx
#define bx ebx
#define sp esp
#define bp ebp
#define si esi
#define di edi
#define ip eip
#endif

class Utils
{
public:
	static vector<uint8_t> ulongToByteList(u_long value)
	{
		// printf("ulongToByteList,input=%x\n", value);
		int longLen = sizeof(u_long);

		vector<uint8_t> byteList;
		for (size_t i = 0; i < longLen; i++)
		{
			int tempValue = value;

			tempValue = value << (8 * ((longLen - 1) - i));
			tempValue >>= (8 * (longLen - 1));
			byteList.push_back(tempValue);
		}

		// Utils::print_byteList_hexArray(byteList);

		return byteList;
	}
	static vector<uint8_t> uint32ToByteList(uint32_t value, int number = 4)
	{
		vector<uint8_t> byteList;
		if (number <= 0 || number > 4)
		{
			return byteList;
		}
		for (size_t i = 0; i < number; i++)
		{
			int tempValue = value;
			if (i == 0)
			{
				tempValue = value << 24;
			}
			if (i == 1)
			{
				tempValue = value << 16;
			}
			if (i == 2)
			{
				tempValue = value << 8;
			}
			tempValue = tempValue >> 24;
			byteList.push_back(tempValue);
		}
		return byteList;
	}
	static vector<uint8_t> uint16ToByteList(uint32_t value)
	{
		vector<uint8_t> byteList;

		int tempValue = 0;
		tempValue = value << 8 >> 8;
		byteList.push_back(tempValue);

		tempValue = value >> 8;
		byteList.push_back(tempValue);

		return byteList;
	}
	void static print_byteList_hexArray(vector<uint8_t> data)
	{
		for (const auto &item : data)
		{
			printf("%02x ", item);
		}
		cout << endl;
	}
	string static byteListToString(vector<uint8_t> data)
	{
		std::string res;
		res.insert(res.begin(), data.begin(), data.end());
		return res;
	}
	void static print_regs(struct user_regs_struct *reg_addr)
	{
		printf("--------------------------\n");
		printf("rax=%p\n", (void *)reg_addr->ax);
		printf("rcx=%p\n", (void *)reg_addr->cx);
		printf("rdx=%p\n", (void *)reg_addr->dx);
		printf("rbx=%p\n", (void *)reg_addr->bx);
		printf("rsp=%p\n", (void *)reg_addr->sp);
		printf("rbp=%p\n", (void *)reg_addr->bp);
		printf("rsi=%p\n", (void *)reg_addr->si);
		printf("rdi=%p\n", (void *)reg_addr->di);
		printf("rip=%p\n", (void *)reg_addr->ip);

		printf("--------------------------\n");
	}
};

class KKptrace
{
private:
	pid_t targetPid = 0;

	const char localLibcPath[39] = "/usr/lib/x86_64-linux-gnu/libc-2.32.so";

	void *getLibcBaseAddr(pid_t pid, const char *localLibcPath)
	{
		char filepath[512];
		void *moduleBaseAddr = NULL;
		snprintf(filepath, 512, "/proc/%d/maps", pid);
		FILE *f = fopen(filepath, "r");
		char line[512];
		while (!feof(f))
		{
			memset(line, 0, 512);
			fgets(line, 512, f);
			// printf(line);
			string lineStr = line;
			if (lineStr.find(localLibcPath) != -1)
			{
				int index = lineStr.find("-");
				string addrStr = lineStr.substr(0, index);
				stringstream ss;
				// puts(lineStr.c_str());
				ss << hex << addrStr.c_str();
				ss >> moduleBaseAddr;
				break;
			}
		}
		if (moduleBaseAddr == NULL)
		{
			cout << "getLibcBaseAddr() error,moduleName=" << localLibcPath << endl;
		}
		fclose(f);
		return moduleBaseAddr;
	}
	void *getRemoteFuncAddr(void *localFuncAddr, const char *libcName, pid_t remotePid = 0, void *remoteLibcBaseAddr = NULL)
	{
		void *remoteFuncAddr;
		if (remotePid == 0)
		{
			remotePid = targetPid;
		}
		if (remoteLibcBaseAddr == NULL)
		{
			remoteLibcBaseAddr = getLibcBaseAddr(remotePid, libcName);
		}

		void *localLibcBaseAddr = getLibcBaseAddr(getpid(), libcName);

		if (localLibcBaseAddr == NULL || remoteLibcBaseAddr == NULL)
		{
			return NULL;
		}
		remoteFuncAddr = (void *)((u_long)localFuncAddr - (u_long)localLibcBaseAddr + (u_long)remoteLibcBaseAddr);
		return remoteFuncAddr;
	}

public:
	bool isExitAttch = false;
	// attachment is premise of all opeartions
	bool isAttachSuccess = false;
	struct user_regs_struct callBefore_regs;

	bool attach(pid_t pid)
	{
		targetPid = pid;
		isAttachSuccess = false;
		int status = 0;
		if (ptrace(PTRACE_ATTACH, targetPid, NULL, 0) < 0)
		{
			printf("KKptrace->attach():attach process error, pid:%d\n", targetPid);
			return false;
		}
		printf("KKptrace->attach(): process success pid:%d\n", targetPid);
		waitpid(targetPid, &status, WUNTRACED);

		isAttachSuccess = true;
		return true;
	}

	bool ptrace_continue()
	{
		if (ptrace(PTRACE_CONT, targetPid, NULL, NULL) < 0)
		{
			printf("KKptrace->attach():ptrace continue error, pid:%d", targetPid);
			return false;
		}
		return true;
	}

	bool detach()
	{
		//kill(targetPid, SIGSTOP);
		//waitpid(targetPid, NULL, 0);
		if (ptrace(PTRACE_DETACH, targetPid, NULL, 0) < 0)
		{
			printf("KKptrace->attach():detach process error, pid:%d\n", targetPid);
			return false;
		}
		return true;
	}

	uint32_t memoryRead_uint32(void *addr)
	{
		if (!isAttachSuccess)
		{
			return 0;
		}
		uint32_t re = ptrace(PTRACE_PEEKTEXT, targetPid, addr, 0);
		return re;
	}
	vector<uint8_t> memoryRead_bytes(void *addr, int number)
	{
		int i = 0;
		uint32_t tempValue = 0;
		vector<uint8_t> reByteList;
		vector<uint8_t> tempByteList;

		for (i = 0; i < number / 4; i++)
		{
			tempValue = memoryRead_uint32((void *)((u_long)addr + (i * 4)));
			//cout << "readMemory_bytes.for->tempValue=" << std::hex << tempValue << endl;
			tempByteList = Utils::uint32ToByteList(tempValue, 4);
			reByteList.insert(reByteList.end(), tempByteList.begin(), tempByteList.end());
		}

		if (number % 4)
		{
			tempValue = memoryRead_uint32((void *)((u_long)addr + (i * 4)));
			tempByteList = Utils::uint32ToByteList(tempValue, number % 4);
			reByteList.insert(reByteList.end(), tempByteList.begin(), tempByteList.end());
		}
		return reByteList;
	}
	bool memoryWrite_string(void *addr, string data)
	{
		if (data.size() == 0)
		{
			return false;
		}
		vector<uint8_t> byteList;
		for (size_t i = 0; i < data.size(); i++)
		{
			byteList.push_back(data[i]);
		}
		byteList.push_back(0);
		return memoryWrite_bytes(addr, byteList);
	}
	bool memoryWrite_chars(void *addr, const char *data)
	{

		vector<uint8_t> byteList;
		for (size_t i = 0; data[i] != 0; i++)
		{
			byteList.push_back(data[i]);
		}
		byteList.push_back(0);
		return memoryWrite_bytes(addr, byteList);
	}

	bool memoryWrite_bytes(void *addr, vector<uint8_t> data)
	{

		// Utils::print_byteList_hexArray(data);
		if (!isAttachSuccess)
		{
			return false;
		}
		int longLen = sizeof(u_long);
		u_long writeValue = 0;
		void *writeAddr = addr;
		size_t i;
		for (i = 0; i < data.size() / longLen; i++)
		{
			writeValue = 0;
			for (size_t j = 0; j < longLen; j++)
			{
				u_long tempValue = 0;
				tempValue = data[i * longLen + j];
				tempValue = tempValue << (8 * j);
				writeValue = writeValue + tempValue;
			}

			writeAddr = (void *)((u_long)addr + longLen * i);
			// printf("PTRACE_POKETEXT,add=%p,value=%x\n", writeAddr, writeValue);
			if (ptrace(PTRACE_POKETEXT, targetPid, writeAddr, writeValue) < 0)
			{
				return false;
			}
		}
		int yu = data.size() % longLen;
		if (yu)
		{
			writeValue = 0;
			writeAddr = (void *)((u_long)addr + longLen * i);
			// 未对齐64bit的情况,需要先取值再覆盖。
			vector<uint8_t> readByteList = memoryRead_bytes(writeAddr, 8);
			// puts("readByteList");
			// Utils::print_byteList_hexArray(readByteList);
			for (size_t j = yu; j < longLen; j++)
			{
				u_long tempValue = readByteList[j];
				tempValue = tempValue << 8 * j;
				writeValue = writeValue + tempValue;
			}
			for (size_t j = 0; j < yu; j++)
			{
				u_long tempValue = 0;
				tempValue = data[i * longLen + j];
				// printf("j < yu;tempValue=%x\n", tempValue);
				tempValue = tempValue << (8 * j);
				writeValue = writeValue + tempValue;
			}
			// printf("PTRACE_POKETEXT_yu,add=%p,value=%x\n", writeAddr, writeValue);
			if (ptrace(PTRACE_POKETEXT, targetPid, writeAddr, writeValue) < 0)
			{
				return false;
			}
		}

		return true;
	}

	bool memoryWrite_long(void *addr, long value)
	{
		return memoryWrite_bytes(addr, Utils::ulongToByteList(value));
	}
	bool memoryWrite_u_long(void *addr, u_long value)
	{
		return memoryWrite_bytes(addr, Utils::ulongToByteList(value));
	}
	bool memoryWrite_int32(void *addr, int32_t data)
	{
		return memoryWrite_bytes(addr, Utils::uint32ToByteList(data));
	}
	bool memoryWrite_uint32(void *addr, uint32_t data)
	{
		return memoryWrite_bytes(addr, Utils::uint32ToByteList(data));
	}
	bool memoryWrite_int16(void *addr, int16_t data)
	{
		return memoryWrite_bytes(addr, Utils::uint16ToByteList(data));
	}
	bool memoryWrite_uint16(void *addr, uint16_t data)
	{
		return memoryWrite_bytes(addr, Utils::uint16ToByteList(data));
	}
	bool memoryWrite_int8(void *addr, int8_t data)
	{
		vector<uint8_t> byteList{(uint8_t)data};
		return memoryWrite_bytes(addr, byteList);
	}
	bool memoryWrite_uint8(void *addr, uint8_t data)
	{
		vector<uint8_t> byteList{data};
		return memoryWrite_bytes(addr, byteList);
	}

	bool getRegs(struct user_regs_struct *regs_addr)
	{
		if (ptrace(PTRACE_GETREGS, targetPid, NULL, regs_addr) < 0)
		{
			printf("KKptrace->getRegs(): fail\n");
			return false;
		}
		return true;
	}
	void regsPrint()
	{
		struct user_regs_struct regs;
		getRegs(&regs);
		Utils::print_regs(&regs);
	}
	bool setRegs(struct user_regs_struct *regs_addr, bool isPrintSetBeforeAndAfter = false)
	{
		struct user_regs_struct regs;

		if (isPrintSetBeforeAndAfter)
		{
			puts("set before");
			Utils::print_regs(regs_addr);
		}
		if (ptrace(PTRACE_SETREGS, targetPid, NULL, regs_addr) < 0)
		{
			printf("KKptrace->setRegs(): fail\n");
			return false;
		}
		if (isPrintSetBeforeAndAfter)
		{
			puts("set before");
			getRegs(&regs);
			Utils::print_regs(&regs);
		}
		return true;
	}
	void call_begin()
	{
		// 1、获取并保留当前寄存器环境,以便函数执行完毕后恢复程序正常流程
		getRegs(&callBefore_regs);
	}
	void call_end()
	{
		// 将寄存器恢复到call之前的状态
		setRegs(&callBefore_regs);
		int state = 0;
		ptrace_continue();
		//waitpid(targetPid, &state, WUNTRACED);
	}
	bool call(void *localFunc, vector<long> paramList, u_long *reValue = NULL, const char *libcName = NULL)
	{
		string libcNameStr = "libc-2.32.so";
		if (libcName == NULL || string(libcName).size() == 0)
		{
			libcName = libcNameStr.c_str();
		}

		void *funcAddr = getRemoteFuncAddr((void *)localFunc, libcName);
		if (funcAddr == NULL)
		{
			printf("call() fail,funcAddr=%p\n", (void *)funcAddr);
			return false;
		}
		printf("call().begin,funcAddr=%p\n", (void *)funcAddr);

		long re = call_funcAddr(funcAddr, paramList);
		if (reValue)
		{
			*reValue = re;
		}
		return true;
	}

	void push(struct user_regs_struct *regs, u_long value)
	{
		regs->sp -= sizeof(void *);
		memoryWrite_u_long((void *)regs->sp, value);
	}
	long call_funcAddr(void *targetFuncAddr, vector<long> paramList)
	{

		struct user_regs_struct regs_ori;
		struct user_regs_struct regs;

		getRegs(&regs_ori);
		memcpy(&regs, &regs_ori, sizeof(regs_ori));

		// 获取当前寄存器环境
		// 多于6个参数通过栈传递

#if __WORDSIZE == 64
		// 前6个参数 通过寄存器传参
		switch (paramList.size())
		{
		case 6:
			regs.r9 = paramList[5];
		case 5:
			regs.r8 = paramList[4];
		case 4:
			regs.rcx = paramList[3];
		case 3:
			regs.rdx = paramList[2];
		case 2:
			regs.rsi = paramList[1];
		case 1:
			regs.rdi = paramList[0];
			break;
		}
		for (size_t i = 0; i < 6 && paramList.size() > 0; i++)
		{
			paramList.erase(std::begin(paramList));
		}
#endif

		// 参数>6个,后续参数需要压栈。
		// i386
		if (paramList.size() > 0)
		{
			// regs.sp -= paramList.size() * sizeof(u_long);
			for (int i = paramList.size() - 1; i >= 0; i--)
			{
				push(&regs, paramList[i]);
				// printf(" paramList[i]=%x\n", paramList[i]);
				// memoryWrite_u_long((void*)((u_long)regs.sp + i * sizeof(u_long)), paramList[i]);
			}
		}

		push(&regs, 0);

		// 通过修改RIP,修改CPU执行位置

		regs.ax = 0;

		if (isExitAttch)
		{
			Utils::print_regs(&regs);
			regs.ip = 0;//(u_long)targetFuncAddr;
			// setRegs(&regs);
			detach();
			char temp[30];
			sprintf(temp, "%d", targetPid);
			string cmd = "sudo edb --attach " + string(temp);
			puts(cmd.c_str());
			system(cmd.c_str());
			getchar();
			getchar();
			exit(0);
		}
		else
		{
			regs.ip = (u_long)targetFuncAddr;
			setRegs(&regs);
		}

		int state = 0;
		ptrace_continue();
		waitpid(targetPid, &state, WUNTRACED);
		getRegs(&regs);
		u_long reValue = regs.ax;

		return reValue;
	}

	/**
	 * @brief 将模块注入
	 * @param modulePath 模块路径,例如./test.so
	 * @param funcName 函数名,默认为空,为空则只load so文件 不执行
	 * @param isRestore 注入后是否恢复程序正常运行,默认恢复;若设置不恢复,则需要手动调用call_end();
	 * @param isRestore 注入后是否恢复程序正常运行,默认恢复;若设置不恢复,则需要手动调用call_end();
	 *
	 * @return 返回说明
	 *     -<em>false</em> fail
	 *     -<em>true</em> succeed
	 */
	bool inject(const char *modulePath, const char *funcName = NULL, bool isRestore = true, long *reValue = NULL)
	{
		bool isCallSucess = false;

		vector<long> paramList;

		call_begin();

		// 分配内存地址
		// mmap(0,0x100,可读可写可执行,…)
		paramList.clear();
		paramList.push_back(0);
		paramList.push_back(0x100);
		paramList.push_back(PROT_READ | PROT_WRITE | PROT_EXEC);
		paramList.push_back(MAP_ANONYMOUS | MAP_PRIVATE);
		paramList.push_back(0);
		paramList.push_back(0);
		u_long remoteBuffer;
		call((void *)mmap, paramList, &remoteBuffer);
		if (remoteBuffer == -1)
		{
			printf("mmap() fail\n");
			return false;
		}
		printf("mmap sucess addr=%p\n", (void *)remoteBuffer);

		char realPath[500];
		realpath(modulePath, realPath);
		puts(realPath);
		memoryWrite_chars((void *)remoteBuffer, realPath);

		// 动态加载so文件
		// libcAddr=dlopen(realPath,RTLD_NOW|RTLD_GLOBAL)
		paramList.clear();
		paramList.push_back(remoteBuffer);
		paramList.push_back(RTLD_NOW | RTLD_GLOBAL);
		u_long moduleHandle;
		isCallSucess = call((void *)dlopen, paramList, &moduleHandle, "libdl-2.32.so");
		if (!isCallSucess)
		{
			return false;
		}

		printf("moduleHandle=%p\n", (void *)moduleHandle);

		if (!moduleHandle)
		{
			paramList.clear();
			u_long dlerrorReAddr;
			call((void *)dlerror, paramList, &dlerrorReAddr, "libdl-2.32.so");
			printf("dlopen() error,text=");

			vector<uint8_t> byteList_1 = memoryRead_bytes((void *)dlerrorReAddr, 100);
			puts(Utils::byteListToString(byteList_1).c_str());

			return false;
		}

		// 如果函数名为空,则只load so 不执行函数;
		if (funcName == NULL || string(funcName).size() == 0)
		{
			return true;
		}
		memoryWrite_chars((void *)remoteBuffer, funcName);
		sleep(0.1);


		// 获取函数地址
		// libcAddr=dlsym(libcAddr,funcName)
		paramList.clear();
		paramList.push_back(moduleHandle);
		paramList.push_back(remoteBuffer);
		u_long funcAddr;
		isCallSucess = call((void *)dlsym, paramList, &funcAddr, "libdl-2.32.so");
		if (!isCallSucess)
		{
			return false;
		}

		printf("func addr=%p\n", (void *)funcAddr);
		if (!funcAddr)
		{
			printf("dlsym() error\n");
			return false;
		}

		// 执行已经加载成功的模块中的函数
		paramList.clear();

		long re = call_funcAddr((void *)funcAddr, paramList);
		if (reValue)
		{
			*reValue = re;
		}
		if (isRestore)
		{
			call_end();
		}

		return true;
	}
};

int main()
{

	KKptrace *kkptrace = new KKptrace();
	// pid_t forkPid = fork();
	// if (forkPid == 0)
	// {
	// 	char *argv[] = {};
	// 	execv("/home/ubuntu/Desktop/android_learningRoad/ptrace_reject/plsInjectMe", NULL);
	// 	return 0;
	// }

	// sleep(1);
	// cout << "childPid=" << forkPid << endl;
	pid_t forkPid;
	cin >> forkPid;
	if (!kkptrace->attach(forkPid))
	{
		return 0;
	}
	long re;
	kkptrace->inject("/home/ubuntu/Desktop/android_learningRoad/ptrace_reject/libtest.so", "testEntry", true, &re);

	cout << "injectAfterCall_re=" << re << endl;

	puts("pls input any key exit");
	getchar();

	return 0;
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
`PTRACE_PEEKUSER` 是一个 `ptrace` 系统调用,用于读取目标进程中的用户空间寄存值。它的调用方式如下: ```c long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); ``` 其中,`request` 参数需要设置为 `PTRACE_PEEKUSER`,`pid` 参数为目标进程的进程 ID,`addr` 参数表示要读取的寄存相对于用户空间的偏移地址,`data` 参数是输出参数,表示读取到的寄存值。 在 x86_64 架构下,可以使用 `PTRACE_PEEKUSER` 读取的寄存包括: - `R15`、`R14`、`R13`、`R12`、`RBP`、`RBX`、`R11`、`R10`、`R9`、`R8`、`RAX`、`RCX`、`RDX`、`RSI`、`RDI`、`ORIG_RAX`、`RIP`、`CS`、`EFLAGS`、`RSP`、`SS`、`FS_BASE`、`GS_BASE`、`DS`、`ES`、`FS`、`GS`。 下面是一个使用 `PTRACE_PEEKUSER` 读取 `RIP` 寄存值的示例代码: ```c #include <sys/ptrace.h> #include <sys/user.h> int main() { pid_t pid = 1234; // 目标进程的PID struct user_regs_struct regs; // 使用ptrace附加到目标进程 if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { perror("ptrace attach"); return -1; } // 等待目标进程停止 wait(NULL); // 获取目标进程的RIP寄存值 long rip = ptrace(PTRACE_PEEKUSER, pid, 8 * RIP, NULL); if (rip < 0) { perror("ptrace peekuser"); return -1; } // 打印RIP寄存的值 printf("RIP = %lx\n", rip); // 使用ptrace从目标进程中分离 if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) { perror("ptrace detach"); return -1; } return 0; } ``` 需要注意的是,`addr` 参数表示的是相对于用户空间的偏移地址,而不是绝对地址,因此需要将寄存的名称转换为偏移量,如 `RIP` 对应的偏移量是 `8 * RIP`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值