元旦闲着没事干,就找点有用的库存发一发吧。
如果你会使用CE之类的内存修改器,找到了数据地址又想通过ahk实现读写操作,那么这个库可以帮到你。
仅供学习,请勿用于非法用途。
下面的示例使用CE自带的教程, 注意需要以管理员权限运行代码。
读取,修改内存
首先需要打开你要读写的进程:
对应的代码是:
ps := RemoteProcess.FromProcessName("Tutorial-x86_64.exe") ; 通过进程名打开
; ps := RemoteProcess.FromWindow("步骤 2 ahk_exe Tutorial-x86_64.exe") ; 通过窗口打开
; ps := RemoteProcess.FromProcessId(0x000029F4) ; 通过pid打开
找到健康值的地址:
使用RemoteProcess可以这样读取:
health := ps.ReadNumber(0x00131590, "int") ; int对应4字节
过关条件是把健康调整为1000,可以这样写:
ps.WriteNumber(0x00131590, 1000, "int")
运行上面的代码后就能发现数值变成1000了:
其他数据类型也是一样的,仅仅就是换个参数,如单浮点用float表示,双浮点用double表示:
ps.WriteNumber(0x0013BF78, 5000, "float")
ps.WriteNumber(0x0013BF80, 6000, "double")
多级指针
在CE教程的第8关中,需要用到基址和多级指针,如果你按照CE教程走,可以得到这样的结果:
读取最终的数据,使用RemoteProcess需要这么做:
ps := RemoteProcess.FromProcessName("Tutorial-x86_64.exe") ; 通过进程名打开
baseAddress := ps.GetModuleAddressByName("Tutorial-x86_64.exe") + 0x00306B00 ; 通过模块名获取基址
ptr := ps.TracePointer(baseAddress, 0x10, 0x18, 0, 0x18) ; 通过基址+偏移获取目标地址
health := ps.ReadNumber(ptr, "int")
有的时候基址是这样的:”THREADSTACK0″ – 000003C0, “THREADSTACK1″ + 00000210, 这时需要使用GetThreadStackAddress方法; 如果是”.data”这种段地址,用GetSectionAddressByName; 如果是函数地址,用GetFunctionAddressByName:
baseAddress := ps.GetThreadStackAddress(0) - 0x000003C0
baseAddress := ps.GetThreadStackAddress(1) + 0x00000210
baseAddress := ps.GetSectionAddressByName("User32.dll", ".data")
baseAddress := ps.GetFunctionAddressByName("User32.dll", "MessageBoxW")
掌握以上几个方法基本就差不多了,下面放库, 为了方便运行,建议把下面两个库都放到lib文件夹
RemoteProcess.ahk
/* @Version 0.2 */
#Requires AutoHotkey v2.0.0 64-bit
#Include <ToolHelp>
CONST := CONST ?? {}
CONST.PAGE_NOACCESS := 0x01,
CONST.PAGE_READONLY := 0x02,
CONST.PAGE_READWRITE := 0x04,
CONST.PAGE_WRITECOPY := 0x08,
CONST.PAGE_EXECUTE := 0x10,
CONST.PAGE_EXECUTE_READ := 0x20,
CONST.PAGE_EXECUTE_READWRITE := 0x40,
CONST.PAGE_EXECUTE_WRITECOPY := 0x80,
CONST.PAGE_GUARD := 0x100,
CONST.PAGE_NOCACHE := 0x200,
CONST.PAGE_WRITECOMBINE := 0x400,
CONST.PAGE_GRAPHICS_NOACCESS := 0x0800,
CONST.PAGE_GRAPHICS_READONLY := 0x1000,
CONST.PAGE_GRAPHICS_READWRITE := 0x2000,
CONST.PAGE_GRAPHICS_EXECUTE := 0x4000,
CONST.PAGE_GRAPHICS_EXECUTE_READ := 0x8000,
CONST.PAGE_GRAPHICS_EXECUTE_READWRITE := 0x10000,
CONST.PAGE_GRAPHICS_COHERENT := 0x20000,
CONST.PAGE_GRAPHICS_NOCACHE := 0x40000,
CONST.PAGE_ENCLAVE_THREAD_CONTROL := 0x80000000,
CONST.PAGE_REVERT_TO_FILE_MAP := 0x80000000,
CONST.PAGE_TARGETS_NO_UPDATE := 0x40000000,
CONST.PAGE_TARGETS_INVALID := 0x40000000,
CONST.PAGE_ENCLAVE_UNVALIDATED := 0x20000000,
CONST.PAGE_ENCLAVE_MASK := 0x10000000,
CONST.PAGE_ENCLAVE_DECOMMIT := (CONST.PAGE_ENCLAVE_MASK | 0),
CONST.PAGE_ENCLAVE_SS_FIRST := (CONST.PAGE_ENCLAVE_MASK | 1),
CONST.PAGE_ENCLAVE_SS_REST := (CONST.PAGE_ENCLAVE_MASK | 2)
CONST.MEM_COMMIT := 0x00001000,
CONST.MEM_RESERVE := 0x00002000,
CONST.MEM_REPLACE_PLACEHOLDER := 0x00004000,
CONST.MEM_RESERVE_PLACEHOLDER := 0x00040000,
CONST.MEM_RESET := 0x00080000,
CONST.MEM_TOP_DOWN := 0x00100000,
CONST.MEM_WRITE_WATCH := 0x00200000,
CONST.MEM_PHYSICAL := 0x00400000,
CONST.MEM_ROTATE := 0x00800000,
CONST.MEM_DIFFERENT_IMAGE_BASE_OK := 0x00800000,
CONST.MEM_RESET_UNDO := 0x01000000,
CONST.MEM_LARGE_PAGES := 0x20000000,
CONST.MEM_4MB_PAGES := 0x80000000,
CONST.MEM_64K_PAGES := (CONST.MEM_LARGE_PAGES | CONST.MEM_PHYSICAL),
CONST.MEM_UNMAP_WITH_TRANSIENT_BOOST := 0x00000001,
CONST.MEM_COALESCE_PLACEHOLDERS := 0x00000001,
CONST.MEM_PRESERVE_PLACEHOLDER := 0x00000002,
CONST.MEM_DECOMMIT := 0x00004000,
CONST.MEM_RELEASE := 0x00008000,
CONST.MEM_FREE := 0x00010000
/*
@Example Open a process
; You can open a process by process name, hwnd or pid
ps := RemoteProcess.FromProcessName("explorer.exe")
ps := RemoteProcess.FromWindow(WinExist("ahk_class Shell_TrayWnd"))
ps := RemoteProcess.FromProcessId(WinGetPID("Program Manager"))
@Example Converts the base address described by module name, segment name, function name, or THREADSTACKXX to a numeric address
ps := RemoteProcess.FromWindow(WinExist("ahk_class Notepad"))
MsgBox '"Kernel.dll": ' Format("{:#016x}", ps.GetModuleAddressByName("Kernel32.dll"))
MsgBox '"User32.dll.data": ' Format("{:#016x}", ps.GetSectionAddressByName("User32.dll", ".data"))
MsgBox '"User32.MessageBoxW": ' Format("{:#016x}", ps.GetFunctionAddressByName("User32.dll", "MessageBoxW"))
MsgBox '"THREADSTACK0": ' Format("{:#016x}", ps.GetThreadStackAddress(0))
@Example Find the address of the data we need via base address and offsets, then read & write number from it
ps := RemoteProcess.FromProcessName("TextInputHost.exe")
; In this case, the base address is "THREADSTACK0"-00000750 in CE
ptr := ps.TracePointer(ps.GetThreadStackAddress(0) - 0x750, 0xC8, 0xC0, 0x300, 0x58, 0x8, 0xB0, 0, 0x1C0, 0x48, 0x18, 0x40, 0xF0)
; Read and Write a 32 bit integer
MsgBox ps.ReadNumber(ptr, "int")
ps.WriteNumber(ptr, 0, "int")
MsgBox ps.ReadNumber(ptr, "int")
@Example Read text from Notepad
ps := RemoteProcess.FromWindow(WinExist("ahk_class Notepad"))
ptr := ps.GetModuleAddressByName("textinputframework.dll") + 0xE83C0
MsgBox ps.ReadWString(ptr)
@Example Call a function
MsgBox RunWait("Notepad",,, &pid)
f1::{
ps := RemoteProcess.FromProcessId(pid)
pExitProcess := ps.GetFunctionAddressByName("Kernel32.dll", "ExitProcess")
ps.CreateThread(pExitProcess, 123)
}
*/
class RemoteProcess {
static __TypeSize := {Char: 1, UChar: 1, Short: 2, UShort: 2, Int: 4, UInt: 4, Ptr: A_PtrSize, UPtr: A_PtrSize, Int64: 8, UInt64: 8, Float: 4, Double: 8}
static FromProcessName(processname) {
if !pid := ProcessExist(processname)
throw Error("Cannot find the process.")
return this.FromProcessId(pid)
}
static FromWindow(winTitle) {
if !DllCall("GetWindowThreadProcessId", "ptr", WinExist(winTitle), "uint*", &processId := 0)
throw OSError()
return this.FromProcessId(processId)
}
static FromProcessId(processId) {
if !hProcess := DllCall("OpenProcess", "uint", 0x1fffff, "int", false, "uint", processId, "ptr")
throw OSError()
return this(hProcess)
}
__New(hProcess) {
this.Handle := hProcess
DllCall("IsWow64Process2", "ptr", hProcess, "ushort*", &isWow64 := 0, "ushort*", 0)
this.IsWow64 := isWow64
this.ProcessId := DllCall("GetProcessId", "ptr", hProcess, "uint")
}
__Delete() {
DllCall("CloseHandle", "ptr", this.Handle)
}
Alloc(bytes, allocationType := CONST.MEM_COMMIT, protectType := CONST.PAGE_EXECUTE_READWRITE) {
if !address := DllCall("VirtualAllocEx", "ptr", this.Handle, "ptr", 0, "ptr", bytes, "uint", allocationType, "uint", protectType, "ptr")
throw OSError()
return address
}
Free(address, bytes := 0, freeType := CONST.MEM_RELEASE) {
if !DllCall("VirtualFreeEx", "ptr", this.Handle, "ptr", address, "uptr", bytes, "uint", freeType)
throw OSError()
}
Protect(address, bytes, newProtectType) {
if !DllCall("VirtualProtectEx", "ptr", this.Handle, "ptr", address, "uptr", bytes, "uint", newProtectType, "uint*", &oldProtectType := 0)
throw OSError()
return oldProtectType
}
CreateThread(address, param, &threadId := 0, closeHanlde := true) {
if !hThread := DllCall("CreateRemoteThread", "ptr", this.Handle, "ptr", 0, "uptr", 0, "ptr", address, "ptr", param, "uint", 0, "uint*", &threadId, "ptr")
throw OSError()
if closeHanlde
return DllCall("CloseHandle", "ptr", hThread)
return hThread
}
ReadMemory(dest, src, bytes) {
if !DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", src, "ptr", dest, "uptr", bytes, "uptr*", &bytesRead := 0)
throw OSError()
return bytesRead
}
ReadBuffer(address, bytes) {
if !DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, "ptr", buf := Buffer(bytes), "uptr", bytes, "uptr*", &bytesRead := 0)
throw OSError()
if buf.Size != bytesRead
buf.Size := bytesRead
return buf
}
ReadNumber(address, type) {
if !DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, type "*", &num := 0, "uptr", RemoteProcess.__TypeSize.%type%, "ptr", 0)
throw OSError()
return num
}
ReadString(address, cch := 0, encoding := "cp0") {
offset := char := 0
if cch == 0 {
buf := Buffer(1024)
while DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, "char*", &char, "uptr", 2, "ptr", 0) && char != 0 {
NumPut("char", char, buf, offset)
++address
++offset
if offset == buf.Size
buf.Size += 1024
}
}
else {
buf := Buffer(cch)
while A_Index < cch && DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, "char*", &char, "uptr", 2, "ptr", 0) && char != 0 {
NumPut("char", char, buf, offset)
++address
++offset
}
}
NumPut("char", 0, buf, offset)
return StrGet(buf.Ptr, encoding)
}
ReadWString(address, cch := 0) {
offset := char := 0
if cch == 0 {
buf := Buffer(1024)
while DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, "ushort*", &char, "uptr", 2, "ptr", 0) && char != 0 {
NumPut("ushort", char, buf, offset)
address += 2
offset += 2
if offset == buf.Size
buf.Size += 1024
}
}
else {
buf := Buffer(cch * 2)
while A_Index < cch && DllCall("ReadProcessMemory", "ptr", this.Handle, "ptr", address, "short*", &char, "uptr", 2, "ptr", 0) && char != 0 {
NumPut("short", char, buf, offset)
address += 2
offset += 2
}
}
NumPut("ushort", 0, buf, offset)
return StrGet(buf.Ptr, "utf-16")
}
WriteMemory(dest, src, bytes) {
if !DllCall("WriteProcessMemory", "ptr", this.Handle, "ptr", dest, "ptr", src, "uptr", bytes, "ptr", 0)
throw OSError()
}
WriteNumber(dest, number, type) {
if !DllCall("WriteProcessMemory", "ptr", this.Handle, "ptr", dest, type "*", number, "uptr", RemoteProcess.__TypeSize.%type%, "ptr", 0)
throw OSError()
}
WriteString(dest, str, encoding := "cp0") {
if encoding = "cp0" || encoding = "" {
if !DllCall("WriteProcessMemory", "ptr", this.Handle, "ptr", dest, "astr", str, "uptr", StrPut(str, "cp0"), "ptr", 0)
throw OSError()
}
else {
buf := Buffer(StrPut(str, encoding))
StrPut(str, buf, encoding)
if !DllCall("WriteProcessMemory", "ptr", this.Handle, "ptr", dest, "ptr", buf, "uptr", buf.Size, "ptr", 0)
throw OSError()
}
}
WriteWString(dest, str) {
if !DllCall("WriteProcessMemory", "ptr", this.Handle, "ptr", dest, "wstr", str, "uptr", StrPut(str, "utf-16"), "ptr", 0)
throw OSError()
}
TracePointer(baseAddress, offsetArray*) {
if offsetArray.Length !== 0
lastOffset := offsetArray.Pop()
ptrType := this.IsWow64 ? "uint" : "ptr"
pointer := this.ReadNumber(baseAddress, ptrType)
for , offset in offsetArray
pointer := this.ReadNumber(pointer + offset, ptrType)
return pointer + lastOffset
}
GetModuleAddressByName(moduleName) {
if !moduleEntry := ToolHelpFindModuleByName(moduleName, this.ProcessId)
throw OSError(18)
return moduleEntry.modBaseAddr
}
GetSectionAddressByName(moduleName, sectionName) {
moduleEntry := ToolHelpFindModuleByName(moduleName, this.ProcessId)
fileBuffer := FileRead(moduleEntry.szExePath, "RAW")
pNtHeaders := fileBuffer.Ptr + NumGet(fileBuffer, 60, "int")
pFileHearder := pNtHeaders + 4
pOptionalHearder := pFileHearder + 20
pSectionHeader := pOptionalHearder + NumGet(pFileHearder, 16, "ushort")
loop NumGet(pFileHearder, 2, "ushort") {
if StrGet(pSectionHeader + (A_Index - 1) * 40, 8, "cp0") = sectionName
return moduleEntry.modBaseAddr + NumGet(pSectionHeader, (A_Index - 1) * 40 + 12, "uint")
}
throw Error("Unable to find the address")
}
GetFunctionAddressByName(moduleName, functionName) {
moduleEntry := ToolHelpFindModuleByName(moduleName, this.ProcessId)
fileBuffer := FileRead(moduleEntry.szExePath, "RAW")
pNtHeaders := fileBuffer.Ptr + NumGet(fileBuffer, 60, "int")
pFileHearder := pNtHeaders + 4
pOptionalHearder := pFileHearder + 20
sizeOfOptionalHearder := NumGet(pFileHearder, 16, "ushort")
pSectionHeader := pOptionalHearder + sizeOfOptionalHearder
if NumGet(pOptionalHearder, this.IsWow64 ? 96 : 112, "uint") == 0 {
throw Error("Unable to find the address")
}
pExportDirectory := fileBuffer.Ptr + RvaToFoa(NumGet(pOptionalHearder, this.IsWow64 ? 96 : 112, "uint"))
pFunctionNames := fileBuffer.Ptr + RvaToFoa(NumGet(pExportDirectory, 32, "uint"))
loop NumGet(pExportDirectory, 24, "uint") {
if StrGet(fileBuffer.Ptr + RvaToFoa(NumGet(pFunctionNames, (A_Index - 1) * 4, "uint")), "cp0") = functionName {
pFunctionAddresses := fileBuffer.Ptr + RvaToFoa(NumGet(pExportDirectory, 28, "uint"))
pNameOrdinals := fileBuffer.Ptr + RvaToFoa(NumGet(pExportDirectory, 36, "uint"))
return moduleEntry.modBaseAddr + NumGet(pFunctionAddresses, NumGet(pNameOrdinals, (A_Index - 1) * 2, "ushort") * 4, "uint")
}
}
throw Error("Unable to find the address")
RvaToFoa(rva){
if rva < NumGet(pOptionalHearder, 60, "uint")
return rva
loop NumGet(pFileHearder, 2, "ushort") {
virtualAddress := NumGet(pSectionHeader, (A_Index - 1) * 40 + 12, "uint")
if rva >= virtualAddress && rva < virtualAddress + NumGet(pSectionHeader, (A_Index - 1) * 40 + 8, "uint") {
return rva - virtualAddress + NumGet(pSectionHeader, (A_Index - 1) * 40 + 20, "uint")
}
}
throw Error("Unable to find the address")
}
}
GetThreadStackAddress(threadNumber) {
for threadEntry in ToolHelpEnumThread() {
if threadEntry.th32OwnerProcessID == this.ProcessId && threadNumber-- == 0 {
kernel32ModuleEntry := ToolHelpFindModuleByName("kernel32.dll", this.ProcessId)
if !hThread := DllCall("OpenThread", "uint", 0x1f03ff, "int", 0, "uint", threadEntry.th32ThreadID, "ptr")
throw Error("Unable to find the address")
DllCall("Ntdll\NtQueryInformationThread", "ptr", hThread, "uint", 0, "ptr", threadInfo := Buffer(48), "uint", threadInfo.Size, "uint*", 0)
DllCall("CloseHandle", "ptr", hThread)
pTeb := NumGet(threadInfo, 8, "ptr")
stackBase := ptrType := ptrSize := unset
if this.IsWow64 {
stackBase := this.ReadNumber(pTeb + 4096 * 2 + 4, "uint")
ptrType := "uint"
ptrSize := 4
}
else {
stackBase := this.ReadNumber(pTeb + 8, "uint64")
ptrType := "uint64"
ptrSize := 8
}
stackBuf := this.ReadBuffer(stackBase - 4096, 4096)
; find the stack entry pointing to the function that calls "ExitXXXXXThread"
offsetToExitThread := 4096 - ptrSize
while (offsetToExitThread >= 0) {
pExitThread := NumGet(stackBuf, offsetToExitThread, ptrType)
if pExitThread >= kernel32ModuleEntry.modBaseAddr && pExitThread <= kernel32ModuleEntry.modBaseAddr + kernel32ModuleEntry.modBaseSize
return stackBase - 4096 + offsetToExitThread
offsetToExitThread -= ptrSize
}
throw Error("Unable to find the address")
}
}
throw Error("Unable to find the address")
}
ReadCommandLine() {
if DllCall("Ntdll\NtQueryInformationProcess", "ptr", this.Handle, "uint", 0, "ptr", processBasicInfo := Buffer(48), "uint", processBasicInfo.Size, "uint*", 0)
throw Error("NtQueryInformationProcess failed")
pPeb := NumGet(processBasicInfo, 8, "ptr")
if this.IsWow64 {
pProcessParams := this.ReadNumber(pPeb + 4096 + 16, "uint")
pCommandLineUnicodeString := this.ReadBuffer(pProcessParams + 64, 8)
commandLine := this.ReadBuffer(NumGet(pCommandLineUnicodeString, 4, "uint"), NumGet(pCommandLineUnicodeString, "ushort"))
}
else {
pProcessParams := this.ReadNumber(pPeb + 32, "ptr")
pCommandLineUnicodeString := this.ReadBuffer(pProcessParams + 112, 16)
commandLine := this.ReadBuffer(NumGet(pCommandLineUnicodeString, 8, "ptr"), NumGet(pCommandLineUnicodeString, "ushort"))
}
return StrGet(commandLine, "utf-16")
}
}
ToolHelp.ahk
ToolHelpFindProcessByName(name) {
if !snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x2, "uint", 0, "ptr")
return
entry := PROCESSENTRY32W()
res := DllCall("Process32FirstW", "ptr", snapshot, "ptr", entry)
while res {
if entry.szExeFile = name {
res := entry
break
}
res := DllCall("Process32NextW", "ptr", snapshot, "ptr", entry)
}
DllCall("CloseHandle", "ptr", snapshot)
return res
}
ToolHelpFindModuleByName(moduleName, pid := 0) {
if !snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x18, "uint", pid, "ptr")
return
entry := MODULEENTRY32W()
res := DllCall("Module32FirstW", "ptr", snapshot, "ptr", entry)
while res {
if entry.szModule = moduleName {
res := entry
break
}
res := DllCall("Module32NextW", "ptr", snapshot, "ptr", entry)
}
DllCall("CloseHandle", "ptr", snapshot)
return res
}
ToolHelpEnumProcess() {
snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x2, "uint", 0, "ptr")
_processEntry := PROCESSENTRY32W()
enum.__Delete := (_) => DllCall("CloseHandle", "ptr", snapshot)
return enum(&processEntry) => DllCall(A_Index == 1 ? "Process32FirstW" : "Process32NextW", "ptr", snapshot, "ptr", processEntry := _processEntry)
}
ToolHelpEnumModule(pid := 0) {
snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x18, "uint", pid, "ptr")
_moduleEntry := MODULEENTRY32W()
enum.__Delete := (_) => DllCall("CloseHandle", "ptr", snapshot)
return enum(&moduleEntry) => DllCall(A_Index == 1 ? "Module32FirstW" : "Module32NextW", "ptr", snapshot, "ptr", moduleEntry := _moduleEntry)
}
ToolHelpEnumThread() {
snapshot := DllCall("CreateToolhelp32Snapshot", "uint", 0x4, "uint", 0, "ptr")
_threadEntry := THREADENTRY32()
enum.__Delete := (_) => DllCall("CloseHandle", "ptr", snapshot)
return enum(&threadEntry) => DllCall(A_Index == 1 ? "Thread32First" : "Thread32Next", "ptr", snapshot, "ptr", threadEntry := _threadEntry)
}
class PROCESSENTRY32W {
__New() {
this.Buffer := Buffer(568)
this.Ptr := this.Buffer.Ptr
this.Size := this.Buffer.Size
NumPut("uint", this.Size, this, 0)
}
cntUsage => NumGet(this, 4, "uint")
th32ProcessID => NumGet(this, 8, "uint")
th32DefaultHeapID => NumGet(this, 16, "uptr")
th32ModuleID => NumGet(this, 24, "uint")
cntThreads => NumGet(this, 28, "uint")
th32ParentProcessID => NumGet(this, 32, "uint")
pcPriClassBase => NumGet(this, 36, "int")
dwFlags => NumGet(this, 40, "uint")
szExeFile => StrGet(this.Ptr + 44)
}
class MODULEENTRY32W {
__New() {
this.Buffer := Buffer(1080)
this.Ptr := this.Buffer.Ptr
this.Size := this.Buffer.Size
NumPut("uint", this.Size, this, 0)
}
th32ModuleID => NumGet(this, 4, "uint")
th32ProcessID => NumGet(this, 8, "uint")
GlblcntUsage => NumGet(this, 12, "uint")
ProccntUsage => NumGet(this, 16, "uint")
modBaseAddr => NumGet(this, 24, "ptr")
modBaseSize => NumGet(this, 32, "uint")
hModule => NumGet(this, 40, "ptr")
szModule => StrGet(this.Ptr + 48)
szExePath => StrGet(this.Ptr + 560)
}
class THREADENTRY32 {
__New() {
this.Buffer := Buffer(28)
this.Ptr := this.Buffer.Ptr
this.Size := this.Buffer.Size
NumPut("uint", this.Size, this, 0)
}
th32ThreadID => NumGet(this, 8, "uint")
th32OwnerProcessID => NumGet(this, 12, "uint")
tpBasePri => NumGet(this, 16, "uint")
}
学习了,最近重温木头西游,用到了CE,没想到还能这么搞?
两个库轮流报错 麻了 pid进程名函数有问题
你要说明你的ahk版本,报错代码,错误信息,我都正常使用,猜不出你为什么用不了的
版本是1.1.34.03 RemoteProcess.ahk Error at line 87. Line Text: FromProcessName(processname) {Error: lnvalid class variable declaration. The program will exit. ToolHelp.ahk Error at line 56. Line Text: cntUsage => NumGet(this,4, “uint”) Error: Not a valid method, class or property definition. The program will exit. 这是报错内容可以看下是什么情况吗
版本需要2.0以上
感谢 用2.0成功使用上了 想问一下多级指针怎么写入数据,我的理解是一级一级的往里面写入不知道这样行不,有没有快捷方式??
参考多级指针示例,和读取一样,把ReadNumber改成WriteNumber多填个参数就行了
这个库 有V1版的吗,V2语法不熟悉!
感谢开源
请教人造指针如何读取
大佬 有1.0的吗 2.0的我用不了😭