应用背景是收集指定进程运行中产生的API调用情况,不需要很具体,只要收集到调用序列即可,用于对进程异常检测。然而现在流行的资料大部分是关于对单个API函数的Hooking,无法用到本情况中。原来也找到一个Yariv Kaplan编写的软件APISpy32,满足本应用,可惜不知道它具体是如何实现的。不知哪位大侠能够提供相关资料,或指点迷津说明具体实现方法。
    我的一个想法是既然NTdll.dll是从用户态到内核态的统一入口,能否在此处对特定进程的API进行截获。但我不知道如何实现之。望赐教。

解决方案 »

  1.   

    与这个烙铁的问题差不多
    http://topic.csdn.net/t/20050228/09/3810963.html
      

  2.   

    的确可以实现并且NTDll.dll是在用户模式下执行的,因此hook这里的函数时都可以得到进程ID。NTdll.dll中的函数最终会利用INT 2e 中断切换到内核模式并最终调用SSDT(System Service Dispatch Table)中的服务。
    一种方法是hook所有的NT开头的函数;
    一种是hook INT 2e 指令,patch INT 2e指令地址的内容,让程序执行到INT 2e时先jmp到自己的hook函数,在自己的hook函数中得到eax中的值(也就是SSDT中服务索引号),再根据索引号和系统服务的对应关系就可以找到函数名。
      

  3.   

    宇宙骑士,谢谢指点。
    第一种方法好像是不可完成的任务,而且貌似除了NT开头的,还有其他开头的吧。
    第二种方法的话,看起来可以。
    只是我对hook的具体实现还不是很明白,一般都是Hook一个API函数吧,难道还可以只hook一条指令么。
    另外我想寻觅还有没有更简单的方法,我觉得能不能从NTDll.dll的入口就进行截获,
    在用户态处理起来更容易一些吧,那么得到的就应该只是尚未执行的函数名吧。
      

  4.   

    从pe文件读iat,然后hook iat中所有函数是否可以考虑?
      

  5.   

    Hook函数其实也是hook指令,都是先定位地址,然后替换指令。
      

  6.   

       我觉得Hook所有函数不怎么现实,好几百个呢,并且这里更关注进程从创建到终止全过程对API的动态实时调用情况,应该是对进程没有指定的。
       宇宙骑士,我觉得你的方法已经进入到内核态了,并且似乎饶了一下弯。
       我用过APISpy32,它就能截获全局的API调用,并且能显示出用户态API的函数名和参数,挺好的。
       网址是http://www.internals.com ,大家可以试试看。
       不知道还有没有其他的实现方法么?
      

  7.   

    没有到内核态啊, 调用INT 2e指令后才会进入内核态,之前都是在用户态下完成的。
      

  8.   

    如果支hook某个程序的API调用的话可以用aj3423的方法。
      

  9.   

        通过研究Windows系统调用机制,我发现了该方法一个隐藏的缺陷。因为kernel.dll才通过NTdll.dll产生中断int 2eh进入内核,而User32.dll/GUI.dll却不通过Ntdll.dll,这就导致收集到的系统调用函数不完全。并且User32.dll/GUI.dll进入内核后也不是被NTOSKEL.exe处理,而是被Win32k.sys处理。看来问题一下子变得复杂了。
        如果只考虑NT函数,这种方法就是可以的。我在网上碰见过NtSpy,就是采用相似的方法http://cmp.phys.msu.su:8000/ntclub/pub/ntspy.html
      

  10.   

    detours里面有api hook的例子,楼主不妨一看。另外就是用调试Api监测程序运行时的指令位置,当指令离开主程序的空间时检测程序将要到达地址所属的模块,再在该模块中反查指令地址所对应的函数的名称。
      

  11.   

    boundschecker软件可以跟踪调试软件运行时所有的api调用情况,可以查看api调用时的参数信息,网上下个试试看。此软件很耗资源,尤其内存。另外,软件作者在多年前写过一本书Windows 95 系统程式设计大奥秘(Windows 95 System Programming SECRETS)。其中有一章节专门介绍api拦截,并有实例,为boundschecker的最初原型。有电子书可以下载的,找找看吧。
      

  12.   

    lz想得太复杂了吧?根据问题的要求,最合适的方法就是hook目标程序的所有用到的api. 其实这个办法并不是像lz想象的那么复杂, 而是非常简单,因为程序用到的api绝大部分都是通过导入表加载的。 所以只要替换目标程序的IAT就可以实现hook几乎所有api调用。而要做到这点,并不需要写几百个hook函数去一一替换,可以在运行时动态生成这样一段代码(为了描述,这里用汇编表示,实际运行时你应当直接填写成二进制代码):
    entry_1: CALL bridge_entry
    entry_2: CALL bridge_entry
    entry_3: CALL bridge_entry
    ....
    entry_n: CALL bridge_entry
    bridge_entry: CALL bridge_entry_2
    bridge_entry_2: CALL c_hook_function其中:entry1...entryn对应替换IAT中各个api的入口函数,c_hook_function可以是用c/c++写的实现你目的的函数,声明可以是这样:void __stdcall c_hook_function( unsigned long offset1, unsigned long offset2); 
    (offset1 - offset2) / sizeof(unsigned long) 就是原始IAT中第几个API产生的调用(从尾部算起), 原始参数在(&offset2)[2]位置开始处, 如果你存储了各api相关参数描述的话你也可以通过查表实现列出所有被调用api的函数名,所在dll(这个import table里就有), 甚至参数列表。
    注意一点,当你处理完后, 不能直接return退出c_hook_function, 必须用一小段汇编代码将原始API入口(可以从保存的原始IAT表中根据刚才算出的下标获得)作为c_hook_function的返回地址压入堆栈相应位置,这样当c_hook_function返回时就相当于直接调用了原始api。如果想截获目标程序所有api调用的话,除了上面所说的,对loadlibrary和getprocaddress要进行单独的hook处理,这样就可以通过它们hook并跟踪目标程序动态加载调用的api了。
      

  13.   

    谢谢大家提供了很多好方法。
    调试API的方法可能不适用本情况,因为效率太低,况且我看资料上说debugger关闭后会将被调试程序也关闭。
    12楼的哥们,我还真看过你说的那本书,而且就是通过它才找到APISpy32这个软件的。挺好的一本书,可惜讲
    的什么都忘了。
      

  14.   

    Idle_,你提供的修改函数输入表的方法,挺有参考价值,说得比一般文献详细的多。惭愧的是我对系统编程和
    PE结构还不是很懂,正在饿补当中。
    基本上我打算从截获INT 2EH中断和修改IAT两者中选取一种来实行。
    同时我还有一些问题。一个是修改IAT方法对系统进程诸如lsass.exe、svchost.exe等有效么。还有就是IAT是
    只读的么,能否修改呢,并且修改后的代码放在何处呢,如何保证进程执行时能够自动动态加载。
      

  15.   

    最后分享我最近看的一些资料:
    Ivo Ivanov. 《API Hooking Revealed》http://www.codeproject.com/KB/system/hooksys.aspx 修改IAT方法讲的还算清楚。
    高 岩, 蒋若江. 《主机防护系统中系统调用截获机制的实现》截获INT 2EH中断讲的很清楚。
    梁肇新的《编程高手箴言》,不仅讲如何成为好的程序员,PE文件结构也讲的相当详细。
      

  16.   

    呵呵,这个其实很简单,你只要分析这个程序的 引入表 的情况就行。你通过这个函数ImageNtHeader可以在MSDN上找到详细的技术资料。
      

  17.   

    现在对IAT已经弄得比较明白了。IMAGE_THUNK_DATA DWORD指向的FirstThunk数组每一项对应着一个引入函数,如何截获它呢?我想一种方法是将其先保存然后替换为截获程序的起始地址,待处理完再用跳转指令跳回去?或者如果能直接在入口插入一个跳转指令,处理完再返回就更好,可是不知道怎么加跳转。不知大家有没有什么建议。
      

  18.   

    我准备用拦截INT 2eh中断的方法来做了,配合驱动程序技术,在理论上应该是可行的吧,毕竟Native API对我的用处更大一些。
    最后谢谢大家给的这么多建议!