逆向分析rust - (1)
rust现在用的越来越多了,很多红队也在使用rust进行进攻性工具开发,作为一名逆向工程师肯定也需要了解一下rust,于是就有了本文,在个人学习rust的过程中,顺便对生成的二进制进行逆向分析,一举两得,何乐而不为呢?
为了方便学习,会加载符号来协助分析,且大部分使用Windows平台X64架构Debug版本编译的二进制文件
语法就不讲解了,有兴趣的可以自己去学习相关语法
根据惯例,第一个程序以"hello world"开始
fn main(){
println!("Hello World");
}
Rust默认生成的是动态链接的二进制文件(即运行时需要运行时库,没有运行时库会提示缺少xxx.dll),使用`rustc -C target-feature=+crt-static main.rs`编译生成静态链接文件
查看生成的文件发现文件大小为258kb

作为对比,动态链接生成的文件大小为159kb

查看导入表,只有kernel32.dll

作为对比,动态链接生成的文件导入表有运行时相关库

使用IDA Pro 7.5打开,可以看到入口函数为`mainCRTStartup`,这有点像MSVC编译器生成的C/C++程序,然后内部调用`__scrt_common_main_seh`


在`_cexit`上方两个函数左右进入`main`,真是非常像msvc编译出来的C/C++程序

作为对比,看以下MSVC生成的C++程序,当然,也可能是因为使用了MSVC的工具链

main
函数也很简单,开辟栈空间,操作了`r8`,`rdx`,`rcx`,`r9`四个寄存器,看出来了吗?是不是很像经典的x64调用(cd89),其中`rcx`指向sub_1400010B0

我们看一下IDA的F5,可以判定就是cd89调用,`rcx`为sub_1400010B0,`rdx`为main函数的argc,也就是movsxd rdx,ecx的操作,`r8`为argv(mov r8,rdx),`r9`为0(xor r9d,r9d)

函数std::rt::lang_start::xxx从名称std::rt上来看像是运行时(runtime)相关的函数,我们暂时先不管,先来看看sub_1400010B0

因为我们的程序只有一个打印`hello world`,所以sub_1400010B0可能就是我们的主函数,首先调用了一个格式化字符串函数`core::fmt::xxx`,根据字符串`invalid args`猜测该函数还做了某些判断处理,通常这也符合Debug版本编译的程序的预期操作,然后通过print函数进行打印输出.程序返回后结束,看来没什么值得分析的地方,那么继续返回看看std::rt::lang_start::xxx函数,只有一个`std::rt::lang_start_internal::xxx`的调用

继续跟进去

看着不是很舒服,还是看汇编吧

发现有几个线程相关的函数`Thread::new`,`thread_info::set::xxx`,然后一个间接调用`call qword ptr[rax+28h]`,我们看看间接调用上面的ecx从哪里来的

可以看到ecx从var_50里取到的,var_50在函数入口处保存的是rcx的值,rcx从上面的分析可以看到就是`sub_1400010B0`,使用x64dbg来动态分析

call [rax+28h]函数内部调用了两个函数,一个为`rust_begin_short_backtrace`,一个为`std..process..Termination`,从名字上来看可能为栈追踪和进程结束.并且在函数入口获取了sub_1400010B0函数的地址

跟进`rust_begin_short_backtrace`,又有一个名字为`call_once`的函数调用

跟进去发现`call_once`函数内部有`call rcx`即上面分析的sub_1400010B0函数的流程,执行完返回后调用std..process..Termination,后续调用destroy等函数然后程序退出

main ->
std::rt::lang_start::xxx ->
std::rt::lang_start_internal::xxx ->
call qword ptr[rax+28h] ->
rust_begin_short_backtrace ->
call_once ->
call rcx(helloworld) ->
std::process::Termination
至此我们分析完一个rust的`hello world`程序,从入口到结束的大部分流程