QQ程序(Procedures)(Procedures)在运行过程当中, 所有数据(Data)绝大部分的都是存放在进程空间中,
QQ号码也不例外, 要取
QQ号码, 从
QQ进程空间着手是最保险的.
如何确定
QQ号码在
QQ进程空间的位置? "goomoo"的方法是搜索"clientuin="关键字,这一个关键字之后紧跟着就是
QQ号码. 但我发现, "clientuin="后面也不一定总是登陆的
QQ号码,有时是别的字符,有时是本地登陆的其他
QQ号码, 有时又是好友的
QQ号码. 所以这一个通过这一个关键字来定位是不准确的.
经过分析, 我发现,
QQ运行过程当中会读取"MsgEx.db"文件, 在这一个文件的全路径中就包含了
QQ号码, 路径格式为:
QQ路径 +"\" +
QQ登陆号码 + "\MsgEx.db", 找到"\MsgEx.db"关键字, 然后提取关键字前面的第一个"\"和第二个"\"之间的文本,不就是
QQ号码了吗? 对,正是这样.
在
QQ进程中, "\MsgEx.db" 的地方很多, 有些前面跟的不是
QQ号码.为了保证取到号码的正确性, 我们需要加入一些判断技巧. 大家知道,
QQ号码绝大部分的都是数字格式的, 所以只要我们判断取出来的号码是不是因为数字, 如果不能是因为数字,就继续查找,直到找到是数字的文本为止. 二. 如何搜索
QQ进程空间的数据(Data)? I.应用程序(Procedures)(Procedures)进程
进程是当前操作操作操作系统下一个被加载到计算机内存的,正在运行的应用程序(Procedures)(Procedures)的实例.每一个进程绝大部分的都是由内核对像和地址空间所组成的,内核对像可以让操作操作系统在其计算机内存放有关进程的统计信息并使操作操作系统能够以此来管理进程,而不能是地址空间则包括了所有程序(Procedures)(Procedures)模块的代码和数据(Data)以及线程堆栈,堆分配空间等动态分配的空间.进程仅仅是一个存在,是不能独自完成任何操作的,必须拥有至少一个在其环境下运行的线程,并由其负责执行在进程地址空间内的代码.在进程启动操作系统的同时即同时启动操作系统了一个线程,该线程被称作主线程或是执行线程,由此线程可以继续创建子线程.如果主线程退出,那么进程也就没有存在的可能了,操作操作系统将自动撤消该进程并完成对其地址空间的释放.
加载到进程地址空间的每一个可执行文件或动态链接库文件的映像都会被分配一个与之相关联的全局唯一的实例句柄(Hinstance). II. 进程空间
在WIN32中,每个应用程序(Procedures)(Procedures)都可“看见”4GB的线性地址空间, 当中最开始的4MB和最后的2GB由操作操作操作系统保留,低的2GB为进程的私有空间(如果在Boot.ini文件中使用“/3GB”的开关可以使进程的私有空间增大到3GB,操作操作系统空间1GB).对于每个进程来讲其虚拟的地址空间是连续的,实际上他们是以页面为单位离散的存在于物理计算机内存中,一些可能被交换到计算机硬盘(Hard)上的页面文件中,而不能是且还有大部分的空间是未提交(Uncommitted)的.一个进程的低2GB私有空间的分布如下表: 范围 大小 作用
-----------------------------------------------------------------------------------------------------------------------------
0x0~~0xFFFF 64 KB 不可访问区域,只是用来防止非法的指针访问,访问该范围的地址会导致访问违例.
0x10000~~0x7FFEFFFF 2 GB 减去至少192 KB 进程的私有地址空间
0x7FFDE000~~0x7FFDEFFF 4 KB 进程中第一个线程的线程环境块,即TEB(Thread environment block)
0x7FFDF000~~0x7FFDFFFF 4 KB 进程的进程环境块,即PEB(Process environment block)
0x7FFE0000~~0x7FFE0FFF 4 KB 一个共享(Sharing)的只读用户数据(Data)块,该块映射到到操作操作系统空间的一个数据(Data)块,
当中存放的是一些操作操作系统信息如操作操作系统时间(如果不对,可以去下一下360时间保护器,软件(soft)很小,而不能是且不占内存.是安全不可少的软件(soft),
快活林极力推荐),时钟的滴答数,操作操作系统版本号等.
这样访问这些信息的时候操作操作系统就不用切换到核心模式.
0x7FFE1000~~0x7FFEFFFF 60 KB 不可访问
0x7FFF0000~~0x7FFFFFFF 64 KB 不可访问,用于防止线程的缓冲跨越两种模式空间的边界
一个进程的高2GB空间具体分配如下:
0xFFFFFFFF-0xC0000000的1GB 用于VxD,存储器管理和文件操作操作系统;
0xBFFFFFFF-0x80000000的1GB 用于共享(Sharing)的WIN32 DLL,存储器映射文件和共享(Sharing)存储区;
虚拟计算机内存通常是由固定大小的块来实现的,在WIN32中这些块称为“页”,每页大小为4,096字节.在Intel CPU结构中,通过在一个控制寄存器中设置一位来启用分页.启用分页时CPU并不能直接或者间接访问计算机内存,对每个地址要经过一个映射进程,通过一系列称作“页表”的查找表把虚拟计算机内存地址映射成实际计算机内存地址.通过使用硬件地址映射和页表WIN32可使虚拟计算机内存即有好的性能而不能是且还提供保护.利用处理器的页映射能力,操作操作操作系统为每个进程提供独立的从逻辑地址到物理地址的映射,使每个进程的地址空间对另一个进程完全不可见. 我们要搜索另一个进程空间的数据(Data), 要扫描范围的起点和终点不是从0~~2GB,而不能是只是当中的一部分.要得到这一个起点和终点可以使用API函数GetSystemInfo,函数的原型如下:
VOID GetSystemInfo(
LPSYSTEM_INFO lpSystemInfo
);
而不能是在结构SYSTEM_INFO中有两个值:lpMinimumApplicationAddress和 lpMaximumApplicationAddress,
就是一个应用程序(Procedures)(Procedures)可用的最小和最大的地址空间.这样我们就得到了要扫描的地址的起点和终点.那么是不是因为这起点和终点间所有的地址都要扫描呢?并不可能是这样的,因为一般情况下一个进程是用不着这么大(接近2GB)的地址空间的.所以我们可以得出结论一个进程的大部分地址空间绝大部分的都是未用(Free)或是保留(Reserved)的,真正用到的只是那些已提交(Committed)的计算机内存而不能是已.
计算机内存页面可以有三种状态:未用(Free),保留(Reserved)和提交(Committed).一个未用的页面是指该页面未被保留或是提交,对一个进程来讲一个未用的页面是不可访问的,访问这样的页面将导致访问违例.进程可以要求操作操作系统保留一些页面以备后用,操作操作系统返回一段保留的地址给进程,但是这些地址同样是不可访问的,进程若想使用这段地址空间,使用必须先提交.只有一个提交的页面才是一个真正可以访问的页面.不过您提交了一个页面,操作操作系统并不怎么会马上分配物理页面,只有在该页面第一次被访问到时,操作操作系统才会分配页面并初始化.另外,这三个状态的两两之间绝大部分的都是可以相互转化的.
这样我们的工作(Work)已大大减少了,只需要扫描那些提交的页面就好了.接下来要做的就是得到一个进程的已提交的页面范围.这就要用到另外两个API函数VirtualQuery和VirtualQueryEx.两个函数的功能相似,不同就是VirtualQuery只是查询本进程而不能是VirtualQueryEx可以查询指定进程的计算机内存空间信息,后者正是我们所需要的,函数原型如下:
DWORD VirtualQueryEx(
HANDLE hProcess, // 进程的句柄
LPCVOID lpAddress, // 计算机内存地址指针
PMEMORY_BASIC_INFORMATION lpBuffer, // 指向MEMORY_BASIC_INFORMATION结构的指针,用于返回计算机内存空间的信息
SIZE_T dwLength // lpBuffer的长度
); 再来看一下结构MEMORY_BASIC_INFORMATION的声明:
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress; //查询计算机内存块的基地址
PVOID AllocationBase; //用VirtualAlloc分配该计算机内存时实际分配的基地址,可以小于BaseAddress,
//也就是说BaseAddress一定包含在AllocationBase分配的范围内
DWORD AllocationProtect; //分配该页面时,页面的一些属性,如PAGE_READWRITE,PAGE_EXECUTE等
SIZE_T RegionSize; //从BaseAddress开始,具有相同属性的页面的大小
DWORD State; //页面的状态,有三种可能值:MEM_COMMIT,MEM_FREE和MEM_RESERVE,
//这一个参数对我们来说是最重要的了,从中我们便可知指定计算机内存页面的状态了
DWORD Protect; //页面的属性,其可能的取值与AllocationProtect相同
DWORD Type; //该计算机内存块的类型,有三种可能值:MEM_IMAGE,MEM_MAPPED和MEM_PRIVATE
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; 进一步研究发现, 要搜索数据(Data), 只要搜索 类型=MEM_PRIVATE 页面属性=PAGE_READWRITE 的计算机内存块就好了, 这样可以大大提高搜索速度. 这样我们就可得到进程中需要扫描的地址范围了.到这里剩下的基本问题就是要读取指定的进程的指定的地地址空间的内容了.这里要用到的是用于调试程序(Procedures)(Procedures)和错误处理(Debugging and Error Handling)的API函数中的ReadProcessMemory,他的原型如下:
BOOL ReadProcessMemory(
HANDLE hProcess, // 被读取进程的句柄
LPCVOID lpBaseAddress, // 读的起始地址
LPVOID lpBuffer, // 存放读取数据(Data)缓冲区
SIZE_T nSize, // 一次读取的字节数
SIZE_T * lpNumberOfBytesRead // 实际读取的字节数
); 参数很简单从他们的名字都可以猜出其意义了,这里就不多做说明了.要说明的是要对一个进程进行ReadProcessMemory操作,当前进程对要读的进程必须有PROCESS_VM_READ 和 PROCESS_QUERY_INFORMATION 访问权.要获得一个进程的句柄和对这一个进程的一些控制权可以使用API函数OpenProcess得到,其使用不做详细说明了,只给出其原型:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问标志
BOOL bInheritHandle, // 继承标志
DWORD dwProcessId // 进程ID
); III.如何获取
QQ进程ID 进程ID可由 Process32First 和 Process32Next 得到,这两个函数可以枚举出所有开启的进程.
Process32First 和 Process32Next原形如下: BOOL WINAPI Process32First
(
HANDLE hSnapshot //由 CreateToolhelp32Snapshot 返回的操作操作系统快照句柄;
LPPROCESSENTRY32 lppe // 指向一个 PROCESSENTRY32 结构;
);
BOOL WINAPI Process32Next
(
HANDLE hSnapshot // 由 CreateToolhelp32Snapshot 返回的操作操作系统快照句柄;
LPPROCESSENTRY32 lppe // 指向一个 PROCESSENTRY32 结构;
); CreateToolhelp32Snapshot 原形如下:
HANDLE WINAPI CreateToolhelp32Snapshot
(
DWORD dwFlags, // 快照标志;
DWORD th32ProcessID // 进程ID;
); 现在需要的是进程的信息,所以将 dwFlags 指定为 TH32CS_SNAPPROCESS,th32ProcessID 忽略; PROCESSENTRY32 结构如下:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize; // 结构大小;
DWORD cntUsage; // 此进程的引用计数;
DWORD th32ProcessID; // 进程ID;
DWORD th32DefaultHeapID; // 进程默认堆ID;
DWORD th32ModuleID; // 进程模块ID;
DWORD cntThreads; // 此进程开启的线程计数;
DWORD th32ParentProcessID;// 父进程ID;
LONG pcPriClassBase; // 线程优先权;
DWORD dwFlags; // 保留;
char szExeFile[MAX_PATH]; // 进程全名;
} PROCESSENTRY32; 三. 程序(Procedures)(Procedures)流程 知道了上面的原理 , 编写程序(Procedures)(Procedures)就很简单了 .程序(Procedures)(Procedures)流程如下: I. 遍历操作操作系统进程, 找到所有
QQ进程的ID;
II. 通过ID打开每个
QQ进程, 获得操作句柄;
III. 读取
QQ进程 "类型=MEM_PRIVATE 页面属性=PAGE_READWRITE" 的计算机内存块到自己程序(Procedures)(Procedures)的缓冲区, 然后搜索关键字 "\MsgEx.db"位置;
IV. 提取关键字前面的
QQ号码.
V. 显示
QQ号码.结束. -------
以前通过修改计算机内存数据(Data),将一些人临时性的加成自己的好友来聊天.貌似就是取这一个里面的一些数据(Data)的.