第五课 代码注入(C语言)

下面用一个代码注入示例向notepad.exe进程注入简单的代码,注入后会弹出消息框,显示消息

首先,运行notepad.exe 查看notepad.exe的PID,我这里显示的是2608

·

在cmd窗口中运行CodeInjection.exe 并输入命令与参数

然后可以看到一个信息框被注入到notepad.exe进程当中

下面展示一下CodeInjection.exe的源码(用VS2017编译通过)

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"

typedef struct _THREAD_PARAM
{
	FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
	char    szBuf[4][128];          // 接收传递过来的"user32.dll", "MessageBoxA", "www.xuenixiang.com", "Shin"这些字符串
} THREAD_PARAM, *PTHREAD_PARAM;

typedef HMODULE(WINAPI *PFLOADLIBRARYA)
(
	LPCSTR lpLibFileName
	);

typedef FARPROC(WINAPI *PFGETPROCADDRESS)//如果函数调用成功,返回值是DLL中的输出函数地址
(
	HMODULE hModule,//dll模块句柄
	LPCSTR lpProcName;//函数名
);

typedef int (WINAPI *PFMESSAGEBOXA)//MessageBoxA传参结构体
(
	HWND hWnd,
	LPCSTR lpText,
	LPCSTR lpCaption,
	UINT uType
	);

DWORD WINAPI ThreadProc(LPVOID lParam)//线程的起始地址,线程创建成功,返回非零值,否则为0
{
	PTHREAD_PARAM   pParam = (PTHREAD_PARAM)lParam;//接收线程传递给函数使用的CreateThread函数lpParameter参数数据
	HMODULE         hMod = NULL;
	FARPROC         pFunc = NULL;

	// LoadLibrary()
	hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);    // "user32.dll"
	if (!hMod)
		return 1;

	// GetProcAddress()
	pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);  // "MessageBoxA"
	if (!pFunc)
		return 1;

	// MessageBoxA()
	((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

	return 0;
}

BOOL InjectCode(DWORD dwPID)//代码注入的核心部分
{
	HMODULE         hMod = NULL;
	THREAD_PARAM    param = { 0, };
	HANDLE          hProcess = NULL;
	HANDLE          hThread = NULL;
	LPVOID          pRemoteBuf[2] = { 0, };
	DWORD           dwSize = 0;

	hMod = GetModuleHandleA("kernel32.dll");//获取kernel32.dll动态链接库的模块句柄

	// set THREAD_PARAM
	param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");//返回值是DLL中LoadLibraryA函数的地址
	param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
	strcpy_s(param.szBuf[0], "user32.dll");
	strcpy_s(param.szBuf[1], "MessageBoxA");
	strcpy_s(param.szBuf[2], "www.xuenixiang.com");
	strcpy_s(param.szBuf[3], "Shin");

	// Open Process 打开一个已存在的进程对象,并返回进程的句柄
	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS,   // dwDesiredAccess
		FALSE,                // bInheritHandle
		dwPID)))             // dwProcessId
	{
		printf("OpenProcess() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}

	// Allocation for THREAD_PARAM
	dwSize = sizeof(THREAD_PARAM);
	if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess,          // hProcess
		NULL,                 // lpAddress
		dwSize,               // dwSize
		MEM_COMMIT,           // flAllocationType
		PAGE_READWRITE)))    // flProtect
	{
		printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}
	/*hProcess 由OpenProcess返回的进程句柄。
	如参数传数据为 INVALID_HANDLE_VALUE 【即 - 1】目标进程为自身进程
	lpBaseAddress要写的内存首地址
	再写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。
	lpBuffer
	指向要写的数据的指针。
	nSize
	要写入的字节数。
	返回值
	非零值代表成功。
	可用GetLastError获取更多的错误详细信息。*/
	if (!WriteProcessMemory(hProcess,                       // hProcess
		pRemoteBuf[0],                  // lpBaseAddress
		(LPVOID)&param,                 // lpBuffer
		dwSize,                         // nSize
		NULL))                         // [out] lpNumberOfBytesWritten
	{
		printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}

	// Allocation for ThreadProc() 为线程函数申请内存空间 执行成功就返回分配内存的首地址,不成功就是NULL
	dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
	if (!(pRemoteBuf[1] = VirtualAllocEx(hProcess,          // hProcess
		NULL,                 // lpAddress
		dwSize,               // dwSize
		MEM_COMMIT,           // flAllocationType
		PAGE_EXECUTE_READWRITE)))    // flProtect
	{
		printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}

	if (!WriteProcessMemory(hProcess,                       // hProcess
		pRemoteBuf[1],                  // lpBaseAddress
		(LPVOID)ThreadProc,             // lpBuffer
		dwSize,                         // nSize
		NULL))                         // [out] lpNumberOfBytesWritten
	{
		printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}

	if (!(hThread = CreateRemoteThread(hProcess,            // hProcess
		NULL,                // lpThreadAttributes
		0,                   // dwStackSize
		(LPTHREAD_START_ROUTINE)pRemoteBuf[1],     // dwStackSize
		pRemoteBuf[0],       // lpParameter
		0,                   // dwCreationFlags
		NULL)))             // lpThreadId
	{
		printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
		return FALSE;
	}

	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	CloseHandle(hProcess);

	return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
	TOKEN_PRIVILEGES tp;
	HANDLE hToken;
	LUID luid;
	//要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,
	//只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,
	//就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行
	//OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID)还是会遇到“访问拒绝”的错误。什么原因呢?
	//原来在默认的情况下进程的一些访问权限是没有被启用(Enabled)的,所以我们要做的首先是启用这些权限。
	//与此相关的一些API函数有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges。
	//我们要修改一个进程的访问令牌,首先要获得进程访问令牌的句柄,这可以通过OpenProcessToken得到
	if (!OpenProcessToken(GetCurrentProcess(),//用来打开与进程相关联的访问令牌
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
		&hToken))
	{
		printf("OpenProcessToken error: %u\n", GetLastError());
		return FALSE;
	}

	if (!LookupPrivilegeValue(NULL,           // lookup privilege on local system
		lpszPrivilege,  // privilege to lookup 
		&luid))        // receives LUID of privilege
	{
		printf("LookupPrivilegeValue error: %u\n", GetLastError());
		return FALSE;
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	if (bEnablePrivilege)
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
		tp.Privileges[0].Attributes = 0;

	// Enable the privilege or disable all privileges.
	if (!AdjustTokenPrivileges(hToken,
		FALSE,
		&tp,
		sizeof(TOKEN_PRIVILEGES),
		(PTOKEN_PRIVILEGES)NULL,
		(PDWORD)NULL))
	{
		printf("AdjustTokenPrivileges error: %u\n", GetLastError());
		return FALSE;
	}

	if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
	{
		printf("The token does not have the specified privilege. \n");
		return FALSE;
	}

	return TRUE;
}

int main(int argc, char *argv[])
{
	DWORD dwPID = 0;

	if (argc != 2)
	{
		printf("\n USAGE  : %s <pid>\n", argv[0]);
		return 1;
	}

	// change privilege
	if (!SetPrivilege(SE_DEBUG_NAME, TRUE))
		return 1;

	// code injection
	dwPID = (DWORD)atol(argv[1]);
	InjectCode(dwPID);

	return 0;
}

 

 

InjectCode()是代码注入技术的核心部分。两次调用VirtualAllocEx和WriteProcessMemory,第一次调用分配内存空间并写入远程线程的数据,第二次调用分配内存空间并写入远程线程的代码。这是代码注入的一个非常重要的特征

 

下面动态调试代码注入的过程

首先用OD调试notepad.exe ,F9跑起来

然后设置OD,选项 调试设置 勾选中断于新线程

现在开始,每当notepad.exe进程中生成新的线程,调试器就暂停在线程函数开始的代码位置,现在运行CodeInjection.exe,输入相应的PID

[esp+8]就是Iparam参数

插入到notepad.exe进程的汇编代码(宏观)

高级语言中函数大小的计算方法类似于汇编语言中代码所占空间的计算方法(详情可以百度或者查阅《计算机组成原理和汇编语言》一书)

 

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuenixiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值