공부/리버싱핵심원리

2장 Hello, World! 리버싱

리버싱 핵심원리 책 표지.

11월 21일, 대학교에 합격하였다. 그렇기에 그동안 못했던 공부를 하고 싶었고, 리버싱 핵심원리 책을 통하여 리버싱을 공부하려 하였다. 혼자서 머리속으로 끙끙 앓으며 공부를 하기 보다는, 누군가에게 설명을 하듯이 진행을 하는 방법이 편해 블로그를 통해 공부를 하려 한다.

 

본격적인 공부에 앞서, 책의 예제파일은 reversecore님의 블로그에서 다운받을 수 있으며, OllyDbg 32bit + Plugin으로 진행한다.

 

먼저, OllyDbg를 관리자권한으로 실행 후 HelloWorld.exe를 열어보자

Hello, World.exe 디버깅 시작

잘 열렸다.

 

좌측 상단을 Code Window, 좌측 하단을 Dump Window, 우측 상단을 Register Window, 우측 하단을 Stack Window라

한다.

Code Window

  1. 기본적으로 disassembly code를 표시하여 각종 comment, label을 보여준다.
  2. 코드를 분석하여 loop, jump 위치등의 정보를 표시한다.

Register Window

  1. CPU register 값을 실시간으로 표시하며 특정 register들은 수정도 가능하다.

Dump Window

  1.  프로세스에서 원하는 memory 주소 위치를 Hex와 ASCII/유니코드 값으로 표시하고 수정도 가능.

Stack Window

  1. ESP register가 가리키는 프로세스 stack memory를 실시간으로 표시하고 수정도 가능함.

EP 코드 ??

디버깅을 시작할 때 가장 먼저 디버거가 멈춘 곳이다.

디버거는 004011A0에 멈추어 있다. 이 위치를 EP(Entry Point)코드라고 한다.

CALL과 JMP가 눈에 띄는데, 위에서 아래로 읽어보면,  아래와 같이 생각할 수 있다.

"0040270C 주소의 함수를 CALL 하고, 0040104F 주소로 JMP 하라."

이 프로그램은 실행되면 MessageBox()함수를 호출한다고 한다. 그러므로 쭉쭉 디버깅을 진행해보자.

 

40270C 함수 따라가기

 

명령어 단축키 설명
Restart [Ctrl+F2] 다시 처음부터 디버깅 시작
(디버깅 당하는 프로세스 종료후 재시작)
Step Into [F7] 하나의 OP code 실행
(CALL 명령이 있으면, 그 함수 내부로 따라 들어감)
Step Over [F8] 하나의 OP code 실행
(CALL 명령을 만나면, 따라 들어가지 않고 그냥 함수 자체를 실행)
Execute till Return [Ctrl+F9] 함수 코드 내에서 RETN 명령어까지 실행
(함수 탈출 목적)

왜인지 모르겠지만, 이런 명령어만 계속 누르고 있을 것 같고, 그렇기에 단축키는 확실히 외워놔야 겠다.

 

먼저, 004011A0(EP CODE)에서 Step Info를 통해 0040270C 함수로 들어가보자.

끝이 보이지 않는다...

매우 복잡한 코드가 등장했다. 겁먹지 말고 계속 나아가보자....
먼저 오른쪽은 Comment(주석)이다. 빨간색 글씨는 API 함수 이름이다. 이 프로그램의 소스코드를 참고해보면, 원본 코드에는 없는 API들이 호출되고 있다. 그렇기에 main함수라고 생각할 수 없다.

(Visual C++ Stub Code로, 컴파일러에서 프로그램 실행을 위해 추가시킨 부분이다)

Step Over를 통해 RETN까지 가자.

wOwwwwwwww 찾았다.

004027A1이 RETN이 있는 주소이다. 따라서 Step Into를 진행해주면, 함수가 호출 된 주소 쪽으로 돌아갈것이다. 
아마 004011A5로 돌아갈 것이다.

예ㅔㅔㅔㅔㅔㅔㅔㅔ

맞다. 다시 돌아왔다. 이제, 0040104F 주소로 JMP 될 것이다.

역시 어렵군...

난 아직 초보자라 잘 모르겠지만, 이 코드도 Stub Code라고 한다. 우선, main함수가 시작되면 MessageBox() API를 호출하는 코드가 있기에, 이것을 힌트로 계속 찾아봐야 할 것 같다.

(아직 대학입학도 하지 않았으니, 천천히 F7을 통해 노가다 해보자. (대회때 이러면 ...))

계속해서 F7을 하면 00402524를 CALL하게 된다. 따라 들어가보자.

그나마 짧은 함수이다.

MessageBox() API가 보이지 않기에, Ctrl+F9를 누르면 00402568까지 디버깅이 진행되고, 이때 F7이나 F8을 누르면 00402568(RETN)으로 넘어가진다. 이후 F7을 한번 더 눌러 빠져나가자.

분석해야 할 것이 산더미이다.

다시, 나가진 것이 확인된다.  이런 식으로 계속 분석해보자. 

이...이게 뭐노......

중간에 이런 ???가 발견되는데, 아놔---------Shift+F9를 누르면 해결된다고 하길래, 눌렀는데 그냥 쭉 다 실행되어버렸다. 으아아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ아아아아아아아악

다시... 진행하자........

찾았따 ㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜ

F7 F8 Ctrl+F9, 결국 못 찾으면 Ctrl+F2 이 과정을 진짜 미친듯이 반복했다. 엄청난 노가다였지만, 약간 얻게된 교훈은, Ctrl+F9를 적절히 사용 못하면 디버깅도 하기 전에 프로그램이 실행되어서 끝나버린다는 것이다. 조심히 사용해야 겠다.

디버깅 명령어를 더 익혀 능숙히 사용해보자.

 

디버거를 좀 더 능숙하게 다루자

우선, 디버거 동작 명령에 대해 정리해보겠다. (Code Window에서 동작)

명령어 단축키 설명
Go TO Ctrl+G 원하는 주소로 이동
(코드/메모리를 확인할 때 사용, 실행되는 것은 아님.)
Execute till Cursor [F4] cursor 위치까지 실행
(디버깅하고 싶은 주소까지 바로 갈 수 있음)
Comment ; Comment 추가
User-defined comment   마우스 우측메뉴 Search for User defined comment
Label : Label 추가
User-defined label   마우스 우측메뉴 Search for User-defined label
Set/Reset BreakPoint [F2] BP 설정/해제
Run [F9] 실행
(BP가 걸려있으면 그곳에서 정지)
Show the current EIP * 현재 EIP 위치 보여줌
Show the previous Cursor - 직전 커서 위치를 다시 보여줌
Preview CALL/JMP address [ENTER] 커서가 CALL/JMP 등의 명령어에 있다면, 해당 주소를 따라가서 보여줌
(실행되는 것은 아님.)

또한, 본인의 경우 디버깅을 하다가 실패를 하면 Ctrl+F2로 처음부터 다시 시작했는데, 베이스캠프를 설정해주면, 바로 해당 위치로 가서 작업을 할 수 있다.

 

베이스캠프를 설정해 주는 데는 4가지 방법이 있다.

 

1. Goto 명령 (Ctrl+G)

단순히 베이스캠프의 주소를 기억해주고, 디버깅을 시작할 때 명령하면 된다.

예를 들어 main함수가 있던 401000으로 바로 가보겠다.

OKOKOKOK
성공

이렇게 한 번에 main 함수로 가지는 것이 확인된다. 물론 main함수의 위치는 노가다로 알아야 할 수도 있지만, main함수부터 분석을 시작해야 할 때에는 상당히 유용하다.

2. BP(Break Point)설치 (F2)

main 함수 위치까지 파악을 하였다면, F2를 눌러 BP를 설치하자. 이후 실행(F9)를 해주면 BP에서 멈추게 된다.

성공.

BP가 걸려있어 색이 변한 것 또한 확인할 수 있다.

3. 주석(Comment) (;)

모든 주석은 한 번에 확인할 수 있다. 그러니 main함수 시작 주소에 주석을 달아놓고 확인해주는것도 방법이다.

...?

마우스 우클릭 - Search for - User - defined comment를 통해 확인해줄 수 있다.

오예ㅖㅖㅖ

저 주석을 클릭해주면 main함수로 넘어가진다. 

4. 레이블(Comment) (:)

다음은 레이블이다. 콜론을 이용하여 레이블을 설정할 수 있다.

레이블 생성.

이러한 방식으로 레이블이 생성되었으며, main함수를 호출하는 곳에 식별하기 쉽게 생겼다.

 

원하는 코드를 좀 더 빨리 찾자.

원하는 코드를 빨리 찾을 수만 있다면, 엄청 수월하게 작업할 수 있을 것이다. 본 책에서는 4가지 방법을 설명하고 있다.

우리가 찾고 싶은 main()함수는 Stub Code때문에 한참 머리 떨어져 있다.

1. 코드 실행

코드 실행 방법은 위에서 main함수를 찾으러 가는 노가다 방법이랑 비슷하기에 직접 실습은 진행하지 않았다.

결국, F8(Step Over)를 하다가 MessageBox()가 실행되면 BP를 설정하거나 Goto를 통해, 해당 주소의 함수를 F7(Step Into)를 진행해주면 된다. 하지만, 코드의 크기가 크고 복잡한 경우에는 적절하지 않다.

 

2. 문자열 검색

OllyDbg의 경우 디버깅할 프로그램을 로딩할 때 사전 분석과정을 걸친 후 참조되는 문자열들과 호출되는 API들을 목록으로 만들어 놓는다. 문자열의 경우 'All referenced text strings' 명령을 하면 된다.

오오

상단에 두 문자열이 확인된다. 마찬가지로 클릭을 통해 해당 코드로 바로 넘어갈 수 있다.

3. API 검색 - 호출 코드에 BP

먼저, 코드에서 사용된 API 호출 목록을 보기 위해서, 'All intermodular calls' 명령을 하면 된다.

 All intermodular call 클릭

이렇게 모든 API들이 보여지고, MessageBox API 를 더블클릭해주면,

쨔쟈쟈쟈쟈ㅑㅑㅑ잔

main 함수에서 MessageBox를 CALL 하는 위치로 바로 가진다.

4. API 검색 -  API 코드에 직접 BP

설명에 앞서, Packer/Protector를 사용하게 되면, 실행 파일이 압축(보호)되고, 파일 구조가 변경되어 OllyDbg에서 API 호출 목록이 보이지 않는다. 이럴 때는 프로세스 메모리에 로딩된 라이브러리(DLL 코드)에 직접 BP를 걸면 된다.

 

API라는 것은 OS에서 제공한 함수이고, system32 폴더안 *.dll로 구현되어 있다. 결국, 본 프로그램에서 MessageBox() API를 반드시 호출해야 하고, 그 DLL 파일들은 프로세스 메모리에 로딩이 되어야 한다. 

 

먼저, Search for 에서 Name in all modules 명령을 하자. 

MessageBoxW 함수 선택하자.

이후, MessageBoxW 함수를 더블클릭해주면

HelloWorld.exe의 주소와 확연히 틀린 주소가 보인다. 여기에 BP 설치하고 실행해보자.

그럼, 75EAF3C0 주소에서 멈추게 되는데, 먼저 ESP를 확인해보자.

ESP = 0019FF18

ESP값이 0019FF18로, 

이런 값이 있으며 0040100E 주소로부터 호출이 된 것을 확인할 수 있다.

 

"Hello, World!" 문자열을 패치해보장

이제 어느정도 흐름은 알 수 있게 되었으니, 패치를 해보자. 패치를 이해 버그를 수정하거나, 새로운 기능을 추가시킬 수도 있다. 패치 대상은 파일, 메모리 또는 프로그램의 코드와 데이터까지 모두 가능하다. 지금은 "Hello World"라는 문자열을 다른 문자열로 패치해보겠다. 난 Hello World를 Hello mm0ck3r 로 패치하겠다. 

패치를 하는 쉬운 두 가지 방법으로 진행해보겠다.

 

1. 문자열 버퍼를 직접 수정

먼저, Hello World의 주소를 확인해야 한다. 

004092A0으로 확인이 된다. 이후 덤프 창에서 Ctrl+G를 통해 004092A0을 검색해주고, Ctrl+E를 통하여 패치하자

패치 성공 ^~^

패치에 성공하였다....! 현재 main함수 시작에 BP가 걸려 있으니 F9를 통해 실행해보자.

성공

성공했다. 이제 이러한 변경내용을 적용한 실행 파일로 생성해보자.

수정된 문자열 (Hello mm0ck3r)을 선택하여 Copy to executable file을 선택하고 SaveFile을 하자.

2. 다른 메모리 영역에 새로운 문자열을 생성하여 전달

위와같은 경우, 패치되는 문자열이 world보다 기므로 위험한 패치라고 할 수 있다. (mm0ck3r이 world보다 더 길어 뒤의 유의미한 값을 바꿀 수 있기 때문이다.)

그런 경우 NULL padding 부분에 데이터를 넣어주면 된다. PUSH 004092A0은 결국 004092A0주소의 "Hello World!"문자열을 파라미터로 넘기고 있는 것인데, 다른 문자열 주소를 넣어주면 되는 것이다.

덤프창을 내리다 보면 이런 프로그램에서 사용되지 않는 NULL padding 영역을 확인할 수 있다. 

 

(NULL padding 영역이 진짜 NULL padding 영역인지 확인하기 위한 좋은 방법은 링크 걸어놓겠다.)

crasy.tistory.com/57

 

windows pe) null패딩이 진짜 null패딩인지 확인하기

파일이 메모리에 올라가면 null공간이 생기는데 이공간에 dll injection이나 악성파일등을 넣을수있습니다, 허나 이null공간이 나중에 메모리에서 사용하는공간이면 에러가 나옴니다. 이null공간이

crasy.tistory.com

따라서 본인은 00409F50에 패치할 문자열을 쓰겠다. ("Hello, mm0ck3r World !!!")

이렇게 문자열을 남겨주자. 이후 스택에 PUSH하는 연산의 주소를 바꿔주자.

그럼 이런 아무리 긴 문자열이라도 잘 들어가지는 것을 확인 할 수 있다.

성공했다.

 

 

 

 

끄읏ㅅㅅㅅㅅㅅㅅㅅ

'공부 > 리버싱핵심원리' 카테고리의 다른 글

6장 abex' crackme #1 - 책의 풀이  (0) 2020.12.01
6장 abex' crackme #1 - 나의 풀이  (0) 2020.11.30
5장 스택  (0) 2020.11.30
4장 IA-32 Register  (0) 2020.11.30
3장 리틀 엔디언 표기법  (1) 2020.11.28