이 글에서는...
프로그램을 패치하는 방법 중 하나인 인라인 패치 기법에 대해 알아봅니다.
사전 지식
본 글을 이해하기 위해 필요한 사전 지식입니다.
- x86 어셈블리어 초급
- Windows PE 파일 구조
- ollydbg 도구 사용법
- 실행 압축과 패커의 개념
- RVA 주소를 Offset 주소로, 또는 반대로 변환하는 방법
인라인 패치 (Inline Patching) 란?
인라인 패치 기법이란, 프로그램에 있는 빈 공간에 원하는 코드를 삽입하고 그 코드의 실행이 끝났을 때 다시 원래 프로그램 코드의 흐름으로 돌아가도록 프로그램을 패치하는 기법입니다.
(그 삽입된 코드를 Code Cave 라고 합니다.)
인라인 패치 기법은 프로그램 코드를 직접 수정하기 어려운 경우에 사용되는 기법입니다.
프로그램 코드를 직접 수정하기 어려운 경우란, 대표적으로 패커에 의해 프로그램 코드가 인코딩 되어 있는 경우입니다.
그 인코딩된 코드가 런타임(프로그램이 실행되었을 때)에 디코딩이 된다면, 프로그램의 코드를 수정하여 패치할 수가 없습니다.
따라서 프로그램의 빈 공간에 원하는 코드를 작성하고, 그 빈 공간의 주소로 점프해서 코드를 실행하도록 해야 합니다.
실습 요약
[리버싱 핵심원리] 실습예제 에서 예제 파일을 다운로드하실 수 있습니다.
글을 읽기 전에 직접 분석을 먼저 해보시면 글이 더 재미있을 것입니다.


두 텍스트 "You must patch this NAG!!!"와 "You must unpack me!!!"를 수정하는 패치를 목표로 하고 디버깅을 했습니다.
ollydbg로 프로그램을 열어서 디코딩하는 부분을 분석해 보면 다음과 같은 프로그램 구조를 그릴 수 있습니다.

이렇게 프로그램의 구조를 그렸다면, 다음은 프로그램의 실행 흐름을 파악해야 합니다.
프로그램의 실행 흐름은 다음과 같습니다.
[B] 영역
xor [C] with 44
xor [A] with 7
xor [C] with 11
[A] 영역
checksum [C]
jump to OEP
위에서 아래로 실행의 흐름이 진행된다고 보시면 됩니다.
[A] 영역에서 OEP로 점프하는 코드 대신에 우리가 작성한 코드로 점프하도록 수정하면 됩니다.
그리고 우리가 작성한 코드가 끝나면, 다시 OEP로 점프하도록 하면 됩니다.
그럼 이제 빈 영역에 두 텍스트를 수정하는 코드를 삽입해 보도록 합시다.
빈 영역 찾기
실행의 권한이 있는 섹션인 .text 영역의 빈 공간을 찾아 우리가 실행할 코드를 삽입하겠습니다.

PEview로 프로그램을 열고 .text 섹션을 보면 Size of Raw Data가 0x400, Pointer to Raw Data가 0x400이니까, 파일에서 .text 영역이 차지하는 공간은 0x400 ~ 0x800이라는 것을 알 수 있습니다.
그런데 Virtual Size(메모리에서 섹션이 차지하는 공간의 크기)가 0x280이니까 .text 섹션이 실질적으로 파일에서 차지하는 영역은 0x400 ~ 0x680이라는 것을 알 수 있고, 0x680 ~ 0x800은 NULL로 채워진 빈 공간이라는 것을 알 수 있습니다.
실제로 HxD로 해당 영역을 살펴보면

빈 공간이 있습니다.
0x680은 offset 값이고, 이를 RVA로 변환하면 0x1280 입니다.
0x1280 = 0x680 - 0x400(Pointer to Raw Data) + 0x1000(.text section's RVA)
Image Base인 0x400000를 더해주면 0x401280이 되고, ollydbg로 프로그램을 열었을 때, 이 위치에 빈 공간이 있을 것입니다.

위 사진처럼 OEP(0x40121E) 아래에 0x401280부터 빈 영역이 존재합니다.


빈 영역에 위와 같이 어셈블리어 코드를 작성해 줍니다.
이제 아까 그림에서 [A] 영역에서 OEP 대신 여기로 점프하도록 코드를 수정해 주면 됩니다.

그런데 여기서 잊지 말아야 할 것이 하나 있습니다.
지금 수정하려고 하는 영역인 [A] 영역도 xor with 7로 인코딩 되어 있었다는 점입니다.
따라서 그냥 저렇게 패치해 버리면 안 되고, 명령어의 옵코드인 E9 F8 01 00 00을 한 바이트씩 7과 xor 연산을 해서 인코딩해줘야 한다는 것입니다.
그러면 프로그램에서 디코딩할 때 다시 한 바이트씩 7과 xor 연산을 할 텐데, 그러면 원래 옵코드로 돌아올 것입니다. (어떤 수 a에 어떤 수 n을 두 번 xor 할 경우 다시 a가 된다는 점을 기억하세요.)
HxD에서 수정하도록 하겠습니다.

파란색 영역이 해당 옵코드 부분입니다. (0x401083 주소를 offset으로 변환하면 0x483 입니다.)
위에서 확인한 옵코드와 7을 xor 연산한 값을 해당 영역에 때워주도록 하겠습니다.

이렇게 하면 최종적으로, [A] 부분의 코드 실행이 끝나면 우리의 코드로 점프하여 두 텍스트를 수정한 뒤 OEP로 돌아갑니다. 그렇게 두 텍스트가 수정되는 것입니다.
이제 저장한 뒤 프로그램을 실행해보면


원하는 텍스트로 변경된 것을 알 수 있습니다.
느낀 점
처음 디코딩 코드 분석을 시작하고 전체적인 큰 그림이 잡힐때 까지는 많이 헤맬 수 있습니다.
저는 메모리 구조를 직접 그려보니 이해하기 쉬웠습니다. 리버싱을 잘 하기 위해서는 메모리 구조를 그려서 내가 지금 어느 영역에 있는지 제대로 알고, 영역 간 이동을 제대로 인지하고 있어야 하는 것 같습니다. 코드의 실행 흐름을 제대로 알아야 합니다.
'정보보안 > 리버싱' 카테고리의 다른 글
| 추억의 게임 렙업만이살길2 리버싱 일기 - 1편 (1) | 2025.06.01 |
|---|---|
| DLL Injection 기법 - 프로세스에 침투하기 (0) | 2024.02.08 |
| 윈도우 이벤트 메시지 후킹 기법 (0) | 2024.02.06 |
| PE 파일 분석을 위한 C++ 프로그램 제작 (0) | 2024.02.01 |
| Frida로 유니티 게임 리버싱하기 (궁수의 전설) (0) | 2024.01.29 |