我正在使用Ubuntu Linux编写两个程序。 我正在尝试从另一个进程更改整数的值。 我的第一个过程(一)是一个简单的程序,永远循环,并显示在屏幕上的值。 这个程序按预期工作,只是在屏幕上显示值-1430532899(0xAABBCCDD)。
#include <stdio.h> int main() { //The needle that I am looking for to change from another process int x = 0xAABBCCDD; //Loop forever printing out the value of x int counter = 0; while(1==1) { while(counter<100000000) { counter++; } counter = 0; printf("%d",x); fflush(stdout); } return 0; }
在单独的terminal中,我使用ps -e
命令列出进程并logging进程(A)的进程标识。 下一步作为根用户(sudo),我运行这个下一个程序(B)并input我从进程(A)中注意到的进程ID。
程序主要search内存后退的针(DD CC BB AA)找针,并记下地址。 然后,它会尝试将hex值(0xEEEEEEEE)写入同一位置,但是当errno设置为14
时,会出现错误的地址错误。 奇怪的是稍后在地址空间,我能够成功地将值写入地址(0x601000),但针(0xAABBCCDD)在0x6005DF我不能写在那里的地址。 (但可以明显看出,因为那是我find针的地方)
#include <stdio.h> #include <iostream> #include <sys/uio.h> #include <string> #include <errno.h> #include <vector> using namespace std; char getHex(char value); string printHex(unsigned char* buffer, int length); int getProcessId(); int main() { //Get the process ID of the process we want to read and write int pid = getProcessId(); //Lists of addresses where we find our needle 0xAABBCCDD and the addresses where we simply cannot read vector<long> needleAddresses; vector<long> unableToReadAddresses; unsigned char buf1[1000]; //buffer used to store memory values read from other process //Number of bytes read, also is -1 if an error has occurred ssize_t nread; //Structures used in the process_vm_readv system call struct iovec local[1]; struct iovec remote[1]; local[0].iov_base = buf1; local[0].iov_len = 1000; remote[0].iov_base = (void * ) 0x00000; //start at address 0 and work up remote[0].iov_len = 1000; for(int i=0;i<10000;i++) { nread = process_vm_readv(pid, local, 1, remote, 1 ,0); if(nread == -1) { //errno is 14 then the problem is "bad address" if(errno == 14) unableToReadAddresses.push_back((long)remote[0].iov_base); } else { cout<<printHex(buf1,local[0].iov_len); for(int j=0;j<1000-3;j++) { if(buf1[j] == 0xDD && buf1[j+1] == 0xCC && buf1[j+2] == 0xBB && buf1[j+3] == 0xAA) { needleAddresses.push_back((long)(remote[0].iov_base+j)); } } } remote[0].iov_base += 1000; } cout<<"Addresses found at..."; for(int i=0;i<needleAddresses.size();i++) { cout<<needleAddresses[i]<<endl; } //How many bytes written int nwrite = 0; struct iovec local2[1]; struct iovec remote2[1]; unsigned char data[] = {0xEE,0xEE,0xEE,0xEE}; local2[0].iov_base = data; local2[0].iov_len = 4; remote2[0].iov_base = (void*)0x601000; remote2[0].iov_len = 4; for(int i=0;i<needleAddresses.size();i++) { cout<<"Attempting to write "<<printHex(data,4)<<" to address "<<needleAddresses[i]<<endl; remote2[0].iov_base = (void*)needleAddresses[i]; nwrite = process_vm_writev(pid,local2,1,remote2,1,0); if(nwrite == -1) { cout<<"Error writing to "<<needleAddresses[i]<<endl; } else { cout<<"Successfully wrote data"; } } //For some reason THIS will work remote2[0].iov_base = (void*)0x601000; nwrite = process_vm_writev(pid,local2,1,remote2,1,0); cout<<"Wrote "<<nwrite<<" Bytes to the address "<<0x601000 <<" "<<errno; return 0; } string printHex(unsigned char* buffer, int length) { string retval; char temp; for(int i=0;i<length;i++) { temp = buffer[i]; temp = temp>>4; temp = temp & 0x0F; retval += getHex(temp); temp = buffer[i]; temp = temp & 0x0F; retval += getHex(temp); retval += ' '; } return retval; } char getHex(char value) { if(value < 10) { return value+'0'; } else { value = value - 10; return value+'A'; } } int getProcessId() { int data = 0; printf("Please enter the process id..."); scanf("%d",&data); return data; }
底线是我不能修改从另一个进程重复打印的整数。
我至少可以看到这些问题。
没有人保证在进程的可写内存中的任何地方都有0xAABBCCDD。 编译器可以完全优化它,或者放入一个寄存器。 一种方法来保证一个变量将被放置在主内存中是声明它是volatile
。
volatile int x = 0xAABBCCDDEE;
没有人保证在进程的只读内存中没有 0xAABBCCDD。 相反,可以肯定的是,在那里实际上有这样的价值。 程序可能还有什么地方可以获得它来初始化变量? 初始化可能转化为与此类似的汇编指令
mov eax, 0xAABBCCDD
不出所料,它包含一个匹配0xAABBCCDD的位模式。 地址0x6005DF可能在.text部分。 它在堆栈上是不太可能的,因为堆栈地址通常靠近地址空间的顶部。
64位进程的地址空间是巨大的。 没有希望在合理的时间内遍历它。 人们需要以某种方式限制地址范围。