开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 8656|回复: 11
收起左侧

[已解决] 从进程中获取QQ号码(终结修改篇+源码)(修正)

 关闭 [复制链接]
结帖率:100% (4/4)
发表于 2010-1-7 14:02:09 | 显示全部楼层 |阅读模式   内蒙古自治区呼和浩特市
100精币
转发自官网,原帖的附件已经失效了,求这个文件,或者用这个思路自己编个源码出来,只要能准确取到QQ号码即可,要求QQ2009,数据文件为Msg2.0.db
原帖地址:http://bbs.eyuyan.com/dispbbs.asp?boardID=124&ID=55752

从进程中获取QQ号码(终结修改篇+源码)(修正)   Post By:2005-6-4 12:16:00

修正后的源码及EXE , 有易友提出源程序出错, 这里说明一下, 源程序是用4.0测试版5编写的 , 用3.8打开会出错 :  下载信息  [文件大小:506.1 KB  下载次数:142]
点击浏览该文件:
前段时间在论坛上, 易友 "wjkplx" 问 "如何取正在运行的QQ号" (dispbbs.asp?BoardID=1&ID=51048), 后来有"方德软件","近在眼前","goomoo","云德武" 几位热心的大侠各自提出了自己的思路及解决办法. 几位的贴子如下: "goomoo":
dispbbs.asp?boardid=1&star=1&replyid=8168&id=55328&skin=0&page=1
近在眼前:
dispbbs.asp?boardid=1&star=1&replyid=9038&id=55314&skin=0&page=1
dispbbs.asp?boardid=1&star=1&replyid=8223&id=55487&skin=0&page=1
方德软件:
dispbbs.asp?BoardID=1&ID=55343
云城武:
dispbbs.asp?BoardID=1&ID=55599 几位易友的方法各不相同, 各有优点, 也都不完美. "方德软件"的方法是查找硬盘文件, 编程简单,但只能取最后一个登陆的QQ号码,不能取多个QQ的号码. "近在眼前"最初的办法好像是搜索QQ目录以数字命名的文件夹, 也同样有 "方德软件" 的缺点, "近在眼前"之后修改的版本, 采用了moogoo的方法. 相对来说, "goomoo" 的方法是最好的, 直接在QQ进程空间里查找QQ号码,因为QQ的任何数据都是放在进程空间里的(包括QQ号码),所以这个方法是绝对能成功的.当然,前提是QQ必须先登陆成功. "goomoo" 的方法可能是考虑得不够周全, 比如有些号码找不到, 而有些找到了又是别的号码."云城武" 的方法跟 "goomoo"是一样的. 我花了两天时间, 仔细地研究了各位的方法, 并到网上搜索了相关的资料, 集合几位大侠的思路, 做出了取QQ登陆号码的程序. 这个程序可以提取腾讯QQ所有版本的登陆成功后的QQ号码. 可以在任何操作系统下使用. 运行速度快, 取号码准确, 目前为止,没有发现取错号.
制作这个程序的过程中,我查阅了很多相关资料, 对系统编程有了进一步的了解, 下面我就编制这个程序的原理过程和一些心得写出来, 给大家参考. 一. 取QQ号码原理: QQ程序在运行过程中, 所有数据都是存放在进程空间中,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进程空间的数据? 1.应用程序进程
  进程是当前操作系统下一个被加载到内存的、正在运行的应用程序的实例。每一个进程都是由内核对象和地址空间所组成的,内核对象可以让系统在其内存放有关进程的统计信息并使系统能够以此来管理进程,而地址空间则包括了所有程序模块的代码和数据以及线程堆栈、堆分配空间等动态分配的空间。进程仅仅是一个存在,是不能独自完成任何操作的,必须拥有至少一个在其环境下运行的线程,并由其负责执行在进程地址空间内的代码。在进程启动的同时即同时启动了一个线程,该线程被称作主线程或是执行线程,由此线程可以继续创建子线程。如果主线程退出,那么进程也就没有存在的可能了,系统将自动撤消该进程并完成对其地址空间的释放。
  加载到进程地址空间的每一个可执行文件或动态链接库文件的映象都会被分配一个与之相关联的全局唯一的实例句柄(Hinstance)。 2. 进程空间
在WIN32中,每个应用程序都可“看见”4GB的线性地址空间, 其中最开始的4MB和最后的2GB由操作系统保留,低的2GB为进程的私有空间(如果在Boot.ini文件中使用“/3GB”的开关可以使进程的私有空间增大到3GB,系统空间1GB)。对于每个进程来讲其虚拟的地址空间是连续的,实际上它们是以页面为单位离散的存在于物理内存中,一些可能被交换到硬盘上的页面文件中,而且还有大部分的空间是未提交(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 一个共享的只读用户数据块,该块映射到到系统空间的一个数据块,
其中存放的是一些系统信息如系统时间、时钟的滴答数、系统版本号等。
这样访问这些信息的时候系统就不用切换到核心模式。
0x7FFE1000~~0x7FFEFFFF 60 KB 不可访问
0x7FFF0000~~0x7FFFFFFF 64 KB 不可访问,用于防止线程的缓冲跨越两种模式空间的边界
一个进程的高2GB空间具体分配如下:
0xFFFFFFFF-0xC0000000的1GB 用于VxD、存储器管理和文件系统;
0xBFFFFFFF-0x80000000的1GB 用于共享的WIN32 DLL、存储器映射文件和共享存储区;
虚拟内存通常是由固定大小的块来实现的,在WIN32中这些块称为“页”,每页大小为4,096字节。在Intel CPU结构中,通过在一个控制寄存器中设置一位来启用分页。启用分页时CPU并不能直接访问内存,对每个地址要经过一个映射进程,通过一系列称作“页表”的查找表把虚拟内存地址映射成实际内存地址。通过使用硬件地址映射和页表WIN32可使虚拟内存即有好的性能而且还提供保护。利用处理器的页映射能力,操作系统为每个进程提供独立的从逻辑地址到物理地址的映射,使每个进程的地址空间对另一个进程完全不可见。

我们要搜索另一个进程空间的数据, 要扫描范围的起点和终点不是从0~~2GB,而只是其中的一部分。要得到这个起点和终点可以使用API函数GetSystemInfo,函数的原型如下:
VOID GetSystemInfo(
LPSYSTEM_INFO lpSystemInfo
);
而在结构SYSTEM_INFO中有两个值:lpMinimumApplicationAddress和 lpMaximumApplicationAddress,
就是一个应用程序可用的最小和最大的地址空间。这样我们就得到了要扫描的地址的起点和终点。那么是不是这起点和终点间所有的地址都要扫描呢?并不是这样的,因为一般情况下一个进程是用不着这么大(接近2GB)的地址空间的。因此一个进程的大部分地址空间都是未用(Free)或是保留(Reserved)的,真正用到的只是那些已提交(Committed)的内存而已。

内存页面可以有三种状态:未用(Free)、保留(Reserved)和提交(Committed)。一个未用的页面是指该页面未被保留或是提交,对一个进程来讲一个未用的页面是不可访问的,访问这样的页面将导致访问违例。进程可以要求系统保留一些页面以备后用,系统返回一段保留的地址给进程,但是这些地址同样是不可访问的,进程若想使用这段地址空间,使用必须先提交。只有一个提交的页面才是一个真正可以访问的页面。不过你提交了一个页面,系统并不会马上分配物理页面,只有在该页面第一次被访问到时,系统才会分配页面并初始化。另外,这三个状态的两两之间都是可以相互转化的。
这样我们的工作已大大减少了,只需要扫描那些提交的页面就好了。接下来要做的就是得到一个进程的已提交的页面范围。这就要用到另外两个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; 进一步研究发现, 要搜索数据, 只要搜索 类型=MEM_PRIVATE 页面属性=PAGE_READWRITE 的内存块就好了, 这样可以大大提高搜索速度. 这样我们就可得到进程中需要扫描的地址范围了。到这里剩下的问题就是要读取指定的进程的指定的地地址空间的内容了。这里要用到的是用于调试程序和错误处理(Debugging and Error Handling)的API函数中的ReadProcessMemory,它的原型如下:
BOOL ReadProcessMemory(
HANDLE hProcess, // 被读取进程的句柄
LPCVOID lpBaseAddress, // 读的起始地址
LPVOID lpBuffer, // 存放读取数据缓冲区
SIZE_T nSize, // 一次读取的字节数
SIZE_T * lpNumberOfBytesRead // 实际读取的字节数
); 参数很简单从它们的名字都可以猜出其意义了,这里就不多做说明了。要说明的是要对一个进程进行ReadProcessMemory操作,当前进程对要读的进程必须有PROCESS_VM_READ 和 PROCESS_QUERY_INFORMATION 访问权。要获得一个进程的句柄和对这个进程的一些控制权可以使用API函数OpenProcess得到,其使用不做详细说明了,只给出其原型:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问标志
BOOL bInheritHandle, // 继承标志
DWORD dwProcessId // 进程ID
);


3.如何获取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;
三. 程序流程 知道了上面的原理 , 编写程序就很简单了 .程序流程如下: 1. 遍历系统进程, 找到所有QQ进程的ID;
2. 通过ID打开每个QQ进程, 获得操作句柄;
3. 读取QQ进程 "类型=MEM_PRIVATE 页面属性=PAGE_READWRITE" 的内存块到自己程序的缓冲区, 然后搜索关键字 "\MsgEx.db"位置;
4. 提取关键字前面的QQ号码.
5. 显示QQ号码.结束.

最佳答案

查看完整内容

期待会内存操作的高手回答;

回答提醒:如果本帖被关闭无法回复,您有更好的答案帮助楼主解决,请发表至 源码区 可获得加分喔。
友情提醒:本版被采纳的主题可在 申请荣誉值 页面申请荣誉值,获得 1点 荣誉值,荣誉值可兑换荣誉会员、终身vip用户组。
快捷通道:申请荣誉值无答案申请取消悬赏投诉有答案未采纳为最佳
结帖率:43% (3/7)

签到天数: 7 天

发表于 2010-1-7 14:02:10 | 显示全部楼层   广东省揭阳市
期待会内存操作的高手回答;
回复

使用道具 举报

结帖率:0% (0/4)
发表于 2010-1-7 14:36:48 | 显示全部楼层   上海市上海市
顶一下。看看,其实上网可以找到些资料的。
回复

使用道具 举报

结帖率:60% (3/5)
发表于 2010-1-7 14:38:40 | 显示全部楼层   上海市上海市
太深奥了!!
回复

使用道具 举报

结帖率:100% (4/4)
 楼主| 发表于 2010-1-7 14:40:09 | 显示全部楼层   内蒙古自治区呼和浩特市
我先发一个,大家看看有没有效果

.程序集 窗口程序集1
.程序集变量 关键字, 字节集, , , \MsgEx.db
.程序集变量 路径分隔符, 字节集, , , \
.程序集变量 QQ进程名, 文本型, , , QQ.EXE
.程序集变量 耗时, 整数型

.子程序 _开始搜索_被单击
.局部变量 进程句柄, 整数型
.局部变量 进程快照, 整数型
.局部变量 进程信息, PROCESSENTRY32

耗时 = 取启动时间 ()

QQ进程名 = “QQ.EXE”
路径分隔符 = 到字节集 (“\”)
关键字 = 到字节集 (“\MsgEx.db”)

进程快照 = CreateToolhelp32Snapshot (#TH32CS_SNAPALL, 0)
.如果真 (进程快照 = 0)
    信息框 (“创建进程快照列表失败 .”, #信息图标, “提示”)
    返回 ()
.如果真结束
进程信息.dwSize = 296  ' = len (uProcess)
进程句柄 = Process32First (进程快照, 进程信息)

列表框1.清空 ()
.判断循环首 (进程句柄 ≠ 0)
    .如果真 (寻找文本 (到文本 (进程信息.szExeFile), QQ进程名, , 真) ≠ -1)
        列表框1.加入项目 (“ID: ” + 到文本 (进程信息.th32ProcessID) + “    QQ号码: ” + 查找QQ号码 (进程信息.th32ProcessID), )
    .如果真结束
    进程句柄 = Process32Next (进程快照, 进程信息)
.判断循环尾 ()
CloseHandle (进程快照)

耗时 = 取启动时间 () - 耗时
标签1.标题 = “耗时: ” + 到文本 (耗时) + “ 毫秒”

.子程序 查找QQ号码, 文本型
.参数 进程ID, 整数型
.局部变量 操作句柄, 整数型
.局部变量 内存地址, 整数型
.局部变量 读取结果, 整数型
.局部变量 内存块长度, 整数型
.局部变量 开始地址, 整数型
.局部变量 结束地址, 整数型
.局部变量 数据缓冲区, 字节集
.局部变量 QQ号码, 文本型
.局部变量 内存块信息, MEMORY_BASIC_INFORMATION
.局部变量 地址偏移, 整数型
.局部变量 地址值, 字节型

操作句柄 = OpenProcess (#PROCESS_VM_READ + #PROCESS_QUERY_INFORMATION, 0, 进程ID)
.如果真 (操作句柄 = 0)
    信息框 (“不能打开进程 .”, #信息图标, “提示”)
    返回 (“”)
.如果真结束

内存块长度 = 28
.判断循环首 (真)
    .如果真 (VirtualQueryEx (操作句柄, 内存地址, 内存块信息, 内存块长度) = 0)
        CloseHandle (操作句柄)
        返回 (“”)
    .如果真结束

    .如果真 (内存块信息.Type = #MEM_PRIVATE 且 内存块信息.Protect = #PAGE_READWRITE)
        数据缓冲区 = 取空白字节集 (内存块信息.RegionSize)
        读取结果 = ReadProcessMemory (操作句柄, 内存地址, 数据缓冲区, 内存块信息.RegionSize, 0)
        .如果 (读取结果 ≠ 0)
            结束地址 = 寻找字节集 (数据缓冲区, 关键字, )
            .判断循环首 (结束地址 ≠ -1)
                开始地址 = 倒找字节集 (数据缓冲区, 路径分隔符, 结束地址 - 1)
                .如果真 (开始地址 ≠ -1 且 结束地址 - 开始地址 < 16 且 结束地址 - 开始地址 > 4)
                    .变量循环首 (开始地址 + 1, 结束地址 - 1, 1, 地址偏移)  ' 判断是否为数字格式
                        地址值 = 取字节集数据 (取字节集中间 (数据缓冲区, 地址偏移, 1), #字节型)
                        .如果真 (地址值 < 48 或 地址值 > 57)
                            跳出循环 ()
                        .如果真结束

                    .变量循环尾 ()
                    .如果真 (地址偏移 = 结束地址)
                        QQ号码 = 取字节集数据 (取字节集中间 (数据缓冲区, 开始地址 + 1, 结束地址 - 开始地址 - 1), #文本型)
                        CloseHandle (操作句柄)
                        返回 (QQ号码)
                    .如果真结束

                .如果真结束
                结束地址 = 寻找字节集 (数据缓冲区, 关键字, 结束地址 + 1)
            .判断循环尾 ()
        .否则
            ' 输出调试文本 (“不能读取内存块: ” + 到文本 (内存地址))
        .如果结束

    .如果真结束
    内存地址 = 内存地址 + 内存块信息.RegionSize
.判断循环尾 ()
CloseHandle (操作句柄)
返回 (“”)
回复

使用道具 举报

结帖率:100% (4/4)
 楼主| 发表于 2010-1-7 14:41:24 | 显示全部楼层   内蒙古自治区呼和浩特市
2009的QQ,注意将里面的MsgEx.db改为Msg2.0.db
回复

使用道具 举报

结帖率:100% (4/4)
发表于 2010-2-22 14:51:46 | 显示全部楼层   河北省邯郸市
好牛啊   内存的还是  不懂  期待高手
回复

使用道具 举报

头像被屏蔽
结帖率:45% (20/44)
发表于 2010-2-22 15:14:57 | 显示全部楼层   河南省新乡市
帮你顶上...
回复

使用道具 举报

发表于 2010-5-21 02:05:21 | 显示全部楼层   浙江省温州市
版主在写论文吗?好长。
回复

使用道具 举报

结帖率:100% (6/6)
发表于 2010-5-22 20:52:33 | 显示全部楼层   广东省清远市
头好晕啊,可能是今天心情不好
等高手来吧
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表