[C++] 进程注入dll版

前言

这几天在弄进程注入的事情,一直做不出来直接的进程注入,也就是不要dll的注入。因为dll注入据说容易触发杀软,但是弄了两天没弄出来。代码明明不报错,目标进程一旦注入就崩溃,完全没有提示信息,windows日志也没有什么有价值的线索。所以今天就尝试了dll注入,结果一次成功了。。。

前提

  • 进程间内存独立

运行在安全模式下的Windows,每个进程的内存空间都是独立的,互相不能访问彼此,所以代码注入才是一件比较麻烦的事情

  • 所有进程中 kernel32.dll地址相同

每个进程都会加载kernel32.dll,并且在那些进程中,这个库的地址都是一样的

原理

dll注入主要分为如下几个步骤

  • 提权:如果不提权的话,进程列表中很多进程是打不开的,并且可能获取不到PROCESS_ALL_ACCESS权限
  • 获取PID:这部分不是必须的,但是PID每次运行都不一样,所以最好能获取,这里通过CreateToolhelp32Snapshot()函数获取进程,遍历结果之后得到对应的PID
  • 打开进程:通过OpenProcess()函数打开进程,同时指定权限是PROCESS_ALL_ACCESS权限,否则后面操作会失败,如果要获取这个权限就得先提权
  • 通过VirtualAllocEx()函数在目标进程中申请内存,然后用WriteProcessMemory()函数将LoadLibraey()函数的参数准备好写入到内存中
  • 通过LoadLibrary()函数加载kernel32.dll,实际上进程都会加载这个,第二次加载似乎直接获取模块句柄了。
  • 通过GetProcAddress()函数获取到LoadLibraryW()函数的地址,因为kernel32.dll在所有进程中的地址都相同,所以,在本进程中获取的函数地址在目标进程中也是那个函数
  • 通过CreateRemoteThread()函数在目标进程中创建一个线程,执行LoadLibraryW()函数载入自己的dll

代码

  • 注入代码
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>

using namespace std;

DWORD GetPidByName(LPCWSTR lpName)
{
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	if (!hSnap)
	{
		cout << "创建进程快照失败" << endl;
		return 0;
	}
	PROCESSENTRY32 pe;
	pe.dwSize = sizeof(PROCESSENTRY32);
	Process32First(hSnap, &pe);
	do
	{
		if (!_wcsicmp(lpName, pe.szExeFile))
		{
			return pe.th32ProcessID;
		}
	} while (Process32Next(hSnap,&pe));
	return 0;
}

// 提权
int EnableDebugPrivilege()
{
	HANDLE token;
	TOKEN_PRIVILEGES tp;
	// 打开进程令牌环
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
	{
		cout << "打开进程令牌失败" << endl;
		return 0;
	}
	//  获取进程本地唯一ID
	LUID luid;
	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
	{
		cout << "获取LUID失败" << endl;
		return 0;
	}
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	tp.Privileges[0].Luid = luid;
	// 调整进程权限
	if (!AdjustTokenPrivileges(token, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
	{
		cout << "提权失败" << endl;
		return 0;
	}
	return 1;
}

int main()
{
	if (!EnableDebugPrivilege())
	{
		cout << "提权失败"<<endl;
		return 0;
	}
	DWORD dwTargetPid = GetPidByName(L"notepad.exe");
	if (!dwTargetPid)
	{
		cout << "获取PID失败" << endl;
		return 0;
	}
	cout << "PID: " << dwTargetPid << endl;

	// 打开进程
	HANDLE hTarget = OpenProcess(PROCESS_ALL_ACCESS,false,dwTargetPid);
	if (!hTarget)
	{
		cout << "打开进程失败"<<GetLastError() << endl;
		return 0;
	}

	void* pLoadLibFuncParam = nullptr;
	pLoadLibFuncParam = VirtualAllocEx(hTarget,0,4096,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
	if (pLoadLibFuncParam == nullptr)
	{
		cout << "申请内存失败" << endl;
		CloseHandle(hTarget);
		return 0;
	}

	LPCTSTR lpParam = L"D:\\1.dll";
	if (!WriteProcessMemory(hTarget, pLoadLibFuncParam, (LPCVOID)lpParam, (wcslen(lpParam)+1)*sizeof(TCHAR), NULL))
	{
		cout << "写入内存失败" << endl;
		CloseHandle(hTarget);
		return 0;
	}
	HMODULE hNtdll = LoadLibrary(L"kernel32.dll");
	if (!hNtdll)
	{
		cout << "加载模块错误" <<GetLastError()<< endl;
		CloseHandle(hTarget);
		return 0;
	}
	cout << "模块句柄: " << hNtdll << endl;
	void* pLoadLibrary = nullptr;
	pLoadLibrary = GetProcAddress(hNtdll, "LoadLibraryW");
	if (pLoadLibrary == nullptr)
	{
		cout << "找不到函数" << endl;
		CloseHandle(hTarget);
		return 0;
	}
	cout << "函数地址: " << pLoadLibrary << endl;
	DWORD dwThreadId = 0;
	HANDLE hRemoteThread = CreateRemoteThread(hTarget, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, (LPVOID)pLoadLibFuncParam, 0,&dwThreadId);
	if (!hRemoteThread)
	{
		cout << "创建进程失败" << endl;
		CloseHandle(hTarget);
		return 0;
	}
	cout<<"运行结束"<< hRemoteThread <<endl;
	getchar();
	getchar();
	CloseHandle(hTarget);
	return 0;
}
  • DLL中的代码

代码编译之后,放到D:\1.dll的位置

DWORD WINAPI ThreadProc()
{
    MessageBox(NULL,L"我已成功打入敌人内部 By Startu",L"报告首长",0);
    return 0;
}

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

一些问题

  • 为什么是LoadLibraryW()

我最开始获取的函数是LoadLibrary()函数,但是实际上这个函数并不存在,这个只是一个宏,在windows中,有两个函数LoadLibraryW()LoadLibraryA()分别用于,不同的编码

  • 为什么需要在目标进程中申请地址存放参数

当代码被注入到目标进程之后,实际上这段代码算是目标进程的,如果直接传入参数的本质是传入地址,直接在本进程中传入地址,

  • 调试问题

dll中的代码,所有的printf() 或者 cout的输出都看不到,曾经我诧异了很久,直到突然想起来,这里的输出都应该是由宿主程序输出的,但是宿主程序没有控制台,所以就看不到了。这种时候 MessageBox()就要用到了。另外,通过 命令行rundll32 1.dll func的方式可以加载dll,执行里面的函数

posted @ 2020-06-21 11:21  Startu  阅读(1613)  评论(0编辑  收藏  举报