-
-
[原创]DynamoRIO源码分析(一)--劫持进程
-
发表于: 2023-4-6 10:39 19022
-
本系列将以官网资料为基础主要通过动态跟踪来解析DynamoRIO的源代码。因为如果不结合实例只是将各函数的作用写出来,实在无法很好的说明问题。我们将以代码覆盖工具drcov为例,分析DynamoRIO的执行流程。
本系列主要的参考资料是《Efficient, Transparent, and Comprehensive Runtime Code Manipulation》(一定要看这篇论文)。
本章主要讲述DynamoRIO如何劫持控制目标进程。
DynamoRIO是一个运行时代码操作系统,支持在程序执行时对其任何部分进行代码转换。DynamoRIO输出了一个接口,用于建立动态工具,用途广泛:程序分析和理解、剖析、仪表、优化、翻译等。与许多动态工具系统不同,DynamoRIO并不局限于插入调用/中断,通过强大的IA-32/AMD64/ARM/AArch64指令操作库,允许对应用程序指令进行任意修改。DynamoRIO提供了高效、透明和全面的操作,可以对运行在库存操作系统(Windows、Linux或Android,试验性支持Mac)和商品IA-32、AMD64、ARM和AArch64硬件上的未修改的应用程序进行操作。
我们希望有一个全面的工具平台,它能够系统地将自己置于运行中的应用程序执行的每条指令和底层硬件之间,如图所示:
我们的目标是构建一个灵活的软件层,它将自己完全地插入到正在运行的应用程序和底层平台之间。该层充当运行时控制点,允许自定义工具嵌入其中。
首先需要准备一个程序,因为我们主要以此程序为例分析DynamoRIO,所以程序不应太过复杂。我们使用的简单程序如下
在命令行输入
开启子进程调试,下断并运行到主函数中,接下来我们开始。
主函数在drdeploy.c中 我将把主函数简化保留关键的部分。主函数如下:
可以看到主函数的关键就在于这三个函数,我们接下来逐个分析。
首先我们看看官网对此函数是如何进行解释的:
为指定的可执行文件和命令行创建一个新进程。进程中的初始线程被暂停。
参数
[in] app_name 目标可执行文件的路径
[in] app_cmdline 一个以NULL结尾的字符串数组,代表应用程序的命令行
[out] data 一个不透明的指针,它应该被传递给后续的drinject*例程以引用这个进程。
简化后dr_inject_process_create主要执行流程如下:
可以看到此函数主要是以暂停线程的方式创建了目标进程。我们跟踪进去看看info的结构,发现info主要保存一些进程信息
在执行dr_inject_process_inject之前会在C:\Users\Lenovo\dynamorio目录下创建以进程名和pid命名的配置文件,我们可以查看此配置文件:
之后调用dr_inject_process_inject
将DynamoRIO注入由dr_inject_process_create()创建的进程中。
参数
[in] data 由dr_inject_process_create()返回的指针。
[in] force_injection 即使进程被配置为不在DynamoRIO下运行,也会要求注入。
[in] library_path 要使用的DynamoRIO库的路径。如果为空,将使用目标进程所配置的库。
dr_inject_process_inject的主要执行流程如下:
可以看到此函数是一个封装,主要实现在inject_into_new_process中
简化后inject_into_new_process的主要实现流程如下:
我们知道了此函数的大致流程,接下来我们来深入研究其各个参数和各函数的实现过程。
我们首先验证一下image_entry的值:
执行get_remote_process_entry后image_entry为0x1ab118,
可以看到实际上image_entry存放着EntryPoint
接下来我们查看nt_get_context的实现过程:
实际上nt_get_context的实现是通过变参宏调用NTGetContextThread函数。
查看hook_location
可以看到目标进程主线程的eip为RtlUserThreadStart赋值给hook_location.
分析inject_gencode_mapped
inject_gencode_mapped主要流程如下:
此函数利用NtCreateSection和NtMapViewOfSection将dynamorio.dll注入到目标进程中:
之后调用inject_gencode_mapped_helper函数
inject_gencode_mapped_helper
inject_gencode_mapped_helper主要执行流程如下:
我们同样跟踪验证此函数。让大家对此过程更清晰。
内存分配后各个参数
remote_code_buf = 0xb60000
remote_data = 0xb61000
local_code_buf = 0x010d0000
查看agrs:
args写入到目标进程的第二个page
查看构造的代码
写入remote_code_buf
我们现在已经分析完inject_gencode_mapped_helper,现在我们回到inject_into_new_process,hook_target就是我们的remote_code_buf:
执行后如下:
总结
可以看到我们劫持目标进程是通过修改目标进程主线程的eip到remote_code_buf来实现的,我们在整个过程中都没有修改hook_location里的值,所以我认为那些恢复hook的操作是没有必要的。
恢复由dr_inject_process_create()创建的进程中的暂停线程。
参数
[in] data 由dr_inject_process_create()返回的指针。
执行ResumeThread后,我们切换到子进程,记住此时的寄存器信息:
主函数已经分析完了,我们来总结一下这个过程,首先使用暂停线程的方式打开目标进程,将dynamorio.dll注入到目标进程空间中,在目标进程申请空间并写入跳转到dynamorio!dynamorio_earliest_init_takeover的代码,修改目标进程线程EIP到这段代码中。这样之后当恢复线程运行的时候,就会运行到dynamorio_earliest_init_takeover中。
dynamorio_earliest_init_takeover函数 是在x86.asm_core.s中用汇编写的,如下:
此函数主要调用dynamorio!dynamorio_earliest_init_takeover_C,这之前的操作主要为了构建priv_mcontext_t结构。
priv_mcontext_t结构保存了目标进程的上下文。对比刚切换子进程时的上下文,可以看到此时EIP为RtlUserThreadStart:
arg_ptr为remote_data
此函数的关键在于dynamorio_app_init和dynamorio_app_take_over_helper。
由于初始化操作很多,当以后用到的时候我们再回头分析。
此函数执行了很多初始化操作,我先贴出对现在有用的操作,当在之后用到的时候再回头分析。现在instrument_init是关键。我们进入查看其实现。
可以看到此函数调用了drcov!dr_client_main,让我们接着深入看看覆盖率信息是怎么收集的。
options_init
此函数的作用是验证参数,比如-logdir可以指定覆盖率文件路径。由于我们没有指定参数,所以此函数ops输出如下:
之后调用drcovlib_init。
drcovlib_init
官方解释如下:
初始化drcovlib扩展。必须在任何其他例程之前调用。可以多次调用(通常由不同的组件调用),但每次调用必须与相应的drcovlib_exit()的调用配对。
一旦这个例程被调用,drcovlib的操作就会生效,并开始收集覆盖信息。
参数
[in] ops 指定控制drcovlib操作方式的可选参数。
drmgr_register_bb_instrumentation_event
这个回调注册函数到底将回调注册到了哪里,到底在什么时候会调用我们的回调函数,我们接着深入研究。
drmgr_bb_cb_add
此函数调用了set_cb_fields,set_cb_fields是一个函数指针参数,实际调用的是cb_entry_set_fields_instrumentation
我们现在知道注册drmgr_analysis_cb_t回调就是将回调函数地址赋值给new_e->cb.pair.analysis_cb。但是什么时候调用它将在之后的章节中分析。
总结
drcov首先创建log文件,之后注册收集覆盖率信息的回调函数和进程退出回调,在进程退出的时候会将覆盖率信息写入log文件中。此过程会在之后的章节中分析。
dynamorio_app_init分析完毕,接下来分析dynamorio_app_take_over_helper
调用call_switch_stack
可以看到此函数将堆栈切换到dstack,之后调用d_r_dispatch。
我们成功劫持控制了目标程序,并且注册了收集覆盖率信息的回调函数,最后以一个干净的堆栈调用d_r_dispatch。
d_r_dispatch是DynamoRIO控制管理的中心,下一章我们将分析d_r_dispatch。请记住一点DynamoRIO永远不会运行目标程序代码,而是让目标程序代码以一个基本块复制到代码缓存中,然后在本地执行缓存的代码。此过程将在下一章详细分析。
[1] https://dynamorio.org/
[2] 《Efficient, Transparent, and Comprehensive Runtime Code Manipulation》
[3] https://bbs.kanxue.com/thread-263357.htm
#include <stdio.h>
#include <windows.h>
int
main(
int
argc, char
*
argv[])
{
int
count
=
0
;
for
(
int
j
=
0
; j <
2
; j
+
+
)
{
for
(
int
i
=
0
; i <
60
; i
+
+
)
{
if
(i <
30
)
{
count
+
+
;
}
else
{
count
-
-
;
}
}
}
printf(
"count:%d\n"
, count);
return
0
;
}
#include <stdio.h>
#include <windows.h>
int
main(
int
argc, char
*
argv[])
{
int
count
=
0
;
for
(
int
j
=
0
; j <
2
; j
+
+
)
{
for
(
int
i
=
0
; i <
60
; i
+
+
)
{
if
(i <
30
)
{
count
+
+
;
}
else
{
count
-
-
;
}
}
}
printf(
"count:%d\n"
, count);
return
0
;
}
"E:\windbg\Debuggers\x86\windbg.exe"
E:\dynamorio\build32\bin32\drrun.exe
-
t drcov
-
-
E:\test\Test.exe
"E:\windbg\Debuggers\x86\windbg.exe"
E:\dynamorio\build32\bin32\drrun.exe
-
t drcov
-
-
E:\test\Test.exe
int_tmain(
int
argc, TCHAR
*
targv[])
{
...
/
*
开头主要进行读取参数 验证参数,初始化等操作
*
/
/
*
比如会找drcov.dll和dynamorio.dll的绝对路径等
*
/
dr_inject_process_create(app_name, app_argv, &inject_data);
...
/
*
在C:\Users\Lenovo\dynamorio下创建并写入配置文件
*
/
dr_inject_process_inject(inject_data, force_injection, drlib_path)
dr_inject_process_run(inject_data)
}
int_tmain(
int
argc, TCHAR
*
targv[])
{
...
/
*
开头主要进行读取参数 验证参数,初始化等操作
*
/
/
*
比如会找drcov.dll和dynamorio.dll的绝对路径等
*
/
dr_inject_process_create(app_name, app_argv, &inject_data);
...
/
*
在C:\Users\Lenovo\dynamorio下创建并写入配置文件
*
/
dr_inject_process_inject(inject_data, force_injection, drlib_path)
dr_inject_process_run(inject_data)
}
int
dr_inject_process_create(const char
*
app_name, const char
*
*
argv, void
*
*
data OUT)
{
dr_inject_info_t
*
info
=
HeapAlloc(GetProcessHeap(),
0
, sizeof(
*
info));
...
/
*
进行格式化参数,填充info等操作
*
/
res
=
CreateProcess(wapp_name, wapp_cmdline, NULL, NULL, TRUE,
CREATE_SUSPENDED |
((debug_stop_function && info
-
>using_debugger_injection)
? DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS
:
0
),
NULL, NULL, &si, &info
-
>pi);
*
data
=
(void
*
)info;
}
int
dr_inject_process_create(const char
*
app_name, const char
*
*
argv, void
*
*
data OUT)
{
dr_inject_info_t
*
info
=
HeapAlloc(GetProcessHeap(),
0
, sizeof(
*
info));
...
/
*
进行格式化参数,填充info等操作
*
/
res
=
CreateProcess(wapp_name, wapp_cmdline, NULL, NULL, TRUE,
CREATE_SUSPENDED |
((debug_stop_function && info
-
>using_debugger_injection)
? DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS
:
0
),
NULL, NULL, &si, &info
-
>pi);
*
data
=
(void
*
)info;
}
bool
dr_inject_process_inject(void
*
data,
bool
force_injection, const char
*
library_path)
{
dr_inject_info_t
*
info
=
(dr_inject_info_t
*
)data;
...
/
*
如果library_path
=
NULL 就会从配置文件中读取dynamorio.dll的路径赋值给library_path
*
/
res
=
inject_into_new_process(info
-
>pi.hProcess, info
-
>pi.hThread,
(char
*
)library_path, true
/
*
map
*
/
,
INJECT_LOCATION_ThreadStart, NULL);
}
bool
dr_inject_process_inject(void
*
data,
bool
force_injection, const char
*
library_path)
{
dr_inject_info_t
*
info
=
(dr_inject_info_t
*
)data;
...
/
*
如果library_path
=
NULL 就会从配置文件中读取dynamorio.dll的路径赋值给library_path
*
/
res
=
inject_into_new_process(info
-
>pi.hProcess, info
-
>pi.hThread,
(char
*
)library_path, true
/
*
map
*
/
,
INJECT_LOCATION_ThreadStart, NULL);
}
#define EARLY_INJECT_HOOK_SIZE 14
bool
inject_into_new_process(HANDLE phandle, HANDLE thandle, char
*
dynamo_path,
bool
map
,
uint inject_location, void
*
inject_address)
{
uint64 image_entry
=
0
;
uint64 hook_target
=
0
;
byte hook_buf[EARLY_INJECT_HOOK_SIZE];
uint old_prot;
switch (inject_location)
{
case INJECT_LOCATION_ThreadStart:
/
*
此函数读取目标进程PE文件 最终将EntryPoint给image_entry
*
/
image_entry
=
get_remote_process_entry(phandle, &x86_code);
if
(thandle !
=
NULL) {
if
(IF_X64(!) is_32bit_process(phandle)) {
cxt.cxt.ContextFlags
=
CONTEXT_CONTROL;
/
*
通过NTGetContextThread函数获取目标进程主线程上下文
*
/
if
(NT_SUCCESS(nt_get_context(thandle, &cxt.cxt)))
/
*
将目标进程主线程EIP赋值给hook_location
*
/
hook_location
=
cxt.cxt.CXT_XIP;
}
}
/
*
判断hook_location
=
0
所以下面一般不会执行
*
/
if
(hook_location
=
=
0
) {
bool
target_64
=
!x86_code IF_X64(|| DYNAMO_OPTION(inject_x64));
uint64 ntdll_base
=
find_remote_dll_base(phandle, target_64,
"ntdll.dll"
);
uint64 thread_start
=
get_remote_proc_address(phandle, ntdll_base,
"RtlUserThreadStart"
);
if
(thread_start !
=
0
)
hook_location
=
thread_start;
}
/
*
hook_location仍为
0
的情况下使用image_entry
*
/
if
(hook_location
=
=
0
) {
hook_location
=
image_entry;
}
}
/
*
将hook_location
14
字节的数据读到hook_buf
*
/
/
*
14
字节是因为最大字节数是x64下 jmp (
6
bytes)
+
target (
8
bytes).
*
/
read_remote_memory_maybe64(phandle, hook_location, hook_buf, sizeof(hook_buf),
&num_bytes_out)
/
*
利用NtProtectVirtualMemory将hook_location
14
个字节保护更改为可读可写可执行
*
/
remote_protect_virtual_memory_maybe64(phandle, hook_location, sizeof(hook_buf),
PAGE_EXECUTE_READWRITE, &old_prot)
/
*
此函数将在后面详细分析
*
/
hook_target
=
inject_gencode_mapped(phandle, dynamo_path, hook_location, hook_buf,
NULL, x86_code, late_injection, old_prot);
/
*
将hook_target给主线程eip
*
/
if
(inject_location
=
=
INJECT_LOCATION_ThreadStart && hook_location !
=
image_entry &&
thandle !
=
NULL) {
if
(IF_X64_ELSE(true, is_32bit_process(phandle))) {
cxt.cxt.ContextFlags
=
CONTEXT_CONTROL;
if
(NT_SUCCESS(nt_get_context(thandle, &cxt.cxt))) {
cxt.cxt.CXT_XIP
=
(ptr_uint_t)hook_target;
if
(NT_SUCCESS(nt_set_context(thandle, &cxt.cxt)))
skip_hook
=
true;
}
}
}
/
*
恢复hook_location 但我感觉没有必要 因为没有改变过hook_location
*
/
write_remote_memory_maybe64(phandle, hook_location, hook_buf, sizeof(hook_buf),
&num_bytes_out)
}
#define EARLY_INJECT_HOOK_SIZE 14
bool
inject_into_new_process(HANDLE phandle, HANDLE thandle, char
*
dynamo_path,
bool
map
,
uint inject_location, void
*
inject_address)
{
uint64 image_entry
=
0
;
uint64 hook_target
=
0
;
byte hook_buf[EARLY_INJECT_HOOK_SIZE];
uint old_prot;
switch (inject_location)
{
case INJECT_LOCATION_ThreadStart:
/
*
此函数读取目标进程PE文件 最终将EntryPoint给image_entry
*
/
image_entry
=
get_remote_process_entry(phandle, &x86_code);
if
(thandle !
=
NULL) {
if
(IF_X64(!) is_32bit_process(phandle)) {
cxt.cxt.ContextFlags
=
CONTEXT_CONTROL;
/
*
通过NTGetContextThread函数获取目标进程主线程上下文
*
/
if
(NT_SUCCESS(nt_get_context(thandle, &cxt.cxt)))
/
*
将目标进程主线程EIP赋值给hook_location
*
/
hook_location
=
cxt.cxt.CXT_XIP;
}
}
/
*
判断hook_location
=
0
所以下面一般不会执行
*
/
if
(hook_location
=
=
0
) {
bool
target_64
=
!x86_code IF_X64(|| DYNAMO_OPTION(inject_x64));
uint64 ntdll_base
=
find_remote_dll_base(phandle, target_64,
"ntdll.dll"
);
uint64 thread_start
=
get_remote_proc_address(phandle, ntdll_base,
"RtlUserThreadStart"
);
if
(thread_start !
=
0
)
hook_location
=
thread_start;
}
/
*
hook_location仍为
0
的情况下使用image_entry
*
/
if
(hook_location
=
=
0
) {
hook_location
=
image_entry;
}
}
/
*
将hook_location
14
字节的数据读到hook_buf
*
/
/
*
14
字节是因为最大字节数是x64下 jmp (
6
bytes)
+
target (
8
bytes).
*
/
read_remote_memory_maybe64(phandle, hook_location, hook_buf, sizeof(hook_buf),
&num_bytes_out)
/
*
利用NtProtectVirtualMemory将hook_location
14
个字节保护更改为可读可写可执行
*
/
remote_protect_virtual_memory_maybe64(phandle, hook_location, sizeof(hook_buf),
PAGE_EXECUTE_READWRITE, &old_prot)
/
*
此函数将在后面详细分析
*
/
hook_target
=
inject_gencode_mapped(phandle, dynamo_path, hook_location, hook_buf,
NULL, x86_code, late_injection, old_prot);
/
*
将hook_target给主线程eip
*
/
if
(inject_location
=
=
INJECT_LOCATION_ThreadStart && hook_location !
=
image_entry &&
thandle !
=
NULL) {
if
(IF_X64_ELSE(true, is_32bit_process(phandle))) {
cxt.cxt.ContextFlags
=
CONTEXT_CONTROL;
if
(NT_SUCCESS(nt_get_context(thandle, &cxt.cxt))) {
cxt.cxt.CXT_XIP
=
(ptr_uint_t)hook_target;
if
(NT_SUCCESS(nt_set_context(thandle, &cxt.cxt)))
skip_hook
=
true;
}
}
}
/
*
恢复hook_location 但我感觉没有必要 因为没有改变过hook_location
*
/
write_remote_memory_maybe64(phandle, hook_location, hook_buf, sizeof(hook_buf),
&num_bytes_out)
}
static uint64
inject_gencode_mapped(HANDLE phandle, char
*
dynamo_path, uint64 hook_location,
byte hook_buf[EARLY_INJECT_HOOK_SIZE], void
*
must_reach,
bool
x86_code,
bool
late_injection, uint old_hook_prot)
{
bool
success
=
false;
NTSTATUS res;
HANDLE
file
=
INVALID_HANDLE_VALUE;
HANDLE section
=
INVALID_HANDLE_VALUE;
byte
*
map
=
NULL;
size_t view_size
=
0
;
wchar_t dllpath[MAX_PATH];
uint64 ret
=
0
;
if
(!convert_to_NT_file_path(dllpath, dynamo_path, BUFFER_SIZE_ELEMENTS(dllpath)))
goto done;
NULL_TERMINATE_BUFFER(dllpath);
res
=
nt_create_module_file(&
file
, dllpath, NULL, FILE_EXECUTE | FILE_READ_DATA,
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
0
);
if
(!NT_SUCCESS(res))
goto done;
res
=
nt_create_section(§ion, SECTION_ALL_ACCESS, NULL,
/
*
full
file
size
*
/
PAGE_EXECUTE_WRITECOPY, SEC_IMAGE,
file
,
/
*
XXX: do we need security options to put
in
other process?
*
/
NULL
/
*
unnamed
*
/
,
0
, NULL, NULL);
if
(!NT_SUCCESS(res))
goto done;
res
=
nt_raw_MapViewOfSection(section, phandle, &
map
,
0
,
0
/
*
not
page
-
file
-
backed
*
/
,
NULL, (PSIZE_T)&view_size, ViewUnmap,
0
/
*
no special top
-
down
or
anything
*
/
,
PAGE_EXECUTE_WRITECOPY);
if
(!NT_SUCCESS(res))
goto done;
ret
=
inject_gencode_mapped_helper(phandle, dynamo_path, hook_location, hook_buf,
map
,
must_reach, x86_code, late_injection, old_hook_prot);
done:
if
(ret
=
=
0
) {
close_handle(
file
);
close_handle(section);
}
return
ret;
}
static uint64
inject_gencode_mapped(HANDLE phandle, char
*
dynamo_path, uint64 hook_location,
byte hook_buf[EARLY_INJECT_HOOK_SIZE], void
*
must_reach,
bool
x86_code,
bool
late_injection, uint old_hook_prot)
{
bool
success
=
false;
NTSTATUS res;
HANDLE
file
=
INVALID_HANDLE_VALUE;
HANDLE section
=
INVALID_HANDLE_VALUE;
byte
*
map
=
NULL;
size_t view_size
=
0
;
wchar_t dllpath[MAX_PATH];
uint64 ret
=
0
;
if
(!convert_to_NT_file_path(dllpath, dynamo_path, BUFFER_SIZE_ELEMENTS(dllpath)))
goto done;
NULL_TERMINATE_BUFFER(dllpath);
res
=
nt_create_module_file(&
file
, dllpath, NULL, FILE_EXECUTE | FILE_READ_DATA,
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
0
);
if
(!NT_SUCCESS(res))
goto done;
res
=
nt_create_section(§ion, SECTION_ALL_ACCESS, NULL,
/
*
full
file
size
*
/
PAGE_EXECUTE_WRITECOPY, SEC_IMAGE,
file
,
/
*
XXX: do we need security options to put
in
other process?
*
/
NULL
/
*
unnamed
*
/
,
0
, NULL, NULL);
if
(!NT_SUCCESS(res))
goto done;
res
=
nt_raw_MapViewOfSection(section, phandle, &
map
,
0
,
0
/
*
not
page
-
file
-
backed
*
/
,
NULL, (PSIZE_T)&view_size, ViewUnmap,
0
/
*
no special top
-
down
or
anything
*
/
,
PAGE_EXECUTE_WRITECOPY);
if
(!NT_SUCCESS(res))
goto done;
ret
=
inject_gencode_mapped_helper(phandle, dynamo_path, hook_location, hook_buf,
map
,
must_reach, x86_code, late_injection, old_hook_prot);
done:
if
(ret
=
=
0
) {
close_handle(
file
);
close_handle(section);
}
return
ret;
}
typedef struct {
uint64 app_xax;
uint64 dr_base;
uint64 ntdll_base;
uint64 tofree_base;
uint64 hook_location;
uint hook_prot;
bool
late_injection;
char dynamorio_lib_path[MAX_PATH];
} earliest_args_t;
static uint64
inject_gencode_mapped_helper(HANDLE phandle, char
*
dynamo_path, uint64 hook_location,
byte hook_buf[EARLY_INJECT_HOOK_SIZE], byte
*
map
,
void
*
must_reach,
bool
x86_code,
bool
late_injection,
uint old_hook_prot)
{
const size_t remote_alloc_sz
=
2
*
PAGE_SIZE;
const size_t code_alloc_sz
=
PAGE_SIZE;
earliest_args_t args;
/
*
使用NTAllocateVirtualMemory在目标进程的虚拟空间中申请
2
page内存
*
/
remote_code_buf
=
(uint64)allocate_remote_code_buffer(phandle, remote_alloc_sz, must_reach);
/
*
在本进程申请
1
page内存
*
/
local_code_buf
=
allocate_remote_code_buffer(NT_CURRENT_PROCESS, code_alloc_sz, NULL);
/
*
hook_code_buf和remote_code_buf都指向目标进程第一个page
*
/
hook_code_buf
=
remote_code_buf;
/
*
remote_data此时指向目标进程第二个page
*
/
remote_data
=
remote_code_buf
+
code_alloc_sz;
args.dr_base
=
(uint64)
map
;
args.ntdll_base
=
find_remote_dll_base(phandle, target_64,
"ntdll.dll"
);
if
(args.ntdll_base
=
=
0
)
goto error;
args.tofree_base
=
remote_code_buf;
args.hook_location
=
hook_location;
args.hook_prot
=
old_hook_prot;
args.late_injection
=
late_injection;
strncpy(args.dynamorio_lib_path, dynamo_path,
BUFFER_SIZE_ELEMENTS(args.dynamorio_lib_path));
/
*
将args写入到目标进程的第二个page也就是remote_data中
*
/
write_remote_memory_maybe64(phandle, remote_data, &args, sizeof(args),
&num_bytes_out)
...
/
*
这里会在local_code_buf 构造的代码
*
/
构造的代码如下:
mov dword ptr ds:[remote_data],eax ;也就是将eax存在args.app_xax
mov eax,offset ntdll!RtlUserThreadStart (
772641e0
);hook_location(目标进程主线程eip)
mov dword ptr [eax],
0E9683D83h
;恢复hook
mov dword ptr [eax
+
4
],
74007730h
mov dword ptr [eax
+
8
],
680D8B0Eh
mov byte ptr [eax
+
0Ch
],
0E9h
mov byte ptr [eax
+
0Dh
],
30h
mov eax,remote_data
push offset ntdll!RtlUserThreadStart (
772641e0
);hook_location
jmp dynamorio.dll!dynamorio_earliest_init_takeover
/
*
将上面在local_code_buf 构造的代码 写入到remote_code_buf(hook_code_buf)
*
/
write_remote_memory_maybe64(phandle, hook_code_buf, local_code_buf,
cur_local_pos
-
local_code_buf, &num_bytes_out)
return
hook_code_buf;
}
typedef struct {
uint64 app_xax;
uint64 dr_base;
uint64 ntdll_base;
uint64 tofree_base;
uint64 hook_location;
uint hook_prot;
bool
late_injection;
char dynamorio_lib_path[MAX_PATH];
} earliest_args_t;
static uint64
inject_gencode_mapped_helper(HANDLE phandle, char
*
dynamo_path, uint64 hook_location,
byte hook_buf[EARLY_INJECT_HOOK_SIZE], byte
*
map
,
void
*
must_reach,
bool
x86_code,
bool
late_injection,
uint old_hook_prot)
{
const size_t remote_alloc_sz
=
2
*
PAGE_SIZE;
const size_t code_alloc_sz
=
PAGE_SIZE;
earliest_args_t args;
/
*
使用NTAllocateVirtualMemory在目标进程的虚拟空间中申请
2
page内存
*
/
remote_code_buf
=
(uint64)allocate_remote_code_buffer(phandle, remote_alloc_sz, must_reach);
/
*
在本进程申请
1
page内存
*
/
local_code_buf
=
allocate_remote_code_buffer(NT_CURRENT_PROCESS, code_alloc_sz, NULL);
/
*
hook_code_buf和remote_code_buf都指向目标进程第一个page
*
/
hook_code_buf
=
remote_code_buf;
/
*
remote_data此时指向目标进程第二个page
*
/
remote_data
=
remote_code_buf
+
code_alloc_sz;
args.dr_base
=
(uint64)
map
;
args.ntdll_base
=
find_remote_dll_base(phandle, target_64,
"ntdll.dll"
);
if
(args.ntdll_base
=
=
0
)
goto error;
args.tofree_base
=
remote_code_buf;
args.hook_location
=
hook_location;
args.hook_prot
=
old_hook_prot;
args.late_injection
=
late_injection;
strncpy(args.dynamorio_lib_path, dynamo_path,
BUFFER_SIZE_ELEMENTS(args.dynamorio_lib_path));
/
*
将args写入到目标进程的第二个page也就是remote_data中
*
/
write_remote_memory_maybe64(phandle, remote_data, &args, sizeof(args),
&num_bytes_out)
...
/
*
这里会在local_code_buf 构造的代码
*
/
构造的代码如下:
mov dword ptr ds:[remote_data],eax ;也就是将eax存在args.app_xax
mov eax,offset ntdll!RtlUserThreadStart (
772641e0
);hook_location(目标进程主线程eip)
mov dword ptr [eax],
0E9683D83h
;恢复hook
mov dword ptr [eax
+
4
],
74007730h
mov dword ptr [eax
+
8
],
680D8B0Eh
mov byte ptr [eax
+
0Ch
],
0E9h
mov byte ptr [eax
+
0Dh
],
30h
mov eax,remote_data
push offset ntdll!RtlUserThreadStart (
772641e0
);hook_location
jmp dynamorio.dll!dynamorio_earliest_init_takeover
/
*
将上面在local_code_buf 构造的代码 写入到remote_code_buf(hook_code_buf)
*
/
write_remote_memory_maybe64(phandle, hook_code_buf, local_code_buf,
cur_local_pos
-
local_code_buf, &num_bytes_out)
return
hook_code_buf;
}
bool
dr_inject_process_run(void
*
data)
{
dr_inject_info_t
*
info
=
(dr_inject_info_t
*
)data;
if
(info
-
>attached
=
=
true) {
/
*
detach the debugger
*
/
DebugActiveProcessStop(info
-
>pi.dwProcessId);
}
/
*
resume the suspended app process so its main thread can run
*
/
/
*
恢复主线程
*
/
ResumeThread(info
-
>pi.hThread);
close_handle(info
-
>pi.hThread);
return
true;
}
bool
dr_inject_process_run(void
*
data)
{
dr_inject_info_t
*
info
=
(dr_inject_info_t
*
)data;
if
(info
-
>attached
=
=
true) {
/
*
detach the debugger
*
/
DebugActiveProcessStop(info
-
>pi.dwProcessId);
}
/
*
resume the suspended app process so its main thread can run
*
/
/
*
恢复主线程
*
/
ResumeThread(info
-
>pi.hThread);
close_handle(info
-
>pi.hThread);
return
true;
}
00b60000
89050010b600
mov dword ptr ds:[
0B61000h
],eax
00b60006
b8e0412677 mov eax,
772641E0h
00b6000b
c74000833d68e9 mov dword ptr [eax],
0E9683D83h
00b60012
c7400430770074 mov dword ptr [eax
+
4
],
74007730h
00b60019
c740080e8b0d68 mov dword ptr [eax
+
8
],
680D8B0Eh
00b60020
c6400ce9 mov byte ptr [eax
+
0Ch
],
0E9h
00b60024
c6400d30 mov byte ptr [eax
+
0Dh
],
30h
00b60028
b80010b600 mov eax,
0B61000h
;remote_data
00b6002d
68e0412677
push
772641E0h
;RtlUserThreadStart
00b60032
e9a634256f jmp dynamorio!dynamorio_earliest_init_takeover (
6fdb34dd
)
00b60000
89050010b600
mov dword ptr ds:[
0B61000h
],eax
00b60006
b8e0412677 mov eax,
772641E0h
00b6000b
c74000833d68e9 mov dword ptr [eax],
0E9683D83h
00b60012
c7400430770074 mov dword ptr [eax
+
4
],
74007730h
00b60019
c740080e8b0d68 mov dword ptr [eax
+
8
],
680D8B0Eh
00b60020
c6400ce9 mov byte ptr [eax
+
0Ch
],
0E9h
00b60024
c6400d30 mov byte ptr [eax
+
0Dh
],
30h
00b60028
b80010b600 mov eax,
0B61000h
;remote_data
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)