---
title: "Win32 윈도우 프로시저에 다섯 번째 인자를 JIT으로 끼워넣기"
published: 2025-12-13T23:39:48.000Z
canonical: https://jeff.news/article/825
---
# Win32 윈도우 프로시저에 다섯 번째 인자를 JIT으로 끼워넣기

Win32 WNDPROC에 컨텍스트 포인터를 전달하는 방법이 마땅치 않은 문제를, JIT 컴파일된 트램폴린으로 다섯 번째 인자를 추가해 해결하는 기법을 소개.

- Win32 윈도우 프로시저(WNDPROC)에는 컨텍스트 포인터를 전달할 방법이 없음. 네 개의 파라미터가 전부 Win32가 결정하기 때문. 그래서 보통 글로벌 변수를 쓰거나(지저분함), GWLP_USERDATA에 포인터를 붙이는 방식을 쓰는데(셋업이 번거로움), 이 글에서는 JIT 컴파일된 트램폴린으로 **다섯 번째 인자를 추가**하는 방법을 소개함

- 핵심 아이디어: x64 호출 규약에서 처음 네 인자는 레지스터로, 나머지는 스택으로 전달됨. 트램폴린이 새로운 스택 프레임을 만들고 다섯 번째 인자(컨텍스트 포인터)를 스택에 올린 뒤, 원래 네 파라미터는 건드리지 않고 확장된 Wndproc5를 호출하는 구조임

## 실행 가능한 메모리 할당의 진화

- 예전에는 VirtualAlloc(또는 mmap)으로 실행 가능 메모리를 할당했는데, 이러면 코드와 데이터에서 2GB 이상 떨어질 수 있어서 상대 주소 지정이 안 됨. 이번에는 **로더에게 쓰기+실행 가능한 메모리 블록을 요청**하는 방식을 씀

- GNU 스타일 어셈블리로 `.exebuf`라는 새 섹션을 정의해서 2MB의 쓰기·실행 가능 메모리를 런타임에 할당함. `.bss`처럼 동작하되 실행 가능하다는 점만 다름. 이 메모리가 로드된 이미지의 일부이므로 코드·데이터와 가까이 있어서 small code model을 가정할 수 있음

## 트램폴린 구현

- `make_wndproc` 함수가 Wndproc5와 컨텍스트 포인터를 받아서 일반 WNDPROC을 반환함. 어셈블리가 하는 일은 의외로 간단: 새 스택 프레임 할당 → callee shadow space 확보 → 다섯 번째 인자 저장 → 원래 파라미터 그대로 두고 call. 패치해야 할 곳은 컨텍스트 포인터와 32비트 상대 주소 두 군데뿐

- 컨텍스트 포인터는 나중에 업데이트도 가능하고, 서로 다른 컨텍스트로 여러 프로시저를 만들 수도 있음. Control Flow Guard가 활성화된 상태에서도 동작한다는 게 좀 놀라웠다고 함

- 저자도 인정하듯이 GWLP_USERDATA보다 작업량이 많고, 실제 프로그램에서 윈도우 프로시저 수는 보통 하나뿐이라 최적의 예시는 아님. 진짜 실용적인 용도는 **약한 커스텀 얼로케이터 인터페이스를 우회**하는 것 — 런타임에 특정 할당 영역에 바인딩된 할당 함수를 만들 수 있음

## 핵심 포인트

- x64 호출 규약을 이용해 트램폴린이 스택 프레임을 만들고 다섯 번째 인자를 추가
- 로더에게 쓰기+실행 가능 메모리를 요청하여 small code model 유지
- Control Flow Guard가 활성화된 상태에서도 동작
- 실용적 용도는 약한 커스텀 얼로케이터 인터페이스 우회

## 인사이트

C에서 클로저를 만드는 로우레벨 테크닉의 진화. 실무보다는 시스템 프로그래밍의 깊은 이해를 보여주는 글.
