15.1 DLL注入

目前公开的DLL注入技巧共有以下几种:

  • 1、注入表注入
  • 2、ComRes注入
  • 3、APC注入
  • 4、消息钩子注入
  • 5、远线程注入
  • 6、依赖可信进程注入
  • 7、劫持进程创建注入
  • 8、输入法注入

远程线程注入的方式在于使用一个Windows API函数CreateRemoteThread,通过它可以在另外一个进程中注入一个线程并执行。

实验环境

  1. 操作系统:win10_64
  2. 被注入程序:系统自带notepad_32

工具整理

  1. Procexp 查看进程模块是否被注入成功的工具

编程思路

  • 1 在目标进程中申请一个空间
  • 2 把dll的路径写入到对方的进程空间中
  • 3 创建一个远程线程,让目标进程调用LoadLibrary
  • 4 释放申请的虚拟内存空间

使用的API:

FindWindow

查找处理窗口的类名和窗口名称匹配指定的字符串

  1. WINUSERAPI
  2. HWND
  3. WINAPI
  4. FindWindow(
  5. //参数指向类名
  6. _In_opt_ LPCSTR lpClassName,
  7. //指向窗口名
  8. _In_opt_ LPCSTR lpWindowName);
  9. WINUSERAPI
  • lpClassName

指向一个以NULL字符结尾的、用来指定类名的字符串或一个可以确定类名字符串的原子。如果这个参数是一个原子,那么它必须是一个在调用此函数前已经通过GlobalAddAtom函数创建好的全局原子。这个原子(一个16bit的值),必须被放置在lpClassName的低位字节中,lpClassName的高位字节置零。

如果该参数为null时,将会寻找任何与lpWindowName参数匹配的窗口。

  • lpWindowName

指向一个以NULL字符结尾的、用来指定窗口名(即窗口标题)的字符串。如果此参数为NULL,则匹配所有窗口名。

返回值

如果函数执行成功,则返回值是拥有指定窗口类名或窗口名的窗口的句柄。

如果函数执行失败,则返回值为 NULL。可以通过调用GetLastError函数获得更加详细的错误信息。

GetWindowThreadProcessId

找出某个进程的PID值。

  1. WINUSERAPI
  2. DWORD
  3. WINAPI
  4. GetWindowThreadProcessId(
  5. //窗口的句柄
  6. _In_ HWND hWnd,
  7. //进程号的存放地址(变量地址)
  8. _Out_opt_ LPDWORD lpdwProcessId);

返回值

返回线程号,注意,lpdwProcessId 是存放进程号的变量。

  1. DWORD dwPID, dwTID;
  2. dwTID = GetWindowThreadProcessId( hWnd, &dwPID );

VirtualAllocEx

可以在其他进程分配或者预定一块虚拟内存

  1. WINBASEAPI
  2. _Ret_maybenull_ _Post_writable_byte_size_(dwSize)
  3. LPVOID
  4. WINAPI
  5. VirtualAllocEx(
  6. //申请内存所在的进程句柄。
  7. _In_ HANDLE hProcess,
  8. //保留页面的内存地址;一般用NULL自动分配 。
  9. _In_opt_ LPVOID lpAddress,
  10. //欲分配的内存大小,字节单位;
  11. //注意实际分配的内存大小是页内存大小的整数倍
  12. _In_ SIZE_T dwSize,
  13. //指定内存分配的方式,预定还是要提交
  14. _In_ DWORD flAllocationType,
  15. //指定应用程序读写的权限,内存的保护属性
  16. _In_ DWORD flProtect
  17. );

flAllocationType 可取下列值:

  • MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储

  • MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)

  • MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用

  • MEM_RESET :指明在内存中由参数lpAddress和dwSize指定的数据无效

  • MEM_TOP_DOWN:在尽可能高的地址上分配内存(Windows 98忽略此标志)

  • MEM_WRITE_WATCH:必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)

flProtect可取下列值:

  • 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。

WriteProcessMemory

此函数能写入某一进程的内存区域。

  1. WINBASEAPI
  2. _Success_(return != FALSE)
  3. BOOL
  4. WINAPI
  5. WriteProcessMemory(
  6. //由OpenProcess返回的进程句柄。
  7. _In_ HANDLE hProcess,
  8. //要写的内存首地址
  9. _In_ LPVOID lpBaseAddress,
  10. //指向要写的数据的指针。
  11. _In_reads_bytes_(nSize) LPCVOID lpBuffer,
  12. //要写入的字节数。
  13. _In_ SIZE_T nSize,
  14. //可选参数,指向变量的指针接收的字节数转移到指定的过程
  15. _Out_opt_ SIZE_T * lpNumberOfBytesWritten
  16. );

返回值

非零值代表成功。

可用GetLastError获取更多的错误详细信息。

CreateRemoteThread

可以创建一个在其它进程地址空间中运行的线程。

  1. WINBASEAPI
  2. _Ret_maybenull_
  3. HANDLE
  4. WINAPI
  5. CreateRemoteThread(
  6. //线程所属进程的进程句柄.
  7. _In_ HANDLE hProcess,
  8. //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性.
  9. _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
  10. //线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
  11. _In_ SIZE_T dwStackSize,
  12. //在远程进程的地址空间中,该线程的线程函数的起始地址.
  13. _In_ LPTHREAD_START_ROUTINE lpStartAddress,
  14. //传给线程函数的参数.
  15. _In_opt_ LPVOID lpParameter,
  16. 线程的创建标志.
  17. _In_ DWORD dwCreationFlags,
  18. //指向所创建线程ID的指针,如果创建失败,该参数为NULL.
  19. _Out_opt_ LPDWORD lpThreadId
  20. );

返回值

如果调用成功,返回新线程句柄.

如果失败,返回NULL.

WaitForSingleObject

Windows API函数。当等待仍在挂起状态时,句柄被关闭。

  1. WINBASEAPI
  2. DWORD
  3. WINAPI
  4. WaitForSingleObject(
  5. _In_ HANDLE hHandle,
  6. _In_ DWORD dwMilliseconds
  7. );
  • hHandle

对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。

  • dwMilliseconds

定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

返回值

执行成功,返回值指示出引发函数返回的事件.

VirtualFreeEx

VirtualFreeEx即为目标进程的句柄,可在其它进程中释放申请的虚拟内存空间。

  1. WINBASEAPI
  2. BOOL
  3. WINAPI
  4. VirtualFreeEx(
  5. //目标进程的句柄。该句柄必须拥有 PROCESS_VM_OPERATION 权限。
  6. _In_ HANDLE hProcess,
  7. //指向要释放的虚拟内存空间首地址的指针。
  8. _Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,
  9. //虚拟内存空间的字节数。
  10. _In_ SIZE_T dwSize,
  11. //释放类型
  12. _In_ DWORD dwFreeType
  13. );
  • dwFreeType

释放类型,取值见下表:

    • MEM_DECOMMIT

表示释放后内存空间不可用,但是内存页还存在

    • MEM_RELEASE

表示内存被释放,内存页完全回收

远程线程注入源码:


  1. #include "stdafx.h"
  2. #include <Windows.h>
  3. #define path _T("E:\\09核心编程\\_1304~1\\win原理下1\\Debug\\test.dll")
  4. bool Inject(DWORD dwId,WCHAR* szPath)
  5. {
  6. //1 在目标进程中申请一个空间
  7. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
  8. LPVOID pRemoteAddress = VirtualAllocEx(
  9. hProcess,
  10. NULL,
  11. 1,
  12. MEM_COMMIT,
  13. PAGE_READWRITE
  14. );
  15. //2 把dll的路径写入到对方的进程空间中
  16. DWORD dwWriteSize = 0;
  17. //写一段数据到指定进程所开辟的内存空间
  18. WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);
  19. //3 创建一个远程线程,让目标进程调用LoadLibrary
  20. HANDLE hThread = CreateRemoteThread(
  21. hProcess,
  22. NULL,
  23. 0,
  24. (LPTHREAD_START_ROUTINE)LoadLibrary,
  25. pRemoteAddress,
  26. NULL,
  27. NULL
  28. );
  29. WaitForSingleObject(hThread, -1);
  30. //4 释放申请的虚拟内存空间
  31. VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT);
  32. return 0;
  33. }
  34. int _tmain(int argc, _TCHAR* argv[])
  35. {
  36. DWORD dwId = 0;
  37. printf("请输入一个ID:");
  38. HWND hCalc = FindWindow(NULL, L"FileCleaner2.0");
  39. DWORD dwPid = 0;
  40. DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid);
  41. //选择自己输出PID或者自动获取
  42. // scanf_s("%d", &dwId);
  43. Inject(dwPid, path);
  44. return 0;
  45. }

DLL源码

  1. #include "stdafx.h"
  2. BOOL APIENTRY DllMain( HMODULE hModule,
  3. DWORD ul_reason_for_call,
  4. LPVOID lpReserved
  5. )
  6. {
  7. switch (ul_reason_for_call)
  8. {
  9. case DLL_PROCESS_ATTACH:
  10. {
  11. MessageBox(NULL, L"呵呵", L"成功了,ye", NULL);
  12. }
  13. break;
  14. case DLL_THREAD_ATTACH:
  15. case DLL_THREAD_DETACH:
  16. case DLL_PROCESS_DETACH:
  17. break;
  18. }
  19. return TRUE;
  20. }

注入成果效果图:

注入后的DLL查看:

【windows核心编程】远程线程DLL注入的更多相关文章

  1. 实现远程线程DLL注入

    ### 32位:远程线程注入 远程线程注入是最常用的一种注入技术,该技术利用的核心API是 `CreateRemoteThread()` 这个API可以运行远程线程,其次通过创建的线程调用 `Load ...

  2. windows:shellcode 远程线程hook/注入(三)

    今天介绍第三种远程执行shellcode的思路:函数回调: 1.所谓回调,简单理解: windows出厂时,内部有很多事务的处理无法固化(无法100%预料外部会遇到哪些情况),只能留下一堆的接口,让开 ...

  3. windows:shellcode 远程线程hook/注入(一)

    https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...

  4. 远程线程DLL注入64位进程

    int main() { BOOL bFlag = FALSE; char *szDllName = "MSGDLL.dll"; //bFlag = EnablePrivilege ...

  5. Windows核心编程 第二十章 DLL的高级操作技术

    第2 0章 D L L的高级操作技术 看了下这章的内容,谈不上高级,都是些常用相关,但是还是有一些细节需要注意. 20.1 DLL模块的显式加载和符号链接 如果线程需要调用D L L模块中的函数,那么 ...

  6. windows:shellcode 远程线程hook/注入(五)

    前面几篇文章介绍了通过APC注入.进程注入.windows窗口处理函数回调.kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行she ...

  7. windows:shellcode 远程线程hook/注入(二)

    https://www.cnblogs.com/theseventhson/p/13218651.html   上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...

  8. 【windows核心编程】线程局部存储TLS

    线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来 ...

  9. 《windows核心编程》- 线程栈

    当系统创建线程的时候,会为线程栈预订一块地址空间区域,并给该区域调拨一些物理存储器.默认会预订1MB的地址空间并调拨两个页面的存储器.但是在构建 应用程序的时候可以改变这个默认值 在构建应用程序的时候 ...

随机推荐

  1. bzoj1003/luogu1772 物流运输 (dijkstra+dp)

    先求出某一段时间[i,j]一直用同一个路径的最短路,乘上天数,记作cost[i,j] 那就可以设f[i]是前i天的最小代价,f[i]=f[j]+cost[j+1,i]+K #include<bi ...

  2. oracle调用DLL

    具体步骤:1.创建Oracle Library  Create Library  AAA as  'C:\UserData\xuxia\TestProc\Debug\TestProc.dll' 可以通 ...

  3. 【bzoj4818】 Sdoi2017—序列计数

    http://www.lydsy.com/JudgeOnline/problem.php?id=4818 (题目链接) 题意 一个长度为$n$的序列,每个元素是不超过$m$的正整数,且这$n$个数的和 ...

  4. 【洛谷P1858】多人背包

    题目大意:求解 0-1 背包前 K 优解的和. 题解:首先,可知对于状态 \(dp[j]\) 来说,能够转移到该状态的只有 \(dp[j],dp[j-w[i]]\).对于 K 优解来说,只需对状态额外 ...

  5. SNP (Single Nucleotide Polymorphism), SNV ( single nucleotide variants ) , Indel (insertion-deletion) 的区别

    SNP (Single Nucleotide Polymorphism):强调在一个群体中具有一定频率的变异,一般为二态性.比如G→C SNV ( single nucleotide variants ...

  6. quartz简单定时任务【可以处理完一个任务才开启下一个线程】【我】

    maven jar project项目一个 pom文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:x ...

  7. Mysql错误 ERROR 1064 (42000): You have an error in your SQL syntax

    语法错误,可能有各种各样的问题. 我这个摔的太疼了. 字段名不能用空格..

  8. 深入分析HashMap

    本文基于jdk1.8 HashMap特点: HashMap具体方法分析: put方法分析: 执行流程图: public V put(K key, V value) { return putVal(ha ...

  9. js 数组与字符串的相互转化

    数组转字符串:join() 字符串转数组:split('')

  10. Cannot send, channel has already failed: tcp://127.0.0.1:8161

    解决方案一 我觉得你可能需要把服务完全停掉了 然后重启一下. http://localhost:8161(管理端口) tcp://127.0.0.1:61616(服务端口)