欢迎光临散文网 会员登陆 & 注册

浅谈内存搜索

2023-01-04 16:50 作者:拾柒-光羽  | 我要投稿

                                                               

先来细谈原理


    对系统进程遍历 打印 获取进程 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快 应该是没进行内存对齐的问题()可以多开两个线程加快速度

附上搜索截图


搜索Caculator.exe中字节码为0的内存


浅谈内存搜索的评论 (共 条)

分享到微博请遵守国家法律