对抗hook-defeating_hooks

本文探讨了如何通过修改内存映像和导入/导出地址表来避免API钩子的检测,包括内联函数钩子和IAT修改的防御策略。此外,还讨论了如何绕过用户级和内核级的钩子技术。
----------------------------------------------------------------------------------
(1) Defeating IAT or export table modification AND/OR In-line function hooking [1]
----------------------------------------------------------------------------------
 - recall that Import/Export table modification does :

   modify the IAT or export address table of the targeted process and all
   the modules (DLLs) that process/app uses

   each process and module has its own IAT that contains the function
   addresses of functions that process/module uses

   so we replace the addresses in that IAT with addresses to our hooking fxns

   but this method has to take into consideration that some functions will
   be dynamically loaded, e.g. thatdll = LoadLibrary( somedll.dll ) then addy =
   GetProcAddress( thatdll, SomeFunction )

    - so to fully hook would require hooking GetProcAddress so that it
      returns the hooked function address rather than the actual function addy

    - could also modify the export address table of the desired DLLs; this 
      this will cause GetProcAddress(...) to return the addys we desire

      So: GetProcAddress( thatdll, SomeFunction ) looks at the EAT of
      thatdll for SomeFunction; if we have altered the EAT to have our fxn
      address, then our fxn addy will be returned instead of the real fxn addy

 - recall that inline function hooking does : overwrite start of the
   hooked API function with a jmp instruction that causes execution to be
   transferred to our function ('the replacement function')

 DEFEATING BOTH OF THESE APPROACHES :

  - manually read ntdll.dll and kernel32.dll into memory using CreateFile, ReadFile

  - we'll call these manually loaded images "file images"

  - the memory images of ntdll.dll and kernel32.dll are what have been modified

  - iterate through the EAT of the file images of ntdll.dll and kernel32.dll

    -- for each function exported, get that function's length * N

    -- then compare the first N bytes of each of that function from the file image 
       and the memory image

    -- restore each hooked function by copying the relevant bytes from the
       file image to the memory image

  - iterate through the IAT of the memory image of kernel32.dll

    -- for each imported function from ntdll.dll, check that the address
       of this function hasn't been modified

    -- if it has been modified, restore the IAT entry from the file image

  - obtain the function address of CreateProcessA using the file image of kernel32.dll

  - execute desired EXE (app) using CreateProcessA

 NB: relies on fact that only memory image of app and DLLs modified and
 that the actual file on disk (e.g. ntdll.dll) is not changed

 - also seems to rely on fact that disk image of our executable hasn't
   been changed (i.e. had its IAT overwritten on disk)

-------------------------------------------------------------------
(2) Evading userland hooks - problems w/hooking implementations [2]
-------------------------------------------------------------------
 (a) incomplete hooking : don't hook BOTH ANSI and Unicode versions of function

  - ansi fxn names end in A, e.g. CreateProcessA(...)
  - unicode versions end in W, e.g. CreateProcessW(...)

  - ansi functions usually just wrappers which call unicode versions

  - e.g. hook CreateFileA(...) so then attacker just calls
    CreateFileW(...) instead

 (b) not hooking deeply enough

  - kernel32.dll has a lot of functions that are mostly wrappers for ntdll
    functions

  - so if only hook kernel32.dll functions and NOT ntdll.dll functions,
    hooking can be bypassed via calling the ntdll functions directly

  - e.g. GetProcAddress from kernel32.dll and LdrGetProcedureAddress from
    ntdll.dll  

 (c) hooking function f but NOT hooking function g which f calls

  - e.g. may hook WinExec(...)

  - but this is what WinExec(...) call path looks like :

    WinExec(...) --> CreateProcessA(...) --> CreateProcessInternalA(...)

    - if just hook WinExec(...) and CreateProcessA(...) and NOT
      CreateProcessInternalA(...) then attacker can just call that last
      fxn and evade detection

    - CreateProcessInternalA(...) is exported by kernel32.dll so attacker
      could look for its addy via loading kernel32.dll and iterating
      through that module's EAT

  - also this refers to problem mentioned in (1) whereby say some function
    f lives in DLL d but f itself calls function g which lives in DLL d'

    - so we need to recursively be hooking not only our target functions
      but ensuring that any functions THOSE TARGET FUNCTIONS call are
      likewise hooked

 (d) detecting installation of in-line function hooks

  - most win32 api functions begin with a 5-byte preamble

    XP : pre-SP2

       55	; push ebp
       8bec	; mov ebp, esp
       ...

    XP : post-SP2

       8bff	; mov edi, edi (?) -- pg. 75 of Hoglund Rootkits book
       55	; push ebp
       8bec	; mov ebp, esp
       ...

  - after in-line function hooking, a function preamble will look like :

    e8 xx xx xx xx	   ; call xx xx xx xx
    54			   ; push esp
    53			   ; push ebx
    56			   ; push esi
    57			   ; push edi

    or instead of call will have jmp (e9) 

  - so before program P (which wants to bypass hooking) calls function f,
    P looks at the code at f; if it looks like a normal preamble, call f
    else if the first instruction is a call or a jmp to some address, then
    likely in-line function hooking is at work so don't call f or do
    something else... but in any event the hooking has been detected

    --> putting this into detours terminology, basically what the attacker
        would do after probabilistically detecting the hook (above) is
        search for the table which contains the original 5-byte preamble

    --> recall that for detours we have a target function T, a detour
        function (replacement function) D, and a trampoline function TR
        where when T is called, instead D is called then TR is called
        which calls T

    --> so attacker would attempt to call TR then T -- bypassing D
	since TR and T must live in the app memory somewhere

    --> or instead attacker can have its own preamble (keep track of the
        original preamble) then call the target function + 5 bytes, so we
        jump over the "jmp xx xx xx xx" or "call xx xx xx xx" function
        which is our inline function hook (hook hopping)

	==> won't work if another function in the call path is also hooked,
	    e.g. if do hook hopping on WinExec but then WinExec calls
	    CreateProcess and haven't done hook hopping on CreateProcess,
	    then evasion fails

            - so would need to call CreateProcess using stored preamble
	      and do hook hopping on functions called by WinExec

 (e) implementations which overwrite preambles must change the write bits
 on that code ... so that the preamble can be overwritten with the jmp or
 call instruction; but some such implementations never reset the writable
 bit on these code preambles ... so attacker can overwrite overwritten
 preamble with original preamble!

 (f) IAT patching

  - attacker overwrites IAT before hooking mechanism does detection

  - then hooking mechanism will hook desired functions and make those
    functions jump to replacement functions then to the "original API
    function"

    -- but "the original api function" isn't really, it's the function
       address already overwritten by the attacker

       - so the attacker acts as first mover
       - alters the api addy to X
       - then when hooker comes in, it will alter api addy to Y where the
         code at Y does whatever then calls X

  - in particular the systems being attacked here call various ntdll.dll
    functions in order to obtain memory page information so the idea is to
    replace those core functions used by the attackee with functions
    replaced by the attacker

    - context is that attackee is doing buffer overflow protection
    - and is using ntdll.dll functions for this purpose (rather than 
      supplying its own versions of these functions)

 (g) write code which replaces functionality of functions exported by ntdll.dll

  - have already discussed how most ntdll.dll functions basically :

    push args
    move the syscall number to eax
    move pointer to userland stack to edx
    int 2e

  - well attacker can write code which essentially replicates the
    functionality of those ntdll.dll functions

  - so if hook was of all ntdll.dll functions, then attacker can bypass
    that detection

  + of course this doesn't work against kernel-land hooking ... where the
    actual hooking doesn't come into play until *after* int 2e is executed

  * requires knowing the syscall numbers of desired kernel syscalls as well as
    the method signature for each such kernel syscall

    (type and number and order of arguments to that kernel syscall so as
     to conform to it)

----------------------------
(3) Evading kernel hooks [3]
----------------------------
 - basically kernel-level hooks (in NT/2k/XP/2003) rely on modifying the SSDT

 - so this basically restores the SSDT from the disk image of ntoskrnl.exe

 - the methods for doing this restoration mirror closely those which are
   used to alter the SSDT in the first place

 obviously assumes that kernel hooking done via overwriting memory image
 of ntoskrnl.exe data structures (e.g. KiServiceTable) and that disk image
 of ntoskrnl.exe is legit.

 - requires some address conversion from original addys on disk image to RVAs

++++++++++++
References :
++++++++++++

(1) Anti API hooking

 http://www.security.org.sg/code/antihookexec.html

(2) Evading userland hooks and Evading kernel hooks

 http://www.phrack.org/phrack/62/p62-0x05_Bypassing_Win_BufferOverflow_Protection.txt

(3) Defeating native API hookers

 http://www.security.org.sg/code/SIG2_DefeatingNativeAPIHookers.pdf
<think>我们被要求解释shell脚本中do_run_hooks()函数代码的功能和逻辑。虽然提供的引用中没有直接给出do_run_hooks()函数的代码,但我们可以根据引用[2]和引用[4]中提到的关于hooks的信息来推测其可能的逻辑。 引用[4]提到: “Orchestrator中的hooks函数。通过分析这个函数的调用关系,整理如下结论:下面参数指定的hook脚本执行失败会导致recovery过程异常退出:PreFailoverProcessesOnFailureDetectionProcesses PreGracefulTakeoverProcesses 其余的hook执行失败都不会导致recovery异常退出。” 这暗示了在Shell脚本中,hooks的执行是分类型的,有些类型的hook执行失败会导致整个流程退出,而有些则不会。 另外,引用[2]中提到了一个setup.bash/setup.sh脚本,其中包含加载环境钩子的步骤(第6步)。虽然它没有直接给出do_run_hooks函数,但我们可以假设do_run_hooks函数可能是用来执行一系列hook脚本的函数。 在没有具体代码的情况下,我们只能根据常见的hook执行逻辑进行解释。通常,do_run_hooks函数可能具有以下功能: 1. 接收一个参数,该参数指定要执行的hook类型(例如PreFailoverProcesses等)。 2. 根据hook类型,在特定的目录中查找hook脚本。 3. 按照一定的顺序(如字母顺序)执行这些脚本。 4. 在执行每个脚本时,可能会传递一些参数(例如当前上下文的环境变量等)。 5. 根据hook类型和配置,决定当某个hook执行失败时(即返回非零状态)是否终止整个流程。 根据引用[4]的结论,我们可以推断do_run_hooks函数内部可能包含一个条件判断:对于某些关键hook类型(如PreFailoverProcesses, OnFailureDetectionProcesses, PreGracefulTakeoverProcesses),如果其中任何一个hook执行失败,则整个函数会返回非零状态,从而导致上级流程退出;而对于其他非关键的hook类型,即使某个hook失败,也会继续执行剩余的hook,最后可能返回成功或仅记录警告。 因此,我们可以这样描述do_run_hooks函数: 函数名:do_run_hooks 功能:执行指定类型的所有hook脚本,并根据hook的类型处理执行失败的情况。 逻辑步骤: 1. 接收一个参数,即hook类型(例如:$1)。 2. 根据hook类型确定hook脚本所在的目录(可能通过环境变量或配置文件指定)。 3. 检查该目录是否存在,如果不存在则跳过(或记录日志)并返回。 4. 遍历该目录下的所有可执行脚本(通常按文件名排序)。 5. 对每个脚本: a. 执行该脚本(可能使用`source`命令或直接运行),并传递可能的参数(如事件类型、时间戳等)。 b. 检查脚本的退出状态($?)。 c. 如果退出状态非0(表示执行失败): - 如果该hook类型属于关键类型(如PreFailoverProcesses, OnFailureDetectionProcesses, PreGracefulTakeoverProcesses),则立即记录错误并返回非0状态,中断执行。 - 如果是非关键类型,则记录警告,但继续执行下一个hook。 6. 如果所有hook都执行完毕(或者非关键hook中出现了失败但继续执行完),则返回0(成功)或记录非关键hook的失败次数并返回适当的状态。 注意:具体的实现细节可能会因项目而异,比如hook脚本的查找方式、执行方式(source还是fork子进程)、参数传递等。 由于用户没有提供具体的代码,以上是一种通用的逻辑解释。如果用户能提供具体的do_run_hooks函数代码,我们可以进行更精确的分析。 根据引用[4]的结论,我们在回答时可以强调关键hook的执行失败会导致整个流程退出,而非关键hook则不会。 生成相关问题: 1. 如何定义hook脚本的执行顺序? 2. hook脚本执行时如何传递参数? 3. 如何添加一个新的hook类型? 4. 在Shell脚本中,如何安全地执行hook脚本(避免注入攻击等)? </think>### Shell脚本中`do_run_hooks()`函数的功能与逻辑解析 `do_run_hooks()`函数是Shell脚本中用于**批量执行hook脚本**的核心组件,其核心功能是**按需触发预定义的hook流程**,确保系统在关键操作(如故障切换、环境初始化等)前后执行必要的自定义逻辑[^4]。以下是详细的功能逻辑分解: --- #### 一、函数核心功能 1. **动态执行hook序列** 遍历指定目录下的hook脚本,按预设顺序执行: ```shell for hook in $(ls ${HOOKS_DIR}/${hook_type}/ 2>/dev/null); do source "${HOOKS_DIR}/${hook_type}/$hook" || handle_error $? done ``` 2. **关键流程控制** - 对`PreFailoverProcesses`等关键hook类型(见引用[^4]): 若执行失败(返回非0状态码),立即终止主流程并报错 - 对其他非关键hook: 记录错误但继续执行后续hook,保障流程韧性 3. **环境上下文传递** 通过环境变量向hook脚本传递运行时参数(如`FAILOVER_TYPE=graceful`),实现上下文感知[^2] --- #### 二、典型执行逻辑流程 ```mermaid graph TD A[调用 do_run_hooks 'PreFailoverProcesses'] --> B{扫描hooks目录} B -->|存在脚本| C[按文件名排序执行] C --> D{执行状态=0?} D -->|是| E[继续下一个hook] D -->|否| F{是否为关键hook?} F -->|是| G[终止主流程并报错] F -->|否| H[记录日志并继续] B -->|目录为空| I[正常返回] ``` --- #### 三、关键特性说明 1. **错误隔离机制** 非关键hook的错误不会级联影响主流程,符合引用[^4]中关于Orchestrator的容错设计 2. **动态加载能力** 支持运行时添加/删除hook脚本(无需修改主程序),扩展性极强 3. **安全增强** 通过`|| handle_error $?`实现: - 防止单个hook崩溃导致整个进程退出 - 精确捕获hook退出码用于审计 --- #### 四、应用场景示例 根据引用[^2][^4],典型使用场景包括: 1. **系统初始化** 在`source setup.bash`时加载环境钩子(Env Hooks) 2. **故障恢复流程** 数据库切换前执行`PreGracefulTakeoverProcesses`钩子 3. **部署流水线** 集成测试前执行环境校验hook > 提示:实际实现需注意hook脚本的**执行权限**和**依赖隔离**(如通过`env -i`创建干净子环境) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值