Windows Trampoline
최근에 약간 실력을 키우기 위해서 아시는분에게 과제를 받고있다.
위 과제는 Foo을 호출시시작과 끝에 Boo와 Hoo과 출력되게 만들어야한다는것이다.
처음에는 std::cout와 std::endl 오버로딩으로 풀었지만 이는 답이 아니였다. 그래서 생각해낸게 코드패치이다.
코드 패치란 파일이나 메모리상의 코드영역을 수정하는 것이며 이를 통해 기존의 동작이 아닌 별도의 동작을 하게되는것이다.
{
DWORD oldProect;
VirtualProtect((LPVOID)Foo, FUN_SIZE, PAGE_EXECUTE_READWRITE, &oldProect);
memcpy(orginCord.data(), (void*)Foo, FUN_SIZE);
BYTE pBuf[5] = { 0xE9,0, };
DWORD jmpaddress = relativityFunctoin((std::uint64_t)Foo, (std::uint64_t)FNA);
memcpy(&pBuf[1], &jmpaddress, 4);
memcpy(Foo, pBuf, FUN_SIZE);
VirtualProtect((LPVOID)Foo, FUN_SIZE, oldProect, &oldProect);
}
코드 패치하는 코드는 위와 같다.
VirtualProtect((LPVOID)Foo, FUN_SIZE, PAGE_EXECUTE_READWRITE, &oldProect);
memcpy(orginCord.data(), (void*)Foo, FUN_SIZE);
기본적으로 코드영역은 읽기 영역이기 때문에 쓰기 영역으로 바꾸어 주는 작업이 별도로 필요하다. VirtualProtect은 가상메모리 영역의 속성을 변경해주는 함수이다. 그후 함수 부분의 5바이트을 백업을 해둔다.
BYTE pBuf[5] = { 0xE9,0, };
DWORD jmpaddress = relativityFunctoin((std::uint64_t)Foo, (std::uint64_t)FNA);
memcpy(&pBuf[1], &jmpaddress, 4);
이 부분은 코드영역에 쓸 데이터를 작성해주는 부분이다.
pbuf의 [0]번지에 0x59인 JMP가 0x59이기때문이고 그뒤에 값은 4바이트인 상대 경로를 적는다.
상대 주소 계산은 다음과 같다.
DWORD relativityFunctoin(std::uint64_t orginFn, std::uint64_t newOrg) {
return newOrg - orginFn - 5;
}
-5을 하는이유는 코드 패치하는 영역이 5바이트를 사용하기 때문이다.
memcpy(Foo, pBuf, FUN_SIZE);
VirtualProtect((LPVOID)Foo, FUN_SIZE, oldProect, &oldProect);
그 후에 코드영역에 데이터을 써주고 아까전에 쓰기권한을 일기 권한으로 설정을 해주면 준비는 끝난다.
나머지는 Hook을 풀어주는 간단한 작업이고 코드만 봐두 이해가 가능할거같다 설명을 생략하였다.
완성된 코드는 과제는 다음과 같다.
// ConsoleApplication6.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <string>
#include <vector>
#include <Windows.h>
using namespace std;
void Foo(string val1, int val2, int val3, int val4, int val5, int val6, int val7, int val8, int val9) {
cout << val1 << val2 << val3 << val4 << val5 << val6 << val7 << val8 << val9 << endl;
}
void Boo() {
cout << "Boo" << endl;
}
void Hoo() {
cout << "Hoo" << endl;
}
void UnHook();
void FNA() {
Boo();
UnHook();
Foo("Test", 1, 2, 3, 4, 5, 6, 7, 8);
Hoo();
}
DWORD relativityFunctoin(std::uint64_t orginFn, std::uint64_t newOrg) {
return newOrg - orginFn - 5;
}
const int FUN_SIZE = 5;
std::vector<char> orginCord(FUN_SIZE, 0);
void UnHook() {
DWORD oldProect;
VirtualProtect((LPVOID)Foo, FUN_SIZE, PAGE_EXECUTE_READWRITE, &oldProect);
memcpy(Foo, orginCord.data(), FUN_SIZE);
VirtualProtect((LPVOID)Foo, FUN_SIZE, oldProect, &oldProect);
}
void Hook() {
DWORD oldProect;
VirtualProtect((LPVOID)Foo, FUN_SIZE, PAGE_EXECUTE_READWRITE, &oldProect);
memcpy(orginCord.data(), (void*)Foo, FUN_SIZE);
BYTE pBuf[5] = { 0xE9,0, };
DWORD jmpaddress = relativityFunctoin((std::uint64_t)Foo, (std::uint64_t)FNA);
memcpy(&pBuf[1], &jmpaddress, 4);
memcpy(Foo, pBuf, FUN_SIZE);
VirtualProtect((LPVOID)Foo, FUN_SIZE, oldProect, &oldProect);
}
void Koo() {
Hook();
}
void wmain() {
auto i = Hook;
i();
for (int i = 0; i < 100; ++i) {
}
Koo();
Foo("Test", 1, 2, 3, 4, 5, 6, 7, 8);
}
이 예제에서는 작은 프로그램이여서 JMP 명령어을 사용하였지만 x64로 컴파일된 큰 프로젝트는 JMP을 사용하면 안된다. 방법을 찾고싶으면 https://www.sysnet.pe.kr/2/0/12148 이글을 참조하자.