ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [리버싱 핵심원리 study] 20장 인라인 패치 실습
    Reverse Engineering 2021. 1. 1. 22:23

    1. 인라인 패치(Inline Patch)

    인라인 패치는 코드를 직접 수정하기 어려울 때 간단히 코드 케이브(Code Cave)라고 하는 패치 코드를 삽입한 후 실행해 프로그램을 패치시키는 방법이다. 주로 대상 프로그램이 실행 압축되어 있어서 직접 수정하기 어려운 상황에 사용되는 기법이다.

     

    인라인 코드 패치

    위와 같은 그림에서 만약에 패치를 원하는 영역이 암호화된 영역에 있다면, 복호화 과정에서 그 값이 변질되므로 그냥 패치시켜서는 안 된다. 이와 같은 문제를 해결하기 위해서 복호화 과정 이후 Code Cave로 JMP 하여 원하는 패치를 진행한 후 OEP로 JMP 하는 방식을 이용할 수 있다.

     

    2. 실습 - Patchme

    리버싱 핵심원리 책에서는 ap0x 리버서가 만든 patchme 예제를 이용하여 실습을 진행한다. 먼저 해당 파일을 실행해보자.

    메시지 박스

    확인 버튼을 눌러보자.

    메인 다이얼로그

    위와 같이 두 부분을 패치하면되지만, 파일에서 해당 부분이 암호화되어 있다.

     

    3. 디버깅 - 코드 흐름 살펴보기

    OllyDbg로 파일을 열어보자.

    EP 코드

    위와 같이 401007 이후에는 암호화된 코드가 나타난다. 문자열 패치를 위해 'Search for All referenced text strings'를 이용해보자.

    문자열

    역시 암호화되어 있어 이 상태로는 원하는 문자열을 찾을 수가 없다. EP 코드 그림에서 CALL 4010E9를 따라 들어가다 보면 다음과 같은 코드를 만난다.

    복호화 루프

    위 코드는 복호화 코드로, 4010A3의 XOR 명령으로 복호화한다. 4010B0의 CALL 4010BD를 따라 들어가면 다음과 같은 복호화 루프를 발견할 수 있다.

    또 다른 복호화 코드

    4010C8 주소의 XOR 명령에 의해 401007 ~ 401085 영역이 복호화되고, 4010DB의 XOR 명령에 의해 4010F5 ~ 401248영역이 복호화된다. 후자는 앞서 봤던 복호화 루프와 동일한 부분을 가리키므로 이중으로 암호화된 것을 알 수 있다. 4010BD의 함수 호출이 끝나면, CALL 401039 명령을 만나게 되는데, 해당 부분 내부로 들어가면 다음과 같은 코드가 나타난다.

    401039 함수

    위 표시한 부분에서 EDX와 어떠한 상수값을 비교하는 것을 확인할 수 있는데, 이는 Checksum을 계산하는 부분이다. 위의 ADD EDX, DWORD PTR DS:[EBX] 명령어가 반복적으로 호출되면서 4byte씩 더해지고, 이 값을 31EB8DB0와 비교하여 만약에 다르면 error("CrC of this file has been modified!!!")가 발생한다. 복호화 과정을 거쳐 OEP에 이르면 다음과 같은 코드를 볼 수 있다.

     

    OEP 코드

    해당 부분은 DialogBox를 생성하는 API를 호출하는 부분으로, DlgProc 매개변수(함수)에 의해 문자열 출력 등에 대한 처리가 이뤄진다. DlgProc 부분 내부로 들어가면 다음과 같다.

    DlgProc 내부

    위와 같이 40110A에는 "You must unpack"이라는 문자열이, 401123에는 "You must patch !"라는 문자열이 있고, 밑의 SegDlgItemText와 MessageBox API의 인자로 문자열들의 주소가 넘어가는 것을 확인할 수 있다. 이로써 프로그램의 개략적인 흐름은 확인하였다. 하지만 해당 프로그램은 곳곳이 암호화되어 있어 일반적인 파일 패치로는 해결할 수 없고 인라인 패치 방법을 이용하는 것이 좋다.

     

    4. 코드 구조

    위에서 습득한 정보를 기반으로 코드 구조를 도식화하면 다음과 같다.

    코드 구조

    [A], [B], [C]영역은 암호화된 영역이며, 간략한 흐름은 다음과 같다.

     

    (1) [B]를 XOR 44로 복호화

    (2) [A]를 XOR 7로 복호화

    (3) [B]를 XOR 11로 복호화

    (4) CHECKSUM [B]

    (5) [C]를 XOR 17로 복호화

     

    5. 인라인 패치 실습

    패치시켜야할 문자열들이 [B] 영역에 있고, 이중으로 암호화되어 있으며 CHECKSUM까지 판별하기 때문에 직접적으로 문자열을 수정하기는 힘들다. 이때 코드 케이브라는 패치 코드를 이용할 수 있는데 과정은 다음과 같다.

     

    (1) OEP로 JMP하기 직전에 Code Cave로 JMP 한다.

    (2) Code Cave 내부에서 패치를 수행한다.

    (3) 수행을 마친 후, OEP로 JMP 한다.

     

    그렇다면 패치코드는 어디에 설치하는 것이 좋을까? 패치 코드를 설치하는 방법에는 크게 세 가지 정도의 방법이 있는데 파일의 빈 영역에 설치하거나, 마지막 섹션을 확장하여 설치하거나, 새로운 섹션을 추가한 후 설치하는 방법이 있다. 우선 PEView로 실습 파일을 열고 첫 번째 섹션(.text)을 살펴보자.

    text 섹션 헤더

    위 정보로부터 text section은 file에서는 400(file alignment에 의해 280 + NULL), memory에서는 1000(section alignment에 의해 280 + NULL)을 차지하는 것을 알 수 있다. text section의 실내용이 끝나는 부분인 680(Pointer to Raw Data + Virtual Size = 400 + 280)위치를 HxD로 확인해보자.

    text section

    위와 같이 비어있는 것을 확인할 수 있다. 이 곳에 코드 케이브를 설치해보자.

     

    우선 OllyDbg로 OEP 영역까지 가보면 다음과 같다.

    패치 코드 편집 영역

    file offset 680 ~ 800은 메모리에서 401280 ~ 401400의 위치에 매핑된다. (400000(ImageBase) + 1000(RVA of text section) + 280(virtual size)) 401280에 다음과 같이 코드를 입력하고, 4012B8영역에는 dump창에 다음과 같이 문자열을 입력해보자.

     

    코드 패치

    이제 패치 코드도 삽입하였으니, OEP로 점프하기 전에 코드케이브로 JMP 하도록 코드를 작성하면 된다. 그런데 OEP로 JMP 하는 명령어는 다음과 같은 위치에 있다.

    401083에 위치한 OEP JMP

    그렇다, 해당 부분은 아쉽게도 암호화되어 있는 영역(XOR 7)에 속해있는 명령어이며, 현재는 복호화된 상태로, 그냥 코드를 수정하게 되면 문제가 발생한다. 401083에 해당하는 부분을 RVA to RAW 공식을 이용하여 계산하면 file offset은 483이 된다. 해당 부분을 HxD로 살펴보자.

    암호화된 모습

    위와 같이 메모리에서의 모습과는 다른 것을 확인할 수 있다. 앞에서 [A] 영역은 401085까지만 복호화를 진행하므로, 483 ~ 485의 3 byte에 대해서 복호화가 진행된다 따라서 JMP 401280에 대한 어셈블리 명령어는 E9 F8 01 00 00 인데 이에 대해 앞의 3byte를 XOR 7한 결과를 HxD에 저장하자.

     

    수정 후

     

    마지막으로 프로그램을 실행시켜서 결과를 확인해보자.

    실행 화면1
    실행화면 2

    (이제 보니 왜 Pack Succded!라는 문자열로 패치했는지는 모르겠다...)

    반응형

    댓글

Designed by Tistory.