2015-02-20 14 views
11

Informacje o programie

Mam program, który zapisuje w pamięci gry, z którą eksperymentuję. Kod działa dla mnie dobrze, gdy używam zwykłego statycznego adresu, ale z jakiegoś powodu nie mogę tego zrobić, gdy znajdę wskaźnik pracy. Na przykład, uważam to w Cheat Engine po skanowaniu wskaźnika kilka razy:Korzystanie ze wskaźników znalezionych w Cheat Engine w języku C#

enter image description here

Ten adres działa za każdym razem, kiedy załadować grę i zmienić wartość. Problem polega na tym, że nie rozumiem, jak go używać w moim programie. Oto moje zadeklarowane zmienne, które próbowałem podłączyć wartości:

bool UnlimitedAmmo = false; 
string AmmoPointer = "031B7324"; // <--- The address 
int[] AmmoOffset = { 0x2c, 0x1e8, 0x3c8, 0x6d4, 0x508 }; // <--- It's pointers 
int AmmoToFill = 1337; // <--- The Amount of ammo to give 

olewam w tych zmiennych w następujący sposób:

MyMemory.ReadProcess = MyProcess[0]; 
MyMemory.Open(); 

int PointerAddress = HexToDec(AmmoPointer); 
int[] PointerOffest = AmmoOffset; 
int BytesWritten; 
byte[] ValueToWrite = BitConverter.GetBytes(AmmoToFill); 
string WrittenAddress = MyMemory.PointerWrite((IntPtr)PointerAddress, ValueToWrite, 
    PointerOffest, out BytesWritten); 
MyMemory.CloseHandle(); 

ja kiedyś statycznego adresu (na inną grę) i mój kod zadziałał dobrze po podłączeniu adresu i przesunięciu. Tym razem jestem zaskoczony. Każda pomoc i wyjaśnienia byłyby głęboko docenione. Z góry dziękuję.

+0

Zacząłem nagrodę za to pytanie, ponieważ obecnie staram się rozgryźć to samo. Mianowicie, jak uzyskać adres 'THREADSTACK0' w języku C#. Znalazłem kilka informacji na temat tego, jak jest on stworzony w cheatengine, i mogę odczytać adres w cheatengine, ale żadna z losowanych przeze mnie znaków nie jest zgodna z tym, co zwraca CE – user1274820

+0

Zobacz tutaj: http://forum.cheatengine.org/ viewtopic.php? p = 5487976 – user1274820

Odpowiedz

3

Pomyślałem, że w przyszłości zamieszczę dla nich rozwiązanie dla ludzi.

Jeden sposób można sobie z tym poradzić, jeśli nie chcą nurkować do kodu C++ zapisanego tam i przepisać w C# jest po prostu użyć tego programu na github:

https://github.com/makemek/cheatengine-threadstack-finder

bezpośredni link do pobrania jest tutaj:

https://github.com/makemek/cheatengine-threadstack-finder/files/685703/threadstack.zip

Możesz przekazać ten plik wykonywalny identyfikator procesu i analizowania na adres wątek trzeba.

Zasadniczo to, co zrobiłem, to mój proces uruchamia exe, przekierowuje wyjście i analizuje je.

Potem proces się kończy i robimy to, czego potrzebujemy - wydaje mi się, że jestem oszustem, ale działa.

Wyjście dla threadstack.exe zazwyczaj wygląda następująco:

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 

Oto kod I ostatecznie wykorzystane do uzyskania adresu mi potrzeba:

[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); 
} 

Final

Jeśli "Jestem zainteresowany aktualizacją kodu C++, uważam, że odpowiednia część jest tutaj.

To faktycznie nie wygląda zbyt skomplikowane - Myślę, że jesteś po prostu chwytając adres bazowy kernal32.dll i szuka tego adresu w stosie wątku, sprawdzając, czy jest >= do adresu bazowego lub <= do base address + size podczas czytania 4 bajtów - musiałbym jednak z tym grać.

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; 
} 

można uzyskać adresy bazowe wątek w C# tak:

https://stackoverflow.com/a/8737521/1274820

Najważniejsze jest, aby wywołać funkcję NtQueryInformationThread. To nie jest całkowicie "oficjalna" funkcja (być może nieudokumentowana w przeszłości?), Ale dokumentacja nie sugeruje żadnej alternatywy dla uzyskania adresu początkowego wątku.

Zawiniłem je w połączenie .NET przyjazne, które pobiera identyfikator wątku i zwraca adres początkowy jako IntPtr. Ten kod został przetestowany w trybie x86 i x64, w tym ostatnim testowany był zarówno w 32-bitowym, jak i 64-bitowym procesie docelowym.

Jedną z rzeczy, których nie testowałem, było uruchamianie tego z małymi przywilejami; Spodziewam się, że ten kod wymaga od dzwoniącego posiadania 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 
    } 
} 

Wyjście na moim systemie:

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 

oprócz rzeczy w nawiasach ponieważ wymaga dodatkowego P/Invoke jest.


chodzi SymFromAddress „nie znaleziono moduł” błąd, chciałem tylko wspomnieć, że trzeba zadzwonić SymInitialize z fInvadeProcess = true lub ręcznie załadować moduł, as documented on MSDN.

Powiązane problemy