Ich dachte, ich würde eine Lösung für diese Menschen in der Zukunft veröffentlichen.
Ein Weg, dies umgehen können, wenn Sie in den C nicht dort gespeichert ++ Code tauchen wollen und schreiben in C#, ist einfach dieses Programm auf Github zu verwenden:
https://github.com/makemek/cheatengine-threadstack-finder
Der direkte Download-Link hier ist:
https://github.com/makemek/cheatengine-threadstack-finder/files/685703/threadstack.zip
Sie diese ausführbare Datei eine Prozess-ID übergeben kann und die Thread-Adresse analysieren, die Sie benötigen.
Im Grunde, was ich getan habe, ist mein Prozess führt die exe, leitet die Ausgabe um, und analysiert es.
Dann schließt sich der Prozess und wir tun, was wir brauchen - ich fühle mich irgendwie wie ich betrüge, aber es funktioniert.
Der Ausgang für threadstack.exe
sieht in der Regel wie folgt aus:
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
Hier ist der Code ich die Adresse Ich brauche schließlich verwendet zu bekommen: Wenn Sie
[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);
}
Ich bin daran interessiert, den C++ Code zu aktualisieren, ich glaube der relevante Teil ist hier.
Es sieht eigentlich nicht zu kompliziert - ich glaube, du bist nur die Basisadresse der kernal32.dll
Grabbing und die Suche nach der Adresse in dem Thread-Stapel durch zu überprüfen, ob es >=
an der Adresse Base oder <=
zu die base address + size
beim Lesen von jeweils 4 Bytes - ich müsste aber damit spielen.
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;
}
Sie können die Fadenbasisadressen in C# wie diese:
https://stackoverflow.com/a/8737521/1274820
Der Schlüssel ist die NtQueryInformationThread
Funktion aufzurufen. Dies ist keine komplett "offizielle" Funktion (möglicherweise in der Vergangenheit undokumentiert?), Aber die Dokumentation schlägt keine Alternative vor, um die Startadresse eines Threads zu erhalten.
Ich habe es in einen .NET-freundlichen Anruf eingewickelt, der eine Thread-ID akzeptiert und die Startadresse als IntPtr
zurückgibt. Dieser Code wurde im x86- und x64-Modus getestet, und im letzteren Fall wurde er sowohl auf einem 32-Bit- als auch auf einem 64-Bit-Zielprozess getestet.
Eine Sache, die ich nicht getestet habe, war das mit geringen Privilegien; Ich würde erwarten, dass dieser Code erfordert, dass der Anrufer die SeDebugPrivilege
hat.
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
}
}
Ausgang auf meinem System:
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
abgesehen von dem Zeug in Klammern, da dies erfordert zusätzliche P/Invoke ist.
In Bezug auf SymFromAddress
„Modul nicht gefunden“ Fehler, ich wollte nur, dass man erwähnen muss SymInitialize
mit fInvadeProcess = true
anrufen oder das Modul manuell laden, as documented on MSDN.
Ich begann ein Kopfgeld auf diese Frage, wie ich derzeit versuche, das gleiche herauszufinden. Nämlich wie man die Adresse von 'THREADSTACK0' in C# bekommt. Ich habe einige Informationen darüber gefunden, wie es in cheatengine erstellt wird, und ich kann die Adresse in cheatengine lesen, aber keine der threadstartaddresses, die ich ziehe C# entspringe, was CE zurückgibt – user1274820
Sehen Sie hier: http://forum.cheatengine.org/ viewtopic.php? p = 5487976 – user1274820