9

[分享]udbg在目标进程内动态Hook、执行任意函数

 2 years ago
source link: https://bbs.pediy.com/thread-267630.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
[分享]udbg在目标进程内动态Hook、执行任意函数-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[HOOK/注入] [工具脚本] [分享]udbg在目标进程内动态Hook、执行任意函数
6天前 1579

  1. 通过spy调试引擎附加到notepad进程

    PS D:\dist> notepad.exe
    PS D:\dist> .\udbg.exe -A spy -a notepad.exe

  2. 在udbg内执行.edit temp命令,会自动在script/autorun目录下打开(创建)temp.lua文件,并监控其写入,然后自动执行

  3. 执行下面的示例脚本:在temp.lua里编辑脚本并保存,udbg自动执行

udbg版本:https://gitee.com/udbg/udbg/releases/v0.1.0-nightly

udbg的spy调试引擎,会向目标进程内注入一个模块,然后使用封装好的udbg.uspy模块可以在目标进程内执行lua脚本,然后通过uspy提供的lua接口完成Hook任意函数、调用任意函数的任务

在目标进程内执行lua代码的基本用法如下

local uspy = require "udbg.uspy"
local s = 'hello'
uspy(function()
log(s, 'world')
end)
  • uspy会把传入的function(闭包)转换为字节码,然后通过RPC发送到目标进程进行执行
  • 假如闭包捕获了upvalue,也会将这些upvalue序列化并传过去,前提是被捕获的upvalue都可进行序列化

后面的示例脚本没有特殊说明,默认都是通过uspy(function() ... end)在目标进程内进行调用的

Hook任意函数

local uspy = require "udbg.uspy"
local CreateFileW = PA 'kernelbase!CreateFileW'
uspy(function()
local libffi = require 'libffi'
local api = require 'win.api'
-- Hook CreateFileW 函数,并打印第一个参数
inline_hook(CreateFileW, function(args)
local path = libffi.read_pack(args[1], 'w')
log('CreateFileW', path)
end)
end)

Hook并进行参数替换

inline_hook(CreateFileW, function(args)
-- args[1] 在x64下相当于 args.rcx
local path = libffi.read_pack(args[1], 'w')
log('CreateFileW', path)
if path:find 'a.txt$' then
path = path:gsub('a.txt$', 'b.txt')
log('[redirect]', 'a.txt', '->', path)
args[1] = topointer(path:to_utf16())
end
end)

Hook并调用原函数、阻止原函数继续执行

local MessageBoxA = api.GetProcAddress(api.LoadLibraryA('user32'), 'MessageBoxA')
inline_hook(MessageBoxA, function(args)
-- 手动调用原函数
libffi.fn(args.trampoline)(0, 'LALALA', 'AAAAAA', 0)
-- 返回后不再调用原函数
args 'reject'
end)
local msgbox = libffi.fn(MessageBoxA)
msgbox(0, 'ABC', 'DEF', 0)

libffi的使用

无类型调用

上述Hook示例中,对 MessageBoxA 函数的调用就是一个无类型调用的例子

local msgbox = libffi.fn(MessageBoxA)
msgbox(0, 'ABC', 'DEF', 0)

libffi.fn函数直接传入一个整数(C函数地址),返回一个无类型的函数对象,无类型的函数对象会根据传入的lua参数类型自动为每个C参数分配类型,映射规则如下

  • string|userdata => pointer
  • integer|boolean => size_t
  • nil|none => NULL
  • number => double

无类型的调用在于写起来非常简单,能够覆盖大部分应用场景,但有些时候还是需要知道明确的函数参数才能成功调用一些函数,比如涉及到浮点数的函数、非标准的调用约定等情况

声明函数类型

声明函数类型需要指定函数的返回值类型和参数类型 libffi.fn(returnType, {argsType...}),支持的类型如下

  • void
  • char int8
  • byte uchar
  • short int16
  • ushort uint16
  • int int32
  • uint uint32
  • int64 long long
  • uint64
  • float
  • double
  • pointer
local pow = api.GetProcAddress(api.LoadLibraryA'msvcrt', 'pow')
-- 通过fn声明函数类型
pow = libffi.fn('double', {'double', 'double'})(pow)
log('powf(2, 2)', pow(2, 2))
local powf = api.GetProcAddress(api.LoadLibraryA'msvcrt', 'powf')
powf = libffi.fn('float', {'float', 'float'})(powf)
log('powf(2, 3)', powf(2, 3))

指定x86调用约定: TODO

生成回调函数

local WNDENUMPROC = libffi.fn('int', {'pointer', 'pointer'})
api.EnumChildWindows(0, WNDENUMPROC(function(hwnd, param)
log('HWND:', hwnd)
end), 0)

主线程执行

有些函数只能在某些特定线程中调用,一般是UI主线程,可以Hook GetMessageW PeekMessageW之类的函数,Hook触发时,脚本则是在UI线程中调用的,然后通过libffi去调用想测试的函数

-- inline_once是对inline_hook函数的封装,Hook触发一次后立即消除Hook
inline_once(api.GetProcAddress(api.LoadLibraryA'user32', 'GetMessageW'), function()
local msgbox = libffi.fn(MessageBoxA)
msgbox(0, 'ABC', 'DEF', 0)
end)

udbg本身功能也使用了很多libffi调用,比如 script/win/api.lua script/win/win.lua

第五届安全开发者峰会(SDC 2021)议题征集正式开启!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK