浅谈内存搜索


先来细谈原理

对系统进程遍历 打印 获取进程 PID 句柄
以下简单的控制台应用程序获取正在运行的进程列表。 首先,该 GetProcessList 函数使用 CreateToolhelp32Snapshot 拍摄系统中当前正在执行的进程快照,然后使用 Process32First 和 Process32Next 遍历快照中记录的列表。 对于每个进程,GetProcessList依次调用ListProcessModules遍历模块列表中所述的函数,以及ListProcessThreads遍历线程列表中所述的函数。
一个简单的错误报告函数 printError显示任何失败的原因,这通常源于安全限制。 例如, OpenProcess 对于 Idle 和 CSRSS 进程失败,因为它们的访问限制会阻止用户级代码打开它们。
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
// Forward declarations:
BOOL GetProcessList( );
BOOL ListProcessModules( DWORD dwPID );
BOOL ListProcessThreads( DWORD dwOwnerPID );
void printError( TCHAR* msg );
int main( void )
{
GetProcessList( );
return 0;
}
BOOL GetProcessList( )
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hProcessSnap == INVALID_HANDLE_VALUE )
{
printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
return( FALSE );
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( PROCESSENTRY32 );
// Retrieve information about the first process,
// and exit if unsuccessful
if( !Process32First( hProcessSnap, &pe32 ) )
{
printError( TEXT("Process32First") ); // show cause of failure
CloseHandle( hProcessSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
_tprintf( TEXT("\n\n=====================================================" ));
_tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile );
_tprintf( TEXT("\n-------------------------------------------------------" ));
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
if( hProcess == NULL )
printError( TEXT("OpenProcess") );
else
{
dwPriorityClass = GetPriorityClass( hProcess );
if( !dwPriorityClass )
printError( TEXT("GetPriorityClass") );
CloseHandle( hProcess );
}
_tprintf( TEXT("\n Process ID = 0x%08X"), pe32.th32ProcessID );
_tprintf( TEXT("\n Thread count = %d"), pe32.cntThreads );
_tprintf( TEXT("\n Parent process ID = 0x%08X"), pe32.th32ParentProcessID );
_tprintf( TEXT("\n Priority base = %d"), pe32.pcPriClassBase );
if( dwPriorityClass )
_tprintf( TEXT("\n Priority class = %d"), dwPriorityClass );
// List the modules and threads associated with this process
ListProcessModules( pe32.th32ProcessID );
ListProcessThreads( pe32.th32ProcessID );
} while( Process32Next( hProcessSnap, &pe32 ) );
CloseHandle( hProcessSnap );
return( TRUE );
}
BOOL ListProcessModules( DWORD dwPID )
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
// Take a snapshot of all modules in the specified process.
hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
if( hModuleSnap == INVALID_HANDLE_VALUE )
{
printError( TEXT("CreateToolhelp32Snapshot (of modules)") );
return( FALSE );
}
// Set the size of the structure before using it.
me32.dwSize = sizeof( MODULEENTRY32 );
// Retrieve information about the first module,
// and exit if unsuccessful
if( !Module32First( hModuleSnap, &me32 ) )
{
printError( TEXT("Module32First") ); // show cause of failure
CloseHandle( hModuleSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the module list of the process,
// and display information about each module
do
{
_tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule );
_tprintf( TEXT("\n Executable = %s"), me32.szExePath );
_tprintf( TEXT("\n Process ID = 0x%08X"), me32.th32ProcessID );
_tprintf( TEXT("\n Ref count (g) = 0x%04X"), me32.GlblcntUsage );
_tprintf( TEXT("\n Ref count (p) = 0x%04X"), me32.ProccntUsage );
_tprintf( TEXT("\n Base address = 0x%08X"), (DWORD) me32.modBaseAddr );
_tprintf( TEXT("\n Base size = %d"), me32.modBaseSize );
} while( Module32Next( hModuleSnap, &me32 ) );
CloseHandle( hModuleSnap );
return( TRUE );
}
BOOL ListProcessThreads( DWORD dwOwnerPID )
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( hThreadSnap == INVALID_HANDLE_VALUE )
return( FALSE );
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if( !Thread32First( hThreadSnap, &te32 ) )
{
printError( TEXT("Thread32First") ); // show cause of failure
CloseHandle( hThreadSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do
{
if( te32.th32OwnerProcessID == dwOwnerPID )
{
_tprintf( TEXT("\n\n THREAD ID = 0x%08X"), te32.th32ThreadID );
_tprintf( TEXT("\n Base priority = %d"), te32.tpBasePri );
_tprintf( TEXT("\n Delta priority = %d"), te32.tpDeltaPri );
_tprintf( TEXT("\n"));
}
} while( Thread32Next(hThreadSnap, &te32 ) );
CloseHandle( hThreadSnap );
return( TRUE );
}
void printError( TCHAR* msg )
{
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError( );
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
sysMsg, 256, NULL );
// Trim the end of the line and terminate it with a null
p = sysMsg;
while( ( *p > 31 ) || ( *p == 9 ) )
++p;
do { *p-- = 0; } while( ( p >= sysMsg ) &&
( ( *p == '.' ) || ( *p < 33 ) ) );
// Display the message
_tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
}
-----选自微软开发文档
使用window的api 对目标进程读取
ULONG ReadMemory(
ULONG offset,
PVOID lpBuffer,
ULONG cb,
PULONG lpcbBytesRead
);
-----选自微软开发文档 记得加windows.h

搜索算法 KMP
具体的算法原理 介绍 再此暂不涉及 KMP算法的介绍
KMP是字符串匹配算法 他的作用是用来匹配字节在内存中的位置
字节的类型
unsigned char
KMP算法的简单实现 写的不规范请见谅
set<UINT_PTR> address_list;
void KMP(unsigned char* zichuan, unsigned char* muchuan,UINT_PTR len_zi,UINT_PTR len_mu,UINT_PTR fujia=0) {
if (len_zi <= len_mu) {
int* next = (int*)init_ptr(len_mu * 4);
int prefix_len = 0;//当前的共同前后缀长度
for (int i = 1; i <= len_mu - 1;) {
if (muchuan[prefix_len] == muchuan[i]) {
prefix_len += 1;
next[i] = prefix_len;
i += 1;
}//相同的时候很简单 如上
else {
//接下来分为俩种 如果当前前后缀的相同长度为0 直接可以跳过赋0
if (prefix_len == 0) {
next[i] = 0;
i += 1;
}
else {
prefix_len = next[prefix_len - 1];//将当前长度设置为前缀中相同位置的前一个 然后进入下一次循环 i不变
}
}
}
//printf("\n开始执行KMP算法\n\n");
/*上面是计算 next
这里开始 查找*/ int i = 0/*指向母串的指针*/, j = 0/*指向子串的指针*/;
while (i <= len_mu - 1) {
/*printf("第%d个 匹配中 : ", i);*/
/*if (j >= 子串长度) {
i = i - j + 2;
j = 0;
j = 0;
}*/
if (zichuan[j] == muchuan[i]) {
i += 1;
j += 1;
/*printf("相等\n");*/
}
else if (j > 0) {
j = next[j - 1];//通过next数组回退
/*printf("回退\n");*/
}
else {
/*printf("i进一位 不用回退\n");*/
i += 1;//子串第一个就匹配失败 也就不用回退了
}
/*printf(" 当前j值%d \n", j);*/
if (j == len_zi) {
address_list.insert(i - j + fujia);
/*Sleep(1000000);*/
i = i - j + 2;
j = 0;
/*printf("\n找到 在 %d 位\n\n", i-j);*/
};//匹配成功 插入set 注意是i-j
}
free(next);
}
}
接下来就是如何组装他们了

首先 遍历进程获取到权限句柄
HANDLE handle
然后 读取目标进程的内存
这里我们可以优化为一次读取一块内存 然后对内存块使用KMP算法 加上内存块的首地址 便是我们想要得到的地址了 没有考虑内存对齐的情况 大佬在写的时候可以修改
void search(HANDLE hprocess,unsigned char* zijieji,UINT_PTR len,UINT_PTR startaddress=0,UINT_PTR finaladdress= 0x7fffffffffff)
{
unsigned char arBytes[BLOCKMAXSIZE]; // 409600的数据缓冲区,用来存放一个内存页
int i = 0;
address_list.clear();
/*int weishu = len;*/
unsigned long long BlockSize;
MEMORY_BASIC_INFORMATION mbi;
UINT_PTR address_now = startaddress;
/*VirtualQueryEx(hprocess, (LPCVOID)(address_now), &mbi, sizeof(mbi));*/
while (address_now<finaladdress && VirtualQueryEx(hprocess, (LPCVOID)(address_now), &mbi, sizeof(mbi))!=0)
{
//获取可读可写和可读可写可执行的内存块
if (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_EXECUTE_READWRITE)
{
i = 0;
BlockSize = mbi.RegionSize;
//搜索这块内存
if (BlockSize < 0) {
break;
}
while (BlockSize >= BLOCKMAXSIZE)
{
if (ReadProcessMemory(hprocess, (LPCVOID)(address_now + (BLOCKMAXSIZE * i)), arBytes, BLOCKMAXSIZE, NULL)) {
KMP(zijieji, arBytes, len, BLOCKMAXSIZE, address_now + (BLOCKMAXSIZE * i));
}
BlockSize -= BLOCKMAXSIZE;
i++;
}
if (ReadProcessMemory(hprocess, (LPCVOID)(address_now + (BLOCKMAXSIZE * i)), arBytes, BlockSize, NULL)) {
KMP(zijieji, arBytes, len, BlockSize, address_now + (BLOCKMAXSIZE * i));
}
}
address_now += mbi.RegionSize;
}
}
将读取到的内存块进行匹配 最后对容器打印输出便完成啦
不过不如ce快 应该是没进行内存对齐的问题()可以多开两个线程加快速度
附上搜索截图

