지난번에 올린
Pin 을 이용한 메모리 조작 분석에 이어서 ~
이번에도 '게임 해킹툴 분석' 측면으로 접근해보려 합니다 :)
물론 응용하기에 따라 다른 쪽으로 이용도 가능하겠지만요.. ^^;;;
Pin 을 이용한 비정상 함수 호출 분석
게임 내부의 함수들은 게임이 진행되는 중에 게임에서 호출하는 것이 일반적입니다.
FPS 게임에서 캐릭터가 죽는 경우를 예를 들어봅시다.
게임 내부적으로 무기 데미지 / 캐릭터 체력 / 착탄 부위 등 이것저것 계산을 해서
조건을 만족하는 경우에
게임이 Kill 함수를 호출하는 것이 정상적인 흐름입니다.
만약 이 Kill 함수가 게임이 아닌 다른 곳에서 호출되면 어떻게 될까요...? @_@
게임 진행과 상관없이 캐릭터가 죽겠죠... -_-;;;
이런 식으로 게임 해킹툴이 게임 내부 함수를 호출하는 것을
'비정상 함수 호출' 이라고 합니다.
Pin 을 이용해서 인젝션 된 DLL 이 대상 프로세스의 함수를 호출하는 것을 분석해봅시다.
테스트 샘플 구성은 '메모리 조작 분석' 과 동일합니다.
[ Sample.zip 다운로드 ]
- Sample.exe : 게임 프로세스
- SampleDll.dll : 게임 프로세스에 인젝션되는 게임 해킹툴
Sample.exe 가 실행될 때 SampleDll.dll 을 로드하고
DLL 이 로드된 후, 'F2' 키를 누르면~ SampleDll.dll 이 Sample.exe 의 함수를 호출합니다.
|
Sample.exe 실행 |
'F2' 키를 눌렀을 때... 실행되는 SampleDll.dll 의 코드는 아래와 같습니다.
|
SampleDll.dll 의 비정상 함수 호출 코드 (소스코드) |
참고로 lpFunc 는 프로세스 베이스 주소 + 0x1000 의 값으로 고정해두었습니다.
이 코드를 디버거로 보면 아래와 같은 디스어셈블 코드가 나옵니다.
|
SampleDll.dll 의 비정상 함수 호출 코드 (디스어셈블 코드) |
노란 박스 안의 저 "CALL" 명령으로 시작되는 코드를 Pin 으로 잡아내면 되겠죠~? :)
핵심 API 는
"INS_IsCall" 입니다.
CALL 명령이 나오면 이 API 의 리턴값이 TRUE 가 됩니다.
아래는 Pin tool 예제 코드입니다.
// CallTrace.cpp
#include "pin.H"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
ofstream TraceLog;
UINT32 SampleBaseAddr = 0;
UINT32 SampleMappedSize = 0;
UINT32 DllBaseAddr = 0;
UINT32 DllMappedSize = 0;
//-------------------------------------------------------------------
INT32 Usage()
{
return -1;
}
VOID Fini(INT32 code, VOID *v)
{
TraceLog << endl << "#eof..." << endl;
if (TraceLog.is_open()) TraceLog.close();
}
VOID ImageLoad(IMG img, VOID *v)
{
// 메인 프로세스 : Sample.exe
if (IMG_IsMainExecutable(img) == TRUE) {
SampleBaseAddr = IMG_StartAddress(img);
SampleMappedSize = IMG_SizeMapped(img);
TraceLog << "* Process Name : " << IMG_Name(img) << endl;
TraceLog << " StartAddress : " << hexstr(SampleBaseAddr)
<< ", MappedSize : " << hexstr(SampleMappedSize) << endl;
}
// SampleDll.dll
if (IMG_Name(img).find("SampleDll.dll") != string::npos) {
DllBaseAddr = IMG_StartAddress(img);
DllMappedSize = IMG_SizeMapped(img);
TraceLog << "* Sample Dll : " << IMG_Name(img) << endl;
TraceLog << " StartAddress : " << hexstr(DllBaseAddr)
<< ", MappedSize : " << hexstr(DllMappedSize) << endl;
}
}
VOID Log_Call(string &CallFunc, ADDRINT target)
{
if ((target >= SampleBaseAddr) &&
(target < (SampleBaseAddr + SampleMappedSize))) {
TraceLog << CallFunc + " [ Target : " + hexstr(target) + " ]" << endl;
}
}
VOID Trace(TRACE trace, VOID *v)
{
for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
{
INS tail = BBL_InsTail(bbl);
ADDRINT Address = INS_Address(tail);
if ((Address >= DllBaseAddr) &&
(Address <= (DllBaseAddr + DllMappedSize))) {
if (INS_IsCall(tail))
{
string CallFunc = "- eip: " + hexstr(Address) +
" [CallFunc] " + INS_Disassemble(tail);
INS_InsertPredicatedCall(tail, IPOINT_BEFORE, AFUNPTR(Log_Call),
IARG_PTR, new string(CallFunc),
IARG_BRANCH_TARGET_ADDR,
IARG_END);
}
}
}
}
//-------------------------------------------------------------------
int main(int argc, char *argv[])
{
PIN_InitSymbols();
if (PIN_Init(argc, argv)) return Usage();
TraceLog.open("CallTrace_Log.txt", ofstream::out);
TraceLog << "### Call Trace Log ###" << endl << endl;
IMG_AddInstrumentFunction(ImageLoad, NULL);
TRACE_AddInstrumentFunction(Trace, NULL);
PIN_AddFiniFunction(Fini, NULL);
PIN_StartProgram();
return 0;
}
빌드해서 생성된 CallTrace.dll 을 이용해보면~
|
Sample.exe 실행 (with Pin - CallTrace.dll) |
|
CallTrace.dll 에서 남긴 로그 |
이번에도 역시 SampleDll.dll 에서 비정상 함수 호출을 하는 코드가 로그 파일에 제대로 기록되었네요~ :)
마치며
Pin 을 이용해서 '메모리 조작 분석' 과 '비정상 함수 호출 분석' 을 분석하는 방법을 살펴봤는데요.
기능별로 설명(?)하기 위해 MemTrace / CallTrace 로 나눴지만...
잘 조립하면 하나의 DLL 로 합칠 수도 있습니다 :)
실제 게임에 적용하기 위해서는 이것저것 고려해야 될 사항들과 장벽들이 많지만... @_@;;
Pin 을 공부하시는 분들께 조금이나마 도움이 되었으면 좋겠네요~~ :)