本帖最后由 人造人 于 2019-2-14 20:34 编辑
先贴出代码
ProcessOperation.hpp
#ifndef _PROCESS_OPERATION_HPP_
#define _PROCESS_OPERATION_HPP_
#include <string>
#include <windows.h>
HANDLE GetProcessHandle(std::string ProcessName);
void CloseProcessHandle(HANDLE hProcess);
UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress);
void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data);
#endif
ProcessOperation.cpp
#include "ProcessOperation.hpp"
#include <tlhelp32.h>
static DWORD GetProcessIdByName(std::string ProcessName)
{
DWORD dwProcessId = 0;
PROCESSENTRY32 pe32;
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hProcessSnap, &pe32);
do
{
if(ProcessName == pe32.szExeFile)
{
dwProcessId = pe32.th32ProcessID;
break;
}
}
while(Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return dwProcessId;
}
HANDLE GetProcessHandle(std::string ProcessName)
{
DWORD dwProcessId = GetProcessIdByName(ProcessName);
if(dwProcessId == 0)
return nullptr;
return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, 0, dwProcessId);
}
void CloseProcessHandle(HANDLE hProcess)
{
CloseHandle(hProcess);
}
UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress)
{
UINT32 data;
SIZE_T NumberOfBytesRead;
ReadProcessMemory(hProcess, (LPCVOID)BaseAddress, &data, 4, &NumberOfBytesRead);
return data;
}
void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data)
{
SIZE_T NumberOfBytesWritten;
WriteProcessMemory(hProcess, (LPVOID)BaseAddress, &data, 4, &NumberOfBytesWritten);
}
main.cpp
#include "ProcessOperation.hpp"
#include <iostream>
#include <string>
char *SearchString(HANDLE hProcess, DWORD begin_address, DWORD end_address, std::string string)
{
auto iter = string.begin();
char *result = (char *)begin_address;
for(UINT32 i = 0; begin_address + i < end_address; ++i)
{
char buf[4];
*(UINT32 *)&buf[0] = ReadAddress_4Byte(hProcess, begin_address + i);
if(iter == string.end())
break;
if(*iter == buf[0])
{
++iter;
}
else
{
iter = string.begin();
result = (char *)(begin_address + i + 1);
}
}
if(iter == string.end())
return result;
return nullptr;
}
int main()
{
HANDLE hProcess = GetProcessHandle("C.exe");
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
CloseProcessHandle(hProcess);
return 0;
}
C.exe
#include <stdio.h>
int main(void)
{
getchar();
printf("%s\n", "hello world!");
return 0;
}
程序打开C.exe这个进程,在这个进程中搜索字符串“hello world!”
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
我们没有必要搜索0x00000000~0xFFFFFFFF,这全部的4GB内存,可以通过分析PE结构得到当前字符串可能在哪些段
例如当前程序(C.exe)中的“hello world!”,这个字符串在.rdata段
可以看到“hello world!”字符串在C.exe文件中的偏移是0x64f8
0x64f8位于.rdata段
.rdata段从文件中偏移为0x5800的位置开始
0x64f8 - 0x5800 = 0xcf8
0xcf8就是“hello world!”字符串在C.exe文件中,在.rdata段的偏移
这是在文件中的偏移
运行后.rdata段从0x17000开始,这也是一个偏移,是从ImageBase开始的偏移
从图中可以看到,ImageBase是0x400000
0x400000 + 0x17000 = 0x417000
0x417000是运行后.rdata段的虚拟地址,在文件中“hello world!”字符串在.rdata段的偏移是0xcf8,运行后在.rdata段的偏移也是0xcf8
0x417000 + 0xcf8 = 0x417cf8
0x417cf8这是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址
但是看SearchString.exe的输出
为什么是00BB7CF8,还有
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
0xbb7000和0xbba000是怎么回事?
前面说ImageBase是0x400000,但是这是一个建议的地址,操作系统可以不使用这个地址
在我的系统上就没有使用这个地址
用CE找到的“hello world!”字符串在地址00BB7CF8
用这个地址反向计算出实际加载的ImageBase
bb7cf8 - cf8 = bb7000
bb7000 - 17000 = ba0000
ba0000就是实际使用的ImageBase
那么
ba0000 + 17000 + cf8 = bb7cf8
0xbb7cf8就是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址
SearchString.exe输出的也是这个地址
0xbb7000是在内存中.rdata段的开始
0xbba000是在内存中.rdata段的结束
我们只搜索.rdata段
有些编译器会把只读的数据放在代码段(.text段),这时我们就需要搜索.text段
还有一些其他数据可能在其他段,我们可以分析PE结构来搜索一切可能的段
有一点我目前还不明白,通过malloc申请的内存在哪个段?
我认为可以通过分析某些数据结构得到这个段的开始地址和结束地址
这依然没有必要搜索全部的4GB内存
还有最后一个问题
之前我们的ImageBase是通过CE找到的,我们如何在我们的程序中直接添加这个功能?直接在我们的程序中计算.rdata段的开始地址和结束地址?
这个问题交给你了,如果你有兴趣的话
我目前还没有好的方法
^_^