都符合PE结构
静态库(lib)
引用lib:
需要头文件和lib文件在当前项目文件夹中
#include "StaticLib1.h"
项目属性-链接器-输入:附加依赖项:加入lib文件
#pragma comment(lib,"路径")
然后直接调用静态库中的函数了.
动态库(dll)
创建动态链接库:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //附加到进程的时候触发事件
break;
case DLL_THREAD_ATTACH: //附加到线程的时候触发事件
break;
case DLL_THREAD_DETACH: //进程剥离的时候触发事件
break;
case DLL_PROCESS_DETACH: //线程剥离的时候触发事件
break;
}
return TRUE;
}
添加头文件,声明函数;
#include <iostream>
void show(const char* szBuffer);
导出:
_declspec(dllexport)void show(const char* szBuffer);
这种方式导出函数,会触发名称粉碎机制
extern"C"或EXTERN_C _declspec(dllexport)void show(const char* szBuffer);
这种方式导出函数,不会触发名称粉碎机制
添加模块定义文件
LIBRARY
EXPORTS
show
这种方式导出函数,不会触发名称粉碎机制
-
名称粉碎机制
C++独有
调用动态链接库:
把头文件粘贴到项目文件夹中(不粘贴也可以)
#include <Windows.h>
typedef void(*MyShow)(const char* szBuffer);
int main()
{
HMODULE hDll = LoadLibrary(L"Dll1.dll");
//在某个动态链接库中找到函数地址
MyShow func = (MyShow)GetProcAddress(hDll,"Show");
func("WdIg");
FreeLibrary(hDll);
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //附加到进程的时候触发事件
MessageBox(NULL,L"加载模块","rkvir",MB_OK);
break;
case DLL_THREAD_ATTACH: //附加到线程的时候触发事件
break;
case DLL_THREAD_DETACH: //进程剥离的时候触发事件
break;
case DLL_PROCESS_DETACH: //线程剥离的时候触发事件
break;
}
return TRUE;
}
附加到进程的时候,会先弹出对话框(不用导出,只要加载,就会执行)
- 被动加载:
远线程注入DLL
void Inject(DWORD PID, const WCHAR * szBuffer)
{
//打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
//在进程内存中申请一段内存
LPVOID lpAddr = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
//向内存中写入要注入的dll文件路径
SIZE_T dwWrittenSize = 0;
WriteProcessMemory(hProcess, lpAddr, szBuffer, (wcslen(szBuffer) + 1) * 2, &dwWrittenSize);
//创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddr, NULL, NULL);
WaitForSingleObject(hThread, -1);
VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hThread);
}
-
我们来分析一下这里的注入dll是如何实现的:
- 首先,我们打开了指定的进程(也就是我们要注入的进程)
- 然后,在打开的进程中,使用VirtualAllocEx API,申请一段内存,注意这里的内存属性为PAGE_READWRITE(可读可写)这里的内存用于保存我们要注入的dll文件路径
- 我们使用WriteProcessMemory API,将我们要注入的文件路径写入进程内存,参数(我们打开的进程句柄,写入我们刚申请的内存,要注入的dll文件路径,要写入的字符串长度,返回实际写入的长度)
- 之后,我们创建远程线程(也就是跨进程创建线程)参数(我们刚打开的(也就是要注入的进程)句柄,NULL,NULL,强转为LPTHREAD_START_ROUTINE的LoadLibraryW API,lpAddr,NULL,NULL);
- 等待线程执行结束
- 关闭无关句柄
-
重点:
为什么我们能够使用这种方式注入dll呢?创建远程现成的时候,不是要提供线程回调函数吗?我们来深究一下:
我们先来看看CreateRemoteThread API原型:
CreateRemoteThread( _In_ HANDLE hProcess, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD lpThreadId );
其他参数都正常传递,我们来看看第四个参数,也就是线程回调函数
这个参数是LPTHREAD_START_ROUTINE类型的,我们跟进去:
线程回调函数的原型实际上是这样:
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)( LPVOID lpThreadParameter ); typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
接下来我们再看看LoadLibraryW API:
HMODULE WINAPI LoadLibraryA( _In_ LPCSTR lpLibFileName );
我们来分析一下这俩函数:
首先,返回值,一个是DWORD,一个是HMODULE,在x86环境下,都是一个四字节整数,没有什么区别
然后,调用约定,都是WINAPI(__stdcall),也没有什么区别
最后,参数,都是需要指向一段内存,所以也没有什么区别
所以,这里的远线程注入dll,本质上就是跨进程创建一个线程,然后将LoadLibrary API当作线程回调使用了,所以就完成了注入。
遍历模块:
#include <TlHelp32.h>
void ShowModule(DWORD PID){
//创建快照:
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32,PID);
MODULEENTRY32 me32 ={size0f(MODULEENTRY32)};
while(MOdule32Next(hSnap,&me32)){
//这里添加操作
}
}
MFC动态链接库
只有一个入口点
优势:创建界面非常快