워게임/리버싱

[Reversing] Handray (Asm to C)

lawence 2025. 5. 4. 19:09

 

 

 

 

시작 전에 x86-64 Calling Convention에 의해서 함수에 들어가는 인자를 알아보겠습니다.

 

 

가변 인자를 사용해도 되겠지만, 조금 더 이해하기 쉽도록 add 함수에 인자를 8개 넣어서 컴파일 했습니다.

 

 

정상적으로 실행이 됩니다.

 

이제 이 바이너리를 gdb로 돌려봐서 레지스터를 확인해보면

 

add 함수를 호출하기 전 1, 3, 5, 7, 9, 0xb, 0xd, 0xf가 알맞게 잘 들어가 있습니다.

 

각 인자들은 차례로 rdi, rsi, rdx, rcx, r8, r9을 사용하며, 이 외에 인자들은 스택을 이용합니다.

 

 

main +12로 r9 레지스터를 이용하기 직전으로 가보면 스택에 0xf와 0xd가 차례대로 들어와 있는 것을 확인할 수 있습니다.

 

 

 

 

 

Handray 1

   0x0000000000001149 <+0>:     endbr64
   0x000000000000114d <+4>:     push   rbp
   0x000000000000114e <+5>:     mov    rbp,rsp
   0x0000000000001151 <+8>:     lea    rax,[rip+0xeac]        # 0x2004
   0x0000000000001158 <+15>:    mov    rdi,rax
   0x000000000000115b <+18>:    call   0x1050 <puts@plt>
   0x0000000000001160 <+23>:    mov    eax,0x0
   0x0000000000001165 <+28>:    pop    rbp
   0x0000000000001166 <+29>:    ret

 

주어진 코드는 위와 같습니다.

 

코드가 짧으므로 간단하게 C로 바꿔보면, (편의상 main 함수라고 가정)

 

main +8에서 rax에 [rip + 0xeac]를 복사합니다. main +15에서 이를 rdi에 넣고, main +18에서 puts 함수를 호출합니다.

int main() {
    puts("~~~");
    return 0;
}

 

 

 

 

 

Handray 2

   0x0000000000001189 <+0>:     endbr64
   0x000000000000118d <+4>:     push   rbp
   0x000000000000118e <+5>:     mov    rbp,rsp
   0x0000000000001191 <+8>:     sub    rsp,0x20
   0x0000000000001195 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x000000000000119e <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011a2 <+25>:    xor    eax,eax
   0x00000000000011a4 <+27>:    lea    rax,[rbp-0x18]
   0x00000000000011a8 <+31>:    mov    rsi,rax
   0x00000000000011ab <+34>:    lea    rax,[rip+0xe52]        # 0x2004
   0x00000000000011b2 <+41>:    mov    rdi,rax
   0x00000000000011b5 <+44>:    mov    eax,0x0
   0x00000000000011ba <+49>:    call   0x1090 <__isoc99_scanf@plt>
   0x00000000000011bf <+54>:    mov    DWORD PTR [rbp-0x14],0x0
   0x00000000000011c6 <+61>:    jmp    0x121c <main+147>
   0x00000000000011c8 <+63>:    mov    DWORD PTR [rbp-0x10],0x0
   0x00000000000011cf <+70>:    jmp    0x11df <main+86>
   0x00000000000011d1 <+72>:    mov    edi,0x20
   0x00000000000011d6 <+77>:    call   0x1070 <putchar@plt>
   0x00000000000011db <+82>:    add    DWORD PTR [rbp-0x10],0x1
   0x00000000000011df <+86>:    mov    eax,DWORD PTR [rbp-0x18]
   0x00000000000011e2 <+89>:    sub    eax,DWORD PTR [rbp-0x14]
   0x00000000000011e5 <+92>:    sub    eax,0x1
   0x00000000000011e8 <+95>:    cmp    DWORD PTR [rbp-0x10],eax
   0x00000000000011eb <+98>:    jl     0x11d1 <main+72>
   0x00000000000011ed <+100>:   mov    DWORD PTR [rbp-0xc],0x0
   0x00000000000011f4 <+107>:   jmp    0x1204 <main+123>
   0x00000000000011f6 <+109>:   mov    edi,0x2a
   0x00000000000011fb <+114>:   call   0x1070 <putchar@plt>
   0x0000000000001200 <+119>:   add    DWORD PTR [rbp-0xc],0x1
   0x0000000000001204 <+123>:   mov    eax,DWORD PTR [rbp-0x14]
   0x0000000000001207 <+126>:   add    eax,eax
   0x0000000000001209 <+128>:   cmp    DWORD PTR [rbp-0xc],eax
   0x000000000000120c <+131>:   jle    0x11f6 <main+109>
   0x000000000000120e <+133>:   mov    edi,0xa
   0x0000000000001213 <+138>:   call   0x1070 <putchar@plt>
   0x0000000000001218 <+143>:   add    DWORD PTR [rbp-0x14],0x1
   0x000000000000121c <+147>:   mov    eax,DWORD PTR [rbp-0x18]
   0x000000000000121f <+150>:   cmp    DWORD PTR [rbp-0x14],eax
   0x0000000000001222 <+153>:   jl     0x11c8 <main+63>
   0x0000000000001224 <+155>:   mov    eax,0x0
   0x0000000000001229 <+160>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x000000000000122d <+164>:   sub    rdx,QWORD PTR fs:0x28
   0x0000000000001236 <+173>:   je     0x123d <main+180>
   0x0000000000001238 <+175>:   call   0x1080 <__stack_chk_fail@plt>
   0x000000000000123d <+180>:   leave
   0x000000000000123e <+181>:   ret

 

jmp, jl, je같은 명령어가 있는 걸 보아 제어문이 포함된 것 같습니다.

 

 

   0x000000000000118d <+4>:     push   rbp
   0x000000000000118e <+5>:     mov    rbp,rsp

 

함수 프롤로그 입니다.

 

   0x0000000000001191 <+8>:     sub    rsp,0x20

 

스택을 0x20만큼 할당합니다.

 

   0x0000000000001195 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x000000000000119e <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011a2 <+25>:    xor    eax,eax

 

8 Bytes Canary를 활성화하고, RAX를 0으로 초기화합니다.

 

   0x00000000000011a4 <+27>:    lea    rax,[rbp-0x18]
   0x00000000000011a8 <+31>:    mov    rsi,rax
   0x00000000000011ab <+34>:    lea    rax,[rip+0xe52]        # 0x2004
   0x00000000000011b2 <+41>:    mov    rdi,rax
   0x00000000000011b5 <+44>:    mov    eax,0x0
   0x00000000000011ba <+49>:    call   0x1090 <__isoc99_scanf@plt>

 

main +27에서 [rbp - 0x18]에 변수 하나를 생성하고, 이를 RSI 레지스터에 넣습니다.

main +34에서 [rip + 0xe52]에 있는 값을 RAX에 넣고, 이를 RDI에 넣습니다.

RAX를 0으로 초기화 하고 scanf() 함수를 호출합니다.

 

여기까지 봤을 때 C 코드는 아래와 같습니다.

int main() {
    int a;
    scanf("%d", &a);

    return 0;
}

 

[rbp - 0x18]이 앞으로 호출될 때 DWORD (4 Bytes)로 호출되기 때문에 int 형으로 생각했습니다. (하위 4Bytes만 쓰인 것인지는 모르겠음)

[rip + 0xe52]는 scanf()에서 쓰인 포맷 스트링인 %d 입니다.

 

 

   0x00000000000011bf <+54>:    mov    DWORD PTR [rbp-0x14],0x0
   0x00000000000011c6 <+61>:    jmp    0x121c <main+147>

 

반복을 위한 초기 변수를 선언하고 0으로 초기화합니다. 이후 main +147로 점프합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;

    return 0;
}

 

 

 

   0x00000000000011c8 <+63>:    mov    DWORD PTR [rbp-0x10],0x0
   0x00000000000011cf <+70>:    jmp    0x11df <main+86>

 

새로운 지역변수가 선언되고, 0으로 초기화 되는 것을 보아 두 번째 반복문이 선언됐습니다. 이후 main+86으로 점프합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0;

    return 0;
}

 

 

 

   0x00000000000011d1 <+72>:    mov    edi,0x20
   0x00000000000011d6 <+77>:    call   0x1070 <putchar@plt>

 

putchar의 인자로 0x20이 쓰였습니다. 0x20은 공백(' ')입니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0;
	    putchar(' ');

    return 0;
}

 

 

 

   0x00000000000011db <+82>:    add    DWORD PTR [rbp-0x10],0x1

두 번째 반복 변수에 쓰였던 j를 1씩 더합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; ;j++) {
	    putchar(' ');
	}
    
    return 0;
}

 

 

 

   0x00000000000011df <+86>:    mov    eax,DWORD PTR [rbp-0x18]
   0x00000000000011e2 <+89>:    sub    eax,DWORD PTR [rbp-0x14]
   0x00000000000011e5 <+92>:    sub    eax,0x1
   0x00000000000011e8 <+95>:    cmp    DWORD PTR [rbp-0x10],eax
   0x00000000000011eb <+98>:    jl     0x11d1 <main+72>

 

처음에 scanf로 사용자에게 입력받았던 변수 a에서 첫 번째 반복 변수인 i를 뺄셈합니다. 그리고, 이 값에 1을 더 뺍니다.

이후 cmp 명령어를 통해 위에서 연산한 값인 RAX와 두 번째 반복 변수인 j를 비교합니다.

main +98에 있는 jl 명령어는 비교 연산자로 less than 이라는 의미를 가집니다. 즉 부등호 ' < '를 의미합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

    return 0;
}

 

 

 

   0x00000000000011ed <+100>:   mov    DWORD PTR [rbp-0xc],0x0
   0x00000000000011f4 <+107>:   jmp    0x1204 <main+123>

 

[rbp - 0xc]라는 새로운 지역변수가 생기고 0으로 초기화 됐습니다. main +123으로 점프한 다는 것을 보아 세 번째 반복문으로 보입니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0;

    return 0;
}

 

 

 

   0x00000000000011f6 <+109>:   mov    edi,0x2a
   0x00000000000011fb <+114>:   call   0x1070 <putchar@plt>

 

putchar의 인자로 0x2a가 들어왔습니다. 0x2a는 ' * ' 입니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0;
	    putchar('*');

    return 0;
}

 

 

 

   0x0000000000001200 <+119>:   add    DWORD PTR [rbp-0xc],0x1

 

세 번째 반복 변수에 쓰였던 k에 1을 더합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0; ; k++) {
	    putchar('*');
	}

    return 0;
}

 

 

 

   0x0000000000001204 <+123>:   mov    eax,DWORD PTR [rbp-0x14]
   0x0000000000001207 <+126>:   add    eax,eax
   0x0000000000001209 <+128>:   cmp    DWORD PTR [rbp-0xc],eax
   0x000000000000120c <+131>:   jle    0x11f6 <main+109>

 

첫 번째 반복 변수인 i를 RAX에 넣고, 이를 자기 자신과 더합니다. 즉, 곱하기 2를 했습니다.

이제 RAX를 세 번째 반복 변수인 k와 비교하고, main +109로 점프합니다.

jle 명령어는 less than equal의 뜻을 가진 비교 연산자입니다. 즉, <=입니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0; k <= i * 2 ; k++) {
	    putchar('*');
	}

    return 0;
}

 

 

 

   0x000000000000120e <+133>:   mov    edi,0xa
   0x0000000000001213 <+138>:   call   0x1070 <putchar@plt>

 

putchar의 인자로 0xa를 넣습니다. 0xa를 줄 바꿈 문자 '\n' 입니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0;
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0; k <= i * 2 ; k++) {
	    putchar('*');
	}

	putchar('\n');

    return 0;
}

 

 

 

   0x0000000000001218 <+143>:   add    DWORD PTR [rbp-0x14],0x1

 

첫 번째 반복 변수인 i에 1을 더합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0; ; i++) {
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0; k <= i * 2 ; k++) {
	    putchar('*');
	}

	putchar('\n');
    }

    return 0;
}

 

 

 

   0x000000000000121c <+147>:   mov    eax,DWORD PTR [rbp-0x18]
   0x000000000000121f <+150>:   cmp    DWORD PTR [rbp-0x14],eax
   0x0000000000001222 <+153>:   jl     0x11c8 <main+63>

 

scanf를 통해 입력한 값인 a를 첫 번째 반복 변수인 i와 비교합니다. 이후 less than ' < ' 연산자로 main +63으로 점프합니다.

 

int main() {
    int a;
    scanf("%d", &a);

    for (int i = 0; i < a; i++) {
	for (int j = 0; j < a - i - 1 ; j++) {
	    putchar(' ');
	}

	for (int k = 0; k <= i * 2 ; k++) {
	    putchar('*');
	}

	putchar('\n');
    }

    return 0;
}

 

최종 코드입니다.

 

   0x0000000000001224 <+155>:   mov    eax,0x0
   0x0000000000001229 <+160>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x000000000000122d <+164>:   sub    rdx,QWORD PTR fs:0x28
   0x0000000000001236 <+173>:   je     0x123d <main+180>
   0x0000000000001238 <+175>:   call   0x1080 <__stack_chk_fail@plt>
   0x000000000000123d <+180>:   leave
   0x000000000000123e <+181>:   ret

 

스택을 보호하기 위해 Canary 값과 비교하고, 스택을 정리합니다.

 

 

 

위와 같이 코드를 작성하고 컴파일하면

 

별 찍기 프로그램인 것을 알 수 있습니다.

 

 

 

 

Handray 3

   0x00000000000011a9 <+0>:     endbr64
   0x00000000000011ad <+4>:     push   rbp
   0x00000000000011ae <+5>:     mov    rbp,rsp
   0x00000000000011b1 <+8>:     sub    rsp,0x90
   0x00000000000011b8 <+15>:    mov    rax,QWORD PTR fs:0x28
   0x00000000000011c1 <+24>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011c5 <+28>:    xor    eax,eax
   0x00000000000011c7 <+30>:    movabs rax,0x6563696c41
   0x00000000000011d1 <+40>:    mov    edx,0x0
   0x00000000000011d6 <+45>:    mov    QWORD PTR [rbp-0x80],rax
   0x00000000000011da <+49>:    mov    QWORD PTR [rbp-0x78],rdx
   0x00000000000011de <+53>:    mov    DWORD PTR [rbp-0x70],0x0
   0x00000000000011e5 <+60>:    mov    DWORD PTR [rbp-0x6c],0x12
   0x00000000000011ec <+67>:    mov    DWORD PTR [rbp-0x68],0x1
   0x00000000000011f3 <+74>:    mov    QWORD PTR [rbp-0x60],0x626f42
   0x00000000000011fb <+82>:    mov    QWORD PTR [rbp-0x58],0x0
   0x0000000000001203 <+90>:    mov    DWORD PTR [rbp-0x50],0x0
   0x000000000000120a <+97>:    mov    DWORD PTR [rbp-0x4c],0x14
   0x0000000000001211 <+104>:   mov    DWORD PTR [rbp-0x48],0x3
   0x0000000000001218 <+111>:   mov    rax,QWORD PTR [rbp-0x80]
   0x000000000000121c <+115>:   mov    rdx,QWORD PTR [rbp-0x78]
   0x0000000000001220 <+119>:   mov    QWORD PTR [rbp-0x40],rax
   0x0000000000001224 <+123>:   mov    QWORD PTR [rbp-0x38],rdx
   0x0000000000001228 <+127>:   mov    rax,QWORD PTR [rbp-0x74]
   0x000000000000122c <+131>:   mov    rdx,QWORD PTR [rbp-0x6c]
   0x0000000000001230 <+135>:   mov    QWORD PTR [rbp-0x34],rax
   0x0000000000001234 <+139>:   mov    QWORD PTR [rbp-0x2c],rdx
   0x0000000000001238 <+143>:   mov    rax,QWORD PTR [rbp-0x60]
   0x000000000000123c <+147>:   mov    rdx,QWORD PTR [rbp-0x58]
   0x0000000000001240 <+151>:   mov    QWORD PTR [rbp-0x24],rax
   0x0000000000001244 <+155>:   mov    QWORD PTR [rbp-0x1c],rdx
   0x0000000000001248 <+159>:   mov    rax,QWORD PTR [rbp-0x54]
   0x000000000000124c <+163>:   mov    rdx,QWORD PTR [rbp-0x4c]
   0x0000000000001250 <+167>:   mov    QWORD PTR [rbp-0x18],rax
   0x0000000000001254 <+171>:   mov    QWORD PTR [rbp-0x10],rdx
   0x0000000000001258 <+175>:   mov    DWORD PTR [rbp-0x84],0x2
   0x0000000000001262 <+185>:   mov    DWORD PTR [rbp-0x88],0x0
   0x000000000000126c <+195>:   jmp    0x1384 <main+475>
   0x0000000000001271 <+200>:   lea    rcx,[rbp-0x40]
   0x0000000000001275 <+204>:   mov    eax,DWORD PTR [rbp-0x88]
   0x000000000000127b <+210>:   movsxd rdx,eax
   0x000000000000127e <+213>:   mov    rax,rdx
   0x0000000000001281 <+216>:   shl    rax,0x3
   0x0000000000001285 <+220>:   sub    rax,rdx
   0x0000000000001288 <+223>:   shl    rax,0x2
   0x000000000000128c <+227>:   add    rax,rcx
   0x000000000000128f <+230>:   mov    rsi,rax
   0x0000000000001292 <+233>:   lea    rax,[rip+0xd6b]        # 0x2004
   0x0000000000001299 <+240>:   mov    rdi,rax
   0x000000000000129c <+243>:   mov    eax,0x0
   0x00000000000012a1 <+248>:   call   0x10b0 <printf@plt>
   0x00000000000012a6 <+253>:   mov    eax,DWORD PTR [rbp-0x88]
   0x00000000000012ac <+259>:   movsxd rdx,eax
   0x00000000000012af <+262>:   mov    rax,rdx
   0x00000000000012b2 <+265>:   shl    rax,0x3
   0x00000000000012b6 <+269>:   sub    rax,rdx
   0x00000000000012b9 <+272>:   shl    rax,0x2
   0x00000000000012bd <+276>:   add    rax,rbp
   0x00000000000012c0 <+279>:   sub    rax,0x2c
   0x00000000000012c4 <+283>:   mov    eax,DWORD PTR [rax]
   0x00000000000012c6 <+285>:   mov    esi,eax
   0x00000000000012c8 <+287>:   lea    rax,[rip+0xd3f]        # 0x200e
   0x00000000000012cf <+294>:   mov    rdi,rax
   0x00000000000012d2 <+297>:   mov    eax,0x0
   0x00000000000012d7 <+302>:   call   0x10b0 <printf@plt>
   0x00000000000012dc <+307>:   lea    rax,[rip+0xd34]        # 0x2017
   0x00000000000012e3 <+314>:   mov    rdi,rax
   0x00000000000012e6 <+317>:   mov    eax,0x0
   0x00000000000012eb <+322>:   call   0x10b0 <printf@plt>
   0x00000000000012f0 <+327>:   mov    eax,DWORD PTR [rbp-0x88]
   0x00000000000012f6 <+333>:   movsxd rdx,eax
   0x00000000000012f9 <+336>:   mov    rax,rdx
   0x00000000000012fc <+339>:   shl    rax,0x3
   0x0000000000001300 <+343>:   sub    rax,rdx
   0x0000000000001303 <+346>:   shl    rax,0x2
   0x0000000000001307 <+350>:   add    rax,rbp
   0x000000000000130a <+353>:   sub    rax,0x28
   0x000000000000130e <+357>:   mov    eax,DWORD PTR [rax]
   0x0000000000001310 <+359>:   cmp    eax,0x4
   0x0000000000001313 <+362>:   je     0x1363 <main+442>
   0x0000000000001315 <+364>:   cmp    eax,0x4
   0x0000000000001318 <+367>:   ja     0x1373 <main+458>
   0x000000000000131a <+369>:   cmp    eax,0x3
   0x000000000000131d <+372>:   je     0x1352 <main+425>
   0x000000000000131f <+374>:   cmp    eax,0x3
   0x0000000000001322 <+377>:   ja     0x1373 <main+458>
   0x0000000000001324 <+379>:   cmp    eax,0x1
   0x0000000000001327 <+382>:   je     0x1330 <main+391>
   0x0000000000001329 <+384>:   cmp    eax,0x2
   0x000000000000132c <+387>:   je     0x1341 <main+408>
   0x000000000000132e <+389>:   jmp    0x1373 <main+458>
   0x0000000000001330 <+391>:   lea    rax,[rip+0xce8]        # 0x201f
   0x0000000000001337 <+398>:   mov    rdi,rax
   0x000000000000133a <+401>:   call   0x1090 <puts@plt>
   0x000000000000133f <+406>:   jmp    0x1373 <main+458>
   0x0000000000001341 <+408>:   lea    rax,[rip+0xce0]        # 0x2028
   0x0000000000001348 <+415>:   mov    rdi,rax
   0x000000000000134b <+418>:   call   0x1090 <puts@plt>
   0x0000000000001350 <+423>:   jmp    0x1373 <main+458>
   0x0000000000001352 <+425>:   lea    rax,[rip+0xcd9]        # 0x2032
   0x0000000000001359 <+432>:   mov    rdi,rax
   0x000000000000135c <+435>:   call   0x1090 <puts@plt>
   0x0000000000001361 <+440>:   jmp    0x1373 <main+458>
   0x0000000000001363 <+442>:   lea    rax,[rip+0xccf]        # 0x2039
   0x000000000000136a <+449>:   mov    rdi,rax
   0x000000000000136d <+452>:   call   0x1090 <puts@plt>
   0x0000000000001372 <+457>:   nop
   0x0000000000001373 <+458>:   mov    edi,0xa
   0x0000000000001378 <+463>:   call   0x1080 <putchar@plt>
   0x000000000000137d <+468>:   add    DWORD PTR [rbp-0x88],0x1
   0x0000000000001384 <+475>:   mov    eax,DWORD PTR [rbp-0x88]
   0x000000000000138a <+481>:   cmp    eax,DWORD PTR [rbp-0x84]
   0x0000000000001390 <+487>:   jl     0x1271 <main+200>
   0x0000000000001396 <+493>:   mov    eax,0x0
   0x000000000000139b <+498>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x000000000000139f <+502>:   sub    rdx,QWORD PTR fs:0x28
   0x00000000000013a8 <+511>:   je     0x13af <main+518>
   0x00000000000013aa <+513>:   call   0x10a0 <__stack_chk_fail@plt>
   0x00000000000013af <+518>:   leave
   0x00000000000013b0 <+519>:   ret

 

전체 코드입니다.

 

   0x00000000000011ad <+4>:     push   rbp
   0x00000000000011ae <+5>:     mov    rbp,rsp
   0x00000000000011b1 <+8>:     sub    rsp,0x90
   0x00000000000011b8 <+15>:    mov    rax,QWORD PTR fs:0x28
   0x00000000000011c1 <+24>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011c5 <+28>:    xor    eax,eax

 

함수 프롤로그 입니다. 지역변수 할당을 위해 스택을 0x90만큼 할당하였습니다.

또한 스택 보호를 위해 Canary를 설정하였습니다.

 

   0x00000000000011c7 <+30>:    movabs rax,0x6563696c41

 

RAX에 Little Endian 방식으로 'Alice'를 넣음.

movabs 명령어는 Move Absolute로, 64 Bits 레지스터에 64비트 정수의 상수를 바로 넣을 때 사용합니다.

32 Bits 까지는 mov로 되지만, 64 Bits는 movabs를 사용해야 한다고 하네요.

 

   0x00000000000011d1 <+40>:    mov    edx,0x0
   0x00000000000011d6 <+45>:    mov    QWORD PTR [rbp-0x80],rax
   0x00000000000011da <+49>:    mov    QWORD PTR [rbp-0x78],rdx
   0x00000000000011de <+53>:    mov    DWORD PTR [rbp-0x70],0x0
   0x00000000000011e5 <+60>:    mov    DWORD PTR [rbp-0x6c],0x12
   0x00000000000011ec <+67>:    mov    DWORD PTR [rbp-0x68],0x1

 

우선, 이렇게 일정한 크기로 여러번 접근하는 것은 구조체를 의심할 수 있습니다.

EDX에 0을 넣습니다. 이후 8 Bytes로 위에서 RAX에 넣었던 Alice를 넣고, 나머지 8 Bytes를 0으로 채웁니다.

즉, 상위 8 Bytes에는 Alice가 들어갔고, 하위 8Bytes에는 0이 들어갔습니다. [rbp - 0x70]에 0을 넣은 것은 널 문자인지 잘 모르겠

습니다.

이후 main +60에 4Bytes로 0x12를 넣고, 같은 크기로 0x1을 넣습니다.

 

이를 통해서 해당 값들이 구조체라고 했을 때, 각 필드는 16 Bytes의 string, int 타입의 구조체 멤버 2개를 갖고 있음을 알 수 있습니

다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[1] = {
	{"Alice", 0x12, 0x1}
    }    

    return 0;
}

 

구조체 필드 명이나, 멤버, 변수 이름 등은 제가 임의로 지었습니다. num은 아마 학번?으로 생각합니다.

 

 

 

   0x00000000000011f3 <+74>:    mov    QWORD PTR [rbp-0x60],0x626f42
   0x00000000000011fb <+82>:    mov    QWORD PTR [rbp-0x58],0x0
   0x0000000000001203 <+90>:    mov    DWORD PTR [rbp-0x50],0x0
   0x000000000000120a <+97>:    mov    DWORD PTR [rbp-0x4c],0x14
   0x0000000000001211 <+104>:   mov    DWORD PTR [rbp-0x48],0x3

 

구조체의 두 번째 객체 입니다.

위와 같은 방식으로 "Bob"이라는 이름을 갖고 있고, 나이는 0x14, 학번은 3을 갖고 있습니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    return 0;
}

 

 

 

   0x0000000000001218 <+111>:   mov    rax,QWORD PTR [rbp-0x80]
   0x000000000000121c <+115>:   mov    rdx,QWORD PTR [rbp-0x78]
   0x0000000000001220 <+119>:   mov    QWORD PTR [rbp-0x40],rax
   0x0000000000001224 <+123>:   mov    QWORD PTR [rbp-0x38],rdx
   0x0000000000001228 <+127>:   mov    rax,QWORD PTR [rbp-0x74]
   0x000000000000122c <+131>:   mov    rdx,QWORD PTR [rbp-0x6c]
   0x0000000000001230 <+135>:   mov    QWORD PTR [rbp-0x34],rax
   0x0000000000001234 <+139>:   mov    QWORD PTR [rbp-0x2c],rdx
   0x0000000000001238 <+143>:   mov    rax,QWORD PTR [rbp-0x60]
   0x000000000000123c <+147>:   mov    rdx,QWORD PTR [rbp-0x58]
   0x0000000000001240 <+151>:   mov    QWORD PTR [rbp-0x24],rax
   0x0000000000001244 <+155>:   mov    QWORD PTR [rbp-0x1c],rdx
   0x0000000000001248 <+159>:   mov    rax,QWORD PTR [rbp-0x54]
   0x000000000000124c <+163>:   mov    rdx,QWORD PTR [rbp-0x4c]
   0x0000000000001250 <+167>:   mov    QWORD PTR [rbp-0x18],rax
   0x0000000000001254 <+171>:   mov    QWORD PTR [rbp-0x10],rdx

 

위 내용들을 복사하여 새로운 변수를 생성합니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    return 0;
}

 

 

 

   0x0000000000001258 <+175>:   mov    DWORD PTR [rbp-0x84],0x2
   0x0000000000001262 <+185>:   mov    DWORD PTR [rbp-0x88],0x0
   0x000000000000126c <+195>:   jmp    0x1384 <main+475>

 

반복을 설정합니다. 이후 main +475로 점프합니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {

    }

    return 0;
}

 

정확한 반복 조건은 나오지 않았기 때문에 저렇게 뒀습니다.

 

 

 

   0x0000000000001271 <+200>:   lea    rcx,[rbp-0x40]
   0x0000000000001275 <+204>:   mov    eax,DWORD PTR [rbp-0x88]
   0x000000000000127b <+210>:   movsxd rdx,eax
   0x000000000000127e <+213>:   mov    rax,rdx
   0x0000000000001281 <+216>:   shl    rax,0x3
   0x0000000000001285 <+220>:   sub    rax,rdx
   0x0000000000001288 <+223>:   shl    rax,0x2
   0x000000000000128c <+227>:   add    rax,rcx
   0x000000000000128f <+230>:   mov    rsi,rax
   0x0000000000001292 <+233>:   lea    rax,[rip+0xd6b]        # 0x2004
   0x0000000000001299 <+240>:   mov    rdi,rax
   0x000000000000129c <+243>:   mov    eax,0x0
   0x00000000000012a1 <+248>:   call   0x10b0 <printf@plt>

 

배열의 인덱스를 계산하여 person[i]의 주소를 계산하고, person[i].name의 주소를 구한 후 RSI에 저장합니다.

포맷 스트링 "%s"를 RAX에 저장합니다 [rip + 0xd6b] 이후 printf를 호출합니다.

 

- movsxd: Move with Sign Extend Doubleword

32비트 정수를 64비트 주소 계산에 사용할 수 있게 확장합니다. 32비트 레지스터를 64비트 레지스터로 옮기되, 부호를 확장합니다.

- shl: Shift Left

왼쪽으로 비트를 옮깁니다. -> 2의 제곱

shl rax, 0x3   --> rax *= 2^3

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);

    }

    return 0;
}

 

 

 

   0x00000000000012a6 <+253>:   mov    eax,DWORD PTR [rbp-0x88]
   0x00000000000012ac <+259>:   movsxd rdx,eax
   0x00000000000012af <+262>:   mov    rax,rdx
   0x00000000000012b2 <+265>:   shl    rax,0x3
   0x00000000000012b6 <+269>:   sub    rax,rdx
   0x00000000000012b9 <+272>:   shl    rax,0x2
   0x00000000000012bd <+276>:   add    rax,rbp
   0x00000000000012c0 <+279>:   sub    rax,0x2c
   0x00000000000012c4 <+283>:   mov    eax,DWORD PTR [rax]
   0x00000000000012c6 <+285>:   mov    esi,eax
   0x00000000000012c8 <+287>:   lea    rax,[rip+0xd3f]        # 0x200e
   0x00000000000012cf <+294>:   mov    rdi,rax
   0x00000000000012d2 <+297>:   mov    eax,0x0
   0x00000000000012d7 <+302>:   call   0x10b0 <printf@plt>

 

위의 과정과 동일합니다. 이번엔 Age 멤버에 접근합니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);

    }

    return 0;
}

 

 

 

 

   0x00000000000012dc <+307>:   lea    rax,[rip+0xd34]        # 0x2017
   0x00000000000012e3 <+314>:   mov    rdi,rax
   0x00000000000012e6 <+317>:   mov    eax,0x0
   0x00000000000012eb <+322>:   call   0x10b0 <printf@plt>

 

[rip + 0xd34]가 포맷 스트링이라고 생각했지만, printf()를 호출하는데, 인자를 두 개 사용하지 않았습니다.

그래서 [rip + 0xd34]가 문자열 리터럴이라고 생각했고, printf("~~~~\n")은 puts("~~~~")와 같기 때문에, 컴파일러 단에서 바뀐 것으로 보았습니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);
	puts("~~~~");
    }

    return 0;
}

 

 

 

   0x00000000000012f0 <+327>:   mov    eax,DWORD PTR [rbp-0x88]
   0x00000000000012f6 <+333>:   movsxd rdx,eax
   0x00000000000012f9 <+336>:   mov    rax,rdx
   0x00000000000012fc <+339>:   shl    rax,0x3
   0x0000000000001300 <+343>:   sub    rax,rdx
   0x0000000000001303 <+346>:   shl    rax,0x2
   0x0000000000001307 <+350>:   add    rax,rbp
   0x000000000000130a <+353>:   sub    rax,0x28
   0x000000000000130e <+357>:   mov    eax,DWORD PTR [rax]

 

반복 변수 i값을 기준으로 구조체 배열의 age 멤버를 가리키는 주소를 계산합니다.

최종적으로 eax에 person[i].age 값이 들어갑니다.

 

 

 

   0x0000000000001310 <+359>:   cmp    eax,0x4
   0x0000000000001313 <+362>:   je     0x1363 <main+442>
   
   ....
   
   0x0000000000001363 <+442>:   lea    rax,[rip+0xccf]        # 0x2039
   0x000000000000136a <+449>:   mov    rdi,rax
   0x000000000000136d <+452>:   call   0x1090 <puts@plt>
   0x0000000000001372 <+457>:   nop

 

비교문입니다. eax == 4라면 main +442로 점프합니다.

main +442에는 puts("~~~")를 선언합니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);
	puts("~~~~");

	if (person[i].age == 4) {
	    puts("~~~");
    }

    return 0;
}

 

 

 

   0x0000000000001315 <+364>:   cmp    eax,0x4
   0x0000000000001318 <+367>:   ja     0x1373 <main+458>

   0x000000000000131a <+369>:   cmp    eax,0x3
   0x000000000000131d <+372>:   je     0x1352 <main+425>

   0x000000000000131f <+374>:   cmp    eax,0x3
   0x0000000000001322 <+377>:   ja     0x1373 <main+458>

   0x0000000000001324 <+379>:   cmp    eax,0x1
   0x0000000000001327 <+382>:   je     0x1330 <main+391>

   0x0000000000001329 <+384>:   cmp    eax,0x2
   0x000000000000132c <+387>:   je     0x1341 <main+408>

   0x000000000000132e <+389>:   jmp    0x1373 <main+458>

 

위와 같은 방식으로 C로 바꿔줍니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);
	puts("~~~~");

	if (person[i].age == 4) {
	    puts("4");
        }
	else if (person[i].age > 4) {
            puts("Over 4");
        } 
	else if (person[i].age == 3) {
            puts("3");
        } 
	else if (person[i].age > 3) {
            puts("Over 3");
        } 
	else if (person[i].age == 1) {
            puts("1");
        } 
	else if (person[i].age == 2) {
            puts("2");
        } 
	else {
            puts("???");
        }


    return 0;
}

 

puts() 안의 내용은 임의로 바꿨습니다.

 

 

 

   0x0000000000001373 <+458>:   mov    edi,0xa
   0x0000000000001378 <+463>:   call   0x1080 <putchar@plt>

 

0xa -> '\n'

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    }    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; 2; ) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);
	puts("~~~~");

	if (person[i].age == 4) {
	    puts("4");
        }
	else if (person[i].age > 4) {
            puts("Over 4");
        } 
	else if (person[i].age == 3) {
            puts("3");
        } 
	else if (person[i].age > 3) {
            puts("Over 3");
        } 
	else if (person[i].age == 1) {
            puts("1");
        } 
	else if (person[i].age == 2) {
            puts("2");
        } 
	else {
            puts("???");
        }

	putchar('\n');
    }
    return 0;
}

 

 

 

   0x000000000000137d <+468>:   add    DWORD PTR [rbp-0x88],0x1
   0x0000000000001384 <+475>:   mov    eax,DWORD PTR [rbp-0x88]
   0x000000000000138a <+481>:   cmp    eax,DWORD PTR [rbp-0x84]
   0x0000000000001390 <+487>:   jl     0x1271 <main+200>

 

반복문의 조건을 설정합니다.

 

typedef struct {
    char name[16];
    int age;
    int num;
} People;

int main() {

    People person[2] = {
	{"Alice", 0x12, 0x1},
	{"Bob", 0x14, 0x3}
    };    

    People alice = person[0];
    People bob = person[1];

    for (int i = 0; i < 2; i++) {
	printf("Name: %s\n", person[i].name);
        printf("Age: %d\n", person[i].age);
	puts("~~~~");

	if (person[i].age == 4) {
	    puts("4");
        }
	else if (person[i].age > 4) {
            puts("Over 4");
        } 
	else if (person[i].age == 3) {
            puts("3");
        } 
	else if (person[i].age > 3) {
            puts("Over 3");
        } 
	else if (person[i].age == 1) {
            puts("1");
        } 
	else if (person[i].age == 2) {
            puts("2");
        } 
	else {
            puts("???");
        }

	putchar('\n');
    }
    return 0;
}

 

최종 코드 입니다.

 

 

 

   0x0000000000001396 <+493>:   mov    eax,0x0
   0x000000000000139b <+498>:   mov    rdx,QWORD PTR [rbp-0x8]
   0x000000000000139f <+502>:   sub    rdx,QWORD PTR fs:0x28
   0x00000000000013a8 <+511>:   je     0x13af <main+518>
   0x00000000000013aa <+513>:   call   0x10a0 <__stack_chk_fail@plt>
   0x00000000000013af <+518>:   leave
   0x00000000000013b0 <+519>:   ret

 

두 번째 핸드레이와 같이 스택을 보호하기 위해 Canary 값과 비교하고, 스택을 정리합니다.

 

 

 

컴파일하고 실행한 결과입니다.