Symbol recovery on rust executables
Why ?
- Rust is hard to reverse engineer
- Compilation leaves a lot of artifacts by default
Symbol recovery usual workfow
Find target dependencies
(e.g Tokio v1.2)
Symbol recovery usual workfow
Find target dependencies
(e.g Tokio v1.2)
push rbp
push rbx
sub rsp, 58h
lea rdi, off_2E60F8
call std::thread::local::Loca...
mov rcx, 736F6D6570736575h
xor rcx, rax
mov rsi, 646F72616E646F6Dh
xor rsi, rdx
mov rdi, 6C7967656E657261h
xor rdi, rax
mov r8, 7465646279746573h
xor r8, rdx
Symbol recovery usual workfow
Find which compiler was used by your target
Find target's dependencies
(e.g Tokio v1.2)
Symbol recovery usual workfow
Find which compiler was used by your target
Compile the dependency yourself
Find target's dependencies
(e.g Tokio v1.2)
Symbol recovery usual workfow
Find which compiler was used by your target
Find target's dependencies
(e.g Tokio v1.2)
Compile the dependency yourself
Compile the dependency yourself
push rbp
push rbx
sub rsp, 58h
lea rdi, off_2E60F8
call std::thread::local::Loca...
mov rcx, 736F6D6570736575h
xor rcx, rax
mov rsi, 646F72616E646F6Dh
xor rsi, rdx
mov rdi, 6C7967656E657261h
xor rdi, rax
mov r8, 7465646279746573h
xor r8, rdx
Symbol recovery usual workfow
Find which compiler was used by your target
Compile the dependency yourself
Find target's dependencies
(e.g Tokio v1.2)
1:1 CODE MATCH !
push rbp
push rbx
sub rsp, 58h
lea rdi, off_2E60F8
call std::thread::local::Loca...
mov rcx, 736F6D6570736575h
xor rcx, rax
mov rsi, 646F72616E646F6Dh
xor rsi, rdx
mov rdi, 6C7967656E657261h
xor rdi, rax
mov r8, 7465646279746573h
xor r8, rdx
Compile the dependency yourself
push rbp
push rbx
sub rsp, 58h
lea rdi, off_2E60F8
call std::thread::local::Loca...
mov rcx, 736F6D6570736575h
xor rcx, rax
mov rsi, 646F72616E646F6Dh
xor rsi, rdx
mov rdi, 6C7967656E657261h
xor rdi, rax
mov r8, 7465646279746573h
xor r8, rdx
Symbol recovery usual workfow
Find which compiler was used by your target
Compile the dependency yourself
Find target's dependencies
(e.g Tokio v1.2)
Sign it (IDA's sigmake,
binja's sigkit...)
tokiov1.2.sig
Symbol recovery usual workfow
Find which compiler was used by your target
Compile the dependency yourself
Find target's dependencies
(e.g Tokio v1.2)
Sign it (IDA's sigmake,
binja's sigkit...)
tokiov1.2.sig
Symbol recovery usual workfow
Find which compiler was used by your target
Compile the dependency yourself
Find target's dependencies
(e.g Tokio v1.2)
Sign it (IDA's sigmake,
binja's sigkit...)
tokiov1.2.sig
Rust compilation artifacts
user@computer:~$ strings target.exe
[...]
addr2line-0.17.0/src/function.rs [...]
aes-0.7.5/src/soft/fixslice64.rs [...]
bytes-1.4.0/src/buf/buf_impl.rs [...]
[...]
Dependencies & versions
user@computer:~$ strings target.exe
[...]
/rustc/90c541806f23a127002de5b4038be731ba1458ca/core/[...]
/rustc/90c541806f23a127002de5b4038be731ba1458ca/core[...]
...
Rust compiler commit hash
Issues
- rustc does not guarantee order of fields in structs
Issues
- rustc does not guarantee order of fields in structs
- Rust has generics ("Monomorphization")
struct Box<T>
Issues
- Attacker can compile with LTO (Link Time Optimization)
push rbp
push rbx
sub rsp, 58h
lea rdi, off_2E60F8
call sub_4B224
mov rcx, 736F6D6570736575h
push rbp
push rbx
sub rsp, 58h
; <some bad boy disappeared here !>
call sub_4B224
mov rcx, 736F6D6570736575h
Same library, same version, compiled with LTO on different targets
Workaround
-
Rust libraries usually well maintained / developed
-
tests, benches, examples available to build
- good code coverage
- many different usage of the API
-
tests, benches, examples available to build
Workaround
-
Rust libraries usually well maintained / developed
-
tests, benches, examples available to build
- good code coverage
- many different usage of the API
-
tests, benches, examples available to build
Results in a lot of executables to sign !
(1000+ sometimes)
3mn is such a short time frame !
Way more details on my blogpost + tool trying to automate the process
https://nofix.re/
deck
By Nofix
deck
- 62