FindQQByProcess

看网上有许多通过进程寻找QQ号的例子,看了一下,里面涉及的知识点还是比较多,但网上的兼容性不太好,而且没有给出匹配字符的来源,所以自己动手写了一下,顺便给出一些我调试的结果。

#include "stdafx.h"
#include <windows.h>
#include <TlHelp32.h>

DWORD FindByPID(WCHAR* ProcessName);
int ReadMemory(DWORD PID);
int SearchStr(char* MemoryString, int StrLength, char* Special);

int main()
{

	WCHAR MyQQ[] = L"QQ.exe";
	DWORD QQID = FindByPID(MyQQ);
    return 0;
}

DWORD FindByPID(WCHAR* ProcessName)
{
	DWORD ProcessID = 0;
	HANDLE ProcessSnapHANDLE;
	PROCESSENTRY32 pe32;

	ProcessSnapHANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (ProcessSnapHANDLE == INVALID_HANDLE_VALUE)
	{
		return 0;
	}
	pe32.dwSize = sizeof(PROCESSENTRY32);
	if (!Process32First(ProcessSnapHANDLE, &pe32))
	{
		CloseHandle(ProcessSnapHANDLE);
		return 0;
	}
	do
	{
		//找到QQ进程
		if (wcscmp(ProcessName, pe32.szExeFile) == 0)
		{
			ProcessID = pe32.th32ProcessID;
			printf("ProcessID = %d \r\n", ProcessID);
			//开始内存搜索
			ReadMemory(ProcessID);

		}
	}while (Process32Next(ProcessSnapHANDLE, &pe32));//继续找下一个进程
	CloseHandle(ProcessSnapHANDLE);
	return ProcessID;
}

int ReadMemory(DWORD PID)
{
	//要搜索的特征码
	char Special[] = "User uin=";
	//特征码出现的位置
	int Pos = 0;
	HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, PID);
	if (ProcessHandle == NULL)
	{
		return 0;
	}
	SYSTEM_INFO si;
	GetSystemInfo(&si);

	MEMORY_BASIC_INFORMATION mbi;
	DWORD Address = (DWORD)si.lpMinimumApplicationAddress; //可申请的最小值
	while (Address < (DWORD)si.lpMaximumApplicationAddress)
	{
		if (VirtualQueryEx(ProcessHandle, (LPVOID)Address, &mbi, sizeof(mbi)) != sizeof(mbi))
		{
			return 0;
		}

		if ((mbi.State == MEM_COMMIT) && (mbi.Protect == PAGE_READWRITE))  //限制条件,缩小范围
		{
			DWORD BaseAddress = Address;
			int ReadSize = mbi.RegionSize;

			if (ReadSize >= 1024)
			{
				DWORD Bytes = 0;
				char*  MemBuffer = (char *)malloc(ReadSize * sizeof(char));

				if (ReadProcessMemory(ProcessHandle, (LPCVOID)BaseAddress, MemBuffer, ReadSize, &Bytes))
				{
					//开始搜索特征码
					Pos = SearchStr(MemBuffer, Bytes, Special);
					if (Pos)
					{
						printf("Address: 0x%08X\n", BaseAddress + Pos * sizeof(char));
						//指向QQ号码的第一个字符
						char *ptsQQ = &MemBuffer[Pos + strlen(Special)];
						printf("QQ: ");
						//利用指针来打印出当前QQ进程的QQ号码,
						//QQ号码之后的字符是‘"‘
						for (*ptsQQ++; *ptsQQ != ‘"‘; *ptsQQ++)
						{
							//注意这里是循环打印出QQ号码的每个字符,而不是整个字符串
							printf("%c", *ptsQQ);
						}
						printf("\r\n");
						//找到1个就OK了,去除break可继续找
						break;
					}
				}
				free(MemBuffer);
			}
		}
		//从下一块内存块继续找
		Address = (DWORD)mbi.BaseAddress + mbi.RegionSize;
	}
	return Pos;
}

int SearchStr(char* MemoryString, int StrLength, char* Special)
{
	int i = 0;
	int SpecialLength = strlen(Special);

	while ((i + SpecialLength) <= StrLength)
	{
		int n = 0;
		//先匹配两个字串的第一个字符
		if (Special[0] == MemoryString[i])
		{
			//若相等,则开始逐字符匹配
			for (int j = 0; j < SpecialLength; j++)
			{
				//相同位置字符匹配
				if (Special[j] == MemoryString[i + j])
				{
					//若相同位置字符匹配成功,则计数器加1
					n++;
				}
				else  //相同位置字符匹配失败
				{
					//源字符串位置跳过匹配相同的n个字符
					i = i + n;
					//跳出当前匹配循环,开始新位置的匹配
					break;
				}
			}
			//若匹配成功,计数和目标字符串长度相等,则找到目标
			if (SpecialLength == n)
			{
				//i为找到的目标字符串在源字符串中的起点位置,

				return i;
			}
		}
		else  //若两字符串的第一个字符不同
		{
			//开始反向找源字符串相对目标字符串的后一个字符是否在目标字符串内
			for (int j = SpecialLength - 1; j >= 0; j--)
			{
				//找到存在紧跟其后的那个字符
				if (Special[j] == MemoryString[i + SpecialLength])
				{
					//该字符出现在目标字符串中的位置
					n = j;
					//只需知道排在倒数第一那个位置,跳出循环开始移动位置
					break;
				}
			}
			i = i + SpecialLength - n;
		}
	}
	return 0;
}

  

代码虽然比较简单,但是其中还是有一些需要注意的地方。

首先,为了方便从内存中粘出来内存数据,我都是用ASCII的单字符,而不是宽字符,因为宽字符的内存中会在每个字节后加上一个 00 ,看到的就是一个黑点。但是在进程查找匹配过程中,发现怎么也匹配不到 "QQ.exe",好像strcmp中用了强制类型转换就不行了,无奈只好把这个地方换成宽字节,但对于最后的结果没有影响。

还有就是由于操作系统不同,特征码"index?uin="我在运行时没有找到,我就换了一种思路,将我QQ号的前5位作为特征码来匹配,此时要注意代码中的结束字符,可能是&或者",自己多尝试一下。

我就找到了第一个符合条件的

/*
"1.0" encoding="utf-8"?>.<NewsFeed>.???<User uin="2294
800094"><minifeed appid="1" apptitle="title" showarea=

*/

可以看出我们要匹配的字符是  User uin=" ,但是两个双引号连在一起就视为空,又不会处理了,也没法用单引号代替,只能少一个引号,而在for循环的时候,用

*ptsQQ++   来跳过第一个字符。

下面是我找到的第二个:

/*
...........down?..€.)‘PE?‘P).*[???wp??w?8?v?].w?].w_ER
ROR_CODE_OK, status-code=200, url=http://s.url.cn/qqwe
b/qunactivity/img/icon_qq_2014.png.1546250318/15462503
18-3167137263-CEE4381B4D81CC2812670D031D808E69/0?vuin=
2294800094&term=1&srvver=26650&rf=naio.i.p.....???L???
L???L???L???L???L???L?R?L?M?L?s?L?n?L?.?L?.?L?.?L?:?L?
*/

可以看出 uin= 在两个里面都出现了,应该是兼容性较好的一个特征码了,如果使用 uin= ,那么刚刚for循环一定改成这样:

for (; *ptsQQ != ‘&‘; *ptsQQ++)

就可以找到QQ号码了。

时间: 2024-10-11 07:24:10

FindQQByProcess的相关文章