Tôi đã tìm ra một giải pháp cho việc này cho mọi người trong tương lai.
Một cách để bạn có thể xử lý này nếu bạn không muốn đi sâu vào các mã C++ lưu ở đó và viết lại bằng C# là chỉ cần sử dụng chương trình này trên github:
https://github.com/makemek/cheatengine-threadstack-finder
Liên kết tải xuống trực tiếp là ở đây:
https://github.com/makemek/cheatengine-threadstack-finder/files/685703/threadstack.zip
bạn có thể vượt qua thực thi này một quá trình ID và phân tích ra các địa chỉ chủ đề mà bạn cần.
Về cơ bản, những gì tôi đã làm là quy trình của tôi chạy exe, chuyển hướng đầu ra và phân tích cú pháp nó.
Sau đó, quá trình đóng và chúng tôi làm những gì chúng tôi cần - Tôi cảm thấy như tôi đang lừa dối, nhưng nó hoạt động.
Kết quả cho threadstack.exe
thường trông như thế này:
PID 6540 (0x198c)
Grabbing handle
Success
PID: 6540 Thread ID: 0x1990
PID: 6540 Thread ID: 0x1b1c
PID: 6540 Thread ID: 0x1bbc
TID: 0x1990 = THREADSTACK 0 BASE ADDRESS: 0xbcff8c
TID: 0x1b1c = THREADSTACK 1 BASE ADDRESS: 0x4d8ff8c
TID: 0x1bbc = THREADSTACK 2 BASE ADDRESS: 0x518ff8c
Đây là mã tôi cuối cùng sử dụng để có được những địa chỉ tôi cần:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
////////////////////////////////////////////////////////////////////
// These are used to find the StardewValley.Farmer structure //
//////////////////////////////////////////////////////////////////
private IntPtr Thread0Address;
private IntPtr FarmerStartAddress;
private static int[] FARMER_OFFSETS = { 0x4, 0x478, 0x218, 0x24C };
private static int FARMER_FIRST = 0x264;
//////////////////////////////////////////////////////////////////
private async void hookAll()
{
SVProcess = Process.GetProcessesByName("Stardew Valley")[0];
SVHandle = OpenProcess(ProcessAccessFlags.All, true, SVProcess.Id);
SVBaseAddress = SVProcess.MainModule.BaseAddress;
Thread0Address = (IntPtr) await getThread0Address();
getFarmerStartAddress();
}
private Task<int> getThread0Address()
{
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "threadstack.exe",
Arguments = SVProcess.Id + "",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
if (line.Contains("THREADSTACK 0 BASE ADDRESS: "))
{
line = line.Substring(line.LastIndexOf(":") + 2);
return Task.FromResult(int.Parse(line.Substring(2), System.Globalization.NumberStyles.HexNumber));
}
}
return Task.FromResult(0);
}
private void getFarmerStartAddress()
{
IntPtr curAdd = (IntPtr) ReadInt32(Thread0Address - FARMER_FIRST);
foreach (int x in FARMER_OFFSETS)
curAdd = (IntPtr) ReadInt32(curAdd + x);
FarmerStartAddress = (IntPtr) curAdd;
}
private int ReadInt32(IntPtr addr)
{
byte[] results = new byte[4];
int read = 0;
ReadProcessMemory(SVHandle, addr, results, results.Length, out read);
return BitConverter.ToInt32(results, 0);
}
Nếu bạn đang quan tâm đến việc cập nhật mã C++, tôi tin rằng phần có liên quan là ở đây. Nó thực sự không quá phức tạp - Tôi nghĩ bạn chỉ cần lấy địa chỉ cơ sở của kernal32.dll
và tìm địa chỉ đó trong ngăn xếp chuỗi bằng cách kiểm tra xem địa chỉ đó là >=
đến địa chỉ cơ sở hay <=
để các base address + size
trong khi đọc mỗi 4 byte - tôi sẽ phải chơi với nó mặc dù.
DWORD GetThreadStartAddress(HANDLE processHandle, HANDLE hThread) {
/* rewritten from https://github.com/cheat-engine/cheat-engine/blob/master/Cheat%20Engine/CEFuncProc.pas#L3080 */
DWORD used = 0, ret = 0;
DWORD stacktop = 0, result = 0;
MODULEINFO mi;
GetModuleInformation(processHandle, GetModuleHandle("kernel32.dll"), &mi, sizeof(mi));
stacktop = (DWORD)GetThreadStackTopAddress_x86(processHandle, hThread);
/* The stub below has the same result as calling GetThreadStackTopAddress_x86()
change line 54 in ntinfo.cpp to return tbi.TebBaseAddress
Then use this stub
*/
//LPCVOID tebBaseAddress = GetThreadStackTopAddress_x86(processHandle, hThread);
//if (tebBaseAddress)
// ReadProcessMemory(processHandle, (LPCVOID)((DWORD)tebBaseAddress + 4), &stacktop, 4, NULL);
CloseHandle(hThread);
if (stacktop) {
//find the stack entry pointing to the function that calls "ExitXXXXXThread"
//Fun thing to note: It's the first entry that points to a address in kernel32
DWORD* buf32 = new DWORD[4096];
if (ReadProcessMemory(processHandle, (LPCVOID)(stacktop - 4096), buf32, 4096, NULL)) {
for (int i = 4096/4 - 1; i >= 0; --i) {
if (buf32[i] >= (DWORD)mi.lpBaseOfDll && buf32[i] <= (DWORD)mi.lpBaseOfDll + mi.SizeOfImage) {
result = stacktop - 4096 + i * 4;
break;
}
}
}
delete buf32;
}
return result;
}
Bạn có thể lấy địa chỉ chủ đề cơ bản trong C# như thế này:
https://stackoverflow.com/a/8737521/1274820
Điều quan trọng là để gọi NtQueryInformationThread
chức năng. Đây không phải là một chức năng "chính thức" hoàn toàn (có thể không có giấy tờ trong quá khứ?), Nhưng tài liệu cho thấy không có cách nào khác để lấy địa chỉ bắt đầu của một chuỗi.
Tôi đã kết hợp nó thành một cuộc gọi thân thiện với .NET lấy ID luồng và trả về địa chỉ bắt đầu là IntPtr
. Mã này đã được thử nghiệm ở chế độ x86 và x64, và sau này nó đã được thử nghiệm trên cả hai quá trình nhắm mục tiêu 32 bit và 64 bit.
Một điều tôi không thử nghiệm đang chạy với đặc quyền thấp; Tôi hy vọng rằng mã này yêu cầu người gọi có số SeDebugPrivilege
.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
PrintProcessThreads(Process.GetCurrentProcess().Id);
PrintProcessThreads(4156); // some other random process on my system
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
static void PrintProcessThreads(int processId)
{
Console.WriteLine(string.Format("Process Id: {0:X4}", processId));
var threads = Process.GetProcessById(processId).Threads.OfType<ProcessThread>();
foreach (var pt in threads)
Console.WriteLine(" Thread Id: {0:X4}, Start Address: {1:X16}",
pt.Id, (ulong) GetThreadStartAddress(pt.Id));
}
static IntPtr GetThreadStartAddress(int threadId)
{
var hThread = OpenThread(ThreadAccess.QueryInformation, false, threadId);
if (hThread == IntPtr.Zero)
throw new Win32Exception();
var buf = Marshal.AllocHGlobal(IntPtr.Size);
try
{
var result = NtQueryInformationThread(hThread,
ThreadInfoClass.ThreadQuerySetWin32StartAddress,
buf, IntPtr.Size, IntPtr.Zero);
if (result != 0)
throw new Win32Exception(string.Format("NtQueryInformationThread failed; NTSTATUS = {0:X8}", result));
return Marshal.ReadIntPtr(buf);
}
finally
{
CloseHandle(hThread);
Marshal.FreeHGlobal(buf);
}
}
[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtQueryInformationThread(
IntPtr threadHandle,
ThreadInfoClass threadInformationClass,
IntPtr threadInformation,
int threadInformationLength,
IntPtr returnLengthPtr);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[Flags]
public enum ThreadAccess : int
{
Terminate = 0x0001,
SuspendResume = 0x0002,
GetContext = 0x0008,
SetContext = 0x0010,
SetInformation = 0x0020,
QueryInformation = 0x0040,
SetThreadToken = 0x0080,
Impersonate = 0x0100,
DirectImpersonation = 0x0200
}
public enum ThreadInfoClass : int
{
ThreadQuerySetWin32StartAddress = 9
}
}
Output trên hệ thống của tôi:
Process Id: 2168 (this is a 64-bit process)
Thread Id: 1C80, Start Address: 0000000001090000
Thread Id: 210C, Start Address: 000007FEEE8806D4
Thread Id: 24BC, Start Address: 000007FEEE80A74C
Thread Id: 12F4, Start Address: 0000000076D2AEC0
Process Id: 103C (this is a 32-bit process)
Thread Id: 2510, Start Address: 0000000000FEA253
Thread Id: 0A0C, Start Address: 0000000076F341F3
Thread Id: 2438, Start Address: 0000000076F36679
Thread Id: 2514, Start Address: 0000000000F96CFD
Thread Id: 2694, Start Address: 00000000025CCCE6
ngoài những thứ trong ngoặc vì đó đòi hỏi phải có thêm P/Invoke của.
Về SymFromAddress
lỗi "không tìm thấy mô-đun", tôi chỉ muốn đề cập đến một cần gọi SymInitialize
với fInvadeProcess = true
HOẶC nạp module bằng tay, as documented on MSDN.
Tôi đã bắt đầu một tiền thưởng cho câu hỏi này vì tôi hiện đang cố gắng tìm ra điều tương tự. Cụ thể là, cách lấy địa chỉ 'THREADSTACK0' trong C#. Tôi đã tìm thấy một số thông tin về cách nó được tạo ra trong cheatengine, và tôi có thể đọc địa chỉ trong cheatengine, nhưng không ai trong số các threadstart địa chỉ tôi đang kéo trong C# phù hợp với những gì CE trả về – user1274820
Xem tại đây: http://forum.cheatengine.org/ viewtopic.php? p = 5487976 – user1274820