[System Hacking][DreamHack][Stage2] Quiz: x86 Assembly1

marceline·2023년 3월 31일
0

[System Hacking]

목록 보기
10/17
post-thumbnail

Quiz: x86 Assembly1

1. 2.

[Register]
rbx = 0x401A40

=================================

[Memory]
0x401a40 | 0x0000000012345678
0x401a48 | 0x0000000000C0FFEE
0x401a50 | 0x00000000DEADBEEF
0x401a58 | 0x00000000CAFEBABE
0x401a60 | 0x0000000087654321

=================================

[Code]
1: mov rax, [rbx+8]
2: lea rax, [rbx+8]

mov 는 두번째 인자의 값을 첫번째 인자에 대입하는 명령어이고, lea 는 두번째 인자의 주소값을 첫번째 인자에 대입한다.
따라서 code 1 까지 실행한 rax 의 값은 0x00000000COFFEE 이다.
이는 [rbx+8] = [0x401A48] 이기 때문이다.
같은 이유로 code 2 까지 실행하면 rax 에는 0x401A48 이 저장된다.

3. 4. 5.

[Register]
rax = 0x31337
rbx = 0x555555554000
rcx = 0x2

=================================

[Memory]
0x555555554000| 0x0000000000000000
0x555555554008| 0x0000000000000001
0x555555554010| 0x0000000000000003
0x555555554018| 0x0000000000000005
0x555555554020| 0x000000000003133A

==================================

[Code]
1: add rax, [rbx+rcx*8]
2: add rcx, 2
3: sub rax, [rbx+rcx*8]
4: inc rax

code 1 에서 [rbx+rcx*8] 을 먼저 구해보면 다음과 같다.

rcx*8 = 0x2*8 = 0x10 

[rbx+rcx*8] = 0x555555554000 + 0x10 = 0x555555554010

따라서 add rax, [rbx+rcx*8] 의 값은

add rax, [rbx+rcx*8] = 0x31337 + 0x0000000000000003 = 0x3133A

code 2 에서 rcx 가 2만큼 증가하여 0x4 가 된다.
code 3 에서의 결과는 아래와 같다.

sub rax, [rbx+rcx*8]

[rbx+rcx*8] = [rbx+0x4*8] = [rbx+0x20] = [0x555555554020] = 0x000000000003133A

==> rax == 0x3133A - 0x000000000003133A == 0

즉, code 3 까지 실행한 결과 rax = 0 이다.
마지막으로 code 4 에서 inc rax 를 한 결과는 1이므로 최종적으로 rax 에는 1이 저장된다.

6. 7. 8

[Register]
rax = 0xffffffff00000000
rbx = 0x00000000ffffffff
rcx = 0x123456789abcdef0

==================================

[Code]
1: and rax, rcx
2: and rbx, rcx
3: or rax, rbx

code 1에서 rax 의 결과는 아래와 같다.

and rax, rcx

==> 0xffffffff00000000 AND 0x123456789abcdef0
==> 0x1234567800000000

rax = 0x1234567800000000

code 2 에서 rbx 는 아래와 같다.

and rbx, rcx

==> 0x00000000ffffffff AND 0x123456789abcdef0
==> 0x000000009abcdef0

rbx = 0x000000009abcdef0

code 3 에서 rax 의 값은 아래와 같다.

or rax, rbx

==> 0x1234567800000000 OR 0x000000009abcdef0
==> 0x1234567800000000

rax = 0x123456789abcdef0

9. 10. 11.

[Register]
rax = 0x35014541
rbx = 0xdeadbeef

==================================

[Code]
1: xor rax, rbx
2: xor rax, rbx
3: not eax

code 1 까지 실행한 rax 의 값은 다음과 같다.

xor rax, rbx

==> 0x35014541 XOR 0xdeadbeef
==> 0xebacfbae

rax = 0xebacfbae

XOR 연산부터는 복잡해서 사이트에서 했다;;
https://miniwebtool.com/ko/bitwise-calculator/?data_type=16&number1=0x35014541&number2=0xdeadbeef&operator=XOR

code 2 결과

xor rax, rbx

==> 0xebacfbae XOR 0xdeadbeef
==> 0x35014541

rax = 0x35014541

code 3 에서 eax가 rax의 하위 32비트 가리키므로 not eax 는 not rax 라고 생각할 수 있다.

not eax = not rax

==> not (0x35014541) == 0xcafebabe

rax = 0xcafebabe

12.

end 로 점프시 프로그램이 종료된다 가정한다.
프로그램이 종료 됐을 때, 0x400000 부터 0x400019 까지의 데이터를 대응되는 아스키 문자로 변환하면 어느 문자열이 나오는가?

[Register]
rcx = 0
rdx = 0
rsi = 0x400000

=======================

[Memory]
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00

=======================

[code]
1: mov dl, BYTE PTR[rsi+rcx]
2: xor dl, 0x30
3: mov BYTE PTR[rsi+rcx], dl
4: inc rcx
5: cmp rcx, 0x19
6: jg end
7: jmp 1

먼저 code 1을 실행하기위해서 dl 레지스터에 대해 알아봤다.

위 사진에 의해서 dl == 0x00 이다

1: mov dl, BYTE PTR[rsi+rcx]

==> dl + BYTE PTR[0x400000 + 0]
==> dl + [0x4]
==> 0x0 + 0x67
==> 0x67

[Memory] 
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00

code 2 를 실행한 결과는 다음과 같다.

2: xor dl, 0x30

==> 0x67 XOR 0x30
==> 0x57
dl = 0x57

0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00

code 3

3: mov BYTE PTR[rsi+rcx], dl

==> BYTE PTR[0x4] = 0x57


0x400000 | 0x57 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x70

code 4

4: inc rcx

rcx = 1

code 5

5: cmp rcx, 0x19

==> 0x1 CMP 0x19
ZF == 0

code 6
jg: Jump If A greater than B

6: jg end
==> if (0x10 > 0x19) true: end
==> if (0x10 > 0x19) false : code 7

code 7 에 도착하면 code 1 로 점프하여 처음부터 다시 해당 과정을 반복한다.

아래에는 해당 과정들을 회차별로 수행한 결과이다.

1st

rcx = 0
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
=======================
[code]
1: mov dl, BYTE PTR[rsi+rcx]
// dl 에 [rsi+rcx] 대입
// dl 에 [0x400000] 대입
// dl = 0x67
2: xor dl, 0x30
// xor 0x67 0x30
// dl = 0x57
3: mov BYTE PTR[rsi+rcx], dl
// 0x400000 | 0x57
4: inc rcx
// rcx = 1
5: cmp rcx, 0x19
// 0x01, 0x19 비교 
// 0x01 != 0x19 이므로 ZF = 0
6: jg end
// 0x10 > 0x19 일시 end 로 jump 
// 그러나 False 이므로 Code 7 로 이동
7: jmp 1
// Code 1 로 점프

2nd

rcx = 1
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x57 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
=======================
[code]
1: mov dl, BYTE PTR[rsi+rcx]
// dl 에 [0x400001] 대입
// dl = 0x55
2: xor dl, 0x30
// 0x55와 0x30 xor 연산
// dl = 0x65
3: mov BYTE PTR[rsi+rcx], dl
// 0x400001 | 0x65
4: inc rcx
// rcx = 2
5: cmp rcx, 0x19
// 0x2, 0x19 비교, 다르므로 ZF = 0
6: jg end
// 0x2 < 0x19 이므로 프로그램 종료 안함
// Code 7 로 이동
7: jmp 1
// Code 1로 이동

3rd

rcx = 0x2
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x57 0x65 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
=======================
[code]
1: mov dl, BYTE PTR[rsi+rcx]
// dl 에 [rsi+rcx] 대입
// dl 에 [0x400002] 대입
// dl=0x5c
2: xor dl, 0x30
// 0x5c 와 0x30 xor 연산
// dl = 0x6c
3: mov BYTE PTR[rsi+rcx], dl
// 0x400002 | 0x6c
4: inc rcx
// rcx = 0x3
5: cmp rcx, 0x19
6: jg end
7: jmp 1

.
.
.

rcx = 0x19 일때까지 Code 1~ 7 반복 실행

rcx = 0x19
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20
0x400008 | 0x74 0x6f 0x20 0x61 0x73 0x73 0x65 0x6d
0x400010 | 0x62 0x6c 0x79 0x20 0x77 0x6f 0x72 0x6c
0x400018 | 0x64 0x11 0x00 0x00 0x00 0x00 0x00 0x00
=======================
[code]
1: mov dl, BYTE PTR[rsi+rcx]
// dl = [rsi+rcx]
// dl = [0x400019]
// dl = 0x11
2: xor dl, 0x30
// 0x11, 0x30의 xor 연산
// dl = 0x21
3: mov BYTE PTR[rsi+rcx], dl
// 0x400019 | 0x21
4: inc rcx
// rcx = 0x1a
5: cmp rcx, 0x19
// 0x1a > 0x19 이다
6: jg end
// 앞서 실행한 비교문에서 전자가 더 크기 때문에 프로그램 종료 !

result

rcx = 0x1a
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x57 0x65 0x6c 0x63 0x6f 0x6d 0x65 0x20
0x400008 | 0x74 0x6f 0x20 0x61 0x73 0x73 0x65 0x6d
0x400010 | 0x62 0x6c 0x79 0x20 0x77 0x6f 0x72 0x6c
0x400018 | 0x64 0x21 0x00 0x00 0x00 0x00 0x00 0x00
=======================

0x400000 ~ 0x400019 까지의 데이터를 ASCII 로 변경하면

이용한 사이트

xor 연산
16진수 ASCII 텍스트 변환기

Quiz: x86 Assembly2

[Code]
main:
    push rbp
    mov rbp, rsp 

새로운 스택프레임 생성을 위해서 rsp 를 rpb 로 옮김

	mov esi, 0xf

esi == 0xf (대입)

	mov rdi, 0x400500

rdi == 0x400500 (대입)

	call 0x400497 <write_n> // write_n 함수 호출
	mov eax, 0x0
    pop rbp
    ret

일단 write_n 으로 이동한다

write_n:
    push rbp
    mov rbp, rsp 

새로운 스택프레임 생성을 위해서 rsp 를 rpb 로 옮김

    mov QWORD PTR [rbp-0x8],rdi 

'rbp-0x8' 의 주소에 8byte 만큼 참조하여 rdi 대입
[rbp-0x8] == rdi == 0x400500

	mov DWORD PTR [rbp-0xc],esi 

'rbp-0xc' 의 주소에 4byte 만큼 참조하여 esi 대입
[rbp-0xc] == esi == 0xf

    xor rdx, rdx // rdx 를 xor 연산
    mov edx, DWORD PTR [rbp-0xc] 

edx 에 'rbp-0xc' 의 주소에 있는 값을 4byte 만큼 참조하여 대입
edx == 0xf

    mov rsi,QWORD PTR [rbp-0x8]

rsi == 'rbp-0x8'의 주소에 있는 값(8byte만큼 참조) == 0x400500

    mov rdi, 0x1

rdi == 0x1

    mov rax, 0x1

rax == 0x1

    syscall

rax == 0x1 이기 때문에 write syscall 이 호출된다.
rdi == 0x1, rsi == 0x400500, edx == 0xf 이므로
커널이 write(0x1(stdout: 표준출력), 0x400500(출력할 데이터의 주소값), 0xf(문자열 길이)) 를 수행!
write 의 첫번째 arg0 은 ‘unsigned int fd’

즉,
0x400500 | 0x3037207964343372
-> 0x400500 | 0x30 0x37 0x20 0x79 0x64 0x34 0x33 0x72
-> (reverse)| 0x72 0x33 0x34 0x64 0x79 0x20 0x37 0x30

0x400508 | 0x003f367562336420
-> 0x400508 | 0x00 0x3f 0x36 0x75 0x62 0x33 0x64 0x20
-> (reverse)| 0x20 0x64 0x33 0x62 0x75 0x36 0x3f 0x00

이다. ASCII 코드로 변환하면, 다음과 같다

    pop rbp
    ret

참고한 사이트

stdout
Linux System call table

0개의 댓글