https://theori.io/ko/blog/game-hacking-2
게임핵의 원리 (2) - ESP - Theori 블로그
ESP 핵은 게임 내 적의 위치, 상태 정보를 화면에 표시하는 기술입니다. 이 글에서는 ESP의 기본 원리부터 메모리 읽기 방식(External vs. Internal), World to Screen 변환, DirectX 후킹, Overlay 기법, Bone ESP 구
theori.io
ESP 핵이란?
게임 내 적의 위치, 상태 정보를 화면에 표시하는 기술 월핵이랑 차이점은 위치만 보이는 건 아니고 상태나 정보까지 화면에 표시하는 형식
기본 원리
ESP 원리 → 메모리를 읽어서 필요한 정보를 얻고 화면에 표시하는 것, 대상 프로세스 메모리 읽기 必
어떤 데이터를 얻어야 하는가?
→ 게임 플레이에 도움되는 정보 (물체 위치, 회전 뱡향, 닉네임, 생사 상태, 팀, 체력, 궁 etc)
→ 메모리 내부 구조체 or 클래스 형태로 저장
→ 메모리 내 포인터 트레이싱 해야함
메모리를 어떻게 읽는가?
- externel, 외부 접근 방식
- windows에서 제공하는 API를 이용해 구현 가능
- 구현 방법
- 유저 모드 API 사용
- 게임 프로세스의 Process ID 구하기 → OpenProcess()로 프로세스 열어서 Process Handle 획득 → 얻은 Process Handle 이용해 ReadProcessMemory()로 게임 프로세스의 메모리 읽음
- 커널 모드 API 사용
- 유저 모드 API 사용
- 특징
- 메모리 읽을 때마다 API 호출 → 속도 느림
- 탐지 가능성 大
- Game 구조체의 주소로부터 각 플레이어의 위치 구하는 코드 예시(externel)
struct Game game;
uint64_t game_addr = ;
// Mem.Read(uint64_t address, size_t length, void* output);
Mem.Read(game_addr, sizeof(struct Game), &game);
for(uint64_t i; i < 4; i++){
struct Player player;
uint64_t player_addr = (uint64_t)game.players[i];
Mem.Read(player_addr, sizeof(struct Player), &player);
printf(
"player %d: pos(%f, %f, %f)\n",
i, player.x, player.y, player.z
);
}
- internel, 내부 접근 방식
- DLL Injection or Code Injection → 게임 프로세스 내부에서 임의의 코드 실행 해서 메모리에 접근하는 방법 사용
- 특징
- 핵 프로세스==게임 프로세스로 취급 → 게임 메모리 접근 용이
- 메모리 읽을 시 포인터로 접근 → 개발 용이
- 메모리 접근 속도 빠름
- Game 구조체의 주소로부터 각 플레이어의 위치를 구하는 코드(internel)
uint64_t game_addr = ;
struct Game *game = (Game*)game_addr;
for(uint64_t i; i < 4; i++){
printf(
"player %d: pos(%f, %f, %f)\n",
i,
game->player[i]->x, game->player[i]->y, game->player[i]->z
);
}
어떻게 물체의 화면 좌표를 구해야 하는가?
위에서 예시 코드에서 얻은 좌표 == 월드 좌표(3D) ≠ 화면 좌표(2D)
3D → 2D: World to Screen
좌측에 눈 = 카메라 (물체를 보기 위한 것)
→ 물체는 카메라를 기준으로 보이게 됨 (카메라 시점에서..)
→ 실제 위치가 어떻게 보일지를 의미하는게 아님
→ 그래서 변환해야된다. 화면좌표로
변환 방법
- Direct 3D 함수를 이용한 좌표 변환
- D3D 함수를 호출해 필요한 값을 얻을 수 있음 → 개발 편리
- D3D 함수를 사용하기 위해 DLL 인젝션 必 → 보통 internel로 구현
D3D함수는 D3DXVec3Project() 이용 (3D→2D 변환하는 핵심 함수)
D3DXVECTOR3* D3DXVec3Project(
_Inout_ D3DXVECTOR3 *pOut, ##화면좌표로변화되어저장
_In_ const D3DXVECTOR3 *pV, ##월드좌표가
_In_ const D3DVIEWPORT9 *pViewport,
_In_ const D3DXMATRIX *pProjection,
_In_ const D3DXMATRIX *pView,
_In_ const D3DXMATRIX *pWorld ##하위4개는 GetViewport(), GetTransform()함수로 얻음 -> pDevice필요함
##EndScene()이나 DIP Hook 등으로 pDevice 얻음 → Wall Hack에서 설명
);
typedef struct D3DVIEWPORT9 { ##단순연산->디바이스 인스턴스 필요없음
DWORD X;
DWORD Y;
DWORD Width;
DWORD Height;
float MinZ;
float MaxZ;
} D3DVIEWPORT9, *LPD3DVIEWPORT9;
HRESULT GetViewport(
D3DVIEWPORT9 *pViewport ##게임화면의 크기, 위치 등의 값
);
HRESULT GetTransform( ##행렬구함
D3DTRANSFORMSTATETYPE State, ##행렬 타입 지정
D3DMATRIX *pMatrix
);
필요한 인자 다 구한 후 함수 호출해 월드 좌표를 화면 좌표로 변환
void WorldToScreen(
D3DXVECTOR3 *world_pos, // input
D3DXVECTOR3 *screen_pos // output
){
D3DVIEWPORT9 vp;
D3DXMATRIX proj_matrix, view_matrix, world_matrix;
pDevice->GetViewport(&vp);
pDevice->GetTransform(D3DTS_VIEW, &view_matrix);
pDevice->GetTransform(D3DTS_PROJECTION, &proj_matrix);
pDevice->GetTransform(D3DTS_WORLD, &world_matrix);
D3DXVec3Project(screen_pos, world_pos, &vp,
&proj_matrix, &view_matrix, &world_matrix);
}
- 행렬을 이용한 좌표 변환
- 좌표 수동 계산 必 → 좌표 변환에 필요한 값 직접 찾아야 함
- 위에 값 얻을 수 있으면 externel 구현 가능
2.1. View Matrix를 이용한 좌표 변환
- View Matrix 값 찾아야함 (변환행렬, 월드좌표*View Matrix = 카메라 좌표계)
- Matrix 변환에 필요한 값 이미 있음 → 계산 간단
- 카메라 위치값 안구해도 됨
그렇다면 View Matrix 어떻게 구하는가? → D3D의 경우, D3DXMatrixLookAtLH() 후킹 or 메모리 스캔
구하고 나면 곱해서 카메라 좌표를 알 수 있는데 이때 xyz뿐만이 아니라 w도 뜬다,
w는 동차 좌표계로 좌표의 차원을 1차원 증가해서 표현하는 방식
x, y, z -> x, y, z, 1 // 일반 좌표계 -> 동차 좌표계
x, y, z, w -> x/w, y/w, z/w // 동차 좌표계 -> 일반 좌표계
편해서 씀 → 자세한건 컴퓨터 그래픽스 관련
이렇게 구한건 카메라 좌표계 → 다시 일반 좌표계(xyz)로 변환한 뒤에 원근법 적용해야됨
→ 삼각함수 계산(D3DXMatrixPerspectiveFovLH() 함수로 가능, 근데 시야각이랑 화면 가로세로 길이 필요, 이때 FOV가 리버싱해서 어디에 저장도는지 알아야한다.)
2.2. 카메라의 방향을 이용한 좌표 변환
- 카메라의 방향(Angle)과 위치(Location)만으로 계산 가능
- 계산 과정 비교적 복잡
- 컴퓨터 그래픽스 기초 지식 要
물체를 어떻게 사용자에게 보여주는가?
화면상으로 변환한 좌표를 적적한 형태로.. 구현하려면?
→ GDI, DirectX, OpenGL etc..
DirectX
- EndScene함수 후킹
- 렌더링 함수임
- 장정: D3D기능 활용해 편리하게 그릴수 있음
- 단점: 게임 내 화면에 덮어 그려 게임 화면이 게임사에 모니터링 될 시 탐지 가능
- Overlay
- D3D 창 생성해 게임과 겹쳐보이도록 하는 기법
- D3D이용하지만 투명 창 생성하는걸로 앞에랑 차이
- 별도 디바이스로 그려지니 모니터링 회피 가능
hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, "", WS_POPUP, rc.left, rc.top, s_width, s_height, NULL, NULL, wc.hInstance, NULL);
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 0, ULW_COLORKEY);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
이런식으로 생성하는데 속성값 바꿔서 투명하게 만들 수 있음
창 생성 이후에 D3D로 그리면 되는데 이때 BackBufferFormat 를 투명도를 지원하는 포맷으로 설정
Bone ESP
3D모델 뼈 형태 보여주는 거
보통 ESP가 점이나 박스형태라 3D 환경에서 충동 구현이 어려우니 뼈모양으로 보이게 하는 것
보통 메모리 상에서 플레이서 구조체 근처에 뼈의 정보가 있을 확률 大
→ 뼈는 캐릭터 고정되어있어도 액션따라서 달라질 수 있어서 부위 식별해야됨
→ 뼈마다 고유 숫자 붙는데 이 숫자 뼈 위치에 그리는게 Bone Index Logger
→ 그거 가지고 뼈끼리 이어버리면 Bone ESP가 됨
'학회_공부해요 > 기술_스터디' 카테고리의 다른 글
[S2W] Detailed Analysis of DocSwap Malware Disguised as Security Document Viewer (0) | 2025.04.06 |
---|---|
[ESTsecurity] Security Report 2025.03 보안동향보고서 (0) | 2025.04.01 |
[IGLOO] 공격 표면 관리(ASM)란 무엇인가요? (0) | 2025.03.23 |
[Theori] 게임핵의 원리(1) - Wall Hack (0) | 2025.02.19 |
[SAMSUNG SDS] 딥시크(DeepSeek)가 촉발한 AI 시장의 지각 변동 (1) | 2025.02.11 |