2015-02-20 3 views
11

О программе

У меня есть программа, которая записывает в память игры, в которой я экспериментирую. код работает для меня очень хорошо, когда я использую обычный статический адрес, но по какой-то причине я не могу это сделать, как только найду рабочий указатель. Например, я считаю, это Чит Engine после сканирования указателя несколько раз:Использование указателей, найденных в Cheat Engine в C#

enter image description here

Этот адрес работает каждый раз, когда я загрузить игру и изменить значение. Проблема в том, что я не понимаю, как ее использовать в моей программе. Вот мои объявленные переменные, которые я попытался подключить значения:

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 

Я передаю в этих переменных следующим образом:

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(); 

Я когда-то использовал статический адрес (для другой игры) и мой код работал нормально, как только я подключил адрес и смещение. На этот раз я в тупик. Любая помощь и объяснения будут глубоко оценены. Заранее спасибо.

+0

Я начал щедрость по этому вопросу, поскольку в настоящее время я пытаюсь понять то же самое. А именно, как получить адрес 'THREADSTACK0' в C#. Я нашел некоторую информацию о том, как он создан в читэнгине, и я могу прочитать адрес в cheatengine, но ни один из threadstartaddresses, в который я втягиваю C#, соответствует тому, что возвращает CE – user1274820

+0

См. Здесь: http://forum.cheatengine.org/ viewtopic.php? р = 5487976 – user1274820

ответ

3

Я решил, что я опубликую решение для этого для людей в будущем.

Один из способов справиться с этим, если вы не хотите, чтобы погрузиться в код C++ сохранённые там и переписать в C# просто использовать эту программу на GitHub:

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

Прямая ссылка скачать здесь:

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

вы можете передать этот исполняемый идентификатор процесса и разобрать адрес нить вам нужно.

В основном, то, что я сделал, это мой процесс, который запускает exe, перенаправляет вывод и анализирует его.

Затем процесс закрывается, и мы делаем то, в чем мы нуждаемся. Мне кажется, что я обманываю, но это работает.

Выход для threadstack.exe обычно выглядит следующим образом:

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 

Вот код, я в конечном счете используется для получения адреса, мне нужно:

[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

Если вы Я заинтересован в обновлении кода на C++, я считаю, что соответствующая часть здесь.

Это на самом деле не выглядит слишком сложным - я думаю, вы просто захватывая базовый адрес kernal32.dll и ищете этот адрес в стеке потока, проверив, чтобы увидеть, если он >= к базовому адресу или <= к base address + size при чтении каждых 4 байтов - мне пришлось бы играть с ним, хотя.

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

Вы можете получить нить базовые адреса в C# как это:

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

Ключ для вызова функции NtQueryInformationThread. Это не полностью «официальная» функция (возможно, недокументированная в прошлом?), Но в документации нет альтернативы для получения начального адреса потока.

Я завернул его в .NET-дружественный вызов, который принимает идентификатор потока и возвращает начальный адрес как IntPtr. Этот код был протестирован в режиме x86 и x64, а в последнем он был протестирован как на 32-битном, так и на 64-битном целевом процессе.

Одна вещь, которую я не тестировал, выполняла это с низкими привилегиями; Я бы ожидал, что этот код требует, чтобы вызывающий абонент имел 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 
    } 
} 

Выход на моей системе:

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 

кроме материала в скобках, так как это требует дополнительных P/Invoke-х.


Что касается SymFromAddress «модуль не найден» ошибка, я просто хотел сказать, что нужно позвонить SymInitialize с fInvadeProcess = true или загрузить модуль вручную, as documented on MSDN.