2015-02-20 12 views
11

thiệu về Chương trình

tôi có một chương trình mà ghi vào bộ nhớ của trò chơi Tôi đang thử nghiệm với. mã hoạt động cho tôi chỉ tốt khi tôi sử dụng một địa chỉ tĩnh thường xuyên, nhưng đối với một số lý do tôi dường như không thể làm điều đó một khi tôi tìm thấy một con trỏ làm việc. Ví dụ, tôi tìm thấy điều này trong Cheat Engine sau khi quét trỏ một vài lần:Sử dụng con trỏ Tìm thấy trong Cheat Engine trong C#

enter image description here

địa chỉ này hoạt động mỗi khi tôi tải các trò chơi và chỉnh sửa các giá trị. Vấn đề là tôi không hiểu cách sử dụng nó trong chương trình của mình. Dưới đây là các biến khai báo của tôi mà tôi đã cố gắng để cắm vào các giá trị:

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 

Tôi đang đi qua trong các biến như sau:

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

Tôi đã từng sử dụng một địa chỉ tĩnh (cho một trò chơi khác nhau) và mã của tôi hoạt động tốt khi tôi đã cắm địa chỉ và bù đắp. Tôi đang bối rối lần này. Bất kỳ trợ giúp và giải thích nào sẽ được đánh giá cao. Cảm ơn trước.

+0

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

+0

Xem tại đây: http://forum.cheatengine.org/ viewtopic.php? p = 5487976 – user1274820

Trả lời

3

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

Final

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.

Các vấn đề liên quan