[分享]udbg在目标进程内动态Hook、执行任意函数
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.
通过spy调试引擎附加到notepad进程
PS D:\dist> notepad.exe
PS D:\dist> .\udbg.exe -A spy -a notepad.exe在udbg内执行
.edit temp
命令,会自动在script/autorun
目录下打开(创建)temp.lua
文件,并监控其写入,然后自动执行- 执行下面的示例脚本:在
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
=> pointerinteger|boolean
=> size_tnil|none
=> NULLnumber
=> 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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK