巡游五角大楼,登录克里姆林宫,进出全球所有计算机系统,摧垮全球金融秩序和重建新的世界格局,谁也阻挡不了我们的进攻,我们才是世界的主宰。 ——凯文·米特尼克

从进程中获取QQ号码

上一篇 / 下一篇  2007-09-08 16:03:54 / 个人分类:技术

制作这个程序的过程中,我查阅了很多相关资料, 对系统编程有了进一步的了解, 下面我就编制这个程序的原理过程和一些心得写出来, 给大家参考.
P8@Z7Ldu-P$~K0海岸线网络聚合Qd/z9Q`
一. 取QQ号码原理:海岸线网络聚合)r2k#BJYoZ#vj

zd+mD5Vvn0QQ程序在运行过程中, 所有数据都是存放在进程空间中,QQ号码也不例外, 要取QQ号码, 从QQ进程空间着手是最保险的.海岸线网络聚合7q X n4sb?
怎样确定QQ号码在QQ进程空间的位置? "goomoo"的方法是搜索"clientuin="关键字,这个关键字之后紧跟着就是QQ号码. 但我发现, "clientuin="后面也不一定总是登陆的QQ号码,有时是别的字符,有时是本地登陆的其他QQ号码, 有时又是好友的QQ号码. 所以这个通过这个关键字来定位是不准确的.海岸线网络聚合QA6C g$}b.fpd/I
经过分析, 我发现,QQ运行过程中会读取"MsgEx.db"文件, 在这个文件的全路径中就包含了QQ号码, 路径格式为: QQ路径 +"\" + QQ登陆号码 + "\MsgEx.db", 找到"\MsgEx.db"关键字, 然后提取关键字前面的第一个"\"和第二个"\"之间的文本,不就是QQ号码了吗? 对,正是这样.海岸线网络聚合 A:\5x+A7F#I baZ
在QQ进程中, "\MsgEx.db" 的地方很多, 有些前面跟的不是QQ号码.为了保证取到号码的正确性, 我们需要加入一些判断技巧. 大家知道,QQ号码都是数字格式的, 所以只要我们判断取出来的号码是不是数字, 如果不是数字,就继续查找,直到找到是数字的文本为止.海岸线网络聚合D8h/J1u"|[O

z r-G6u(y4da0二. 怎样搜索QQ进程空间的数据?海岸线网络聚合1t eXIXO(C6u ~

)Z*P7Y%Kof e01.应用程序进程海岸线网络聚合QN ?7r1cmA/G
  进程是当前操作系统下一个被加载到内存的、正在运行的应用程序的实例。每一个进程都是由内核对象和地址空间所组成的,内核对象可以让系统在其内存放有关进程的统计信息并使系统能够以此来管理进程,而地址空间则包括了所有程序模块的代码和数据以及线程堆栈、堆分配空间等动态分配的空间。进程仅仅是一个存在,是不能独自完成任何操作的,必须拥有至少一个在其环境下运行的线程,并由其负责执行在进程地址空间内的代码。在进程启动的同时即同时启动了一个线程,该线程被称作主线程或是执行线程,由此线程可以继续创建子线程。如果主线程退出,那么进程也就没有存在的可能了,系统将自动撤消该进程并完成对其地址空间的释放。
PL&T.L7d0  加载到进程地址空间的每一个可执行文件或动态链接库文件的映象都会被分配一个与之相关联的全局唯一的实例句柄(Hinstance)。海岸线网络聚合 Ho!j+j,Z'^b0y

H(R8V&b7S'Oz\W&w02. 进程空间
&H(kQ1w(q$OZ*n#B0  在WIN32中,每个应用程序都可“看见”4GB的线性地址空间, 其中最开始的4MB和最后的2GB由操作系统保留,低的2GB为进程的私有空间(如果在Boot.ini文件中使用“/3GB”的开关可以使进程的私有空间增大到3GB,系统空间1GB)。对于每个进程来讲其虚拟的地址空间是连续的,实际上它们是以页面为单位离散的存在于物理内存中,一些可能被交换到硬盘上的页面文件中,而且还有大部分的空间是未提交(Uncommitted)的。一个进程的低2GB私有空间的分布如下表:
)d D;l5gMf/u%}a L;K0海岸线网络聚合sk0C:iA9y%W
  范围       大小       作用海岸线网络聚合;Z~"X4T)T U&x
-----------------------------------------------------------------------------------------------------------------------------
x,m;f~` F0  0x0~~0xFFFF       64 KB       不可访问区域,只是用来防止非法的指针访问,访问该范围的地址会导致访问违例。
g6ng$in'~S-_:\0  0x10000~~0x7FFEFFFF       2 GB 减去至少192 KB     进程的私有地址空间海岸线网络聚合l)h _)xjFWF^
  0x7FFDE000~~0x7FFDEFFF     4 KB       进程中第一个线程的线程环境块,即TEB(Thread environment block)海岸线网络聚合 g:p4U@:N#g,TS
  0x7FFDF000~~0x7FFDFFFF     4 KB       进程的进程环境块,即PEB(Process environment block)海岸线网络聚合7Y!hX/l$g#n2mF
  0x7FFE0000~~0x7FFE0FFF     4 KB       一个共享的只读用户数据块,该块映射到到系统空间的一个数据块,
v0_7Krz5L-B-t0                                          其中存放的是一些系统信息如系统时间、时钟的滴答数、系统版本号等。
^"@p4[Fx0                                          这样访问这些信息的时候系统就不用切换到核心模式。
,L ~F3O9B$~6`9~0  0x7FFE1000~~0x7FFEFFFF     60 KB       不可访问海岸线网络聚合1c?"N1FR-q9e!j8i
  0x7FFF0000~~0x7FFFFFFF     64 KB       不可访问,用于防止线程的缓冲跨越两种模式空间的边界
(Ae0m"t0]n+t0
jQ%Xi |L6Boz0海岸线网络聚合 ?Uw3R;?c
一个进程的高2GB空间具体分配如下:海岸线网络聚合KUp*X$v(ae
  0xFFFFFFFF-0xC0000000的1GB     用于VxD、存储器管理和文件系统;海岸线网络聚合 j/k?+T_
  0xBFFFFFFF-0x80000000的1GB     用于共享的WIN32 DLL、存储器映射文件和共享存储区;海岸线网络聚合8h ^Z!vnB1t2P
海岸线网络聚合1I~{-w/`u+P"d ?3o

zu {u9W)x.z0  虚拟内存通常是由固定大小的块来实现的,在WIN32中这些块称为“页”,每页大小为4,096字节。在Intel CPU结构中,通过在一个控制寄存器中设置一位来启用分页。启用分页时CPU并不能直接访问内存,对每个地址要经过一个映射进程,通过一系列称作“页表” 的查找表把虚拟内存地址映射成实际内存地址。通过使用硬件地址映射和页表WIN32可使虚拟内存即有好的性能而且还提供保护。利用处理器的页映射能力,操作系统为每个进程提供独立的从逻辑地址到物理地址的映射,使每个进程的地址空间对另一个进程完全不可见。
W,P+a/mt:qKqH0
K6}k1Y!J;T/V(O0  我们要搜索另一个进程空间的数据, 要扫描范围的起点和终点不是从0~~2GB,而只是其中的一部分。要得到这个起点和终点可以使用API函数GetSystemInfo,函数的原型如下:
k CSI3eqE0  VOID GetSystemInfo(海岸线网络聚合9Iu bvK#bp
    LPSYSTEM_INFO lpSystemInfo海岸线网络聚合6t0eSTg?UW
  );海岸线网络聚合:XPU}+Cp:K
  而在结构SYSTEM_INFO中有两个值:lpMinimumApplicationAddress和 lpMaximumApplicationAddress,海岸线网络聚合} J}Gk4H
  就是一个应用程序可用的最小和最大的地址空间。这样我们就得到了要扫描的地址的起点和终点。那么是不是这起点和终点间所有的地址都要扫描呢?并不是这样的,因为一般情况下一个进程是用不着这么大(接近2GB)的地址空间的。因此一个进程的大部分地址空间都是未用(Free)或是保留(Reserved) 的,真正用到的只是那些已提交(Committed)的内存而已。海岸线网络聚合@+U!S9wn-C3u H
     
KlNr$C8y#d0  内存页面可以有三种状态:未用(Free)、保留(Reserved)和提交(Committed)。一个未用的页面是指该页面未被保留或是提交,对一个进程来讲一个未用的页面是不可访问的,访问这样的页面将导致访问违例。进程可以要求系统保留一些页面以备后用,系统返回一段保留的地址给进程,但是这些地址同样是不可访问的,进程若想使用这段地址空间,使用必须先提交。只有一个提交的页面才是一个真正可以访问的页面。不过你提交了一个页面,系统并不会马上分配物理页面,只有在该页面第一次被访问到时,系统才会分配页面并初始化。另外,这三个状态的两两之间都是可以相互转化的。
$i}a ]3H0  这样我们的工作已大大减少了,只需要扫描那些提交的页面就好了。接下来要做的就是得到一个进程的已提交的页面范围。这就要用到另外两个API函数 VirtualQuery和VirtualQueryEx。两个函数的功能相似,不同就是VirtualQuery只是查询本进程而 VirtualQueryEx可以查询指定进程的内存空间信息,后者正是我们所需要的,函数原型如下:海岸线网络聚合0z|%z?*~CX:]
  DWORD VirtualQueryEx(海岸线网络聚合~]7D%OrY-pl{
      HANDLE hProcess,               // 进程的句柄海岸线网络聚合^:I^pF(N;Sf3A0p
      LPCVOID lpAddress,             // 内存地址指针
a/{bW K,h:nv#q0      PMEMORY_BASIC_INFORMATION lpBuffer, // 指向MEMORY_BASIC_INFORMATION结构的指针,用于返回内存空间的信息
E(u+|lh Q&K0      SIZE_T dwLength               // lpBuffer的长度
m LwRp7Hn0  );
kg R.g4p5r"DY0
9UR v J{[D M/M C0  再来看一下结构MEMORY_BASIC_INFORMATION的声明:
)hmU9n+_*z;J6N0  typedef struct _MEMORY_BASIC_INFORMATION {
w*y#C@7[} H G0      PVOID BaseAddress;             //查询内存块的基地址海岸线网络聚合-{!ei j1|{)G
      PVOID AllocationBase;           //用VirtualAlloc分配该内存时实际分配的基地址,可以小于BaseAddress,海岸线网络聚合5k&yo Iu/A
                              //也就是说BaseAddress一定包含在AllocationBase分配的范围内海岸线网络聚合 BW3a8y)C0_Gk
      DWORD AllocationProtect;         //分配该页面时,页面的一些属性,如PAGE_READWRITE、PAGE_EXECUTE等海岸线网络聚合8yv1],E,C(C;}{
      SIZE_T RegionSize;             //从BaseAddress开始,具有相同属性的页面的大小海岸线网络聚合;q%`f})vLc S
      DWORD State;                 //页面的状态,有三种可能值:MEM_COMMIT、MEM_FREE和MEM_RESERVE,海岸线网络聚合/R3{-{6Q*o5?
                              //这个参数对我们来说是最重要的了,从中我们便可知指定内存页面的状态了
-J5UN.^m(P,C6I1}Y0      DWORD Protect;               //页面的属性,其可能的取值与AllocationProtect相同海岸线网络聚合ja7@OS N
      DWORD Type;                 //该内存块的类型,有三种可能值:MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE海岸线网络聚合Pg q|,j+a4@ C'Q
  } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
oif)P*pg0
:z;b(xk`'TY0  进一步研究发现, 要搜索数据, 只要搜索 类型=MEM_PRIVATE 页面属性=PAGE_READWRITE 的内存块就好了, 这样可以大大提高搜索速度.海岸线网络聚合F6n8?}/y d1x

eb(i8E5Xp#Dz0  这样我们就可得到进程中需要扫描的地址范围了。到这里剩下的问题就是要读取指定的进程的指定的地地址空间的内容了。这里要用到的是用于调试程序和错误处理(Debugging and Error Handling)的API函数中的ReadProcessMemory,它的原型如下:
2?7y z _Ka9wg|0  BOOL ReadProcessMemory(
Zy X un.Kk0      HANDLE hProcess,         // 被读取进程的句柄海岸线网络聚合2K,v`8p+}7U
      LPCVOID lpBaseAddress,     // 读的起始地址海岸线网络聚合 C&UOt8d,Y%O`
      LPVOID lpBuffer,         // 存放读取数据缓冲区
!PE:bC-_5DN0      SIZE_T nSize,           // 一次读取的字节数海岸线网络聚合:]4~;h+Wl
      SIZE_T * lpNumberOfBytesRead // 实际读取的字节数海岸线网络聚合)oa$g'V8F}'u%c5ol\
  );海岸线网络聚合4BFw7S O6q:~

(L8|M8Kq0  参数很简单从它们的名字都可以猜出其意义了,这里就不多做说明了。要说明的是要对一个进程进行ReadProcessMemory操作,当前进程对要读的进程必须有PROCESS_VM_READ 和 PROCESS_QUERY_INFORMATION 访问权。要获得一个进程的句柄和对这个进程的一些控制权可以使用API函数OpenProcess得到,其使用不做详细说明了,只给出其原型:海岸线网络聚合iQHuM%o
  HANDLE OpenProcess(海岸线网络聚合ZK A CcDG5Y@
      DWORD dwDesiredAccess,     // 访问标志
KfDu!k4Cy#a0      BOOL bInheritHandle,       // 继承标志
m,@,dp;{eKqR!Bx0      DWORD dwProcessId         // 进程ID海岸线网络聚合IOs]W!Zg#nTC
  );海岸线网络聚合 _,v.`)[D,c`x~
海岸线网络聚合:F&o-h%O(bi9~ cv
  3.如何获取QQ进程ID
6j{$h#U7q&f_@#K0
u+|W1e-FGA0进程ID可由 Process32First 和 Process32Next 得到,这两个函数可以枚举出所有开启的进程。海岸线网络聚合"WM0_T!U6v!o
Process32First 和 Process32Next原形如下:
8Yb7Z*t q6I9J0海岸线网络聚合PVM^e;xQ{O y:n
BOOL WINAPI Process32First
D kg(gE_q#} L2W G0(海岸线网络聚合"D,Ha'c*o/c
  HANDLE hSnapshot     //由 CreateToolhelp32Snapshot 返回的系统快照句柄;海岸线网络聚合|;iNV"m9rPQ\o
  LPPROCESSENTRY32 lppe   // 指向一个 PROCESSENTRY32 结构;海岸线网络聚合:V!~ |o&E N
);
CDB)D }8Z IP7w3G0BOOL WINAPI Process32Next海岸线网络聚合#K} `!Qm
(
OTz.~)f f&@&z0  HANDLE hSnapshot     // 由 CreateToolhelp32Snapshot 返回的系统快照句柄;
nF:f$F Li+`\0  LPPROCESSENTRY32 lppe // 指向一个 PROCESSENTRY32 结构;
*@,c2FB}6lQ0);海岸线网络聚合/hdv$Xu0Jua
海岸线网络聚合+bBds$im+f.O!]'w{
CreateToolhelp32Snapshot 原形如下:
Z_-Uk'c0L0HANDLE WINAPI CreateToolhelp32Snapshot
cGa#Q0Z%W?0(
9O0o'pb,b0  DWORD dwFlags,     // 快照标志;海岸线网络聚合'P7Dt,k{\N*~
  DWORD th32ProcessID // 进程ID;
|.] X1mp.[-v0);
'dh0e*G*eVn9R0o,^8k0海岸线网络聚合 N%ZG&Gr3P|
现在需要的是进程的信息,所以将 dwFlags 指定为 TH32CS_SNAPPROCESS,th32ProcessID 忽略;
e`/oF:I G(u0
R`4io]^%b0PROCESSENTRY32 结构如下:
C b _Tjv0typedef struct tagPROCESSENTRY32 {
&|]J4Q_:T6\7zN0  DWORD dwSize;         // 结构大小;
7L~Q$Z9MP/It0  DWORD cntUsage;       // 此进程的引用计数;
`U ~!ZNM%j#m_0  DWORD th32ProcessID;     // 进程ID;海岸线网络聚合n8}D6MG n5o
  DWORD th32DefaultHeapID; // 进程默认堆ID;海岸线网络聚合XJ R1`d
  DWORD th32ModuleID;     // 进程模块ID;海岸线网络聚合 Y G%V0J(j+W\
  DWORD cntThreads;       // 此进程开启的线程计数;海岸线网络聚合 K*n{ f wFxF
  DWORD th32ParentProcessID;// 父进程ID;海岸线网络聚合e z yEW[*JoT#G
  LONG pcPriClassBase;   // 线程优先权;海岸线网络聚合'jPS4a&_ O+[/b
  DWORD dwFlags;         // 保留;海岸线网络聚合;gg6p!`kAK
  char szExeFile[MAX_PATH]; // 进程全名;
dW~wpc(a b,]!]0} PROCESSENTRY32;
8aEh.P S,`}*l3c0海岸线网络聚合 {:z/Z,HF
三. 程序流程
AU}i2~m0海岸线网络聚合9s;ic LGp0xjM
知道了上面的原理 , 编写程序就很简单了 .程序流程如下:海岸线网络聚合2| g:i X9h6T/jm)u
海岸线网络聚合W`#lX7x
1. 遍历系统进程, 找到所有QQ进程的ID;
9@_$i c2l8D]!h02. 通过ID打开每个QQ进程, 获得操作句柄;海岸线网络聚合3J7UV2Q;OZB
3. 读取QQ进程 "类型=MEM_PRIVATE 页面属性=PAGE_READWRITE" 的内存块到自己程序的缓冲区, 然后搜索关键字 "\MsgEx.db"位置;
,D9t TkU U f+h04. 提取关键字前面的QQ号码.海岸线网络聚合0g\ T&\3N A)i!|%@@
5. 显示QQ号码.结束.
^+Y!W,Z? hFY0

TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

Open Toolbar