Hi Hackers,
反向工程、防反向是永恒的博弈艺术
引子:我这两天学习如何编写一个LLVM PASS 纯练手写了一个LLVM “防”反向工程 PASS——ARE(Anti Reverse Engeering) 很KISS(Keep It Simple and Stupid),请大大们轻轻拍砖,目前70+行,实现了:
- 修改函数名称
.text
.file "test-are.c"
.globl login # -- Begin function login
.p2align 4, 0x90
.type login,@function
login: # @login
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi0:
.cfi_def_cfa_offset 16
.Lcfi1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi2:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str, %rax
movabsq $.L.str.1, %rcx
movq %rdi, -24(%rbp)
movq %rsi, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movl $6, %edx
movb $0, %al
callq printf
cmpq $0, -24(%rbp)
je .LBB0_2
# BB#1:
cmpq $0, -16(%rbp)
jne .LBB0_3
.LBB0_2:
movl $-1, -4(%rbp)
jmp .LBB0_4
.LBB0_3:
movl $0, -4(%rbp)
.LBB0_4:
movl -4(%rbp), %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end0:
.size login, .Lfunc_end0-login
.cfi_endproc
# -- End function
.globl logout # -- Begin function logout
.p2align 4, 0x90
.type logout,@function
logout: # @logout
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi3:
.cfi_def_cfa_offset 16
.Lcfi4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi5:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movabsq $.L.str.2, %rax
movabsq $.L.str.1, %rsi
movl %edi, -4(%rbp)
movl -4(%rbp), %ecx
movq %rax, %rdi
movl $14, %edx
movb $0, %al
callq printf
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.Lfunc_end1:
.size logout, .Lfunc_end1-logout
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi6:
.cfi_def_cfa_offset 16
.Lcfi7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi8:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str.4, %rax
movabsq $.L.str.5, %rcx
movabsq $.L.str.3, %rdx
movl $0, -8(%rbp)
movl %edi, -4(%rbp)
movq %rsi, -24(%rbp)
movq %rdx, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movb $0, %al
callq printf
xorl %edi, %edi
xorl %esi, %esi
callq login
movl $4294967295, %edi # imm = 0xFFFFFFFF
callq logout
xorl %eax, %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "DEBUG: %s, line %d\n"
.size .L.str, 20
.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "test-are.c"
.size .L.str.1, 11
.type .L.str.2,@object # @.str.2
.L.str.2:
.asciz "DEBUG: %s, line %d: ID %d\n"
.size .L.str.2, 27
.type .L.str.3,@object # @.str.3
.L.str.3:
.asciz "Vml5Z0pFZGk9UHg2a2dPY0loZW49S3cxN3dVQUFBPT0"
.size .L.str.3, 44
.type .L.str.4,@object # @.str.4
.L.str.4:
.asciz "Hello world: %s\n"
.size .L.str.4, 17
.type .L.str.5,@object # @.str.5
.L.str.5:
.asciz "6xxzcQMhb4WgKX0EUkwG747K"
.size .L.str.5, 25
.ident "Fedora clang version 6.0.0 (trunk 311323) (based on LLVM 6.0.0svn-r311323)"
.section ".note.GNU-stack","",@progbits
将敏感的函数名,例如login、logout,修改成“无意义”的:
.text
.file "test-are.c"
.globl dragonball1 # -- Begin function dragonball1
.p2align 4, 0x90
.type dragonball1,@function
dragonball1: # @dragonball1
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi0:
.cfi_def_cfa_offset 16
.Lcfi1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi2:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str, %rax
movabsq $.L.str.1, %rcx
movq %rdi, -24(%rbp)
movq %rsi, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movl $6, %edx
movb $0, %al
callq printf
cmpq $0, -24(%rbp)
je .LBB0_2
# BB#1:
cmpq $0, -16(%rbp)
jne .LBB0_3
.LBB0_2:
movl $-1, -4(%rbp)
jmp .LBB0_4
.LBB0_3:
movl $0, -4(%rbp)
.LBB0_4:
movl -4(%rbp), %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end0:
.size dragonball1, .Lfunc_end0-dragonball1
.cfi_endproc
# -- End function
.globl dragonball2 # -- Begin function dragonball2
.p2align 4, 0x90
.type dragonball2,@function
dragonball2: # @dragonball2
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi3:
.cfi_def_cfa_offset 16
.Lcfi4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi5:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movabsq $.L.str.2, %rax
movabsq $.L.str.1, %rsi
movl %edi, -4(%rbp)
movl -4(%rbp), %ecx
movq %rax, %rdi
movl $14, %edx
movb $0, %al
callq printf
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.Lfunc_end1:
.size dragonball2, .Lfunc_end1-dragonball2
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi6:
.cfi_def_cfa_offset 16
.Lcfi7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi8:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str.4, %rax
movabsq $.L.str.5, %rcx
movabsq $.L.str.3, %rdx
movl $0, -8(%rbp)
movl %edi, -4(%rbp)
movq %rsi, -24(%rbp)
movq %rdx, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movb $0, %al
callq printf
xorl %edi, %edi
xorl %esi, %esi
callq dragonball1
movl $4294967295, %edi # imm = 0xFFFFFFFF
callq dragonball2
xorl %eax, %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "DEBUG: %s, line %d\n"
.size .L.str, 20
.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "test-are.c"
.size .L.str.1, 11
.type .L.str.2,@object # @.str.2
.L.str.2:
.asciz "DEBUG: %s, line %d: ID %d\n"
.size .L.str.2, 27
.type .L.str.3,@object # @.str.3
.L.str.3:
.asciz "Vml5Z0pFZGk9UHg2a2dPY0loZW49S3cxN3dVQUFBPT0"
.size .L.str.3, 44
.type .L.str.4,@object # @.str.4
.L.str.4:
.asciz "Hello world: %s\n"
.size .L.str.4, 17
.type .L.str.5,@object # @.str.5
.L.str.5:
.asciz "6xxzcQMhb4WgKX0EUkwG747K"
.size .L.str.5, 25
.ident "Fedora clang version 6.0.0 (trunk 311323) (based on LLVM 6.0.0svn-r311323)"
.section ".note.GNU-stack","",@progbits
TODO 还未实现的功能:
- 使常量“人类”不可读,直接修改LLVM IR的Constant:
; ModuleID = 'test-are-mod.bc'
source_filename = "test-are.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-redhat-linux"
@.str = private unnamed_addr constant [20 x i8] c"DEBUG: %s, line %d\0A\00", align 1
@.str.1 = private unnamed_addr constant [11 x i8] c"test-are.c\00", align 1
@.str.2 = private unnamed_addr constant [27 x i8] c"DEBUG: %s, line %d: ID %d\0A\00", align 1
@.str.3 = private unnamed_addr constant [44 x i8] c"Vml5Z0pFZGk9UHg2a2dPY0loZW49S3cxN3dVQUFBPT0\00", align 1
@.str.4 = private unnamed_addr constant [17 x i8] c"Hello world: %s\0A\00", align 1
@.str.5 = private unnamed_addr constant [25 x i8] c"6xxzcQMhb4WgKX0EUkwG747K\00", align 1
; Function Attrs: noinline nounwind optnone uwtable
define i32 @dragonball1(i8*, i8*) #0 {
%3 = alloca i32, align 4
%4 = alloca i8*, align 8
%5 = alloca i8*, align 8
store i8* %0, i8** %4, align 8
store i8* %1, i8** %5, align 8
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 6)
%7 = load i8*, i8** %4, align 8
%8 = icmp ne i8* %7, null
br i1 %8, label %9, label %12
; <label>:9: ; preds = %2
%10 = load i8*, i8** %5, align 8
%11 = icmp ne i8* %10, null
br i1 %11, label %13, label %12
; <label>:12: ; preds = %9, %2
store i32 -1, i32* %3, align 4
br label %14
; <label>:13: ; preds = %9
store i32 0, i32* %3, align 4
br label %14
; <label>:14: ; preds = %13, %12
%15 = load i32, i32* %3, align 4
ret i32 %15
}
declare i32 @printf(i8*, ...) #1
; Function Attrs: noinline nounwind optnone uwtable
define i32 @dragonball2(i32) #0 {
%2 = alloca i32, align 4
store i32 %0, i32* %2, align 4
%3 = load i32, i32* %2, align 4
%4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @.str.2, i32 0, i32 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 14, i32 %3)
ret i32 0
}
; Function Attrs: noinline nounwind optnone uwtable
define i32 @main(i32, i8**) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca i8*, align 8
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
store i8* getelementptr inbounds ([44 x i8], [44 x i8]* @.str.3, i32 0, i32 0), i8** %6, align 8
%7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.4, i32 0, i32 0), i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str.5, i32 0, i32 0))
%8 = call i32 @dragonball1(i8* null, i8* null)
%9 = call i32 @dragonball2(i32 -1)
ret i32 0
}
attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Fedora clang version 6.0.0 (trunk 311323) (based on LLVM 6.0.0svn-r311323)"}
将敏感的常量,例如SKEY,变成“人类”不可读的:
.text
.file "test-are.c"
.globl dragonball1 # -- Begin function dragonball1
.p2align 4, 0x90
.type dragonball1,@function
dragonball1: # @dragonball1
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi0:
.cfi_def_cfa_offset 16
.Lcfi1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi2:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str, %rax
movabsq $.L.str.1, %rcx
movq %rdi, -24(%rbp)
movq %rsi, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movl $6, %edx
movb $0, %al
callq printf
cmpq $0, -24(%rbp)
je .LBB0_2
# BB#1:
cmpq $0, -16(%rbp)
jne .LBB0_3
.LBB0_2:
movl $-1, -4(%rbp)
jmp .LBB0_4
.LBB0_3:
movl $0, -4(%rbp)
.LBB0_4:
movl -4(%rbp), %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end0:
.size dragonball1, .Lfunc_end0-dragonball1
.cfi_endproc
# -- End function
.globl dragonball2 # -- Begin function dragonball2
.p2align 4, 0x90
.type dragonball2,@function
dragonball2: # @dragonball2
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi3:
.cfi_def_cfa_offset 16
.Lcfi4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi5:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movabsq $.L.str.2, %rax
movabsq $.L.str.1, %rsi
movl %edi, -4(%rbp)
movl -4(%rbp), %ecx
movq %rax, %rdi
movl $14, %edx
movb $0, %al
callq printf
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.Lfunc_end1:
.size dragonball2, .Lfunc_end1-dragonball2
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0:
pushq %rbp
.Lcfi6:
.cfi_def_cfa_offset 16
.Lcfi7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Lcfi8:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str.4, %rax
movabsq $.L.str.5, %rcx
movabsq $.L.str.3, %rdx
movl $0, -8(%rbp)
movl %edi, -4(%rbp)
movq %rsi, -24(%rbp)
movq %rdx, -16(%rbp)
movq %rax, %rdi
movq %rcx, %rsi
movb $0, %al
callq printf
xorl %edi, %edi
xorl %esi, %esi
callq dragonball1
movl $4294967295, %edi # imm = 0xFFFFFFFF
callq dragonball2
xorl %eax, %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "DEBUG: %s, line %d\n"
.size .L.str, 20
.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "test-are.c"
.size .L.str.1, 11
.type .L.str.2,@object # @.str.2
.L.str.2:
.asciz "DEBUG: %s, line %d: ID %d\n"
.size .L.str.2, 27
.type .L.str.3,@object # @.str.3
.L.str.3:
.asciz "Vml5Z0pFZGk9UHg2a2dPY0loZW49S3cxN3dVQUFBPT0"
.size .L.str.3, 44
.type main.buf,@object # @main.buf
.section .rodata,"a",@progbits
main.buf:
.ascii "\0224Vx" # 人类不可读
.size main.buf, 4
.type .L.str.4,@object # @.str.4
.section .rodata.str1.1,"aMS",@progbits,1
.L.str.4:
.asciz "Hello world: %s\n"
.size .L.str.4, 17
.type .L.str.5,@object # @.str.5
.L.str.5:
.asciz "6xxzcQMhb4WgKX0EUkwG747K"
.size .L.str.5, 25
.ident "Fedora clang version 6.0.0 (trunk 311323) (based on LLVM 6.0.0svn-r311323)"
.section ".note.GNU-stack","",@progbits
- 派生自LLVM的CallGraphSCCPass类,使得控制流图复杂化。
我在Linux进行了测试,大家可以在Mac上编译安装,期待大家的反馈,谢谢!
注:ARE(归属于 DragonEgg 子项目)使用GPLv2许可证。
Regards,
Leslie Zhai - a LLVM developer