Try Attack/Reverse Engineering[basic]

Code Caving

D4tai1 2018. 12. 18.

1. Code Caving

 ▶ 위 그림의 왼쪽을 보면 instruction1, instruction2, instruction3, instruction4 가 있다.

 ▶ instruction3 대신 다른 코드를 삽입하려고 한다.

 ▶ 그러나 코드의 사이즈가 더욱 커서 instruction3의 사이즈에 들어가지 않는다.

 ▶ 만약 어거지로 끼워 넣는다면 아래 instruction4가 없어지거나 다른 코드가 밀릴 수 있다.

 ▶ -------------------------------------------------------------------------------------------------

 ▶ 그래서 위 그림의 오른쪽을 확인한다.

 ▶ instruction3부분이 jump code1로 변경되어 있다.

 ▶ code1이 가리키는 주소에 [code to insert]가 있다.

 ▶ 여기에 새롭게 추가할 코드를 삽입한다.

 ▶ 이후 원래 있던 instruction3을 적어주고 jmp문을 사용하여 instruction4의 주소로 점프한다.

 ▶ 이것이 Code Caving이다.

 

1) 정의

 ▶ cave는 동굴을 의미하며 악성코드가 원래코드의 뒷부분에 숨기는 것을 말한다.
 ▶ 0~2GB는 내 프로그램, 2~4GB는 DLL이 올라와있다.

 ▶ user32.dll이 올라올 때 매 번 다른 곳에 로드된다.

 ▶ 내 프로그램에 넣는 방법과 DLL 등 다른 곳에 넣는 방법(dll injection)이 있다.

 ▶ 그 중에서도 세부적으로는 jmp문을 사용하여 하는 방법과 call을 사용하는 방법으로 나뉜다.

 

2) 권한

 ▶ 아무 곳에서나 쓸 수 있는 것이 아니고 쓰는 곳이 따로 있다.

 ▶ 쓸 수 있는지는 메모리 맵 내에서 확인할 수 있다.

 ▶ RWE로 권한이 부여되며 E는 리눅스의 x와 비슷하게 실행(Execute)권한을 의미한다.

 ▶ 실행권한은 code segment에만 부여된다.

 ▶ 올리디버거에서는 M 버튼을 눌러서 Memory Map 확인이 가능하다.

 

3) 주의사항

 ▶ 코드캐이빙을 통해 갔다올 경우 레지스터의 값이 복원되어 있어야한다.


 

2. Code Injection

 ▶ 코드 인젝션은 PE파일의 비어있는 공간에 ShellCode를 입력해서 실행하도록 만드는 해킹기술이다.

 ▶ 코드 캐이빙 기술을 사용해서 만들 수도 있다.

 

3. 시연

1) abex' crackme1의 원본소스

 ▶ 위 그림은 abex' crackme1 실행파일이다.


2) call문을 이용한 Code caving

 ▶ 위 그림은 abex' crackme1 실행파일의 call MessageBox 부분을 40106A의 주소로 변경하였다.


3) CodeCaving - call문을 이용한 Code caving

 [1] pushad 

  ▶ pushad는 현재 레지스터의 상태 전체를 push하는 명령어이다.

 [2] 새로운 명령어 추가

  ▶ Code Caving이 새로운 코드를 추가하는데 목적이 있으므로 간단한 명령어를 추가하였다.

 [3] 기존 소스 복사

  ▶ 기존의 메세지박스는 원래대로 출력되어야 하기 때문에 40107C에서 다시 적어준다.

 [4] popad

  ▶ popad는 스택에 저장했던 레지스터 상태를 그대로 복원하는 명령어이다.

 [5] retn

  ▶ 복귀주소를 eip에 넣고 jmp한다.

 [6] jmp문을 이용한 Code caving

  ▶ pushad, popad, ret 명령어 없이 jmp로 와서 원래 실행할 명령어의 주소로 jmp해주면 된다.


4. 시연2 - jmp문과 call문을 함께 사용한 Code caving

1) jmp문을 이용한 Code caving

  ▶ 3. 1)의 원본소스에서 40101D주소에 jmp문을 사용해서 Code caving하는 곳으로 이동하도록 작성한다.


2) call문을 이용한 Code caving

  ▶ 40106A에 이동하니 바로작성하지 않고 Call문을 사용하여 함수를 호출한다.

  ▶ 그리고 함수 복귀(원하던 바를 이룬) 후 jmp문을 이용하여 원래 위치로 되돌아간다.

  ▶ 아래 40107A로 이동하여 pushad 후 간단한 더하는 코드를 작성 후 402000 주소에 저장한다.

  ▶ 이후 popad를 통해 레지스터 상태를 복원한다.

  ▶ 원래 있던 곳에 있던 명령어[inc esi, dec eax]를 지우고 jmp문을 작성하였기 때문에 다시 적어준다.

  ▶ 원래 프로그램에 내용(원하는 동작)만 살짝 추가하는 것이기 때문에 기존의 내용은 최대한 보존하려고 한다.

  ▶ 맨 아래 retn문에서 40106F로 복귀할 것이고 40106F는 40101F로 jmp한다.


5. 시연3 - Code caving을 이용하여 문자를 메모리에 저장 후 결과를 메세지박스의 Title에 출력

1) jmp문을 이용한 Code caving

  ▶ 40101D의 주소만 [jmp short 40106A]로 변경한다.


2) Call문을 이용한 Code caving

  ▶ EAX에 있는 연산결과 A(0x61)를 402000주소에 저장한다.

  ▶ 파라미터의 개수만큼 push 후 MessageBox 함수를 호출한다.

  ▶ 40109C의 자리에 popad가 있어야 하는데 저장이 잘못되었는지 빠져있다.(참고)


6. 시연4 - 전역변수에 Shellcode 작성 후 Code caving하여 삽입해서 셸 띄우기

1) Call문을 이용한 Code caving

  ▶ pushad까지는 동일하다.

  ▶ WinExec( )함수를 호출한다.

 UINT WINAPI WinExec(LPCSTR lpCmdLine, UINT uCmdShow);  함수

  ▶ 함수의 첫 번째 파라미터는 "실행할 파일의 경로", 두 번째 파라미터는 "디스플레이 옵션"을 넘긴다.

  ▶ 디스플레이 옵션은 SW_SHOW(보여주기), SW_HIDE(숨기기), SW_SHOWMAXIMIZED(전체화면) 등이 있다.

  ▶ 함수가 정상실행되면 return value(Error를 확인하기 위해 사용)는 31보다 큰 수가 된다.

  ▶ 4020A0의 주소에 cmd창의 절대경로를 적어준다.

  ▶ Call WinExec를 하기 전 경로와 5(SW_SHOW로 정의된 정수 값)를 파라미터에 넣어준다.

  ▶ 이 후 cmd 창이 뜨는 것을 확인할 수 있고 레지스터상태는 원래대로 복원 후 함수를 종료한다.


7. 시연5 - 스택 내에 Shellcode 작성 후 Code caving하여 삽입해서 셸 띄우기

1) jmp문과 call문을 이용한 Code caving

  ▶ 여기까지 읽었으면 위 소스는 해석할 수 있을 것이라고 생각된다.


2) stack 내에 shellcode 저장

  ▶ 1)에서 가리키는 40109A에서 레지스터의 상태를 전부 저장한다.

  ▶ "\x63\x6d\x64\x2e\x65\x78\x65"의 셸코드를 스택에 삽입한다.

  ▶ 이후 현재 스택포인터를 eax에 저장한다.

  ▶ stackpointer가 가리키고 있는 곳이 "cmd.exe"이므로 WinExec는 cmd창을 열게된다.

  ▶ 이 후 바로 popad이 나왔는데 지금보니 "cmd.exe"에서 사용한 8바이트만큼의 스택포인터를 복원시켜준 후 popad를 사용하는 것이 더욱 적절한 것 같다.

  ▶ 구지 스택에 저장하는 이유는 .data영역에 저장하는 것은 전역변수이기 때문에 지역변수에 저장 후 사용해보려고 한 것이다.


3) Shellcode 내용


※ 1.2)에서 영역에 할당된 권한을 확인했기 때문에 시연하는 부분에서 확인하지 않았지만 항상 메모리맵을 먼저 확인하고 진행하여야 한다.


※ 추가지식

1) 코드캐이빙 하기위해 새로운 내용 작성

 [1] Keep size

  ▶ Keep size는 명령어가 오버될 경우 쓰지 않겠다는 옵션이다.

 [2] Fill rest with NOPs

  ▶ Fill rest with NOPs는 명령어를 변경하여 기존의 명령어의 사이즈에서 남는부분은 NOP로 채운다는 옵션이다.


2) 코드캐이빙 후 실행파일 저장방법

  ▶ [Edit] - [Copy all modifications to executable]를 누르면 3)의 그림과 같은 새 창이 나온다.


3) 저장

  ▶ [Save file]을 클릭 후 다른이름으로 저장과 같은 공용대화상자가 나오고 저장할 파일명 입력 후 저장하면 된다.


4) 코드캐이빙 할 위치

  ▶ 정답은 없어서 어디든 좋지만 jmp문과 같이 최소화된 명령어가 좋다.

  ▶ 메모리 맵을 열어 E가 적혀있는 영역(실행가능)에 저장하고 사용한다.



 

댓글