반응형
1. 개요
✅ 표준 헤더 파일이란?
<stdio.h>, <stdlib.h>, <string.h> 같은 헤더 파일은 C 표준 라이브러리의 일부이다.
이 파일들에는 함수 원형(prototypes), 매크로 정의, 구조체 선언, 상수 등이 정의되어 있다.
실제 구현은 .c나 .o 파일에 들어 있고, 컴파일 할 때 연결됩니다.
// stdio.h 내부 일부 예시
int printf(const char *, ...);
int scanf(const char *, ...);
FILE *fopen(const char *filename, const char *mode);
🛠 사용자 정의 헤더 파일 만드는 법
일반적으로는 아래와 같이 정의합니다.
- .h 파일에는 선언만
- .c 파일에는 구현
예시(main.c, hacker.h)
//main.c
#include "hacker.h"
int main(){
hacker* qwertyou = new_hacker();
printf("%s's age is %d\n",qwertyou->name, qwertyou->age);
printf("%s's level is %d\n",qwertyou->name, qwertyou->level);
printf("\n\n-----qwertyou's skill list-----\n");
for(int i=0;i<qwertyou->skill_num;i++){
printf("%s's skill%d : %s\n",qwertyou->name, i, qwertyou->skill_list[i]);
}
}
//hacker.h
#pragma once
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct hacker{
char name[50];
unsigned int age;
char** skill_list;
int skill_num;
int level;
}hacker;
int input_name(hacker*);
int input_age(hacker*);
int input_new_skill(hacker*);
void increase_size(char***);
hacker* new_hacker() {
hacker* new_ptr = (hacker*)malloc(sizeof(hacker));
memset(new_ptr->name, '\0', sizeof(new_ptr->name));
new_ptr->skill_num = 0;
new_ptr->level = 1;
new_ptr->skill_list = (char**)malloc(sizeof(char*));
input_name(new_ptr);
input_age(new_ptr);
input_new_skill(new_ptr);
return new_ptr;
}
int input_name(hacker* ptr){
write(1,"Input the name : ",17);
return scanf("%s",ptr->name);
}
int input_age(hacker* ptr){
printf("Input the age : ");
return scanf("%u",&ptr->age);
}
int input_new_skill(hacker* ptr){
write(1, "Input new Skill : ", 18);
char buf[50] = {0,};
scanf("%s",buf);
char* tmp = (char*)malloc(strlen(buf)+1);
strcpy(tmp,buf);
ptr->skill_num++;
increase_size(&(ptr->skill_list), ptr->skill_num - 1);
ptr->skill_list[ptr->skill_num-1] = tmp;
return ptr->skill_num;
}
void increase_size(char*** skill_list, int current_size) {
char** new_ptr = (char**)malloc(sizeof(char*) * (current_size + 1));
for (int i = 0; i < current_size; i++) {
new_ptr[i] = (*skill_list)[i];
}
free(*skill_list);
*skill_list = new_ptr;
}
1. 리버싱과의 연관성
(1) 헤더 파일은 '정의된 세계'의 지도다
리버싱 입장에서 실행 파일을 분석할 때 가장 먼저 하는 일 중 하나는:
- “이 함수가 어떤 구조체를 쓰고 있는지”
- “이 포인터가 가리키는 게 어떤 데이터인지”
- “이 함수가 실제 어떤 기능을 수행하는지”
이걸 빠르게 이해하려면?
👉 결국 원래 소스 코드의 정의부(header) 를 추측하거나 유추해야 한다.
(2) 구조체(struct)는 메모리의 청사진이다.
typedef struct hacker {
char name[50]; // +0
unsigned int age; // +50
char** skill_list; // +54
int skill_num; // +58
int level; // +62
} hacker;
이건 컴파일된 후 메모리에 연속적인 구조로 저장됩니다.
Cheat Engine이나 x64dbg로 이걸 역추적할 때 다음과 같은 상황이 생길 수 있습니다.
- 어떤 주소에서 "qwertyou"라는 문자열이 발견.
- 바로 옆 주소에서 int: 30, 포인터: 0x123456, 또 다른 int: 1이 나옴.
- 👉 이걸 보면, 리버서 입장에서는 이건 구조체처럼 생겼다고 추정하게 됨.
즉, 구조체의 정의(=헤더 파일 내용)를 잘 알고 있으면
- 디스어셈블된 코드나 메모리 덤프에서 데이터 해석이 훨씬 쉬워짐.
- 그 반대로, 헤더 구조를 모르면 메모리 덤프를 보고도 뭔지 도무지 모름.
(3) 함수 선언은 시그니처 분석에 도움 된다
int input_name(hacker*);
이 함수는 구조체 포인터를 받아 name을 채웁니다. 하지만 바이너리에서는 아래와 같이 작동합니다.
mov rdi, rax ; 구조체 주소를 rdi에 넣음
call input_name
이런 식으로 보이기 때문에, 함수의 인자와 리턴 타입이 헤더에 정의된 시그니처가 없으면 어셈블리만 보고 추론하는 데 훨씬 더 많은 시간이 걸립니다.
(4) 실제 리버싱 예시 - 게임 해킹
예를 들어 너가 어떤 RPG 게임을 리버싱한다고 해보겠습니다.
- .text 영역에서 플레이어 관련 함수들을 발견.
- PlayerStruct 비슷한 주소를 찾았는데 뭔가 name, HP, level이 있는 것 같다고 추정.
그럼 내부적으로 이렇게 추정하게 됩니다.
typedef struct Player {
char name[32];
int hp;
int level;
float x, y, z;
...
} Player;
이건 마치 hacker.h 구조체를 거꾸로 유추하는 과정입니다. 즉, 리버서가 항상 하는 일이 바로 ‘헤더를 상상’하는 작업입니다.
(5) 결론: 헤더 파일을 공부해야 하는 이유 (리버싱 관점)
이유 | 설명 |
📍 메모리 구조 이해 | 구조체 정의는 메모리 해킹/치트 엔진 분석의 핵심 |
🔍 함수 분석 | 함수 원형은 디스어셈블된 코드에서 인자/리턴 타입 추정에 필요 |
🧠 추론력 향상 | 리버스 시 '원래 코드의 형태'를 상상하는 데 훈련이 됨 |
💉 후킹/패칭 | 어떤 함수에 인자를 어떻게 넘기고 후킹할지 구조가 있어야 판단 가능 |
⚙️ 리버서의 습관 | 실제로 고급 리버서들은 바이너리를 보면 .h부터 상상해 |
반응형
'게임해킹 > knockon 부트캠프' 카테고리의 다른 글
[1주차 TIL] KnockOn Bootcamp - 스택 & 큐 (0) | 2025.04.15 |
---|---|
[1주차 TIL] KnockOn Bootcamp - 연결리스트 (0) | 2025.04.15 |
1주 - 리눅스 명령어 (0) | 2025.03.07 |
1주 - 칼리 리눅스 세팅 (0) | 2025.03.07 |
1주 - Linux 운영체제란? (0) | 2025.03.05 |