The right way to open Notepad: it can render 3D images and play snake

Yunqi information:[ Click to see more industry information]
Here you can find the first-hand cloud information of different industries. What are you waiting for? Come on!

A notepad is enough for rendering 3D images.

Recently, a little brother named "Kyle Halladay" on GitHub uploaded such a project to render images with Notepad.

The effect is as follows:

Cube rotation, shadow change, but also quite internal flavor.

There are also snake eating effects:

So, how does my little brother use Notepad to achieve these effects?

The right way to open Notepad

According to my brother, all the input and rendering effects are done in Notepad.

Before that, some setup work needs to be done.

First, send the key event to the running Notepad.

Here we will use a tool called Spy + + provided by Visual Studio to list all the windows that make up a given application.

Spy + + shows that the Notepad sub window you are looking for is the edit window.

Once I know this, I just need to figure out the right combination of Win32 function calls to get the HWND of the UI element and send the input.

The HWND obtained is as follows:

  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;

Once the right control HWND is obtained, drawing a character in the edit control of Notepad is the problem of sending a WM char event to it using PostMessage.

The next step is to build a memory scanner, which uses a tool called CheatEngine.

The basic algorithm is as follows:

The first thing a memory scanner needs to do is to traverse the memory allocated by the process.

Because the virtual memory range of each 64 bit process on Windows is the same, you need to make a pointer to address 0, and then use VirtualQueryEx to get the virtual address information of the target program.

Organize content pages with the same memory properties into the MEMORY basic information structure, so it is possible that the structure returned by VirtualQueryEx for a given address contains more than 1 page of information.

Once you have the first MEMORY basic information structure, you only need to add the BaseAddress and RegionSize members of the current structure together to iterate in memory, and provide the new address to VirtualQueryEx to get the next set of consecutive pages.

char* FindBytePatternInProcessMemory(HANDLE process, const char* pattern, size_t patternLen)
  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)
      // search this memory for our pattern

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

Then, in the process memory, the work of searching byte pattern needs a tool called ReadProcessMemory.

Once memory is copied to a locally visible buffer, it's easy to search for byte patterns.

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;

    cur = &src[curPos];
  return nullptr;
char* FindBytePatternInProcessMemory(HANDLE process, const char* pattern, size_t patternLen)
  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;
    basePtr = (char*)memInfo.BaseAddress + memInfo.RegionSize;

It should be noted that Notepad stores the text buffer on the screen as UTF-16 data, so the byte mode provided to findbyte patterninmemory() must also be UTF-16.

For more details, please refer to the reference link at the end of the article.

More ways to play Notepad

Of course, there are many other ways to play Notepad.

For example, take Notepad to complete the visualization of "quick arrangement".

Also useful for notepad self-made drawing software.

So, do you have a cool way to play Notepad?

[yunqi online class] product technology experts share every day!
Course address:

Join the community immediately, face to face with experts, and keep abreast of the latest news of the course!
[yunqi online classroom community]

Original release time: May 24, 2020
Author: Jin Lei
This article comes from:“ Qubit official account "To understand the relevant information, we can focus on the official account number QbitAI.

Tags: Windows github

Posted on Tue, 26 May 2020 06:17:33 -0400 by mutedgirl