상세 컨텐츠

본문 제목

asm 분석: 어셈블리 코드에서 소스 코드 변환해보기

REVERSING/Reversing Study

by koharin 2021. 4. 5. 22:00

본문

728x90
반응형

 

example1.asm

.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
  • asm 파일에서 코드 변환에서 본 부분은 아래와 같다.

.LC0:
.string "Hello world"

pushq %rbp ; SFP에 rbp 저장
movq %rsp, %rbp ; rbp = rsp
movl $.LC0, %edi ; printf 첫 번째 인자로 LC0에 저장된 스트링 저장
movl $0, %eax ; eax에 0 저장
call printf ; printf 함수 호출
movl $0, %eax ; eax에 0 저장 -> return 값 0으로
popq %rbp ; rbp
ret ; return address 주소로 리턴

위의 분석으로 다음과 같은 소스코드를 작성했다.

#include <stdio.h>
int main(){
    printf("Hello world");
    return 0;
}

위의 코드를 우분투에서 gcc -o example1 example1.c로 컴파일 한 후,
objdump -d example1으로 어셈블리 코드를 확인했다.

image

  • 위와 같이 동일한 어셈블리 코드를 얻을 수 있다.

example2.asm

.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
movl    $10, -12(%rbp)
movl    $20, -8(%rbp)
movl    -8(%rbp), %eax
movl    -12(%rbp), %edx
addl    %edx, %eax
movl    %eax, -4(%rbp)
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
  • 위의 asm 코드에서는 다음을 보고 코드를 작성했다.

.LC0:
.string "result : %d \n"

pushq %rbp ; 함수 프롤로그
movq %rsp, %rbp
subq $16, %rsp ; rsp - 0x10으로 스택 공간 확보
movl $10, -12(%rbp) ; rbp-0xc에 10 저장 (지역변수)
movl $20, -8(%rbp) ; rbp-0x8에 20 저장 (지역변수)
movl -8(%rbp), %eax ; eax에 rbp-0x8 값 저장
movl -12(%rbp), %edx ; edx에 rbp-0xc 값 저장
addl %edx, %eax ; eax = eax + edx
movl %eax, -4(%rbp) ; rbp-0x4에 eax 값 저장 (지역변수)
movl -8(%rbp), %eax ; eax에 rbp-0x8 값 저장
movl -12(%rbp), %edx ; edx에 rbp-0xc 값 저장
addl %edx, %eax ; eax = eax + edx
movl %eax, %esi ; printf의 두 번째 인자 값 eax를 esi에 저장
movl $.LC0, %edi ; printf의 첫 번째 인자 값을 LC0 주소의 스트링으로 가져와서 edi에 저장
movl $0, %eax ; eax를 0으로 초기화
call printf ; printf 함수 호출
movl $0, %eax ; eax(리턴값)에 0 저장
leave ; 함수 에필로그
ret

#include <stdio.h>
int main(){
    int a = 10; // rbp-0xc
    int b = 20; // rbp-0x8

    int c = a + b; // eax = eax + edx, rbp-0x4
    
    printf("result : %d\n", a + b);
    return 0;
}
  • 분석 결과로 위의 코드를 작성했다.

 

example3.asm

.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
call    puts
.L2:
cmpl    $10, -4(%rbp)
jne    .L3
movl    $.LC1, %edi
call    puts
jmp    .L4
.L3:
cmpl    $20, -4(%rbp)
jne    .L4
movl    $.LC2, %edi
call    puts
.L4:
movl    -8(%rbp), %eax
cmpl    -4(%rbp), %eax
jne    .L5
movl    $.LC3, %edi
call    puts
jmp    .L6
.L5:
movl    $.LC4, %edi
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
  • 위의 asm 코드에서 다음 부분으로 분석했다.

.LC0:
.string "a is 10"
.LC1:
.string "b is 10"
.LC2:
.string "b is 20"
.LC3:
.string "a=b"
.LC4:
.string "a!=b"

main:
pushq %rbp ; 함수 프롤로그
movq %rsp, %rbp
subq $16, %rsp ; rsp - 0x10으로 스택 공간 확보
movl $10, -8(%rbp) ; 스택에 지역변수 값으로 10 저장
movl $20, -4(%rbp) ; 스택에 지역변수 20 저장
cmpl $10, -8(%rbp) ; a == 10 ?
jne .L2 ; 아니면 L2로 점프
movl $.LC0, %edi ; "a is 10" 인자로 가져온다.
call puts ; puts 함수 호출
.L2:
cmpl $10, -4(%rbp) ; b == 10 ?
jne .L3 ; 아니면 L3로 점프
movl $.LC1, %edi ; "b is 10" 스트링 인자로 가져옴
call puts ; puts 함수 호출
jmp .L4 ; L4로 점프
.L3:
cmpl $20, -4(%rbp) ; b == 20 ?
jne .L4 ; 아니면 L4로 점프
movl $.LC2, %edi ; 맞으면 "b is 20" 스트링 puts 인자로 가져옴
call puts ; puts 함수 호출
.L4:
movl -8(%rbp), %eax ; c = a
cmpl -4(%rbp), %eax ; c == b ?
jne .L5 ; 아니면 L5로 점프
movl $.LC3, %edi ; 맞으면 "a=b" 스트링 인자로 가져옴
call puts ; puts 함수 호출
jmp .L6 ; L6로 점프
.L5:
movl $.LC4, %edi ; "a!=b" 스트링 인자로 가져옴
call puts ; puts 함수 호출
.L6:
movl $0, %eax ; 리턴 값 0 저장
leave ; 함수 에필로그
ret

#include <stdio.h>
int main(){
    int a = 10;
    int b = 20;
    int c;

    if(a == 10)
        puts("a is 10");
    if(b == 10)
        puts("b is 10");
    else if(b == 20)
        puts("b is 20");
    if(a == b)
        puts("a=b");
    else
        puts("a!=b");
    return 0;
}

 

example4.asm

.file	"example4.c"
.section	.rodata
.LC0:
	.string	"result : %d\n"
.text
.globl	function
.type	function, @function
function:
	.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	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	movl	-4(%rbp), %eax
	imull	-8(%rbp), %eax
	movl	%eax, %esi
	movl	$.LC0, %edi
	movl	$0, %eax
	call	printf
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
	.LFE0:
	.size	function, .-function
.globl	main
.type	main, @function
main:
	.LFB1:
	.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	$50, -8(%rbp)
	movl	$60, -4(%rbp)
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	movl	%edx, %esi
	movl	%eax, %edi
	call	function
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
	.LFE1:
	.size	main, .-main
.ident	"GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section	.note.GNU-stack,"",@progbits

function 함수

subq $16, %rsp ; rsp - 0x10으로 스택 공간 확보

movl %edi, -4(%rbp) ; 매개변수로 가져온 edi를 rbp-4에 넣는다.

movl %esi, -8(%rbp) 두 번째 매개변수로 가져온 값(esi에 있는)을 rbp-8에 저장

movl -4(%rbp), %eax ; eax에 rbp-4 값 저장

imull -8(%rbp), %eax ; eax 와 rbp-8 값 곱해서 eax에 저장

movl %eax, %esi ; 두 번째 인자로 eax를 esi 레지스터에 넣음

movl $.LC0, %edi ; 첫 번째 인자로 "result : %d\n" 가져옴

movl $0, %eax

call printf ; printf 함수 호출

leave 

ret

main 함수

pushq %rbp ; rbp 레지스터를 SFP(Stack Frame Pointer)에 저장

movq %rsp, %rbp ; rbp = rsp

subq $16, %rsp ; rsp - 0x10으로 스택 공간 확보

movl $50, -8(%rbp) ; rbp-8에 50 저장

movl $60, -4(%rbp) ; rbp-4에 60 저장

movl -4(%rbp), %edx ; edx에 rbp-4 값 저장 edx = rbp-4

movl -8(%rbp), %eax ; eax에 rbp-8 값 저장

movl %edx, %esi ; esi 레지스터에 edx 저장 (function의 두 번째 인자)

movl %eax, %edi ; edi 레지스터에 eax 값 저장 (function의 첫 번째 인자)

call function ; function 함수 호출

movl $0, %eax

leave

ret

#include <stdio.h>
int function(int a, int b){
    
    printf("result : %d\n", a * b);
    return 0;
}

int main(){
    int a = 50;
    int b = 60;

    function(a, b);

    return 0;
}

 

example5.asm

.file	"example5.c"
.section	.rodata
.LC0:
.string	"number %d \n"
.LC1:
.string	"%d * %d = %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
movl	$0, -8(%rbp)
jmp	.L2
.L3:
movl	-8(%rbp), %eax
movl	%eax, %esi
movl	$.LC0, %edi
movl	$0, %eax
call	printf
addl	$1, -8(%rbp)
.L2:
cmpl	$9, -8(%rbp)
jle	.L3
movl	$0, -4(%rbp)
jmp	.L4
.L5:
movl	-8(%rbp), %eax
imull	-4(%rbp), %eax
movl	%eax, %ecx
movl	-4(%rbp), %edx
movl	-8(%rbp), %eax
movl	%eax, %esi
movl	$.LC1, %edi
movl	$0, %eax
call	printf
addl	$1, -4(%rbp)
.L4:
cmpl	$4, -4(%rbp)
jle	.L5
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

.file "example5.c"
.section .rodata
.LC0:
.string "number %d \n"
.LC1:
.string "%d * %d = %d \n"
.text
.globl main
.type main, @function
main:
.LFB0:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp ; 스택 공간 0x10(16바이트)만큼 확보
movl $0, -8(%rbp) ; rbp-0x8에 0 저장 (지역변수)
jmp .L2 ; .L2로 이동
.L3:
movl -8(%rbp), %eax ; eax 레지스터에 rbp-8 저장 
movl %eax, %esi ; esi 레지스터에 eax 값(rbp-8) 저장 (printf의 인자2)
movl $.LC0, %edi ; edi 레지스터에 LC0의 문자열 저장 (printf의 인자1)
movl $0, %eax
call printf ; printf 호출
addl $1, -8(%rbp) ; rbp-8에 1 더한다. (i++) (증감식)
.L2:
cmpl $9, -8(%rbp) ; rbp-8 값과 9 비교 (조건식)
jle .L3 ; 작거나 같으면 L3로 이동 (루프 들어감)
movl $0, -4(%rbp) ; 크면 rbp-4에 0 저장 (int j = 0)
jmp .L4 ; L4로 이동 (루프에서 나옴)
.L5:
movl -8(%rbp), %eax ; eax 레지스터에 rbp-8 저장 (a 값)
imull -4(%rbp), %eax ; eax = eax * [rbp-4] (b에 a 곱함)
movl %eax, %ecx ; ecx에 eax 값 저장 (a*b를 eax 레지스터에 저장
movl -4(%rbp), %edx ; edx에 rbp-4 저장 ; 변수 b 를 edx 레지스터에 저장
movl -8(%rbp), %eax ; eax에 rbp-8 저장 (변수 a를 eax 레지스터에 저장)
movl %eax, %esi ; 두 번째 인자 (변수 a)
movl $.LC1, %edi ; 첫 번째 인자
movl $0, %eax
call printf
addl $1, -4(%rbp) ; rbp-4 1 증가 (증감식)
.L4:
cmpl $4, -4(%rbp) ; rbp-4와 4 비교 (조건식)
jle .L5 ; 4보다 작거나 같으면 루프 안으로 들어감
movl $0, %eax ; return 0
leave
ret

#include <stdio.h>
int main(){
    int a = 0; 
    
    for(int i=0; i<=9; i++){ 
        a = i;
        printf("numebr %d \n", a);
    }

    for(int j=0; j<=4; j++) 
    {
        int b = j;

        printf("%d * %d = %d \n", a, b, a*b);
    }

    return 0;
}

$ objdump -d example5

728x90
반응형

관련글 더보기