正確的「記事本」打開方式:能渲染3D圖像,還能玩貪吃蛇

金磊 發自 凹非寺
量子位 報道 | 公衆號 QbitAI

渲染3D圖像,一個「記事本」就夠了。

最近,GitHub上一名叫“Kyle Halladay”的小哥,便上傳了這樣一個項目,用記事本來渲染圖像。

效果是這樣的:

立方體旋轉、陰影變化,還挺有內味的。

還有貪吃蛇效果的:

那麼,小哥是如何拿記事本,就做到這些效果的呢?

正確的「記事本」打開方式

據小哥介紹,所有的輸入和渲染效果,都是在記事本中完成。

在此之前,需要做一些設置工作。

首先,是將鍵盤事件(Key Event),發送到正在運行的記事本。

這裏就要用到 Visual Studio 提供的一個叫 Spy + + 的工具,可以列出組成給定應用程序的所有窗口。

Spy + + 顯示了要找的記事本子窗口是“編輯”窗口。

一旦我知道了這一點,就只需要搞清楚 Win32函數調用的正確組合,用來獲得該 UI 元素的 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 事件的問題。

接下來,就是建一個內存掃描器 (Memory Scanner),這裏要用到一個叫做 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

內存掃描程序需要做的第一件事,就是遍歷進程分配的內存。

因爲 Windows 上每個64位進程的虛擬內存範圍是相同的,所以需要製作一個指向地址0的指針,然後使用 VirtualQueryEx 獲取目標程序的虛擬地址信息。

將具有相同內存屬性的內容頁,組織到 MEMORY basic information 結構中,因此,可能是 VirtualQueryEx 爲給定地址返回的結構包含超過1頁的信息。

一旦有了第一個 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;
  }
}

然後,是在進程內存中,搜索字節模式 (Byte Pattern)的工作,此處需要一個叫做 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;
}
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;
  }
}

需要注意的是,記事本將屏幕上的文本緩衝區作爲 UTF-16數據存儲,因此提供給 FindBytePatternInMemory ()的字節模式也必須是 UTF-16。

更多細節描述,可以參考文末的參考鏈接。

更多的「記事本」玩法

當然,關於記事本的別樣玩法,還有好多。

例如,有拿記事本完成「快排」的可視化。

還有用記事本自制繪圖軟件的。

那麼,你還有更炫酷的「記事本」玩法嗎?

歡迎在評論區留言推薦~

參考鏈接

https://github.com/khalladay/render-with-notepad
http://kylehalladay.com/blog/2020/05/20/Rendering-With-Notepad.html
https://www.bilibili.com/video/BV1v4411e7Gy?from=search&seid=50634434912662370
https://www.bilibili.com/video/BV1os411u7vD?from=search&seid=11201980142804134991

作者系網易新聞·網易號“各有態度”簽約作者

免費NLP直播課

圖卷神經網絡、BERT、對話生成、知識圖譜

京東智聯雲&貪心學院聯合舉辦,兩週NLP系列直播,講透圖卷積神經網絡、BERT、對話生成、知識圖譜、詞嵌入

第一場《基於知識圖譜和圖卷積神經網絡的應用和開發》楊棟

第二場《深入淺出詞嵌入技術》李文哲

第三場《BERT模型精講》徐路

第四場《對話系統的中的生成問題》鄭銀河

掃碼添加助教,進入直播交流羣~

量子位 QbitAI · 頭條號簽約作者

վ'ᴗ' ի 追蹤AI技術和產品新動態

喜歡就點「在看」吧 !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章