无敌舍友s神免杀学了一个阶段,达者为师,向s师傅学习!!
ps:我的基础实在薄弱,WIN编程甚至都没做过,所以笔记翔实些
一、注入思路
1.在进程中开辟一段空间
2.存入dll绝对路径地址的字符串
3.使用RemoteCreateThread函数带起LoadLibrary函数带起dll
二、具体实现
(一)获取进程ID
CreateToolhelp32Snapshot
、Process32First
和Process32Next
函数。这些函数可以让你遍历系统中所有运行的进程,并获取每个进程的名称和ID。
- 创建进程快照: 使用
CreateToolhelp32Snapshot
函数创建一个系统快照,包含所有进程的信息。 - 遍历进程: 使用
Process32First
和Process32Next
函数遍历快照中的所有进程。 - 匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
- 返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。
1、CreateToolhelp32Snapshot
CreateToolhelp32Snapshot
是Windows API中的一个函数,用于创建系统快照(snapshot),包含指定类型的系统信息。这个快照可以包含进程、线程、堆、模块等信息。创建快照后,你可以使用其他工具帮助API函数(如Process32First
、Process32Next
)来遍历这些信息。
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID
);
-
dwFlags
: 指定快照中包含的信息类型,可以是以下值的组合:TH32CS_SNAPHEAPLIST
: 包含指定进程的堆列表。TH32CS_SNAPMODULE
: 包含指定进程的模块列表。TH32CS_SNAPPROCESS
: 包含系统中所有进程的列表。TH32CS_SNAPTHREAD
: 包含系统中所有线程的列表。TH32CS_SNAPALL
: 包含所有上述信息(等同于TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD
)。TH32CS_INHERIT
: 指定快照句柄是可继承的。
-
th32ProcessID
: 指定进程ID,如果dwFlags
包含TH32CS_SNAPHEAPLIST
、TH32CS_SNAPMODULE
或TH32CS_SNAPALL
,则该参数指定要包含在其快照中的进程ID。如果dwFlags
包含TH32CS_SNAPPROCESS
或TH32CS_SNAPTHREAD
,则该参数可以为0,表示包含所有进程或线程。
- 如果函数成功,返回一个快照句柄(
HANDLE
)。 - 如果函数失败,返回
INVALID_HANDLE_VALUE
(通常为-1
)。
//创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//差错控制
if (snapShot == INVALID_HANDLE_VALUE) {
printf("[-] SnapShot ERROR");
getchar();
exit(-1);
}
2、Process32First和Process32Next
Process32First
和 Process32Next
是 Windows API 中的两个函数,用于遍历通过 CreateToolhelp32Snapshot
创建的进程快照中的所有进程。这两个函数通常一起使用,以遍历系统中的所有进程。
BOOL WINAPI Process32First(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);
BOOL WINAPI Process32Next(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);
hSnapshot
: 通过CreateToolhelp32Snapshot
创建的快照句柄。lppe
: 指向PROCESSENTRY32
结构的指针,用于接收进程信息。
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID; // 进程ID
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID; // 父进程ID
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH]; // 进程可执行文件名
} PROCESSENTRY32, *PPROCESSENTRY32;
//遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
PROCESSENTRY32 p32;
p32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapShot, &p32)) {
do {
if (wcscmp(p32.szExeFile, processName)){
TargetID = p32.th32ProcessID;
break;
}
} while (Process32Next(hSnapShot, &p32));
}
else {
printf("[-] Get first process ERROR");
getchar();
exit(-1);
}
return TargetID;
3、GetProcessIDByName函数实现
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <string>
#include <cstdio>
DWORD GetProcessIDByName(LPCTSTR processName) {
DWORD TargetID = -1;
//创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//差错控制
if (hSnapShot == INVALID_HANDLE_VALUE) {
printf("[-] SnapShot ERROR");
getchar();
return -1;
}
//遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
PROCESSENTRY32 p32;
p32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapShot, &p32)) {
do {
//匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
if (!wcscmp(p32.szExeFile, processName)){
TargetID = p32.th32ProcessID;
break;
}
} while (Process32Next(hSnapShot, &p32));
}
else {
printf("[-] Get first process ERROR");
getchar();
return -1;
}
//返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。
return TargetID;
}
int main() {
DWORD id=GetProcessIDByName(L"notepad.exe");
printf("[+] Find target process id: %d", id);
return 0;
}
(二)DLL注入
1、OpenProcess
OpenProcess
是 Windows API 中的一个函数,用于获取一个已经存在的进程的句柄。这个句柄可以用来对进程进行各种操作,如读取或写入进程内存、终止进程、查询进程信息等。
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
- dwDesiredAccess:
- 指定对进程的访问权限。可以是一组标志的组合,常见的标志包括:
PROCESS_ALL_ACCESS
:所有权限。PROCESS_CREATE_THREAD
:创建线程。PROCESS_VM_OPERATION
:操作进程内存。PROCESS_VM_READ
:读取进程内存。PROCESS_VM_WRITE
:写入进程内存。PROCESS_DUP_HANDLE
:复制句柄。PROCESS_TERMINATE
:终止进程。PROCESS_QUERY_INFORMATION
:查询进程信息。PROCESS_SET_INFORMATION
:设置进程信息。
-
bInheritHandle:
- 指定返回的句柄是否可以被新创建的子进程继承。如果为
TRUE
,则子进程可以继承该句柄;如果为FALSE
,则不能继承。
- 指定返回的句柄是否可以被新创建的子进程继承。如果为
-
dwProcessId:
- 指定要打开的进程的标识符(Process ID)。
- 如果函数成功,返回值是一个指向指定进程的句柄。
- 如果函数失败,返回值为
NULL
。可以使用GetLastError
函数获取扩展错误信息。
//1. 获取目标进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
printf("[-] OpenProcess Error!");
getchar();
return -1;
}
2、VirtualAllocEx
VirtualAllocEx
是 Windows API 中的一个函数,用于在指定进程的地址空间内分配内存。这个函数主要用于进程间通信、代码注入等操作。
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
-
hProcess:
- 目标进程的句柄。这个句柄通常通过
OpenProcess
函数获得。
- 目标进程的句柄。这个句柄通常通过
-
lpAddress:
- 指定希望分配内存的起始地址。如果传入
NULL
,系统会自动选择一个合适的起始地址。
- 指定希望分配内存的起始地址。如果传入
-
dwSize:
- 要分配的内存区域的大小(以字节为单位)。
-
flAllocationType:
- 指定内存分配的类型。常见的类型包括:
MEM_COMMIT
:提交物理存储,为内存区域分配物理内存。MEM_RESERVE
:保留地址空间,但不分配物理内存。MEM_RESET
:标记指定内存区域的内容为未初始化状态。MEM_RESET_UNDO
:取消MEM_RESET
的影响。MEM_LARGE_PAGES
:使用大页内存(需要特殊的权限)。
- 指定内存分配的类型。常见的类型包括:
-
flProtect:
- 指定内存保护类型。常见的类型包括:
PAGE_READONLY
:只读。PAGE_READWRITE
:读写。PAGE_EXECUTE
:可执行。PAGE_EXECUTE_READ
:可执行、只读。PAGE_EXECUTE_READWRITE
:可执行、读写。PAGE_GUARD
:标记为“守卫页”,访问时触发异常。
- 指定内存保护类型。常见的类型包括:
- 如果函数成功,返回值是指向分配内存区域起始地址的指针。
- 如果函数失败,返回值为
NULL
。可以使用GetLastError
函数获取扩展错误信息。
//2. 在目标进程申请分配一块空间
DWORD size = (wcslen(dllName)+1)* sizeof(TCHAR);
LPVOID allocAddr = VirtualAllocEx(hProcess,NULL,size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (allocAddr == NULL) {
printf("[-] VirtualAllocEx Error!");
getchar();
return -1;
}
3、WriteProcessMemory
WriteProcessMemory
是 Windows API 中的一个函数,它允许你将数据写入另一个进程的内存空间。这个函数通常用于进程间通信、调试、代码注入等场景。
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
-
hProcess:
- 目标进程的句柄。这个句柄通常通过
OpenProcess
函数获得。
- 目标进程的句柄。这个句柄通常通过
-
lpBaseAddress:
- 目标进程中要写入数据的目标地址。
-
lpBuffer:
- 指向要写入目标进程的数据缓冲区的指针。
-
nSize:
- 要写入的数据的大小(以字节为单位)。
-
lpNumberOfBytesWritten:
- 指向一个变量的指针,用于接收实际写入的字节数。如果不需要这个信息,可以传入
NULL
。
- 指向一个变量的指针,用于接收实际写入的字节数。如果不需要这个信息,可以传入
- 如果函数成功,返回值为
TRUE
。 - 如果函数失败,返回值为
FALSE
。可以使用GetLastError
函数获取扩展错误信息。
//3. 往目标进程的目标空间写数据
if (!WriteProcessMemory(hProcess,addr,dllName,size,NULL)){
printf("[-] WriteProcessMemory Error!");
getchar();
return -1;
}
4、GetModuleHandle和GetProcAddress
获取 kernel32.dll
中 LoadLibraryW
函数的地址,并将其转换为 LPTHREAD_START_ROUTINE
类型的函数指针。这个操作通常用于创建远程线程或在目标进程中加载 DLL 文件。
-
获取模块句柄:
HMODULE hModule = GetModuleHandle(L"kernel32.dll");
GetModuleHandle
函数返回指定模块的句柄。这里获取的是kernel32.dll
模块的句柄。
-
获取函数地址:
FARPROC pThread = GetProcAddress(hModule, "LoadLibraryW");
GetProcAddress
函数返回指定模块中指定函数的地址。这里获取的是LoadLibraryW
函数的地址。
-
转换为线程函数指针类型:
LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
LPTHREAD_START_ROUTINE
是一个指向线程函数的指针类型。这里将LoadLibraryW
函数的地址转换为LPTHREAD_START_ROUTINE
类型。
5、CreateRemoteThread
CreateRemoteThread 是 Windows API 中的一个函数,它允许你在另一个进程中创建一个线程。这个函数通常用于进程间通信、代码注入等高级操作。
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
-
hProcess:
- 目标进程的句柄。这个句柄通常通过
OpenProcess
函数获得。
- 目标进程的句柄。这个句柄通常通过
-
lpThreadAttributes:
- 指向
SECURITY_ATTRIBUTES
结构的指针,用于指定线程的安全属性。如果为NULL
,则使用默认安全属性。
- 指向
-
dwStackSize:
- 新线程的初始堆栈大小,以字节为单位。如果为 0,则使用与系统相关的默认值。
-
lpStartAddress:
- 指向新线程的起始地址(函数指针)。这个地址必须在目标进程的地址空间中有效。
-
lpParameter:
- 传递给新线程的参数。这个参数可以是任何类型,但必须是目标进程的地址空间中的有效地址。
-
dwCreationFlags:
- 控制线程创建的标志。常见的标志包括:
0
:线程在创建后立即运行。CREATE_SUSPENDED
:线程在创建后处于挂起状态,可以通过ResumeThread
函数恢复运行。
- 控制线程创建的标志。常见的标志包括:
-
lpThreadId:
- 指向一个变量的指针,用于接收新线程的 ID。如果不需要这个信息,可以传入
NULL
。
- 指向一个变量的指针,用于接收新线程的 ID。如果不需要这个信息,可以传入
- 如果函数成功,返回值为新线程的句柄。
- 如果函数失败,返回值为
NULL
。可以使用GetLastError
函数获取扩展错误信息。
//5. CreateRemoteThread创建线程
HANDLE remoteThread = CreateRemoteThread(hProcess,NULL,0, addr, allocAddr, 0,NULL);
if (remoteThread == NULL) {
printf("[-] CreateRemoteThread Error!");
getchar();
return -1;
}
6、WaitForSingleObject
WaitForSingleObject
是 Windows API 中的一个函数,用于等待指定的内核对象进入信号状态。这个函数常用于多线程编程中,以实现线程同步。
DWORD WaitForSingleObject(
HANDLE hHandle, // 要等待的内核对象的句柄
DWORD dwMilliseconds // 等待的时间(以毫秒为单位)
);
-
hHandle:要等待的内核对象的句柄。可以是以下类型的句柄:
- 线程(
CreateThread
返回的句柄) - 进程(
CreateProcess
返回的句柄) - 事件(
CreateEvent
返回的句柄) - 信号量(
CreateSemaphore
返回的句柄) - 互斥体(
CreateMutex
返回的句柄) - 定时器(
CreateWaitableTimer
返回的句柄) - 其他的可等待对象
- 线程(
-
dwMilliseconds:等待的时间(以毫秒为单位)。如果这个值为
INFINITE
,函数将无限期地等待下去,直到对象变为有信号状态。如果这个值为0
,函数将立即返回,不等待。
WaitForSingleObject
返回以下几种可能的值:
- WAIT_OBJECT_0:指定的对象变为有信号状态。
- WAIT_ABANDONED:当等待的对象是互斥体时,互斥体被放弃,通常是因为持有互斥体的线程在退出时没有释放它。
- WAIT_TIMEOUT:在指定的时间内,对象没有变为有信号状态。
- WAIT_FAILED:函数调用失败,可以使用
GetLastError
获取错误码。
//6. 等待线程结束
WaitForSingleObject(remoteThread, INFINITE);
7、VirtualFreeEx
VirtualFreeEx
是 Windows API 中的一个函数,用于在指定的进程的地址空间中释放之前通过 VirtualAllocEx
分配的虚拟内存区域。这个函数允许你释放内存页,并可以选择将这些页面中的内存标记为可用(即解除分配)。
BOOL VirtualFreeEx(
HANDLE hProcess, // 目标进程的句柄
LPVOID lpAddress, // 要释放的内存地址
SIZE_T dwSize, // 要释放的内存大小
DWORD dwFreeType // 释放类型
);
- hProcess:目标进程的句柄。这个句柄必须具有
PROCESS_VM_OPERATION
访问权限。 - lpAddress:指向要释放的内存区域的起始地址的指针。这个地址必须是之前通过
VirtualAllocEx
分配的内存区域的起始地址。 - dwSize:要释放的内存区域的大小(以字节为单位)。如果
dwSize
为 0 并且dwFreeType
为MEM_RELEASE
,则整个区域将被释放。 - dwFreeType:指定释放操作的类型。可以是以下值之一:
- MEM_DECOMMIT:解除分配指定页范围内的内存,但不释放这些页面。这些页面可以稍后重新提交。
- MEM_RELEASE:释放指定页范围内的内存,并将其标记为可用。释放后,这些页面不能再被访问。
- 如果函数成功,返回值为非零值。
- 如果函数失败,返回值为 0。可以使用
GetLastError
获取错误代码。
//7. 释放dll空间
VirtualFreeEx(hProcess, allocAddr, size, MEM_RELEASE);
三、完整代码
(一)弹窗demo.dll
#include <windows.h>
#include <iostream>
// 导出函数:显示消息框
extern "C" __declspec(dllexport) void ShowMessage() {
MessageBox(NULL, TEXT("Hello from MyDLL!"), TEXT("Message"), MB_OK);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
ShowMessage();
}
return TRUE;
}
(二)DLL注入
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <string>
#include <cstdio>
DWORD GetProcessIDByName(LPCTSTR processName) {
DWORD TargetID = -1;
//创建进程快照: 使用`CreateToolhelp32Snapshot`函数创建一个系统快照,包含所有进程的信息。
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//差错控制
if (hSnapShot == INVALID_HANDLE_VALUE) {
printf("[-] SnapShot ERROR");
getchar();
return -1;
}
//遍历进程: 使用`Process32First`和`Process32Next`函数遍历快照中的所有进程。
PROCESSENTRY32 p32;
p32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapShot, &p32)) {
do {
//匹配进程名: 对于每个进程,获取其进程名并与目标进程名进行比较。
if (!wcscmp(p32.szExeFile, processName)) {
TargetID = p32.th32ProcessID;
break;
}
} while (Process32Next(hSnapShot, &p32));
}
else {
printf("[-] Get first process ERROR");
getchar();
return -1;
}
//返回进程ID: 如果找到匹配的进程名,返回对应的进程ID。
return TargetID;
}
DWORD RemoteThreadInject(DWORD pid, LPCUWSTR dllName) {
//1. 获取目标进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
printf("[-] OpenProcess Error!");
getchar();
return -1;
}
//2. 在目标进程申请分配一块空间
DWORD size = (wcslen(dllName)+1)* sizeof(TCHAR);
LPVOID allocAddr = VirtualAllocEx(hProcess,NULL,size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (allocAddr == NULL) {
printf("[-] VirtualAllocEx Error!");
getchar();
return -1;
}
//3. 往目标进程的目标空间写数据
if (!WriteProcessMemory(hProcess,allocAddr,dllName,size,NULL)){
printf("[-] WriteProcessMemory Error!");
getchar();
return -1;
}
//4. 获取 `kernel32.dll` 中 `LoadLibraryW` 函数的地址,并将其转换为 `LPTHREAD_START_ROUTINE` 类型的函数指针
HMODULE hModule = GetModuleHandle(L"kernel32.dll");
FARPROC pThread = GetProcAddress(hModule, "LoadLibraryW");
LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
//5. CreateRemoteThread创建线程
HANDLE remoteThread = CreateRemoteThread(hProcess,NULL,0, addr, allocAddr, 0,NULL);
if (remoteThread == NULL) {
printf("[-] CreateRemoteThread Error!");
getchar();
return -1;
}
//6. 等待线程结束
WaitForSingleObject(remoteThread, INFINITE);
//7. 释放dll空间
VirtualFreeEx(hProcess, allocAddr, size, MEM_RELEASE);
//8. 关闭句柄
CloseHandle(hProcess);
return TRUE;
}
int main() {
DWORD processID = GetProcessIDByName(L"notepad.exe");
if (processID < 0) {
printf("[-] No Target Process");
getchar();
exit(-1);
}
if (RemoteThreadInject(processID, L"C:\\Users\\user\\Desktop\\WIN-AntiAV\\DLL-popwindow\\x64\\Debug\\DLL-popwindow.dll") < 0) {
printf("[-] Inject Failed");
getchar();
exit(-1);
}
return 0;
}