우선 예제 코드들
1. 우선 기본 jump table(switch)을 보기위한 jt_basic.c
#include <stdio.h>
#include <stdlib.h>
__attribute__((noinline))
int basic_switch(int x) {
switch (x) {
case 0: return 11;
case 1: return 22;
case 2: return 33;
case 3: return 44;
case 4: return 55;
default: return -1;
}
}
int main(int argc, char *argv[]) {
int x = 2;
if (argc > 1) {
x = atoi(argv[1]);
}
int r = basic_switch(x);
printf("basic_switch(%d) = %d\n", x, r);
return 0;
}
2. 인덱스 보정 + default + case 병합 유도를 위한 jt_advaced.c
#include <stdio.h>
#include <stdlib.h>
__attribute__((noinline))
int advanced_switch(int opcode) {
// 인덱스 보정: case 범위를 10~16으로 이동
switch (opcode) {
case 10: return 100;
case 11: return 110;
case 12: return 120;
case 13: return 120; // 일부러 12와 동일 (case 병합 가능성)
case 14: return 140;
case 15: return 150;
case 16: return 150; // 일부러 15와 동일
default: return -999;
}
}
int main(int argc, char **argv) {
int op = (argc > 1) ? atoi(argv[1]) : 13;
int r = advanced_switch(op);
printf("advanced_switch(%d) = %d\n", op, r);
return 0;
}
3. 명시적 indirect jump(함수 포인터 디스패치)
#include <stdio.h>
#include <stdlib.h>
typedef int (*handler_t)(int);
__attribute__((noinline)) int h0(int v) { return v + 1; }
__attribute__((noinline)) int h1(int v) { return v + 10; }
__attribute__((noinline)) int h2(int v) { return v + 100; }
__attribute__((noinline)) int h3(int v) { return v + 1000; }
__attribute__((noinline))
int dispatch(int idx, int v) {
static handler_t table[] = { h0, h1, h2, h3 };
if (idx < 0 || idx >= (int)(sizeof(table)/sizeof(table[0])))
return -1;
// 간접 호출 (indirect call/jump 패턴 관찰 가능)
return table[idx](v);
}
int main(int argc, char **argv) {
int idx = (argc > 1) ? atoi(argv[1]) : 2;
int v = (argc > 2) ? atoi(argv[2]) : 7;
int r = dispatch(idx, v);
printf("dispatch(idx=%d, v=%d) = %d\n", idx, v, r);
return 0;
}
참고로 이대로 컴파일하면 최적화 돼서 날아가 버릴 가능성이 매우매우매우 높기때문에
__attribute__((noinline))를 추가해서 함수 인라인 최적화를 막아 놓았다. 제어흐름을 조금 더 잘 보려고 추가했다.
gcc/Clang에서 쓰이는 거니까 gcc를 사용해서 해주도록 하자.
gcc -std=c11 -Wall -Wextra -O2 -g -o jt_basic.exe jt_basic.c
gcc -std=c11 -Wall -Wextra -O2 -g -o jt_advanced.exe jt_advanced.c
gcc -std=c11 -Wall -Wextra -O2 -g -o indirect_dispatch.exe indirect_dispatch.c
-Wall // 경고 활성화
-Wextra // 더 많은 경고 활성화
-O2 // 최적화 수준 2, O0로 할 경우 if-else 체인으로만 가득 채울 수 있어서, 실무 바이너리와 더 유사하게 확인하기 위해 설정
-g // 디버깅 심볼 포
기본 jump table(jt_basic.exe) 분석


상단의 display symbole tree를 누르면 화면 좌측 중단에 트리구조가 나오는데, 거기서 Functions → basic_switch로 이동
그러면 아래같은 디컴파일 코드가 나온다 짜잔
/* WARNING: Unknown calling convention */
int basic_switch(int x)
{
int iVar1;
iVar1 = -1;
if ((uint)x < 5) {
iVar1 = *(int *)(&_CSWTCH.3 + x * 4);
}
return iVar1;
}

대략 해석하자면 아래와 같은 의미로 볼 수 있다.
- iVar1 = -1 //default
- if ((uint))x < 5) // case 유효 범위 체크 0-4
- *(int *)(&_CSWTCH.3 + x * 4); // 점프 테이블/ 케이스 값 테이블 인덱싱
jmp가 나오지 않은 걸로 봐서 최적화가 발생한 것으로 보인다.
CSWTCH는 점프대상 테이블이 아니라 11,22,33,44,55(반환값)를 담고 있는 값테이블(value lookup table)이다.
어쨌든 테이블 비스무리한 걸 찾았으니까 이걸로 일단 계속 진행
위의 CSWTCH를 symbol tree의 Labels 항목에서 찾을 수 있다(눈 빠지게 직접 찾지 말고 검색창이라는 문명의 이기를 사용하자)
그걸 더블클릭하면 listing 창에서 아래와 같은 값을 찾을 수 있다.

4바이트 little-endian, int이니까,
0b 00 00 00 = 0x0000000b = 11
16 00 00 00 = 0x00000016 = 22 ... 와 같이 해석해서 테이블 값을 해석할 수 있다.
이렇게 보면 좀 머리 아프고 생각하기 싫으니까

data → pointer or int 로 바꾸면 깔끔하게 볼 수 있다.

테이블들은 이런식으로 복구하면 될 듯하다.
인덱스 보정 + default + case 병합 유도 (jt_advanced.exe) 분석
위와 같은 방식으로 쭉 확인하면
/* WARNING: Unknown calling convention */
int advanced_switch(int opcode)
{
int iVar1;
iVar1 = -999;
if (opcode - 10U < 7) {
iVar1 = *(int *)(&_CSWTCH.4 + (opcode - 10U) * 4);
}
return iVar1;
}
요런 코드를 확인할 수 있다.
동일한 방식으로 CSWTCH.4를 열어보면

switch문에서 연산을 하고 인덱스 간의 거리로 값을 확인하는 방식을 잘 활용할 수 있다.

또한, 10진수 값 { 100, 110, 120, 120, 140, 150, 150 }인걸로 봐서 최적화는 일어나지 않은 정상적인(?) 상황인 것으로 보인다.
명시적 Indirect Jump(indirect_dispatch.exe) 분석
테이블을 찾는 것이 중요한데, symbol tree에서 검색해서 찾아보자면, 아래와 같이 존재하는 것을 확인할 수 있다.

004014d1에서 확인해 보자면,

이런 구조임을 확인할 수 있다. EAX는 원본 코드의 테이블의 인덱스로 들어가는 idx임으로
jmp [레지스터 + 함수::table+...]의 구조로 indrirect jump가 발생하는 것을 알 수 있다.
옆에 있는 0041460(테이블의 시작 주소, 옆에 흰 글씨로 적혀있음)으로 가면, 아래와 같이 h0로 이어짐 또한 확인할 수 있다.

디컴파일 된걸로만 보면

위와 같이 단순하게 최적화가 된 걸로 보이지만, 어셈블리와 p-code로 표현된 값들을 보면 return 부분에 함수들이 들어가있는 형태임도 추정이 가능하다.
세줄요약
1. 일단 Symbol Tree를 켜서
2. 'table'이나, 'SWTCH', '함수명'::'table이름' 같은 키워드들을 검색하여 테이블이 구성되어있는지 확인
3. 없다면 'jmp [레지스터 +... :: ...]'와 같은 구조가 있는지 확인
'졸업프로젝트' 카테고리의 다른 글
| 16차 : High P-code와 HighFunction 계층 구조 (0) | 2026.06.01 |
|---|---|
| 15차 전원회의 - p-code operator를! 공부해보자 (1) | 2026.05.16 |
| 업데이트 업데이트업데이트없데이트 (1) | 2026.04.11 |
| 자료조사 자료조사 사조료자 랑 테스트 테스트 트스테 (0) | 2026.04.05 |
| 자료 조사랑 런처2.0 제작을 해보자 (0) | 2026.03.30 |