目前公开的DLL注入技巧共有如下几种:编程
远程线程注入的方式在于使用一个Windows API函数CreateRemoteThread,经过它能够在另一个进程中注入一个线程并执行。api
操做系统:win10_64位 被注入程序:系统自带notepad_32位
Procexp : 查看进程模块是否被注入成功的工具
查找处理窗口的类名和窗口名称匹配指定的字符串缓存
WINUSERAPI HWND WINAPI FindWindow( //参数指向类名 _In_opt_ LPCSTR lpClassName, //指向窗口名 _In_opt_ LPCSTR lpWindowName); WINUSERAPI
指向一个以NULL字符结尾的、用来指定类名的字符串或一个能够肯定类名字符串的原子。若是这个参数是一个原子,那么它必须是一个在调用此函数前已经经过GlobalAddAtom函数建立好的全局原子。这个原子(一个16bit的值),必须被放置在lpClassName的低位字节中,lpClassName的高位字节置零。
若是该参数为null时,将会寻找任何与lpWindowName参数匹配的窗口。安全
指向一个以NULL字符结尾的、用来指定窗口名(即窗口标题)的字符串。若是此参数为NULL,则匹配全部窗口名。函数
若是函数执行成功,则返回值是拥有指定窗口类名或窗口名的窗口的句柄。工具
若是函数执行失败,则返回值为 NULL。能够经过调用GetLastError函数得到更加详细的错误信息。操作系统
找出某个进程的PID值。线程
WINUSERAPI DWORD WINAPI GetWindowThreadProcessId( //窗口的句柄 _In_ HWND hWnd, //进程号的存放地址(变量地址) _Out_opt_ LPDWORD lpdwProcessId);
返回线程号,注意,lpdwProcessId 是存放进程号的变量。指针
DWORD dwPID, dwTID; dwTID = GetWindowThreadProcessId( hWnd, &dwPID );
能够在其余进程分配或者预约一块虚拟内存code
WINBASEAPI _Ret_maybenull_ _Post_writable_byte_size_(dwSize) LPVOID WINAPI VirtualAllocEx( //申请内存所在的进程句柄。 _In_ HANDLE hProcess, //保留页面的内存地址;通常用NULL自动分配 。 _In_opt_ LPVOID lpAddress, //欲分配的内存大小,字节单位; //注意实际分配的内存大小是页内存大小的整数倍 _In_ SIZE_T dwSize, //指定内存分配的方式,预约仍是要提交 _In_ DWORD flAllocationType, //指定应用程序读写的权限,内存的保护属性 _In_ DWORD flProtect );
MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)
MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可经过继续调用VirtualAlloc()而被占用
MEM_RESET :指明在内存中由参数lpAddress和dwSize指定的数据无效
MEM_TOP_DOWN:在尽量高的地址上分配内存(Windows 98忽略此标志)
MEM_WRITE_WATCH:必须与MEM_RESERVE一块儿指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)
PAGE_READONLY: 该区域为只读。若是应用程序试图访问区域中的页的时候,将会被拒绝访
PAGE_READWRITE 区域可被应用程序读写
PAGE_EXECUTE: 区域包含可被系统执行的代码。试图读写该区域的操做将被拒绝。
PAGE_EXECUTE_READ :区域包含可执行代码,应用程序能够读该区域。
PAGE_EXECUTE_READWRITE: 区域包含可执行代码,应用程序能够读写该区域。
PAGE_GUARD: 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其余保护标志合并使用,代表区域被第一次访问的权限
PAGE_NOACCESS: 任何访问该区域的操做将被拒绝
PAGE_NOCACHE: RAM中的页映射到该区域时将不会被微处理器缓存(cached)
注:PAGE_GUARD和PAGE_NOCHACHE标志能够和其余标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防御页(guard page),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。
执行成功就返回分配内存的首地址,不成功就是NULL。
此函数能写入某一进程的内存区域。
WINBASEAPI _Success_(return != FALSE) BOOL WINAPI WriteProcessMemory( //由OpenProcess返回的进程句柄。 _In_ HANDLE hProcess, //要写的内存首地址 _In_ LPVOID lpBaseAddress, //指向要写的数据的指针。 _In_reads_bytes_(nSize) LPCVOID lpBuffer, //要写入的字节数。 _In_ SIZE_T nSize, //可选参数,指向变量的指针接收的字节数转移到指定的过程 _Out_opt_ SIZE_T * lpNumberOfBytesWritten );
非零值表明成功。
可用GetLastError获取更多的错误详细信息。
能够建立一个在其它进程地址空间中运行的线程。
WINBASEAPI _Ret_maybenull_ HANDLE WINAPI CreateRemoteThread( //线程所属进程的进程句柄. _In_ HANDLE hProcess, //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性. _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程初始大小,以字节为单位,若是该值设为0,那么使用系统默认大小. _In_ SIZE_T dwStackSize, //在远程进程的地址空间中,该线程的线程函数的起始地址. _In_ LPTHREAD_START_ROUTINE lpStartAddress, //传给线程函数的参数. _In_opt_ LPVOID lpParameter, 线程的建立标志. _In_ DWORD dwCreationFlags, //指向所建立线程ID的指针,若是建立失败,该参数为NULL. _Out_opt_ LPDWORD lpThreadId );
若是调用成功,返回新线程句柄.
若是失败,返回NULL.
Windows API函数。当等待仍在挂起状态时,句柄被关闭。
WINBASEAPI DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, _In_ DWORD dwMilliseconds );
对象句柄。能够指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。
定时时间间隔,单位为milliseconds(毫秒).若是指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。若是dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它老是当即返回。若是dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。
执行成功,返回值指示出引起函数返回的事件.
VirtualFreeEx即为目标进程的句柄,可在其它进程中释放申请的虚拟内存空间。
WINBASEAPI BOOL WINAPI VirtualFreeEx( //目标进程的句柄。该句柄必须拥有 PROCESS_VM_OPERATION 权限。 _In_ HANDLE hProcess, //指向要释放的虚拟内存空间首地址的指针。 _Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress, //虚拟内存空间的字节数。 _In_ SIZE_T dwSize, //释放类型 _In_ DWORD dwFreeType );
释放类型,取值见下表:
表示释放后内存空间不可用,可是内存页还存在
表示内存被释放,内存页彻底回收
#include "stdafx.h" #include <Windows.h> #define path _T("E:\\09核心编程\\_1304~1\\win原理下1\\Debug\\test.dll") bool Inject(DWORD dwId,WCHAR* szPath) { //1 在目标进程中申请一个空间 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId); LPVOID pRemoteAddress = VirtualAllocEx( hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE ); //2 把dll的路径写入到对方的进程空间中 DWORD dwWriteSize = 0; //写一段数据到指定进程所开辟的内存空间 WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize); //3 建立一个远程线程,让目标进程调用LoadLibrary HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL ); WaitForSingleObject(hThread, -1); //4 释放申请的虚拟内存空间 VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT); return 0; } int _tmain(int argc, _TCHAR* argv[]) { DWORD dwId = 0; printf("请输入一个ID:"); HWND hCalc = FindWindow(NULL, L"FileCleaner2.0"); DWORD dwPid = 0; DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid); //选择本身输出PID或者自动获取 // scanf_s("%d", &dwId); Inject(dwPid, path); return 0; }
#include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { MessageBox(NULL, L"呵呵", L"成功了,ye", NULL); } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
注入成果效果图:
注入后的DLL查看: