热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

CreateProcessW实现对进程创建的完全控制

文章标题:CreateProcessW实现对进程创建的完全控制。Linux是中国IT实验室的一个技术频道。包含桌面应用,Linux系统管理,内核研究,嵌入式系统和开源等一些基本分类

  【前言】
  写这篇文档的时候由于我足够菜,碰到了不少问题,多谢bkbll,a1rsupply和SobeIt的指点,还有TCH的辛勤劳动,才有这篇文档的诞生,本文中可能存在一些错误,这些错误都是由于我的失误造成的,如果您有什么意见和看法,欢迎来http://www.itaq.org指出,或者E-mail:zf35@citiz.net
  【概述】
  在服务器上实现对进程创建的控制有很大的意义,通过监控进程的创建,我们可以让被允许运行的进程正确创建,而未被允许的程序则会创建失败,这样就可以防止未知木马,病毒和蠕虫对服务器的威胁。要实现上述目的,必须hook windows创建进程相关的API,根据《inside the windows NT》和《Native API Reference》中记载,加上softIce的实际跟踪,windows创建进程的API调用流程如下:
  
  【代码 】
  CreateProcessA-> CreateProcessW-> CreateProcessInternalW->…->最终调用ZwCreateProcess
  本文档中我们选用CreateProcessW来实现我们的目的,当然你也可以使用其它几个API。本文档的演示代码稍做改动可应用于任意Ring3函数。
  对于hook一个API而言,可使用的办法有很多,本文选用改写函数入口点的办法来实现挂接CreateProcessW,更多的详细资料请参阅SobeIt写的《windows下hook API的几种办法》。
  
  【copy-on-write】
  最初试验时,我使用softice的 a CreateProcessW改写函数入口点的代码,F5切换回windows之后发现一切如愿以偿,但是当我编写程序修改CreateProcessW入口点代码时,发现所做的改动仅对本进程有效,而对于系统的其他进程没有产生任何影响。用softice跟踪后发现本进程中CreateProcessW的虚拟地址被映射到了一个新的,与其它进程不同的物理地址上,如果你读过Webcrazy的《copy-on-write机制》一文,就不难看出这是copy-on-write机制产生的影响。对于系统的dll,每个dll都被映射在不同进程的相同的虚拟地址上,而这些虚拟地址又指向相同的物理地址,通过这种机制,系统实现最低的资源消耗.当某个进程试图改写物理内存中的数据时,为了不对其它进程产生影响,系统自动新分配一块物理内存,把原物理内存中的数据复制过去,改写,然后把改写内存的那个进程的虚拟地址重新映射到新的物理内存上去,而其它进程则还是映射在原来的物理内存上,这就是“写时复制技术”(copy-on-write),那么系统是如何判断何时应该使用copy-on-write呢?这是以虚拟地址的PTE来决定的,当PTE中copy-on-write标志被置位时,任何对该虚拟地址的写操作都将导致一个copy-on-writ
  
  【三种可行的办法】
  为了实现全局hook,我们不能被copy-on-write机制所限制住,目前我想到了三种办法来达到我们的目的。
  1. 通过驱动来修改页表项(PTE)的属性,使CreateProcessW对应的虚拟地址失去copy-on-write的属性,这样在本身进程中对CreateProcessW入口点代码的修改会对系统中所有进程生效,从而实现全局hook。
  
  2. 通过windows本身提供的一个对象\\phymem来对物理内存进行直接读写,先定位本身进程的Eprocess(KTEB)(PS:如何在Ring3下定位任意进程的Eprocess请参考我之前写的《获取进程的Eprocess》一文),获得Eprocess之后,可以得到进程的页目录,然后利用\\phymem读取存放页目录的物理内存的内容,再模拟操作系统进行虚拟地址->物理内存地址的转换,最终得到CreateProcessW所对应的物理地址,利用\\phymem我们避开copy-on-write机制,直接改写CreateProcessW。
  
  3. 通过最常规的手段来达到目的,先枚举系统中所有进程,然后通过VirtualQueryEx,VirtualAllocEx,VirtualProtectEx等函数修改每个进程的页面属性,分配新的空间等。最后将我们的代码用WriteProcessMemory写到各进程的空间中,利用改写CreateProcessW入口为Jmp *******来跳到我们的代码中,改变函数的执行流程。
  
  以上三个办法中,方法1只是一个构想,还没成为现实,有空的话我回去试试看的,当然页欢迎各位高手去实现,然后mail一份代码给我:P方法2我写了一份完整的代码来实现它,但是在本文档中不进行讨论,否则文档会变的很长,我将在另一份文档中专门说明这种办法的具体实现。方法3使本文讨论的重点,下面就方法3进行详细说明。
  
  查询CreateProcessW的基址及属性
  这里我们使用VirtualQueryEx这个函数,其原型如下:
  SIZE_T
  VirtualQueryEx
  (
  HANDLE hProcess,
  LPCVOID lpAddress,
  PMEMORY_BASIC_INFORMATION lpBuffer,
  SIZE_T dwLength
  );
  参数说明:
  HANDLE hProcess 想要查询内存信息的进程句柄
  LPCVOID lpAddress 指向想要查询内存区域的指针
  PMEMORY_BASIC_INFORMATION lpBuffer 指向MEMORY_BASIC_INFORMATION结构的指针
  SIZE_T dwLength lpBuffer的大小
  
  调用这个函数之后,相关的信息存放在lpBuffer指向的结构中
  
  修改CreateProcessW的页属性
  对于一个页来说,有如下几种属性:
  PAGE_EXECUTE
  PAGE_EXECUTE_READ
  PAGE_EXECUTE_READWRITE
  PAGE_EXECUTE_WRITECOPY
  PAGE_NOACCESS
  PAGE_READONLY
  PAGE_READWRITE
  PAGE_WRITECOPY
  
  我们通过VirtualProtectEx来修改页的属性:
  BOOL
  VirtualProtectEx
  (
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD flNewProtect,
  PDWORD lpflOldProtect
  );
  
  参数说明:
  HANDLE hProce
  ss 进程句柄
  LPVOID lpAddress 指向想要修改的内存区域的指针
  SIZE_T dwSize 修改的内存区域的大小
  DWORD flNewProtect 新的页属性
  PDWORD lpflOldProtect 指向保存老的页属性的内存的指针
  
  从后面的代码中我们可以看到,为了改写函数入口点代码,我们必须赋予它PAGE_EXECUTE_READWRITE属性。
  
  在进程中分配可用空间
  光修改函数入口点代码是不够的。我们必须自己编写一段code来接管CreateProcessW的工作,由于进程空间是相互隔离的,为了达到全局hook的目标,我们必须向每个进程索要一块空间来存放我们的代码,这就要用到VirtualAllocEx这个函数了,VirtualAllocEx原型如下:
  LPVOID
  VirtualAllocEx
  (
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD flAllocationType,
  DWORD flProtect
  );
  
  参数说明:
  HANDLE hProcess 进程句柄
  LPVOID lpAddress 指向分配内存区域的指针
  SIZE_T dwSize 分配的区域的大小
  DWORD flAllocationType 内存类型
  DWORD flProtect 新内存的属性
  
  <将代码写入远程进程空间>
  我们使用WriteProcessMemory这个函数来向远程进程写入我们的代码和数据,其原型如下:
  BOOL
  WriteProcessMemory(
  HANDLE hProcess,
  LPVOID lpBaseAddress,
  LPCVOID lpBuffer,
  SIZE_T nSize,
  SIZE_T* lpNumberOfBytesWritten
  );
  
  参数说明:
  HANDLE hProcess 进程句柄
  LPVOID lpBaseAddress 指向写入地址的指针
  LPCVOID lpBuffer 指向写入数据的指针
  SIZE_T nSize lpBuffer的大小
  SIZE_T* lpNumberOfBytesWritten 实际写入的字节数
  
  
  编译器的魔术
  我在使用WriteProcessMemory把我自己写的一个函数JmpToAddress的内容写入远程进程空间时,发现无论我的JmpToAddress的内容是什么,写入空间的都是E9****这几个字节,这令我非常困惑,从机器码来看,这是一条相对跳转指令。那么它又是从何而来呢,为了搞清楚这个问题,我用VC调试了一下,在watch窗口中输入JmpToAddress,显示出JmpToAddress的虚拟地址0x00410XXX,然后打开memory窗口,查看这段内存中存放的内容,发现确实是JmpToAddress的代码,这就奇怪了,那神秘的E9****是从何而来呢,于是我请教了a1rsupply,他告诉我VC的调试版本会生成跳转表,这下真相大白,原来是编译器玩的魔术。
  
  为了向远程进程正确写入代码,我们必须自己计算真正的函数地址,下面我写了一段代码来计算真正的函数地址:
  __asm
  {
  pushad
  lea eax,JmpToAddress
  mov ecx,JmpToAddress
  shr ecx,8
  add eax,ecx
  add eax,5
  mov JmpAfterCalc,eax
  popad
  }
  
  解决麻烦的定位问题
  在编写代码的过程中,我遇到的另一个较大的问题就是如何定位地址。我编写的JmpToAddress()函数如下:
  void __declspec(naked) JmpToAddress(void)
  {
  __a
  
  sm jmp [HookedAddr]
  }
  在本地进程中这句代码没什么问题,但是当它被写入远端进程后便会产生种种问题,我们来看看它的汇编代码,如下
  jmp [00401Cxxx]
  我们注意到本进程中这个虚拟地址里存放的是HookedAddr的地址,但是在远程进程中,这个地址指向的是别的什么东西,jmp过去会产生不可预料的结果,为了实现正确的行为,我们先用WriteProcessMemory向远程进程写入HookedAddr的内容,然后用一个相对地址引用它
  void __declspec(naked) JmpAddress(void)
  {
  __asm call flag
  flag:
  __asm pop eax
  __asm add eax,0x0e
  __asm mov ebx,[eax]
  __asm jmp ebx
  }
  pop eax后,eax里面存放的就是本条指令的虚拟的地址,加上一个固定值后,[eax]就是我们通过WritePr
推荐阅读
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • windows便签快捷键_用了windows十几年,没想到竟然这么好用!隐藏的功能你知道吗?
    本文介绍了使用windows操作系统时的一些隐藏功能,包括便签快捷键、截图功能等。同时探讨了windows和macOS操作系统之间的优劣比较,以及人们对于这两个系统的不同看法。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • 如何在联想win10专业版中修改账户名称
    本文介绍了在联想win10专业版中修改账户名称的方法,包括在计算机管理中找到要修改的账户,通过重命名来修改登录名和属性来修改显示名称。同时指出了windows10家庭版无法使用此方法的限制。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
author-avatar
梅子青时梦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有