📄 013.txt
字号:
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_PROCESS equ 1001
IDC_REFRESH equ 1002
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinList dd ?
.const
szErrTerminate db ~无法结束指定进程!~,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetProcessList proc _hWnd
local @stProcess:PROCESSENTRY32
local @hSnapShot
invoke RtlZeroMemory,addr @stProcess,sizeof @stProcess
invoke SendMessage,hWinList,LB_RESETCONTENT,0,0
mov @stProcess.dwSize,sizeof @stProcess
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov @hSnapShot,eax
invoke Process32First,@hSnapShot,addr @stProcess
.while eax
invoke SendMessage,hWinList,LB_ADDSTRING,\
0,addr @stProcess.szExeFile
invoke SendMessage,hWinList,LB_SETITEMDATA,eax,\
@stProcess.th32ProcessID
invoke Process32Next,@hSnapShot,addr @stProcess
.endw
invoke CloseHandle,@hSnapShot
invoke GetDlgItem,_hWnd,IDOK
invoke EnableWindow,eax,FALSE
ret
_GetProcessList endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
mov eax,wMsg
.if eax == WM_CLOSE
invoke EndDialog,hWnd,NULL
.elseif eax == WM_INITDIALOG
invoke GetDlgItem,hWnd,IDC_PROCESS
mov hWinList,eax
invoke _GetProcessList,hWnd
;********************************************************************
.elseif eax == WM_COMMAND
mov eax,wParam
.if ax == IDOK
invoke SendMessage,hWinList,LB_GETCURSEL,0,0
invoke SendMessage,hWinList,\
LB_GETITEMDATA,eax,0
invoke OpenProcess,PROCESS_TERMINATE,\
FALSE,eax
.if eax
mov ebx,eax
invoke TerminateProcess,ebx,-1
invoke CloseHandle,ebx
invoke Sleep,200
invoke _GetProcessList,hWnd
jmp @F
.endif
invoke MessageBox,hWnd,addr szErrTerminate,\
NULL,MB_OK or MB_ICONWARNING
@@:
;********************************************************************
.elseif ax == IDC_REFRESH
invoke _GetProcessList,hWnd
;********************************************************************
.elseif ax == IDC_PROCESS
shr eax,16
.if ax == LBN_SELCHANGE
invoke GetDlgItem,hWnd,IDOK
invoke EnableWindow,eax,TRUE
.endif
.endif
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,DLG_MAIN,\
NULL,offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
程序在初始化、按下“刷新”按钮以及结束一个线程以后都要调用_GetProcessList子程序来重新获取系统中运行的进程列表。在_GetProcessList子程序中,程序首先向列表框发送LB_RESETCONTENT消息删除原来的列表内容,并调用CreateToolhelp32Snapshot函数获得一个快照。函数的使用格式是:
invoke CreateToolhelp32Snapshot,dwFlags,th32ProcessID
.if eax
mov hSnapShot,eax
.endif
dwFlags参数用来指定“快照”中需要返回的对象,本函数不仅可以获取进程列表,也可以用来获取线程和模块等对象的列表,参数可以指定的值是:
● TH32CS_SNAPHEAPLIST——对指定进程中的堆进行枚举。
● TH32CS_SNAPMODULE——对指定进程中的模块进行枚举。
● TH32CS_SNAPPROCESS——对系统范围中的进程进行枚举。
● TH32CS_SNAPTHREAD——对系统范围中的线程进行枚举。
th32ProcessID参数用来指定一个进程ID,当dwFlags指定为TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE来枚举某个进程中的堆以及模块的时候,这个参数用来指定被枚举的进程ID。对于TH32CS_SNAPPROCESS和TH32CS_SNAPTHREAD标志,由于枚举的范围是系统范围内的,所以th32ProcessID参数将被忽略。
如果函数执行成功,将返回一个快照句柄,否则返回?1。程序可以通过这个快照句柄获取进程列表。
从快照句柄中获取进程参数使用Process32First和Process32Next函数,函数的每次调用仅返回一个进程的信息。Process32First函数用来进行首次调用,以后的调用由Process32Next函数循环完成,直到所有的进程信息都被获取为止,当不再有剩余信息的时候,函数返回FALSE,所以一般使用下面的循环结构来获取进程列表:
.data?
stProcess PROCESSENTRY32
hSnapShot dd ?
.code
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov hSnapShot,eax
mov stProcess.dwSize,sizeof stProcess
invoke Process32First,hSnapShot,addr stProcess
.while eax
;在这里处理返回到PROCESSENTRY32中的进程信息
invoke Process32Next,hSnapShot,addr stProcess
.endw
invoke CloseHandle,hSnapShot
Process32First和Process32Next函数的第一个参数是前面得到的快照句柄,第二个参数指向一个PROCESSENTRY32结构,进程信息将被返回到这个结构中。结构的定义如下:
PROCESSENTRY32 STRUCT
dwSize DWORD ? ;结构的长度,必须预先设置
cntUsage DWORD ? ;进程的引用计数
th32ProcessID DWORD ? ;进程ID
th32DefaultHeapID DWORD ? ;进程默认堆的ID
th32ModuleID DWORD ? ;进程模块的ID
cntThreads DWORD ? ;被进程创建的线程数
th32ParentProcessID DWORD ? ;进程的父进程ID
pcPriClassBase DWORD ? ;被进程创建的线程的基本优先级
dwFlags DWORD ? ;内部使用
szExeFile db MAX_PATH dup(?) ;进程对应的可执行文件名
PROCESSENTRY32 ENDS
注意:在使用前需要先将dwSize填写为结构的长度,否则函数的执行会失败,在返回所有的进程信息后,需要使用CloseHandle函数将快照句柄关闭。
结构中返回的进程ID字段(th32ProcessID)和可执行文件名字段(szExeFile)是我们最关心的。通过比较文件名,就可以找出需要寻找的可执行文件产生的进程,然后通过进程ID就可以用OpenProcess函数获得进程句柄,有了进程句柄以后就可以对进程进行各种操作了。
在例子中,每当在PROCESSENTRY32结构中返回了一个进程的信息后,程序向列表框发送LB_ADDSTRING消息将可执行文件名添加到列表框中。由于列表框能够为每个项目定义一个32位的自定义数据,利用这个特征可将文件对应的进程ID保存到这里,方法就是向列表框发送LB_SETITEMDATA消息,这样在按下“终止”按钮以后,程序就可以通过LB_GETITEMDATA消息取回进程ID,使用OpenProcess函数获得该进程的句柄以后,再使用TerminateProcess函数将进程终止。
当例子程序在Windows 2000中运行时,如果试图打开系统底层的进程(如System或者[System Process]等进程)是不会成功的,因为用户程序并没有这么高的权限。
13.3.2 读写进程的地址空间
1. 进程地址空间的读写函数
当一个进程能够被我们以足够的权限打开以后,就可以通过ReadProcessMemory和WriteProcessMemory函数读写它的地址空间。只要能够对其他进程的地址空间进行读写,那么我们能够做的事情就多了,只要发挥想像力,就能够编出一些超乎想像的程序来。在广为流传的应用程序中,最为著名的就是FPE之类的游戏修改器。
FPE是Fix People Expert的缩写,不过这个软件可不是用来修理人(People)而是用来对付游戏的。FPE程序列出当前系统中运行的进程,让用户选择要对付的游戏程序名(现在读者可以骄傲地说,这一招我也会,13.3.1节中的进程列表例子不就是这样吗),然后让用户输入一个数值,比如现在游戏主角还剩下3条命就输入3,FPE将扫描游戏进程的所有地址空间,将当前数据为3的地址列入黑名单,接下来继续游戏,当又少了一条命的时候,再次输入2并扫描,如果上次黑名单中某个地址中的数据现在变成了2,代表生命的数据十有八九就存放在这个地址中,将它改成100的话,主角就长命百岁了!同样道理想让主角变成千千岁,万万岁也不在话下!另外,如果找到代表生命的地址,让FPE锁定(就是每隔很短的时间将要锁定的数值重新写到这个地址中)数值,游戏主角就是金刚不坏之躯了。
FPE是通用的修改软件,另一类专用的游戏修改器也使用同样的技术,比如打《暗黑破坏神》游戏的时候,很多人用过增加经验点数的修改器,因为这个游戏对数据经过了某种处理,用FPE一类的软件无法直接将要修改的数据搜索出来,有人就通过跟踪找到了变换后的数据地址和变换算法,并专门写了针对这个游戏的进程内存读写程序。
当然,经过本节介绍以后,读者就会觉得这些软件使用的技术并没有那么神秘,我们自己也可以写出同样的程序来。
首先来介绍一下这两个函数的用法:
invoke ReadProcessMemory,hProcess,lpBaseAddress,lpBuffer,\
dwSize,lpNumberOfBytesRead (读进程内存)
invoke WriteProcessMemory,hProcess,lpBaseAddress,lpBuffer,\
dwSize,lpNumberOfBytesWritten (写进程内存)
这两个函数的参数定义是一样的,各参数的定义为:
● hProcess——指定将要被读写的目标进程句柄。
● lpBaseAddress——目标进程中被读写的起始线性地址。
● lpBuffer——用来接收读取数据的缓冲区(对于ReadProcessMemory函数)或者要写到目标进程的数据缓冲区(对于WriteProcessMemory函数)。
● dwSize——要读写的字节数。
● lpNumberOfBytesRead或lpNumberOfBytesWritten——指向一个双字变量,用来供函数返回实际读写的字节数,如果不关心这个结果,可以在这里使用NULL。
如果函数执行成功,那么返回值是非0值,执行失败的话返回0。
使用这两个函数需要注意的地方有:
● 注意lpBaseAddress和lpBuffer参数指向的地址位于不同的进程空间内,lpBuffer指向的缓冲区位于本进程的地址空间内,而lpBaseAddress指向的地址位于目标进程的地址空间内。
● 要对目标进程进行读写的话,打开(或者创建)目标进程的时候必须包含对应的权限,如要读取目标进程必须包括PROCESS_VM_READ权限;要对目标进程进行写操作的时候,必须包括PROCESS_VM_OPERATION或者PROCESS_VM_WRITE权限。
● lpBaseAddress位置开始的dwSize大小的内存必须是可存取的,函数在执行前会对整个区域进行测试,如果中间有某处是不可存取的(如没有被递交到物理内存),那么函数直接返回失败,所以一般不会出现只读写了一部分内存的情况。
● 虽然在自己的进程中,代码段是不可写的,但是使用WriteProcessMemory函数去写目标进程的代码段却是允许的。
FPE等软件就是使用这两个函数来读写目标进程的数据的,但由于这些函数也可以用来
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -