Reversing/Reverse Engineering

어셈블리 코드 변환

 

주어진 어셈블리 코드들은 모두 AT&T 문법으로 작성되어 있다. 이전에 정리했던 instructions는 intel 문법에 따른 어셈블리 코드들로, AT&T 문법에서는 intel 문법과 달리 오른쪽 operand에 값이 저장된다.

1번

.file    "example1.c"
.section    .rodata
.LC0:
.string    "Hello world"
.text
.globl    main
.type    main, @function
main:
.LFB0:
.cfi_startproc

pushq    %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret

.cfi_endproc
.LFE0:
.size    main, .-main
.ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section    .note.GNU-stack,"",@progbits
  • 따로 스택이 필요하지 않은 상황이라 스택을 할당하는 (sub rsp) 과정은 없는 것 같다.
  • movl $.LC0, %edi : edi 레지스터에 LC0의 값 저장
#include <stdio.h>

int main(){
    printf("Hello world");
    return 0;
}

2번

.file    "example2.c"
.section    .rodata
.LC0:
.string    "result : %d \n"
.text
.globl    main
.type    main, @function
main:
.LFB0:
.cfi_startproc

pushq    %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $16, %rsp
#(1)
movl    $10, -12(%rbp)
movl    $20, -8(%rbp)
movl    -8(%rbp), %eax
movl    -12(%rbp), %edx
addl    %edx, %eax
movl    %eax, -4(%rbp)
#(2)
movl    -8(%rbp), %eax
movl    -12(%rbp), %edx
addl    %edx, %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf

movl    $0, %eax
leave
.cfi_def_cfa 7, 8
ret

.cfi_endproc
.LFE0:
.size    main, .-main
.ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section    .note.GNU-stack,"",@progbits
  • subq $16, %rsp : rsp = rsp - 16, 즉 스택을 16byte만큼 할당한다.
레지스터(초기위치) (1) (2)
rbp    
rbp-4 30 (eax) 30
rbp-8 20 (eax) 20 (eax)
rbp-12 10 (edx) 10 (edx)
rbp-16    

(1)

  • addl %edx, %eax : eax = eax + edx (저장된 값) → eax = 30
  • movl %eax, -4(%rbp) : rbp-4의 위치에 eax값을 저장한다

(2)

  • movl -8(%rbp), %eax movl -12(%rbp), %edx : [rbp-8]의 값 20 을 eax에 저장하고, [rbp-12]의 값 10을 edx에 저장한다
  • addl %edx, %eax : eax = eax + edx = 30
  • movl %eax, %esi : esi = eax, eax에 저장된 값(30)을 esi에 저장하고 이 값이 따로 스택 변수에 저장되지 않고 바로 printf 함수의 참조값으로 들어간다.
#include <stdio.h>

int main(){
    int a = 10;
    int b = 20;
    int c = a+b;
    printf("result : %d \n", a+b);
    return 0;
}

[결과]

result : 30


3번

.file    "example3.c"
.section    .rodata
.LC0:
.string    "a is 10"
.LC1:
.string    "b is 10"
.LC2:
.string    "b is 20"
.LC3:
.string    "a=b"
.LC4:
.string    "a!=b"
.text
.globl    main
.type    main, @function

main:
.LFB0:
.cfi_startproc

pushq    %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $16, %rsp
movl    $10, -8(%rbp)
movl    $20, -4(%rbp)
cmpl    $10, -8(%rbp)
jne    .L2
movl    $.LC0, %edi  # "a is 10"
call    puts

.L2:
cmpl    $10, -4(%rbp)
jne    .L3
movl    $.LC1, %edi # "b is 10"
call    puts
jmp    .L4

.L3:
cmpl    $20, -4(%rbp)
jne    .L4
movl    $.LC2, %edi # "b is 20"
call    puts

.L4:
movl    -8(%rbp), %eax
cmpl    -4(%rbp), %eax
jne    .L5
movl    $.LC3, %edi # "a=b"
call    puts
jmp    .L6

.L5:
movl    $.LC4, %edi # "a!=b"
call    puts

.L6:
movl    $0, %eax

leave
.cfi_def_cfa 7, 8
ret

.cfi_endproc
.LFE0:
.size    main, .-main
.ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section    .note.GNU-stack,"",@progbits

 

rbp  
rbp-4 20 (b)
rbp-8 10 (a)
rbp-12  
rbp-16  
  • cmpl $10, -8(%rbp) jne .L2 : [rbp-8]의 값과 10을 비교하고 만약 같지 않은 경우(jne; jump if not equal) .L2로 점프

if ( a == 10 )

   ㄴ puts(.LC0)

 

else 문을 따로 쓰지 않은 이유는 a가 10이 아닌 경우에는 바로 .L2로 점프하지만, a가 10인 경우에는 puts(LC0)를 하고 난 후 .L2 영역을 실행하기 때문이다. 정확히 알지 못하지만 어셈블리 코드의 흐름 상 영역을 편의상 나눠놨지만, 실제로는 쭉 이어지는 코드라고 생각했다.

 

  • L2 영역

cmpl    $10, -4(%rbp)  # [rbp-4]의 값(20; b)와 10을 비교해서
jne    .L3                  # 같지않으면 L3영역으로 점프 / 같으면 바로 다음줄인 movl 명령
movl    $.LC1, %edi    # "b is 10" # edi 레지스터에 LC1의 값 "b is 10"을 저장하고
call    puts               # puts 함수를 호출 >> puts("b is 10")
jmp    .L4                # L4 영역으로 점프

  • L3 영역

cmpl    $20, -4(%rbp) # [rbp-4]의 값(20; b)과 20을 비교해서
jne    .L4                 # 같지 않으면 L4로 점프 / 같으면 바로 다음줄 명령 실행
movl    $.LC2, %edi   # "b is 20" # edi 레지스터에 LC2의 값 "b is 20"을 저장하고
call    puts              # puts 함수를 호출 >> puts("b is 20")

  • L4 영역

movl    -8(%rbp), %eax # [rbp-8]의 값(10; a)을 eax 레지스터에 저장
cmpl    -4(%rbp), %eax # [rbp-4]의 값(20; b)과 eax 레지스터에 저장된 값을 비교 >> a와 b의 값 비교
jne    .L5                   # a!=b 인 경우 L5로 점프
movl    $.LC3, %edi     # "a=b" # edi 레지스터에 LC3의 값 "a=b"를 저장하고
call    puts                # puts 함수를 호출 >> puts("a=b")
jmp    .L6                 # L6로 무조건 점프

  • L5 영역

movl    $.LC4, %edi    # "a!=b" # a!=b인 경우 L5로 점프하고, edi 레지스터에 LC4의 값 "a!=b"를 저장
call    puts               # puts 함수 호출 >> puts("a!=b")

#include <stdio.h>

int main(){
    int a = 10;
    int b = 20;
    //
    if(a == 10) 
        puts("a is 10");
    
    //.L2
    if(b == 10)
    	puts("b is 10");
    //.L3
    else if(b == 20)
    	puts("b is 20");
    
    if(a == b)
    	puts("a=b");
    else
    	puts("a!=b");
    
    return 0;
}
SMALL

'Reversing > Reverse Engineering' 카테고리의 다른 글

매뉴얼 언패킹(MUP) 사례 - ESP Trick으로 OEP 찾기  (1) 2021.05.18
패킹과 언패킹  (0) 2021.05.18
HelloWorld.exe  (2) 2021.04.01
Instruction (명령어)  (0) 2021.03.30
리버싱을 위한 기초 지식(구조)  (0) 2021.03.28