Cykor_week2_001

원상윤·2022년 6월 29일
0

Cykor

목록 보기
1/1

Cykor week 2

- disassmeble -

Cykor 1번째 과제는 32-bit assembly 언어 파일을 C 코드로 변환시키는 것이다.

0x080484cf <+4>:	and    esp,0xfffffff0
0x080484d2 <+7>:	push   DWORD PTR [ecx-0x4]
0x080484d5 <+10>:	push   ebp
0x080484d6 <+11>:	mov    ebp,esp
0x080484d8 <+13>:	push   ecx
0x080484d9 <+14>:	sub    esp,0x34
0x080484dc <+17>:	mov    DWORD PTR [ebp-0xc],0x4030201
0x080484e3 <+24>:	mov    eax,ds:0x804a040
0x080484e8 <+29>:	sub    esp,0x4
0x080484eb <+32>:	push   eax
0x080484ec <+33>:	push   0x2d
0x080484ee <+35>:	lea    eax,[ebp-0x34]
0x080484f1 <+38>:	push   eax
0x080484f2 <+39>:	call   0x8048380 <fgets@plt>
0x080484f7 <+44>:	add    esp,0x10
0x080484fa <+47>:	sub    esp,0x8
0x080484fd <+50>:	lea    eax,[ebp-0x34]
0x08048500 <+53>:	push   eax
0x08048501 <+54>:	push   0x8048610
0x08048506 <+59>:	call   0x8048370 <printf@plt>
0x0804850b <+64>:	add    esp,0x10
0x0804850e <+67>:	sub    esp,0x8
0x08048511 <+70>:	push   DWORD PTR [ebp-0xc]
0x08048514 <+73>:	push   0x804861c
0x08048519 <+78>:	call   0x8048370 <printf@plt>
0x0804851e <+83>:	add    esp,0x10
0x08048521 <+86>:	cmp    DWORD PTR [ebp-0xc],0x4030201
0x08048528 <+93>:	je     0x8048543 <main+120>
0x0804852a <+95>:	cmp    DWORD PTR [ebp-0xc],0xdeadbeef
0x08048531 <+102>:	je     0x8048543 <main+120>
0x08048533 <+104>:	sub    esp,0xc
0x08048536 <+107>:	push   0x8048628
0x0804853b <+112>:	call   0x8048390 <puts@plt>
0x08048540 <+117>:	add    esp,0x10
0x08048543 <+120>:	cmp    DWORD PTR [ebp-0xc],0xdeadbeef
0x0804854a <+127>:	jne    0x804857c <main+177>
0x0804854c <+129>:	sub    esp,0xc
0x0804854f <+132>:	push   0x8048644
0x08048554 <+137>:	call   0x8048390 <puts@plt>
0x08048559 <+142>:	add    esp,0x10
0x0804855c <+145>:	sub    esp,0xc
0x0804855f <+148>:	push   0x804866e
0x08048564 <+153>:	call   0x80483a0 <system@plt>
0x08048569 <+158>:	add    esp,0x10
0x0804856c <+161>:	sub    esp,0xc
0x0804856f <+164>:	push   0x8048678
0x08048574 <+169>:	call   0x8048390 <puts@plt>
0x08048579 <+174>:	add    esp,0x10
0x0804857c <+177>:	mov    eax,0x0
0x08048581 <+182>:	mov    ecx,DWORD PTR [ebp-0x4]
0x08048584 <+185>:	leave  
0x08048585 <+186>:	lea    esp,[ecx-0x4]
0x08048588 <+189>:	ret

음.. 코드가 너무 길기 때문에 일단 코드를 크게 4 부분으로 나누어서 설명해보도록 하자.

>> #1

0x080484cb <+0>:	lea    ecx,[esp+0x4]
0x080484cf <+4>:	and    esp,0xfffffff0
0x080484d2 <+7>:	push   DWORD PTR [ecx-0x4]
0x080484d5 <+10>:	push   ebp
0x080484d6 <+11>:	mov    ebp,esp
0x080484d8 <+13>:	push   ecx
0x080484d9 <+14>:	sub    esp,0x34
0x080484dc <+17>:	mov    DWORD PTR [ebp-0xc],0x4030201

첫번째 구간이다. 차례대로 위에서부터 코드를 이해해보도록 하자. 먼저, ecx 의 값은 esp+4 의 주소값으로 만들어준다. 하지만 초기 esp 값은 우리가 모르는 값이기 때문에 굳이 신경쓸 필요 없는 값이라고 생각해도 될 것 같다.
<+7> 까지는 그냥 esp, ecx 를 이용해서 어떤 작업들을 해주지만, 굳이 C 코드로 변황시켰을 때 구현하지 않아도 된다.

그 후 <+10> 부터 본격적인 C 코드가 시작된다. ebp를 push 해줌으로써 스택의 시작점을 정의하고, esp도 ebp의 위치로 옮겨 스택을 0으로 만들어준다. 그 후에 정의했던 ecx를 push 해줌으로써 esp는 4만큼 내려가게 되고, 그 esp와 ebp 사이에 ecx라는 값이 들어가게 된다.

지금까지의 stack 구조를 살펴보면 다음과 같이 나타낼 수 있겠다.

자 그 후에, esp에 0x34를 더 빼주게 되면서 스택을 0x34 즉, 52만큼 더 늘려주게 된다. 그리고 ebp-0xc 부분에 0x4030201 이라는 값을 넣어주게 되면서 C 코드 상에서 하나의 변수를 정의해준다고 생각해줄 수 있을 것 같다.

따라서 총 stack 구조는 다음과 같다.

지금까지의 assembly를 통해서 C 코드를 생성해주면,

#include<stdio.h>
int main(void)
{
int num1 = 0x4030201;
}

가 되겠다.

>> #2

0x080484e3 <+24>:	mov    eax,ds:0x804a040
0x080484e8 <+29>:	sub    esp,0x4
0x080484eb <+32>:	push   eax
0x080484ec <+33>:	push   0x2d
0x080484ee <+35>:	lea    eax,[ebp-0x34]
0x080484f1 <+38>:	push   eax
0x080484f2 <+39>:	call   0x8048380 <fgets@plt>
0x080484f7 <+44>:	add    esp,0x10
0x080484fa <+47>:	sub    esp,0x8
0x080484fd <+50>:	lea    eax,[ebp-0x34]
0x08048500 <+53>:	push   eax
0x08048501 <+54>:	push   0x8048610
0x08048506 <+59>:	call   0x8048370 <printf@plt>
0x0804850b <+64>:	add    esp,0x10
0x0804850e <+67>:	sub    esp,0x8
0x08048511 <+70>:	push   DWORD PTR [ebp-0xc]
0x08048514 <+73>:	push   0x804861c
0x08048519 <+78>:	call   0x8048370 <printf@plt>

두 번째 부분이다. 복잡하다고 생각될 수도 있겠지만 잘 보면 fgets, printf 함수를 실행하기 위해서 필요한 인자를을 만들어주기 위한 작업, 그리고 stack을 정리해주기 위한 작업들이 있다.

차례대로 fgets 함수를 실행하는 부분부터 보도록 하자.

먼저, eax에 어떤 값을 대입하게 되는데, x/d 명령어를 통해서 확인해보면

이 값이 0을 의미한다는 것을 알 수가 있다. 그리고 esp를 4만큼 더 내려주고, stack 에 push를 3번 해주게 된다. stack에서 push를 해주게 되면, 먼저 넣은 값은 아래에 깔리지만, 함수를 호출할 때는 위에 있는 값부터 인자로 들어가기 때문에 가장 나중에 넣은 것이 첫번째 인자가 된다고 생각해주면 된다.

그렇게 함수를 나타내보면,
fgets( ebp-0x34 , 45 , 0 ) 이라는 함수가 호출됨을 알 수 있겠다.

그런데, stack의 구조상으로 보았을 때,

40 바이트의 공간을 만들어주고, fgets 함수로 45 바이트의 값을 입력받는 걸로 보아 arr[40] 즉, 40 크기의 배열이 생성되는 것을 알 수가 있다.

>> 모두 같은 방법을 통해서 함수들을 만들어주면 <<

결국 이런 C 코드들을 만들 수 있겠다.

fgets(arr, 45, 0);
printf("\n[buf]: %s\n", arr);
printf("[check] %p\n", &num1);

>> #3

0x0804851e <+83>:	add    esp,0x10
0x08048521 <+86>:	cmp    DWORD PTR [ebp-0xc],0x4030201
0x08048528 <+93>:	je     0x8048543 <main+120>
0x0804852a <+95>:	cmp    DWORD PTR [ebp-0xc],0xdeadbeef
0x08048531 <+102>:	je     0x8048543 <main+120>
0x08048533 <+104>:	sub    esp,0xc
0x08048536 <+107>:	push   0x8048628
0x0804853b <+112>:	call   0x8048390 <puts@plt>
0x08048540 <+117>:	add    esp,0x10
0x08048543 <+120>:	cmp    DWORD PTR [ebp-0xc],0xdeadbeef
0x0804854a <+127>:	jne    0x804857c <main+177>
0x0804854c <+129>:	sub    esp,0xc
0x0804854f <+132>:	push   0x8048644
0x08048554 <+137>:	call   0x8048390 <puts@plt>
0x08048559 <+142>:	add    esp,0x10
0x0804855c <+145>:	sub    esp,0xc
0x0804855f <+148>:	push   0x804866e
0x08048564 <+153>:	call   0x80483a0 <system@plt>
0x08048569 <+158>:	add    esp,0x10
0x0804856c <+161>:	sub    esp,0xc
0x0804856f <+164>:	push   0x8048678
0x08048574 <+169>:	call   0x8048390 <puts@plt>
0x08048579 <+174>:	add    esp,0x10
0x0804857c <+177>:	mov    eax,0x0
0x08048581 <+182>:	mov    ecx,DWORD PTR [ebp-0x4]
0x08048584 <+185>:	leave  
0x08048585 <+186>:	lea    esp,[ecx-0x4]
0x08048588 <+189>:	ret

마지막 부분은 조건문이다. cmp 명령어와 je, jne 등의 명령어를 이용해서 우리가 원하는 대로 eip, 즉 실행되는 명령어 위치를 정해줄 수 있다. 그렇다면 한번 보도록 하자.

cmp 명령어는 ZF (Zero Flag) 와 CF (Carry Flag) 이 두 Flag의 값을 변환시킨다.

다음과 같은 규칙으로 변환이 일어나게 된다.

그러면 je, jne 명령어들은 무엇일까? 영어로 풀어보면

  • JE -> Jump if Equal -> Zero Flag 가 1이면 Jump
  • JNE -> Jump if Not Equal -> Zero Flag 가 0이면 Jump

가 된다.

그럼 이 명령어들을 보고 차례대로 반복문과 if 문을 작성해보도록 하자.

if(num1 == 0x4030201 || num1 == 0xdeadbeef);
{
		if(num1 != 0xdeadbeef);
		{
				return 0;
		}
		else
		{				
				puts("Yeah dude! You win!\nOpening your shell...")
				system("/bin/dash");
				puts("Shell closed! Bye.");
		}
}
else
{
		puts("\nYou are on the right way!");
}

사실 반복문과 관해서는 딱히 설명할 부분이 없다. 그냥 if 문을 적절히 사용하다 보면 자연스럽게 C 코드를 완성할 수 있을 것이다. puts 함수나 system 함수의 인자들은 다음과 같은 과정을 통해서 문자열을 알아낼 수 있다.

따라서 모든 코드들을 합쳐 보면 최종적인 코드는

#include<stdio.h>
int main(void)
{
		int num1 = 0x4030201;
		char arr[40];
		fgets(arr, 45, 0);
		printf("\n[buf]: %s\n",  arr);
		printf("[check] %p\n", &num1);
		if(num1 == 0x4030201 || num1 == 0xdeadbeef);
		{
				if(num1 != 0xdeadbeef);
				{
						return 0;
				}
				else
				{				
						puts("Yeah dude! You win!\nOpening your shell...");
						system("/bin/dash");
						puts("Shell closed! Bye.");
				}
		}
		else
		{
				puts("\nYou are on the right way!");
		}
}```

가 되겠다.

0개의 댓글