avatar

目录
Python获取指定模块基址

因为昨天研究FPS游戏时候,发现有个动态地址每次重启电脑都会不同,然后因为有过用C和易语言编写指定模块名获取基址的经验,所以打算用Python来试试

在网上搜索了一点资料,发现有吾爱有一篇是使用Python32位,通过Ntdll库进行模块遍历。

将代码复制粘贴,因为我使用的是Python64位的,改了改代码,但是发现失败了,搜不出来,因为代码涉及到PE头,目前还没碰到这块区域,代码作者也说他只是复制粘贴的,所以也不甚清楚为什么,就只能另寻出路了。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def _ReadMemeryInt(hGameHandle,_address,bufflength):
addr = ctypes.c_ulong()
ReadProcessInt = kernel32.ReadProcessMemory
ReadProcessInt(int(hGameHandle), _address, ctypes.byref(addr), bufflength, None)
return addr.value

def _ReadMemeryWchar(hGameHandle,_address,bufflength):
addr = ctypes.c_wchar_p("0" * bufflength)
ReadProcessInt = kernel32.ReadProcessMemory
ReadProcessInt(int(hGameHandle), _address, addr, bufflength, None)
return addr.value

def ReadVirtualMemory(hGameHandle,addr, n=8):
# 这里定义一个函数来读取,传入三个参数,第一个是进程句柄,第二个是我们要读取的地址,我们可以默认为8,可以偷懒,第三个是要读取的长度
addr = ctypes.c_ulonglong(addr)
ret = ctypes.c_ulonglong()
BufferLength = ctypes.c_ulonglong(n)
ReadProcessInt = kernel32.ReadProcessMemory
ReadProcessInt(int(hGameHandle), addr, ctypes.byref(ret), BufferLength, 0)
return ret.value # c_ulonglong的类型中,他的数值是放在他的属性value中的,所以返回的时候我们只需要获取value中存放的数值就好了

def ReadVirtualMemoryWchar(hGameHandle,addr, n):
# 这里定义一个函数来读取,传入三个参数,第一个是进程句柄,第二个是我们要读取的地址,我们可以默认为8,可以偷懒,第三个是要读取的长度
addr = ctypes.c_ulonglong(addr)
ret = ctypes.c_wchar_p("0" * n)
BufferLength = ctypes.c_ulonglong(n*2+1)
ReadProcessInt = kernel32.ReadProcessMemory
ReadProcessInt(int(hGameHandle), addr, ret, BufferLength, None)
return ret.value # c_ulonglong的类型中,他的数值是放在他的属性value中的,所以返回的时候我们只需要获取value中存放的数值就好了

def GetBaseAddr(hGameHandle, ModuleName):
NumberOfBytesRead = ctypes.c_ulong()
Buffer = PROCESS_BASIC_INFORMATION()
Size = ctypes.c_ulong(48)

name_len = len(ModuleName)

ntdll.NtQueryInformationProcess(int(hGameHandle), 0, ctypes.byref(Buffer), Size, ctypes.byref(NumberOfBytesRead))

ret = ReadVirtualMemory(hGameHandle, Buffer.PebBaseAddress + 24, 8)
print(ret)
ret = ReadVirtualMemory(hGameHandle, ret + 24, 8)

for i in range(1000): # 这里用for循环其实是怕程序卡死,下面如果出了问题不能退出的话,循环结束一样可以退出
modulehandle = ReadVirtualMemory(hGameHandle, ret + 40, 8)
print(modulehandle)
if modulehandle == 0:
break
nameaddr = ReadVirtualMemory(hGameHandle, ret + 96, 8)
print(nameaddr)
name = ReadVirtualMemoryWchar(hGameHandle, nameaddr, name_len)
print(name)
#3ED30000 1054015488
#00CE0000
if name == ModuleName:
return modulehandle
ret = ReadVirtualMemory(hGameHandle, ret + 8, 8)

return 0

返回值是0,我也有尝试过修改 ret + xx后面的数字进行搜索,原作者说这个数是涉及PE头的知识,后面发现,无论怎么改都搜不出来,就放弃了。

使用WinAPI完成指定模块基址读写

1.先了解下要使用到的函数

MODULEENTRY32

MODULEENTRY32是一种编程数据结构。

结构

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Describes an entry from a list of the modules belonging to the specified process.
typedef struct tagMODULEENTRY32 {
DWORD dwSize; //指定结构的长度,以字节为单位。在调用Module32First功能,设置这个成员SIZEOF(MODULEENTRY32)。如果你不初始化的dwSize,Module32First将失败。
DWORD th32ModuleID;//此成员已经不再被使用,通常被设置为1
DWORD th32ProcessID;//正在检查的进程标识符。这个成员的内容,可以使用Win32 API的元素
DWORD GlblcntUsage;//全局模块的使用计数,即模块的总载入次数。通常这一项是没有意义的,被设置为0xFFFF。
DWORD ProccntUsage;//全局模块的使用计数(与GlblcntUsage相同)。通常这一项也是没有意义的,被设置为0xFFFF。
BYTE *modBaseAddr;//模块的基址,在其所属的进程范围内。
DWORD modBaseSize;//模块的大小,单位字节。
HMODULE hModule;//所属进程的范围内,模块句柄。
TCHAR szModule[MAX_PATH];//NULL结尾的字符串,其中包含模块名。
TCHAR szExePath[MAX_PATH];//NULL结尾的字符串,其中包含的位置,或模块的路径。
} MODULEENTRY32, *PMODULEENTRY32, *LPMODULEENTRY32; // 别名

CreateToolhelp32Snapshot

CreateToolhelp32Snapshot可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。

函数原型:

c++
1
2
3
4
>HANDLE WINAPI CreateToolhelp32Snapshot(
>DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
>DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
>);

dwFlags

c++
1
2
3
4
5
6
7
8
>指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个。
>TH32CS_INHERIT(0x80000000) - 声明快照句柄是可继承的。
>TH32CS_SNAPALL - 在快照中包含系统中所有的进程和[线程](https://baike.baidu.com/item/线程/103101)。
>TH32CS_SNAPHEAPLIST(0x00000001) - 在快照中包含在th32ProcessID中指定的进程的所有的堆。
>TH32CS_SNAPMODULE(0x00000008) - 在快照中包含在th32ProcessID中指定的进程的所有的模块。
>TH32CS_SNAPPROCESS(0x00000002) - 在快照中包含系统中所有的进程。
>TH32CS_SNAPTHREAD(0x00000004) - 在快照中包含系统中所有的线程
>H32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)

th32ProcessID

c++
1
>指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。

Module32First

此函数检索与进程相关联的第一个模块的信息

c++
1
2
3
4
>BOOL WINAPI Module32First(
>HANDLE hSnapshot,
>LPMODULEENTRY32 lpme
>);

参数

hSnapshot

调用CreateToolhelp32Snapshot函数返回的快照句柄

lpmeMODULEENTRY32结构的指针。用来返回数据

返回值

成功返回TRUE失败返回FALSE

CloseHandle

关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。在CreateThread成功之后会返回一个hThreadhandle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。

c++
1
2
3
4
5
6
7
8
9
10
>方法名称:CloseHandle
>位置:Kernel32.dll
>BOOL CloseHandle(
>HANDLE hObject
>);
>参数
>hObject :代表一个已打开对象handle。
>返回值
>TRUE:执行成功;
>FALSE:执行失败,可以调用GetLastError()获知失败原因。

返回值

Long,非零表示成功,零表示失败。会设置GetLastError

参数表

参数 类型及说明

hObject Long,欲关闭的一个对象的句柄

注解

除非对内核对象的所有引用都已关闭,否则该对象不会实际删除

Module32Next

此函数检索有关与进程或线程关联的下一个模块的信息。

c++
1
2
3
4
>BOOL WINAPI Module32Next( 
>HANDLE hSnapshot,
>LPMODULEENTRY32 lpme
>);

参数

  • hSnapshot
    [in]处理上一次调用CreateToolhelp32Snapshot函数所返回的快照。
  • lpme
    [out]指向MODULEENTRY32结构的指针。

返回值

TRUE表示模块列表的下一个条目已复制到缓冲区。FALSE表示失败。GetLastError返回的ERROR_NO_MORE_FILES错误值指示没有更多的模块。

1


代码

一开始我是用Python3写的,但是当代码执行到Module32First时候,就会发生报错

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def GetProcessImageBase(ProcessId,moduleName):
pProcessImageBase = 0
hModuleSnap = c_void_p(0)
me32 = MODULEENTRY32()
me32.dwSize = sizeof(MODULEENTRY32)
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId)

ret = Module32First(hModuleSnap, pointer(me32))
print("ret: %d" %ret)
if GetLastError() != 0:
print("hModuleSnap: %d" %hModuleSnap)
CloseHandle(hModuleSnap)
print("错误代码:%d" % GetLastError())
return 'Error'

通过搜索引擎搜索了很久,也没解决,ret执行过后变成了0,但是 hModuleSnap不是空值,me32也没问题,到底是为什么我也不清楚了。

后面,我改成Python2来执行这行代码,竟然发现没有报错,惊了。

1.导入库

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding:utf-8 -*-
"""
@author:
@file: GetBaseAddr.py
@time: 2020-05-13 21:07
@desc: KeyboArd
"""
import win32process
import win32api#调用系统模块
import ctypes#C语言类型
from win32gui import FindWindow#界面
import operator
from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p, Structure, windll, sizeof , POINTER , pointer
from ctypes import wintypes as wt

2.定义结构体

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收进程终止状态
('PebBaseAddress', ctypes.c_ulonglong), # 接收进程环境块地址
('AffinityMask', ctypes.c_ulonglong), # 接收进程关联掩码
('BasePriority', ctypes.c_ulonglong), # 接收进程的优先级类
('UniqueProcessId', ctypes.c_ulonglong), # 接收进程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父进程ID

class MODULEENTRY32(Structure):
_fields_ = [ ( 'dwSize' , c_long ) ,
( 'th32ModuleID' , c_long ),
( 'th32ProcessID' , c_long ),
( 'GlblcntUsage' , c_long ),
( 'ProccntUsage' , c_long ) ,
( 'modBaseAddr' , c_long ) ,
( 'modBaseSize' , c_long ) ,
( 'hModule' , c_void_p ) ,
( 'szModule' , c_char * 256 ),
( 'szExePath' , c_char * 260 ) ]

3.声明全局变量

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")#声明kernel32
GetLastError = kernel32.GetLastError#GetLastError
dwOwnObj = 0xD2FB94#人物基址
dwEntityList = 0x4D43AC4#对象基址
dwGlowObjectManager = 0x528B8B0#发光基址
m_iGlowIndex = 0xA428# 发光
m_iTeamNum = 0xF4#阵营 偏移
m_Hp = 0x100#血量 偏移
TH32CS_SNAPMODULE = 0x00000008#CreateToolhelp32Snapshot 用到的参数
TH32CS_SNAPPROCESS = 0x00000002#CreateToolhelp32Snapshot 用到的参数
TH32CS_SNAPHEAPLIST = 0x00000001#CreateToolhelp32Snapshot 用到的参数
TH32CS_SNAPTHREAD = 0x00000004#CreateToolhelp32Snapshot 用到的参数
STANDARD_RIGHTS_REQUIRED = 0x000F0000#PROCESS_ALL_ACCESS用到的参数
SYNCHRONIZE = 0x00100000#PROCESS_ALL_ACCESS用到的参数
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)#OpenProcess用到的参数

# forigen function
## CreateToolhelp32Snapshot
CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.reltype = c_long
CreateToolhelp32Snapshot.argtypes = [ c_int , c_int ]
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## GetPriorityClass
GetPriorityClass = windll.kernel32.GetPriorityClass
GetPriorityClass.argtypes = [c_void_p]
GetPriorityClass.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
## Module32First
Module32First = windll.kernel32.Module32First
Module32First.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ]
Module32First.rettype = c_int
## Module32Next
Module32Next = windll.kernel32.Module32Next
Module32Next.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ]
Module32Next.rettype = c_int
## GetLastError
GetLastError = windll.kernel32.GetLastError
GetLastError.rettype = c_long

4.代码编写

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def GetProcessImageBase(ProcessId,moduleName):
pProcessImageBase = 0
hModuleSnap = c_void_p(0)
me32 = MODULEENTRY32()#声明结构体变量
me32.dwSize = sizeof(MODULEENTRY32)#先初始化大小
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId)#获取进程信息为指定的进程
ret = Module32First(hModuleSnap, pointer(me32))
print("ret: %d" %ret)
if GetLastError() != 0:
print("hModuleSnap: %d" %hModuleSnap)
CloseHandle(hModuleSnap)
print("错误代码:%d" % GetLastError())
return 'Error'
else:
if (Module32First(hModuleSnap,pointer(me32))):
if me32.szModule == moduleName:#如果进程模块名与要找的模块名相等
# print(me32.modBaseAddr)
CloseHandle(hModuleSnap)
return me32.modBaseAddr#就返回模块基址
else:
Module32Next(hModuleSnap,pointer(me32))#如果没有就在找下一个模块
while int(GetLastError())!=18:#返回值18意思:(18)- 没有更多文件。
if me32.szModule == moduleName:
# print(me32.modBaseAddr)
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap, pointer(me32))
CloseHandle(hModuleSnap)
print('找不到模块名为: %s' % moduleName)
else:
print('Module32First 错误,返回: %s' % GetLastError())
CloseHandle(hModuleSnap)

5.调用

python
1
2
3
4
5
6
7
8
9
10
def main():
ProcessId = _GetProcessId("Valve001", u"Counter**********")

_hGameHandle = _GetPorcessHandle(ProcessId)

moduleName = GetProcessImageBase(ProcessId, "client_*****.dll")
print("0x%08X" %moduleName)

if __name__ == '__main__':
main()

6.完整代码

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# -*- coding:utf-8 -*-
"""
@author:
@file: GetBaseAddr.py
@time: 2020-05-13 21:07
@desc: KeyboArd
@Version: Python2.7
"""
import win32process
import win32api#调用系统模块
import ctypes#C语言类型
from win32gui import FindWindow#界面
import operator
from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p, Structure, windll, sizeof , POINTER , pointer
from ctypes import wintypes as wt

kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")
GetLastError = kernel32.GetLastError

TH32CS_SNAPPROCESS = 0x00000002
dwOwnObj = 0xD2FB94
dwEntityList = 0x4D43AC4
dwGlowObjectManager = 0x528B8B0
m_iGlowIndex = 0xA428
m_iTeamNum = 0xF4
m_Hp = 0x100
TH32CS_SNAPMODULE = 0x00000008
STANDARD_RIGHTS_REQUIRED = 0x000F0000
SYNCHRONIZE = 0x00100000
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)

class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收进程终止状态
('PebBaseAddress', ctypes.c_ulonglong), # 接收进程环境块地址
('AffinityMask', ctypes.c_ulonglong), # 接收进程关联掩码
('BasePriority', ctypes.c_ulonglong), # 接收进程的优先级类
('UniqueProcessId', ctypes.c_ulonglong), # 接收进程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父进程ID


class MODULEENTRY32(Structure):
_fields_ = [ ( 'dwSize' , c_long ) ,
( 'th32ModuleID' , c_long ),
( 'th32ProcessID' , c_long ),
( 'GlblcntUsage' , c_long ),
( 'ProccntUsage' , c_long ) ,
( 'modBaseAddr' , c_long ) ,
( 'modBaseSize' , c_long ) ,
( 'hModule' , c_void_p ) ,
( 'szModule' , c_char * 256 ),
( 'szExePath' , c_char * 260 ) ]


## CreateToolhelp32Snapshot
CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.reltype = c_long
CreateToolhelp32Snapshot.argtypes = [c_int, c_int]
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## GetPriorityClass
GetPriorityClass = windll.kernel32.GetPriorityClass
GetPriorityClass.argtypes = [c_void_p]
GetPriorityClass.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
## Module32First
Module32First = windll.kernel32.Module32First
Module32First.argtypes = [c_void_p , POINTER(MODULEENTRY32)]
Module32First.rettype = c_int
## Module32Next
Module32Next = windll.kernel32.Module32Next
Module32Next.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ]
Module32Next.rettype = c_int
## GetLastError
GetLastError = windll.kernel32.GetLastError
GetLastError.rettype = c_long

def _GetProcessId(className,windowName):
hGameWindow = FindWindow(className, windowName)
pid = win32process.GetWindowThreadProcessId(hGameWindow)[1]
return pid

def _GetPorcessHandle(pid):
hGameHandle = win32api.OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
return hGameHandle

def GetProcessImageBase(ProcessId, moduleName):
pProcessImageBase = 0
hModuleSnap = c_void_p(0)
me32 = MODULEENTRY32()
me32.dwSize = sizeof(MODULEENTRY32)
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId)

ret = Module32First(hModuleSnap, pointer(me32))
# print(ret)
if GetLastError() != 0:
CloseHandle(hModuleSnap)
print('Handle Error %s' % GetLastError())
return 'Error'
else:
if (Module32First(hModuleSnap,pointer(me32))):
if me32.szModule == moduleName:
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap,pointer(me32))
while int(GetLastError())!=18:
if me32.szModule == moduleName:
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap, pointer(me32))
CloseHandle(hModuleSnap)
print('Couldn\'t find Process with name %s' % moduleName)
else:
print('Module32First is False %s' % GetLastError())
CloseHandle(hModuleSnap)

def main():

ProcessId = _GetProcessId("Valve001", u"Counter-********")
# print(ProcessId)
_hGameHandle = _GetPorcessHandle(ProcessId)

moduleName = GetProcessImageBase(ProcessId, "client_******.dll")
print("0x%08X" % moduleName)


if __name__ == '__main__':
main()

Python32 位 模块遍历完整代码

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from win32gui import FindWindow  #获取窗口句柄
from win32api import OpenProcess, CloseHandle #创建进程句柄与关闭进程句柄
from win32con import PROCESS_VM_READ, PROCESS_VM_WRITE,PROCESS_QUERY_INFORMATION #win32con里面放的是一些pywin32中的常量,我们导入我们需要的进程权限
from win32process import GetWindowThreadProcessId #通过窗口句柄获取进程ID
from ctypes import WinDLL, c_ulonglong, byref,c_wchar_p,c_ulong,Structure
"""
WinDLL这个函数是导入系统的动态链接库,因为pywin32中并没有直接可以对内存进行读取和写入操作的函数,对于32位的程序,我们可以用kernel32.dll中的readmemor这些,
但是到了64位程序我们发现没有用了,这也是主要难点,所以需要通过ntdll.dll中的某些未公开的api来达到小青龙们不可告人的秘密
64位地址的长度是8字节,python中调用api时默认的int只有4个字节,所以用ctypes中的c_ulonglong类型来存放我们的数据,避免精度的丢失
byref python本身是没有指针的概念的,所以我们需要通过这个参数来传递指针
c_wchar_p 创建一个wchar类型的指针变量
c_ulong 参考c_ulonglong
Structure 在python中进行结构体的定义的话,需要继承这个类
"""

class PROCESS_BASIC_INFORMATION(Structure):
"""
这里我们通过python定义一个C中的结构体,这个结构体是用来存放我们在下面的函数中获取的进程相关信息的
结构体中每个参数中存放的内容见如下备注

"""
_fields_ = [('ExitStatus',c_ulonglong),#接收进程终止状态
('PebBaseAddress', c_ulonglong),#接收进程环境块地址
('AffinityMask', c_ulonglong),#接收进程关联掩码
('BasePriority', c_ulonglong),#接收进程的优先级类
('UniqueProcessId', c_ulonglong),#接收进程ID
('InheritedFromUniqueProcessId', c_ulonglong)]#接收父进程ID


class Memory64():
"""定义一个类,方便我们的调用"""
def __init__(self, hwnd):#这里我们在类创建的时候,接受一个窗口句柄,然后通过窗口句柄让类自己去创建相应的进程
self.ntdll = WinDLL("ntdll.dll")
pid = GetWindowThreadProcessId(hwnd)
self.hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_WRITE, False, pid[1])
""" 这个函数有三个参数,第一个参数是你需要的权限,我不是很喜欢这种直接获取全部权限,因为有的时候权限太高并不是好事情
容易被检测GG,当然这里是单机无所谓可以随便来
第二个参数是是否继承,一般都是选False,第三个参数是线程的ID,python里一定要记得取第1位的值,也就是第二个值,因为python里
GetWindowThreadProcessId这个函数返回的值有两个"""

def CloseHandle(self):#一定要记得释放资源
CloseHandle(self.hProcess)#因为这里还要调用原始的hProcess类型去关闭进程句柄,所以我们没有一开始对self.hProcess直接转换成int

def __del__(self): # 这里在类被清除时,尝试释放资源,防止忘记释放资源而引起不必要的占用
try:
self.CloseHandle(self.hProcess)
except:
pass



def ReadVirtualMemory64(self,addr, n=8):
# 这里定义一个函数来读取,传入三个参数,第一个是进程句柄,第二个是我们要读取的地址,我们可以默认为8,可以偷懒,第三个是要读取的长度

addr = c_ulonglong(addr)
ret = c_ulonglong()
BufferLength = c_ulonglong(n)

self.ntdll.NtWow64ReadVirtualMemory64(int(self.hProcess), addr, byref(ret), BufferLength, 0)
"""
这个函数并不是一个公开的API,找了很多文献才研究出来怎么用python去调用它,他一共有五个参数
第一个参数是我们通过OpenProcess获取的进程句柄,在python中要记得把这个句柄转换成int类型,默认其实是个句柄类型,不
转换会出错,
第二个参数其实就是我们要读取的地址,我们辛苦找到的基址和便宜终于有了用武之地
第三个参数是一个指针,我们通过ctypes中的byref方法可以将一个指针传进去,函数会把读取到的参数放进这个指针指向的地方,
在这里也就是我们的ret中
第四个参数是我们需要读取的长度
第五个参数也是一个指针,存放实际读取的长度,需要的话可以传一个参数,这里我偷懒填的0
"""

return ret.value # c_ulonglong的类型中,他的数值是放在他的属性value中的,所以返回的时候我们只需要获取value中存放的数值就好了

def ReadProcessMemory64_Wchar(self, addr, n,length):#这个函数用以读取模块名称,与ReadVirtualMemory64不同的点还有一个是我们会传入一个缓冲区的长度length,用于定义一个c_wchar_p的缓冲区
addr = c_ulonglong(addr)
ret = c_wchar_p("0" * length)#这里选用wchar其实与编码有关,感兴趣的同学自行百度wchar,宽字符等关键字学习
BufferLength = c_ulonglong(n)
self.ntdll.NtWow64ReadVirtualMemory64(int(self.hProcess), addr, ret, BufferLength, 0)
return ret.value


def WriteVirtualMemory64(self,addr, s, n=8): # 这个函数与读取的其实是一样的,区别只是一个是读一个写,不作介绍了,参考读取的函数,s参数是我们要写入的数据

addr = c_ulonglong(addr)
ret = c_ulonglong(s)
BufferLength = c_ulonglong(n)
self.ntdll.NtWow64WriteVirtualMemory64(int(self.hProcess), addr, byref(ret), BufferLength, 0)

def GetBaseAddr(self, ModuleName):#传入需要查找的模块的名称,就可以返回相应的模块基址了
NumberOfBytesRead = c_ulong()
Buffer = PROCESS_BASIC_INFORMATION()
Size = c_ulong(48)
name_len = len(ModuleName)

self.ntdll.NtWow64QueryInformationProcess64(int(self.hProcess), 0, byref(Buffer), Size,
byref(NumberOfBytesRead))
"""
这同样是一个未公开的api,可以通过他获取进程的信息,然后存入我们一开始定义的结构体中,他的五个参数分别是:
进程句柄,信息类型,缓冲指针,以字节为单位的缓冲大小, 写入缓冲的字节数
而至于下面为什么要这么写,其实涉及到了程序的PE结构,这里不做赘述,因为这个东西不是一会会说的清楚的,可以自行百度
"""
ret = self.ReadVirtualMemory64(Buffer.PebBaseAddress + 24, 8)
ret = self.ReadVirtualMemory64(ret + 24, 8)

for i in range(100000):#这里用for循环其实是怕程序卡死,下面如果出了问题不能退出的话,循环结束一样可以退出
modulehandle = self.ReadVirtualMemory64(ret + 48, 8)
if modulehandle == 0:
break
nameaddr = self.ReadVirtualMemory64(ret + 96, 8)
name = self.ReadProcessMemory64_Wchar(nameaddr, name_len * 2 + 1, name_len)
if name == ModuleName:
return modulehandle
ret = self.ReadVirtualMemory64(ret + 8, 8)


if __name__ == '__main__':
hwnd = FindWindow("KGWin32App", None) # 获取窗口句柄
m = Memory64(hwnd)#我们创建一个内存的操作类

ModuleBaseAddr = m.GetBaseAddr("JX3RepresentX64.dll")
print("ModuleBaseAddr",ModuleBaseAddr)
"""
然后通过调用我们定义的GetBaseAddr的方法来获取模块的基址,这里需要注意,我们要区分大小写
也可以在修改GetBaseAddr方法,在进行比名称的对比之前,对他们进行强行转码,全部转成大写或者小写,从而忽略大小写的问题
获取到模块基址过后,我们就可以通过我们刚刚找到的基址与偏离开始对游戏的数据进行读取了
"""
addr = m.ReadVirtualMemory64(ModuleBaseAddr+0x509E00) #addr = [jx3representx64 + 0x509E00]
addr = m.ReadVirtualMemory64(addr + 0x1AA28 + 0x10)#addr = [addr + 0x1AA28 + 0x10]
addr = m.ReadVirtualMemory64(addr)#addr = [addr]
addr = m.ReadVirtualMemory64(addr) # addr = [addr]
addr = m.ReadVirtualMemory64(addr+0x65D0) #addr = [addr+0x65D0]
addr = addr + 0x32D0 + 0x00000434 +0x8 #这个时候我们的addr其实已经是存放x坐标的地址了,我们可以在下面进行测试

ret = m.ReadVirtualMemory64(addr,4)#这里记得只读取四个字节,多了的话读取到的数据肯定就是错误的了
print("x坐标",ret)

来源:python制作游戏脚本之网游寻址及64位程序的模块遍历(视频已安排)

如果不能转载的话,评论一下我就删了,我看原贴好像没有禁止转载。


后续

直到后面研究才发现,是Python64位和32位的问题,所以不是代码问题,是版本问题,那就没办法了。 卸载64位,安装32位。

Python3 获取指定模块基址代码

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding:utf-8 -*-
"""
@author:
@file: GetBaseAddr.py
@time: 2020-05-13 21:07
@desc: KeyboArd
"""
import win32process
import win32api#调用系统模块
import ctypes#C语言类型
from win32gui import FindWindow#界面
import operator
from ctypes import c_long , c_int , c_uint , c_char ,c_ulong, c_ubyte , c_char_p , c_void_p, Structure, windll, sizeof , POINTER , pointer
from ctypes import wintypes as wt

kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")
GetLastError = kernel32.GetLastError

TH32CS_SNAPPROCESS = 0x00000002
dwOwnObj = 0xD2FB94
dwEntityList = 0x4D43AC4
dwGlowObjectManager = 0x528B8B0
m_iGlowIndex = 0xA428
m_iTeamNum = 0xF4
m_Hp = 0x100
TH32CS_SNAPMODULE = 0x00000008
STANDARD_RIGHTS_REQUIRED = 0x000F0000
SYNCHRONIZE = 0x00100000
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)

class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收进程终止状态
('PebBaseAddress', ctypes.c_ulonglong), # 接收进程环境块地址
('AffinityMask', ctypes.c_ulonglong), # 接收进程关联掩码
('BasePriority', ctypes.c_ulonglong), # 接收进程的优先级类
('UniqueProcessId', ctypes.c_ulonglong), # 接收进程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父进程ID

class PROCESSENTRY32(Structure):
_fields_ = [ ( 'dwSize' , c_ulong ) ,
( 'cntUsage' , c_ulong) ,
( 'th32ProcessID' , c_ulong) ,
( 'th32DefaultHeapID' , c_void_p) ,
( 'th32ModuleID' , c_ulong) ,
( 'cntThreads' , c_ulong) ,
( 'th32ParentProcessID' , c_ulong) ,
( 'pcPriClassBase' , c_ulong) ,
( 'dwFlags' , c_ulong) ,
( 'szExeFile' , c_char * 260 ) ,
( 'th32MemoryBase' , c_long) ,
( 'th32AccessKey' , c_long ) ]

class MODULEENTRY32(Structure):
_fields_ = [ ( 'dwSize' , c_long ) ,
( 'th32ModuleID' , c_long ),
( 'th32ProcessID' , c_long ),
( 'GlblcntUsage' , c_long ),
( 'ProccntUsage' , c_long ) ,
( 'modBaseAddr' , c_long ) ,
( 'modBaseSize' , c_long ) ,
( 'hModule' , c_void_p ) ,
( 'szModule' , c_char * 256 ),
( 'szExePath' , c_char * 260 ) ]

## Process32First
Process32First = windll.kernel32.Process32First
Process32First.argtypes = [ c_void_p , POINTER( PROCESSENTRY32 ) ]
Process32First.rettype = c_int
## Process32Next
Process32Next = windll.kernel32.Process32Next
Process32Next.argtypes = [ c_void_p , POINTER(PROCESSENTRY32) ]
Process32Next.rettype = c_int
## CreateToolhelp32Snapshot
CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.reltype = c_long
CreateToolhelp32Snapshot.argtypes = [c_int, c_int]
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## GetPriorityClass
GetPriorityClass = windll.kernel32.GetPriorityClass
GetPriorityClass.argtypes = [c_void_p]
GetPriorityClass.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
## Module32First
Module32First = windll.kernel32.Module32First
Module32First.argtypes = [c_void_p , POINTER(MODULEENTRY32)]
Module32First.rettype = c_int
## Module32Next
Module32Next = windll.kernel32.Module32Next
Module32Next.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ]
Module32Next.rettype = c_int
## GetLastError
GetLastError = windll.kernel32.GetLastError
GetLastError.rettype = c_long

def _GetProcessId(className,windowName):
hGameWindow = FindWindow(className, windowName)
pid = win32process.GetWindowThreadProcessId(hGameWindow)[1]
return pid

def _GetPorcessHandle(pid):
hGameHandle = win32api.OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
return hGameHandle

def GetProcessImageBase(ProcessId, moduleName):
#moduleName = "client_panorama.dll"
pProcessImageBase = 0
hModuleSnap = c_void_p(0)
me32 = MODULEENTRY32()
me32.dwSize = sizeof(MODULEENTRY32)
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId)

ret = Module32First(hModuleSnap, ctypes.byref(me32))

print("ret: %d" %ret)
if GetLastError() != 0:
print("hModuleSnap: %d" % hModuleSnap)
CloseHandle(hModuleSnap)
print('Handle Error %s' % GetLastError())
return 'Error'
else:
if (Module32First(hModuleSnap,pointer(me32))):
if me32.szModule.decode() == moduleName:
#这里因为是Python3,输出字符会在前面出现一个b'xxx',所以要先使用decode解码
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap,pointer(me32))
while int(GetLastError())!=18:
if me32.szModule.decode() == moduleName:
#这里因为是Python3,输出字符会在前面出现一个b'xxx',所以要先使用decode解码
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap, pointer(me32))
CloseHandle(hModuleSnap)
print('Couldn\'t find Process with name %s' % moduleName)
else:
print('Module32First is False %s' % GetLastError())
CloseHandle(hModuleSnap)

def main():

ProcessId = _GetProcessId("Valve001", u"Counter-*****")

_hGameHandle = _GetPorcessHandle(ProcessId)

moduleName = GetProcessImageBase(ProcessId, "client_*****.dll")
print("0x%X" % moduleName)

if __name__ == '__main__':
main()

Python2 获取指定模块基址

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# -*- coding:utf-8 -*-
"""
@author:
@file: GetBaseAddr.py
@time: 2020-05-13 21:07
@desc: KeyboArd
@Version: Python2.7
"""
import win32process
import win32api#调用系统模块
import ctypes#C语言类型
from win32gui import FindWindow#界面
import operator
from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p, Structure, windll, sizeof , POINTER , pointer
from ctypes import wintypes as wt

kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")
GetLastError = kernel32.GetLastError

TH32CS_SNAPPROCESS = 0x00000002
dwOwnObj = 0xD2FB94
dwEntityList = 0x4D43AC4
dwGlowObjectManager = 0x528B8B0
m_iGlowIndex = 0xA428
m_iTeamNum = 0xF4
m_Hp = 0x100
TH32CS_SNAPMODULE = 0x00000008
STANDARD_RIGHTS_REQUIRED = 0x000F0000
SYNCHRONIZE = 0x00100000
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)

class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收进程终止状态
('PebBaseAddress', ctypes.c_ulonglong), # 接收进程环境块地址
('AffinityMask', ctypes.c_ulonglong), # 接收进程关联掩码
('BasePriority', ctypes.c_ulonglong), # 接收进程的优先级类
('UniqueProcessId', ctypes.c_ulonglong), # 接收进程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父进程ID


class MODULEENTRY32(Structure):
_fields_ = [ ( 'dwSize' , c_long ) ,
( 'th32ModuleID' , c_long ),
( 'th32ProcessID' , c_long ),
( 'GlblcntUsage' , c_long ),
( 'ProccntUsage' , c_long ) ,
( 'modBaseAddr' , c_long ) ,
( 'modBaseSize' , c_long ) ,
( 'hModule' , c_void_p ) ,
( 'szModule' , c_char * 256 ),
( 'szExePath' , c_char * 260 ) ]


## CreateToolhelp32Snapshot
CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.reltype = c_long
CreateToolhelp32Snapshot.argtypes = [c_int, c_int]
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## GetPriorityClass
GetPriorityClass = windll.kernel32.GetPriorityClass
GetPriorityClass.argtypes = [c_void_p]
GetPriorityClass.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
## Module32First
Module32First = windll.kernel32.Module32First
Module32First.argtypes = [c_void_p , POINTER(MODULEENTRY32)]
Module32First.rettype = c_int
## Module32Next
Module32Next = windll.kernel32.Module32Next
Module32Next.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ]
Module32Next.rettype = c_int
## GetLastError
GetLastError = windll.kernel32.GetLastError
GetLastError.rettype = c_long

def _GetProcessId(className,windowName):
hGameWindow = FindWindow(className, windowName)
pid = win32process.GetWindowThreadProcessId(hGameWindow)[1]
return pid

def _GetPorcessHandle(pid):
hGameHandle = win32api.OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
return hGameHandle

def GetProcessImageBase(ProcessId, moduleName):
pProcessImageBase = 0
hModuleSnap = c_void_p(0)
me32 = MODULEENTRY32()
me32.dwSize = sizeof(MODULEENTRY32)
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId)

ret = Module32First(hModuleSnap, pointer(me32))
# print(ret)
if GetLastError() != 0:
CloseHandle(hModuleSnap)
print('Handle Error %s' % GetLastError())
return 'Error'
else:
if (Module32First(hModuleSnap,pointer(me32))):
if me32.szModule == moduleName:
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap,pointer(me32))
while int(GetLastError())!=18:
if me32.szModule == moduleName:
CloseHandle(hModuleSnap)
return me32.modBaseAddr
else:
Module32Next(hModuleSnap, pointer(me32))
CloseHandle(hModuleSnap)
print('Couldn\'t find Process with name %s' % moduleName)
else:
print('Module32First is False %s' % GetLastError())
CloseHandle(hModuleSnap)

def main():

ProcessId = _GetProcessId("Valve001", u"Counter-********")
# print(ProcessId)
_hGameHandle = _GetPorcessHandle(ProcessId)

moduleName = GetProcessImageBase(ProcessId, "client_******.dll")
print("0x%X" % moduleName)


if __name__ == '__main__':
main()

结语

借用一句看到很不错的话:

技术不分对错.人性才分善恶.

学习逆向的人必须身心放正.

身心放正之人手握屠龙刀,也是保家卫民.

文章作者: KeyboArd
文章链接: https://www.wrpzkb.cn/pythonGetBaseAddr/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 KeyboArd's Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论