[笔记]Windows核心编程《二十二》注入DLL和拦截API(三)

简介: [笔记]Windows核心编程《二十二》注入DLL和拦截API(三)

四、使用远程线程来插入DLL

插入DLL的第三种方法是使用远程线程。这种方法具有更大的灵活性。

原理

特点

基本操作步骤

执行的操作步骤:

  1. 使用VirtualAllocEx函数,分配远程进程的地址空间中的内存。
  2. 使用WriteProcessMemory函数,将D L L的路径名拷贝到第一个步骤中已经分配的内存中。
  3. 使用GetProcAddress函数,获取 LoadLibraryA或LoadLibraryW函数的实地址(在Kernel32.dll中)。
  4. 使用CreateRemoteThread函数,在远程进程中创建一个线程,它调用正确的 LoadLibrary函数,为它传递第一个步骤中分配的内存的地址。这时, DLL已经被插入远程进程的地址空间中,同时 DLL的DllMain函数接收到一个DLL_PROCESS_ATTACH通知,并且能够执行需要的代码。当 DllMain函数返回时,远程线程从它对LoadLibrary的调用返回到 BaseThreadStart函数(第 6章中已经介绍)。然后BaseThreadStart调用ExitThread,使远程线程终止运行。现在远程进程拥有第一个步骤中分配的内存块,而 DLL则仍然保留在它的地址空间中。若要将它删除,需要在远程线程退出后执行下面的步骤:
  5. 使用VirtualFreeEx函数,释放第一个步骤中分配的内存。
  6. 使用GetProcAddress函数,获得FreeLibrary函数的实地址(在Kernel32.dll中)。
  7. 使用CreateRemoteThread函数,在远程进程中创建一个线程,它调用 FreeLibrary函数,传递远程DLL的HINSTANCE。

OpenProcess函数

OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。

HANDLE OpenProcess(
  DWORD dwDesiredAccess, //渴望得到的访问权限(标志)
  BOOL bInheritHandle, // 是否继承句柄
  DWORD dwProcessId// 进程标示符
);

dwDesiredAccess:获取的权限,可分为以下几种

PROCESS_ALL_ACCESS:获取所有权限

PROCESS_CREATE_PROCESS:创建进程

PROCESS_CREATE_THREAD:创建线程

PROCESS_DUP_HANDLE:使用DuplicateHandle()函数复制一个新句柄

PROCESS_QUERY_INFORMATION:获取进程的令牌、退出码和优先级等信息

PROCESS_QUERY_LIMITED_INFORMATION:获取进程特定的某个信息

PROCESS_SET_INFORMATION:设置进程的某种信息

PROCESS_SET_QUOTA:使用SetProcessWorkingSetSize函数设置内存限制

PROCESS_SUSPEND_RESUME:暂停或者恢复一个进程

PROCESS_TERMINATE:使用Terminate函数终止进程

PROCESS_VM_OPERATION:在进程的地址空间执行操作

PROCESS_VM_READ:使用ReadProcessMemory函数在进程中读取内存

PROCESS_VM_WRITE:使用WriteProcessMemory函数在进程中写入内存

SYNCHRONIZE:使用wait函数等待进程终止

bInheritHandle:TRUE或者FALSE

dwProcessId:pid

VirtualAllocEx函数

分配远程进程的地址空间中的内存

WriteProcessMemory函数

GetProcAddress函数

获取 LoadLibraryA或LoadLibraryW函数的实地址(在Kernel32.dll中)

CreateRemoteThread函数

能够创建一个在其它进程地址空间中运行的线程(也称为创建远程线程)。

HANDLE CreateRemoteThread(
     HANDLE hProcess,
     LPSECURITY_ATTRIBUTES lpThreadAttributes,
     SIZE_T dwStackSize,
     LPTHREAD_START_ROUTINE lpStartAddress,
     LPVOID lpParameter,
     DWORD dwCreationFlags,
     LPDWORD lpThreadId
);

hProcess:进程句柄

lpThreadAttributes : 线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针

dwStackSize:线程栈大小,以字节表示

lpStartAddress: 一个LPTHREAD_START_ROUTINE类型的指针,指向在远程进程中执行的函数地址

lpParameter: 传入参数

dwCreationFlags:创建线程的其它标志

lpThreadId:[输出] 线程身份标志,如果为NULL,则不返回

返回值:成功返回新线程句柄,失败返回NULL,并且可调用GetLastError获得错误值。

4.1 Inject Library示例应用程序

使用任务管理获取进程的I D。使用这个I D,该程序将设法通过调用 OpenProcess函数来打开正在运行的进程的句柄,申请相应的访问权。

原理

InjLib.exe应用程序使用CreateRemoteThread函数来插入ImgWalk.DLL

过程

  1. 使用界面输入的ProcesssId,然后获取ImgWalk.dll文件位置。
  2. 调用InjectLib:InjectLib内部调用OpenProcess获得ProcessId的句柄hProcess
  3. 通过进程句柄hProcess,
    调用VirtualAllocEx,分配远程进程的地址空间中的内存
  4. 使用WriteProcessMemory函数,将D L L的路径名拷贝到第一个步骤中已经分配的内存中。
  5. 调用GetProcAddress
    获取 LoadLibraryA或LoadLibraryW函数的实地址(在Kernel32.dll中)
  6. CreateRemoteThread,创建远程线程。
  7. 结束InjectLib后释放句柄hProcess,线程hThread,内存。
  8. 注入成功后,调用EjectLib,同InjectLib,只不过获得FreeLibrary地址,然后通过CreateRemoteThread调用FreeLibrary。

4.2 Image Walk DLL

ImgWalk.dll 是个D L L,一旦它被插入进程的地址空间,就能够报告该进程正在使用的所有 DLL。

原理

ImgWalk遍历进程的地址空间,查找已经映射的文件映像,反复调用 Virtual Query函数,填入一个MEMORY_BASIC_ INFORMATION结构中。运用循环的每个重复操作, ImgWalk找出一个文件路径名,并与一个字符串相连接。该字符串显示在消息框中。

五、使用特洛伊DLL来插入DLL

原理

利用自己伪造的同名DLL取代软件内的将要加载的Xyz.DLL。

特点

  • 在你的Xyz.dll中,输出的全部符号必须与原始的Xyz.dll输出的符号相同

实现方法 :

  1. 使用函数转发器

注意:

虽然函数转发器使你能够非常容易地挂接某些函数,你应该避免使用这种方法,因为它不具备版本升级能力。例如,如果你取代了一个系统 D L L, 而M i c r o s o f t在将来增加了一些新函数,那么你的 D L L将不具备它们的函数转发器。引用这些新函数的应用程序将无法加载和执行。

  1. 改变应用程序的.exe模块的输入节

注意:。

输入节只包含模块需要的 D L L的名字。你可以仔细搜索文件中的这个输入节,并且将它改变,使加载程序加载你自己的 D L L。这种方法相当不错,但是必须要非常熟悉. e x e和D L L文件的格式。

六、将DLL作为调试程序来插入

原理

调试程序能够对被调试的进程执行特殊的操作。

当被调试进程加载时,在被调试进程的地址空间已经作好准备,但是被调试进程的主线程尚未执行任何代码之前,系统将自动将这个情况通知调试程序时,调试程序可以强制将某些代码插入被调试进程的地址空间中(比如使用WriteProcessMemory函数来插入),然后使被调试进程的主线程执行该代码。

特点

  • 这种方法要求你对被调试线程的 CONTEXT结构进行操作,意味着必须编写特定 CPU的代码。必须修改你的源代码,使之能够在不同的 C P U平台上正确地运行。另外,必须对你想让被调试进程执行的机器语言指令进行硬编码。而且调试程序与它的被调试程序之间必须存在固定的关系。
  • 如果调试程序终止运行,Windows将自动撤消被调试进程。而你则无法阻止它。

七、用Windows 98上的内存映射文件插入代码

原理

在Windows 98上插入你自己的代码是非常简单的。在 Windows 98上运行的所有 3 2位 Wi n d o w s应用程序均共享同样的最上面的2 GB地址空间。如果你分配这里面的某些存储器,那么该存储器在每个进程的地址空间中均可使用。若要分配 2 GB以上的存储器,只要使用内存映射文件(第 1 7章已经介绍)。可以创建一个内存映射文件,然后调用 MapViewOfFile函数,使它显示出来。然后将数据填入你的地址空间区域(这是所有进程地址空间中的相同区域)。

特点

  • 必须使用硬编码的机器语言来进行填入数据到地址空间区域,很难移植到别的CPU平台。
  • 必须让其他进程中的线程来执行内存映射文件中的代码。要做到这一点,需要某种方法来控制远程进程中的线程。CreateRemoteThread函数能够很好地执行这个任务,可惜Windows 98不支持该函数的运行。

八、用CreateProcess插入代码

原理

通过当前进程来生成目标进程,在目标进程作为子进程启动之前先将其挂起。同时会得到其主线程的句柄。通过这个句柄可以对线程执行的代码进行修改。

方法

参考

让一个进程对其子进程的主线程进行控制的一种方法:

  1. 让进程生成一个被挂起的子进程
  2. 从exe模块的文件头中得到主线程的起始内存地址
  3. 将位于该内存地址处的机器指令保存起来
  4. 强制将一些手动编写的机器指令写入到该内存地址处。这些指令应该调用一个LoadLibrary来载入一个DLL
  5. 让子进程的主线程恢复运行,从而让这些指令得到执行。
  6. 把保存起来的原始指令恢复到起始地址处
  7. 让进程从起始地址继续执行,就好像什么都没发生一样。

特点

优点:

  • 它在应用程序执行之前就能得到地址空间
  • 它既能在Windows 98上使用,也能在Windows 2000上使用
  • 由于你不是调试者,因此能够很容易使用插入的D L L来调试应用程序
  • 这种方法可以同时用于控制台和 G U I应用程序。

缺点:

  • 只有当你的代码在父进程时,才能插入 DLL。
  • 不能跨越不同的C P U来运行,必须对不同的C P U平台进行相应的修改。

九、拦截API的一个示例

9.1 通过改写代码来拦截API

原理

用汇编代码JUMP覆盖需要拦截目标函数。

步骤

拦截ExitProcess的一个方案是用代码覆盖来拦截。

工作方式如下:

  1. 在内存中,对想要拦截的函数(假设是Kernel32.dll中的ExitProcess)进行定位,从而得到它的内存地址。
  2. 把这个函数起始的几个字节保存到自己的内存中
  3. 用CPU的一条JUMP指令来覆盖这个函数的起始几个字节,这条JUMP指令用来跳转到我们替代函数的内存地址。所有替代函数的签名必须要与拦截函数的签名完全相同:所有参数必须相同,返回值必须相同,调用约定也必须相同。
  4. 当线程调用被拦截函数(hooked function)的时候,跳转指令实际上会跳转到我们的替代函数。这是可以执行自己想要执行的代码。
  5. 为了撤销对该函数的拦截,必须把步骤2)中保存下来的直接放回被拦截函数的起始字节。
  6. 我们调用被拦截函数,让该函数正常处理
  7. 当原来的函数返回时,再次执行步骤2和步骤3,这样我们的代替函数将来还好被调用到。

特点

  • 它对cpu有依赖性x86, x64, ia-64其他cpu的JUMP指令各不相同,为了让这种方法工作必须手工编写机器指令。
  • 其次,这种方法在抢占式,多线程环境下根本不能工作。一个线程覆盖一个函数起始位置的代码是需要时间的,这个过程如果另一个线程试图调用同一个函数,结果是灾难性的。(除非保证任何时候只有一个线程会调用该函数)

9.2 通过操作模块的输入节来拦截API

一个模块的导入段包含一组DLL,为了让模块能够运行,这些DLL是必须的。此外导入段还包含一个符号表,列出了该模块从各DLL中导入的符号。当该模块调用一个导入函数的时候,实际上是先从该模块的导入表中得到相应的导入函数的地址,然后再跳转到那个地址。

原理

修改特点函数在模块的输入节(导入段)中的地址。

特点

  • 完全不存在对cpu的依赖性。
  • 而且并不需要修改函数的代码,
  • 也不用担心线程同步问题。

9.3 LastMsgBoxInfo示例应用程序

该例子拦截了所有对MessageBox(位于User32.dll中)的调用,为了在所有进程中拦截这个函数,应用程序使用了Windows挂钩技术来注入DLL。

总结

1. 第三节 使用Windows挂钩来插入DLL 发现隐藏窗口Wintellect DIPS为NULL

使用64位运行

相关文章
|
1月前
|
监控 Ubuntu Linux
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
这篇文章介绍了如何在Ubuntu和Windows系统中通过设置相同的时区并使用ntp服务来解决时间同步问题。
64 4
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
|
1月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
90 9
|
4月前
|
Linux Apache C++
FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt
该文介绍了如何在Windows环境下为FFmpeg集成SRT协议支持库libsrt。首先,需要安装Perl和Nasm,然后编译OpenSSL。接着,下载libsrt源码并使用CMake配置,生成VS工程并编译生成srt.dll和srt.lib。最后,将编译出的库文件和头文件按照特定目录结构放置,并更新环境变量,重新配置启用libsrt的FFmpeg并进行编译安装。该过程有助于优化直播推流的性能,减少卡顿问题。
119 2
FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt
|
4月前
|
存储 安全 数据安全/隐私保护
Windows 32 汇编笔记(一):基础知识
Windows 32 汇编笔记(一):基础知识
|
3月前
|
存储 编译器 Linux
Windows 32 汇编笔记(二):使用 MASM
Windows 32 汇编笔记(二):使用 MASM
|
5月前
|
编解码 Windows
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
XviD是开源MPEG-4视频编码器,与DivX相似但后者非开源。早期MP4常使用XviD或DivX编码,现已被H.264取代。在Windows上集成FFmpeg的XviD编解码库libxvid,需访问<https://labs.xvid.com/source/>下载源码,解压后在MSYS环境中配置、编译和安装。之后重新配置FFmpeg,启用libxvid并编译安装。详细步骤包括configure命令、make和make install。成功后,通过`ffmpeg -version`检查是否启用libxvid。更多音视频开发技术可参考《FFmpeg开发实战:从零基础到短视频上线》。
107 0
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
|
4月前
|
JavaScript API
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
|
6月前
|
SQL 机器学习/深度学习 人工智能
Web LLM 实验:利用 LLM API 实现 SQL 注入
Web LLM 实验:利用 LLM API 实现 SQL 注入
|
6月前
|
机器学习/深度学习 SQL 人工智能
Web LLM 实验:利用 LLM API 实现命令注入
Web LLM 实验:利用 LLM API 实现命令注入
|
6月前
|
编解码 5G Linux
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
AVS3是中国首个8K及5G视频编码标准,相比AVS2和HEVC性能提升约30%。解码器libuavs3d支持8K/60P视频实时解码,兼容多种平台。《FFmpeg开发实战》书中介绍了在Windows环境下如何集成libuavs3d到FFmpeg。集成步骤包括下载源码、使用Visual Studio 2022编译、调整配置、安装库文件和头文件,以及重新配置和编译FFmpeg以启用libuavs3d。
103 0
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器