修正后的源碼及EXE , 有易友提出源程序出錯, 這里說明一下, 源程序是用4.0測試版5編寫的 , 用3.8打開會出錯。 前段時間在論壇上, 易友 "wjkplx" 問 "如何取正在運行的QQ號" (dispbbs.asp?BoardID=1&ID=51048), 后來有"方德軟件","近在眼前","goomoo","云德武" 幾位熱心的大俠各自提出了自己的思路及解決辦法.
幾位易友的方法各不相同, 各有優點, 也都不完美. "方德軟件"的方法是查找硬盤文件, 編程簡單,但只能取最后一個登陸的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
新聞熱點
疑難解答