[笔记]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位运行

相关文章
|
4月前
|
JSON API 数据格式
实时获取小红书笔记详情的API使用与解析
小红书是一个以分享消费经验、生活方式为主的社交平台,拥有大量的用户和内容。为了更好地了解用户在小红书上的行为和内容,许多开发者选择使用小红书开放平台提供的API接口。本文将介绍如何通过小红书笔记详情API实现实时数据获取,并给出相应的代码示例。
|
4月前
|
存储 缓存 网络协议
dpdk课程学习之练习笔记二(arp, udp协议api测试)
dpdk课程学习之练习笔记二(arp, udp协议api测试)
67 0
|
3月前
|
API C++
socket编程之常用api介绍与socket、select、poll、epoll高并发服务器模型代码实现(1)
前言   本文旨在学习socket网络编程这一块的内容,epoll是重中之重,后续文章写reactor模型是建立在epoll之上的。
36 0
|
3月前
|
监控 安全 Linux
socket编程之常用api介绍与socket、select、poll、epoll高并发服务器模型代码实现(3)
高并发服务器模型-poll poll介绍   poll跟select类似, 监控多路IO, 但poll不能跨平台。其实poll就是把select三个文件描述符集合变成一个集合了。
36 0
|
15天前
|
存储 Java 关系型数据库
掌握Java 8 Stream API的艺术:详解流式编程(一)
掌握Java 8 Stream API的艺术:详解流式编程
47 1
|
3月前
|
API Python Windows
python3应用windows api对后台程序窗口及桌面截图并保存的方法
python3应用windows api对后台程序窗口及桌面截图并保存的方法
97 1
|
1月前
|
算法 Linux API
【Linux系统编程】一文了解 Linux目录的创建和删除API 创建、删除与读取
【Linux系统编程】一文了解 Linux目录的创建和删除API 创建、删除与读取
28 0
【Linux系统编程】一文了解 Linux目录的创建和删除API 创建、删除与读取
|
4月前
|
测试技术 API Python
小红书API接口测试 | 小红书笔记详情 API 接口测试指南
随着互联网的发展,越来越多的应用开始使用API接口来提供服务。而API接口的测试也变得越来越重要。本文将介绍如何使用Python语言进行小红书笔记详情API接口的测试。
|
1月前
|
Linux API C++
【Linux C/C++ 线程同步 】Linux API 读写锁的编程使用
【Linux C/C++ 线程同步 】Linux API 读写锁的编程使用
21 1
|
1月前
|
数据采集 API 开发者
调用API接口获取小红书笔记详情数据(小红书怎么推广)
小红书平台对于其API的使用有严格的规定和限制,并且并非所有的功能和数据都通过公开API提供。关于获取小红书笔记详情的API,以下是一些建议和指导: