Ray tracing di Notepad.exe dengan kecepatan 30 frame per detik

Beberapa bulan yang lalu, sebuah posting diposting di Reddit yang menjelaskan game yang menggunakan klon Notepad open source untuk menangani semua input dan rendering. Membaca tentang ini, saya pikir akan sangat bagus untuk melihat sesuatu yang serupa bekerja dengan Windows Notepad standar. Kemudian saya memiliki terlalu banyak waktu luang.





Saya akhirnya membuat game Snake dan pelacak sinar kecil yang menggunakan Notepad standar untuk semua tugas input dan rendering, dan di sepanjang jalan saya belajar tentang DLL Injection, API Hooking, dan Memory Scanning. Menjelaskan semua yang telah saya pelajari dalam prosesnya mungkin bacaan yang menarik untuk Anda.





Pertama saya ingin berbicara tentang cara kerja pemindai memori dan bagaimana saya menggunakannya untuk mengubah notepad.exe menjadi target render pada 30+ bingkai per detik. Saya juga akan berbicara tentang pelacak sinar yang saya buat untuk rendering di Notepad.





Mengirim peristiwa penting ke notepad

Saya akan mulai dengan berbicara tentang pengiriman peristiwa penting ke instance Notepad yang sedang berjalan. Ini adalah bagian proyek yang membosankan, jadi saya akan singkat.





Win32 (, ), , , , , «», , . , Visual Studio Spy++, , .





Spy ++ Notepad
Spy++

Spy++ , , , «». , , Win32, HWND , . HWND :





HWND GetWindowForProcessAndClassName(DWORD pid, const char* className)
{
  HWND curWnd = GetTopWindow(0); //0 arg means to get the window at the top of the Z order
  char classNameBuf[256];

  while (curWnd != NULL){
    DWORD curPid;
    DWORD dwThreadId = GetWindowThreadProcessId(curWnd, &curPid);

    if (curPid == pid){
      GetClassName(curWnd, classNameBuf, 256);
      if (strcmp(className, classNameBuf) == 0) return curWnd;

      HWND childWindow = FindWindowEx(curWnd, NULL, className, NULL);
      if (childWindow != NULL) return childWindow;
    }
    curWnd = GetNextWindow(curWnd, GW_HWNDNEXT);
  }
  return NULL;
}
      
      



HWND , PostMessage WM_CHAR.





, Spy++, 64- . , Visual Studio 2019 . Visual Studio «spyxx_amd64.exe».





, 10 , , , , 30 . , .





CheatEngine

CheatEngine. , . , // , . .





CheatEngine , . , . , :





  • , (, 100)





  • - , (, 92)





  • , ( 100), , 92





  • , (, , , )









CheatEngine dan Notepad "berteman"
CheatEngine ""

, ,   , , . CheatEngine, ( ) . :





  1. UTF-16, , UTF-8.





  2. , CheatEngine (, ?)





  3. . ,





, , .





, , . CheatEngine, - , :





FOR EACH block of memory allocated by our target process
    IF that block is committed and read/write enabled
        Scan the contents of that block for our byte pattern
        IF WE FIND IT
            return that address

      
      



~ 40 .





, , — .





64- Windows ( 0x00000000000 0x7FFFFFFFFFFF), 0 VirtualQueryEx .





VirtualQueryEx MEMORY_BASIC_INFORMATION



, , , VirtualQueryEx , . MEMORY_BASIC_INFORMATION



.





MEMORY_BASIC_INFORMATION



, BaseAddress RegionSize VirtualQueryEx





char* FindBytePatternInProcessMemory(HANDLE process, const char* pattern, size_t patternLen)
{
  char* basePtr = (char*)0x0;

  MEMORY_BASIC_INFORMATION memInfo;

  while (VirtualQueryEx(process, (void*)basePtr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)))
  {
    const DWORD mem_commit = 0x1000;
    const DWORD page_readwrite = 0x04;
    if (memInfo.State == mem_commit && memInfo.Protect == page_readwrite)
    {
      // search this memory for our pattern
    }

    basePtr = (char*)memInfo.BaseAddress + memInfo.RegionSize;
  }
}
      
      



, / .State .Protect. MEMORY_BASIC_INFORMATION



, , , 0x1000 (MEM_COMMIT



) 0x04 (PAGE_READWRITE



).





( , , ). . ReadProcessMemory.





, , . , , . , .





char* FindPattern(char* src, size_t srcLen, const char* pattern, size_t patternLen)
{
  char* cur = src;
  size_t curPos = 0;

  while (curPos < srcLen){
    if (memcmp(cur, pattern, patternLen) == 0){
      return cur;
    }

    curPos++;
    cur = &src[curPos];
  }
  return nullptr;
}
      
      



FindPattern() , . , FindPattern, , . .





char* FindBytePatternInProcessMemory(HANDLE process, const char* pattern, size_t patternLen)
{
  MEMORY_BASIC_INFORMATION memInfo;
  char* basePtr = (char*)0x0;

  while (VirtualQueryEx(process, (void*)basePtr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION))){
    const DWORD mem_commit = 0x1000;
    const DWORD page_readwrite = 0x04;
    if (memInfo.State == mem_commit && memInfo.Protect == page_readwrite){
      char* remoteMemRegionPtr = (char*)memInfo.BaseAddress;
      char* localCopyContents = (char*)malloc(memInfo.RegionSize);

      SIZE_T bytesRead = 0;
      if (ReadProcessMemory(process, memInfo.BaseAddress, localCopyContents, memInfo.RegionSize, &bytesRead)){
        char* match = FindPattern(localCopyContents, memInfo.RegionSize, pattern, patternLen);

        if (match){
          uint64_t diff = (uint64_t)match - (uint64_t)(localCopyContents);
          char* processPtr = remoteMemRegionPtr + diff;
          return processPtr;
        }
      }
      free(localCopyContents);
    }
    basePtr = (char*)memInfo.BaseAddress + memInfo.RegionSize;
  }
}
      
      



, , «MemoryScanner» github. ! ( , ymmv, ).





UTF-16

, UTF-16, , FindBytePatternInMemory (), UTF-16. . MemoryScanner github :





//convert input string to UTF16 (hackily)
const size_t patternLen = strlen(argv[2]);
char* pattern = new char[patternLen*2];
for (int i = 0; i < patternLen; ++i){
  pattern[i*2] = argv[2][i];
  pattern[i*2 + 1] = 0x0;
}
      
      



, , WriteProcessMemory . , , , Edit.





, Win32 api InvalidateRect, .





, :





void UpdateText(HINSTANCE process, HWND editWindow, char* notepadTextBuffer, char* replacementTextBuffer, int len)
{
  size_t written = 0;
  WriteProcessMemory(process, notepadTextBuffer, replacementTextBuffer, len, &written);

  RECT r;
  GetClientRect(editWindow, &r);
  InvalidateRect(editWindow, &r, false);
}
      
      



. , , , , ,  .





:

















. MoveWindow , , .





, , ( ) , . MoveWindow , WM_CHAR . , .





, , , WM_CHAR.





, . github , .





void PreallocateTextBuffer(DWORD processId)
{
  HWND editWindow = GetWindowForProcessAndClassName(processId, "Edit");

  // it takes 131 * 30 chars to fill a 1365x768 window with Consolas (size 11) chars
  MoveWindow(instance.topWindow, 100, 100, 1365, 768, true); 

  size_t charCount = 131 * 30;
  size_t utf16BufferSize = charCount * 2;

  char* frameBuffer = (char*)malloc(utf16BufferSize);
  for (int i = 0; i < charCount; i++){
    char v = 0x41 + (rand() % 26);
    PostMessage(editWindow, WM_CHAR, v, 0);
    frameBuffer[i * 2] = v;
    frameBuffer[i * 2 + 1] = 0x00;
  }

  Sleep(5000); //wait for input messages to finish processing...it's slow. 
  //Now use the frameBuffer as the unique byte pattern to search for
}
      
      



, , , .





. , (Consolas, 11pt), - WM_SETFONT , , . Consolas 11pt , .





, , , . , ScratchAPixel . , .





, . WriteProcessMemory ( , ), , ( * 2 (- UTF16)). , WriteProcessMemory . :





void drawChar(int x, int y, char c); //local buffer
void clearScreen(); // local buffer
void swapBuffersAndRedraw(); // pushes changes and refreshes screen. 
      
      



, , (131 x 30), , «» . , , , , ascii. , .





. , , . , «» , , .





float aspect = (0.5f * SCREEN_CHARS_WIDE) / float(SCREEN_CHARS_TALL);
      
      



, , , . , , , WM_VSCROLL, « » , . , , , , .





2: Boogaloo!





Bagian selanjutnya (dan terakhir) dari pencarian saya untuk membuat game real-time di Notepad adalah mencari tahu cara menangani input pengguna. Jika Anda ingin lebih, posting selanjutnya dapat ditemukan di sini !








All Articles