
Winbindex를 사용하여 모든 Windows 빌드의 모든 시스템 함수에 대한 범용 서명 생성
Windows PE 함수용 크로스 버전 바이너리 시그니처 및 RVA 오프셋
pe-signgen는 리버스 엔지니어와 보안 연구자를 위한 도구로, 다음 항목을 자동으로 생성합니다:
핵심 아이디어는 Windows 10/11 빌드 전반에 걸쳐 체계적이고 견고한 방식으로 내보내지지 않은 함수에 접근하는 것입니다. 이를 위해 다음을 활용합니다:
⚠️ Windows 버전 지원
pe-signgen은 Windows 10 및 Windows 11만 지원합니다. 이는 의도적인 설계 선택입니다: Winbindex는 이전 버전에 대한 완전한 데이터를 제공하지 않습니다.
내보내지지 않은 Windows 내부 함수
LdrpInitializeTls, RtlpInsertInvertedFunctionTableEntry 등과 같은 함수에 대한 시그니처를 생성합니다.
게임 해킹 / 안티치트 연구 게임 업데이트에도 유지되는 안정적인 시그니처 생성
보안 연구 Windows 빌드 전반에서 보안에 중요한 루틴 찾기
자동화 내부 API 전체 집합에 대한 스크립트 가능한 시그니처 및 오프셋 생성
pe-signgen은 다양한 사용 사례를 위해 세 가지의 서로 다른 출력 형식을 제공합니다:
자동화, 스크립팅 및 다른 도구와의 통합을 위한 구조화된 데이터입니다.```bash pe-signgen --signature ntdll!NtCreateFile -o ntcreatefile.json --output-format json
**출력 구조:**```json
{
"dll_name": "ntdll",
"function_name": "NtCreateFile",
"architecture": "x64",
"generated": "2024-12-11T15:30:00.123456",
"total_builds": 1247,
"unique_signatures": 3,
"signature_groups": [
{
"matched_symbol": "NtCreateFile",
"signature": "4C 8B DC 49 89 5B 08 49 89 6B 10 49 89 73 18 ...",
"length": 48,
"build_count": 845,
"versions": [
{ "major": 10240, "minor": 16384, "build": "10240.16384" },
{ "major": 10586, "minor": 0, "build": "10586.0" }
]
}
]
}
Notes:
major 및 minor는 빌드 문자열을 첫 번째 .에서 분할하여 파생됩니다.
예: "10240.16384" → major = 10240, minor = 16384.build는 내부적으로 사용되는 원래 빌드 문자열 키입니다.임베디드 시스템과 낮은 오버헤드 스캐닝에 최적화된 컴팩트하고 런타임 준비가 된 바이너리 형식입니다.
이 형식들은 write_wsig() 및 write_woff()에 구현된 디스크 레이아웃과 일치합니다.
매직: WSO\0 (0x57 0x53 0x4F 0x00)
현재 버전: 1
목적: 와일드카드 마스크 및 관련 Windows 빌드 버전과 함께 바이너리 서명을 저장합니다.
┌─────────────────────────────────────┐ │ Header (36 bytes) │ ├─────────────────────────────────────┤ │ DLL Name (variable) │ ├─────────────────────────────────────┤ │ Function Name (variable) │ ├─────────────────────────────────────┤ │ Signature / Mask / Build blobs │ ← Arbitrary order, see notes ├─────────────────────────────────────┤ ← Aligned to 4 bytes │ Groups Table (24 × N bytes) │ └─────────────────────────────────────┘
**중요한 레이아웃 참고 사항(`write_wsig`와 일치)**
* 헤더 다음에는 DLL 및 함수 이름이 UTF‑8 바이트로 기록됩니다.
* 각 서명 그룹에 대해 패턴 바이트와 마스크 바이트가 기록된 다음 해당 그룹의 빌드 배열이 이어집니다.
* 이러한 그룹별 영역은 유형별로 전역적으로 그룹화되지 **않습니다**: 패턴, 마스크 및 빌드 배열이 서로 인터리브될 수 있습니다.
* 빌더는 각 빌드 배열 앞과 그룹 테이블 앞에서 **4바이트**로 정렬합니다. 이로 인해 패딩이 발생할 수 있습니다.
* 소비자는 헤더 및 그룹 항목의 오프셋을 **항상** 따라야 하며, 물리적 연속성에 대해 개념 다이어그램에 의존하지 **마십시오**.
##### 헤더 레이아웃(36바이트)```c
// Packed as: "<4sIIIIIIII" (little-endian)
typedef struct {
char magic[4]; // "WSO\0" (WSIG_MAGIC)
uint32_t version; // FORMAT_VERSION (currently 1)
uint32_t arch; // Architecture code (1=x64, 2=ARM64, 3=WoW64)
uint32_t dll_off; // Offset to DLL name string
uint32_t dll_len; // Length of DLL name in bytes
uint32_t func_off; // Offset to function name string
uint32_t func_len; // Length of function name in bytes
uint32_t group_count;// Number of signature groups
uint32_t groups_off; // Offset to groups table
} wsig_header_t; // 36 bytes
각 시그니처 그룹은 하나 이상의 Windows 빌드에 적용되는 고유한 패턴을 나타냅니다.```c // Packed as: "<IIIIII" (little-endian)
typedef struct { uint32_t sig_off; // Offset to signature pattern bytes uint32_t sig_len; // Length of signature pattern (in bytes) uint32_t mask_off; // Offset to wildcard mask bytes uint32_t mask_len; // Length of wildcard mask (≈ ceil(sig_len/8)) uint32_t builds_off; // Offset to build version array uint32_t build_cnt; // Number of builds using this signature } wsig_group_t; // 24 bytes
##### 빌드 버전 항목 (8바이트)
각 빌드 항목은 이 서명을 사용하는 특정 Windows 버전을 식별합니다.```c
typedef struct {
uint32_t major; // e.g. 19041
uint32_t minor; // e.g. 1234
} wsig_build_t; // 8 bytes
major와 minor는 빌드 문자열("A.B" → A, B)을 분할하여 얻습니다. 원본 빌드 문자열은 바이너리 형식에 저장되지 않습니다. 필요하다면 외부에 보관하십시오 (JSON 출력에 있습니다).
마스크는 비트마스크로, 각 비트는 시그니처 패턴의 한 바이트에 대응합니다:
예시:``` Signature: 4C 8B DC 49 89 ?? 08 49 Mask bits: 1 1 1 1 1 0 1 1 (MSB first within each byte) Mask byte: 0xBF (binary: 10111111)
마스크 바이트는 각 바이트 내에서 **리틀 엔디안 비트 순서**로 저장되고 해석됩니다 (C 헬퍼 및 `parse_signature`에서 사용되는 것과 정확히 동일함):```c
uint8_t bit = (mask_bytes[byte_index >> 3] >> (byte_index & 7)) & 1u;
*_len을 사용하고, 그 이상 읽지 마십시오.매직: WOF\0 (0x57 0x4F 0x46 0x00)
현재 버전: 1
목적: Windows 빌드 전반에 걸쳐 함수에 대한 직접 RVA 및 파일 오프셋 저장
┌─────────────────────────────────────┐ │ Header (36 bytes) │ ├─────────────────────────────────────┤ │ DLL Name (variable) │ ├─────────────────────────────────────┤ │ Function Name (variable) │ ├─────────────────────────────────────┤ │ Matched Symbol Names (variable) │ ← One UTF‑8 string per entry ├─────────────────────────────────────┤ ← Aligned to 4 bytes │ Entries Table (32 × N bytes) │ └─────────────────────────────────────┘
레이아웃 세부 정보(`write_woff` 일치):
* 헤더 자리 표시자 뒤에 DLL 및 함수 이름이 UTF‑8 바이트로 작성됩니다.
* 각 빌드에 대해 일치하는 심볼 이름이 UTF‑8 문자열(종결자 없음)로 작성됩니다. 이들은 단순 문자열 풀을 형성합니다.
* 그런 다음 작성자가 4바이트로 정렬하고 고정 크기 항목 테이블을 작성합니다.
* 각 항목에는 이 문자열 풀을 가리키는 오프셋(`matched_off`, `matched_len`)이 포함됩니다.
##### 헤더 레이아웃(36바이트)```c
// Packed as: "<4sIIIIIIII" (little-endian)
typedef struct {
char magic[4]; // "WOF\0" (WOFF_MAGIC)
uint32_t version; // FORMAT_VERSION (currently 1)
uint32_t arch; // Architecture code (1=x64, 2=ARM64, 3=WoW64)
uint32_t dll_off; // Offset to DLL name string
uint32_t dll_len; // Length of DLL name in bytes
uint32_t func_off; // Offset to function name string
uint32_t func_len; // Length of function name in bytes
uint32_t entry_cnt; // Number of offset entries
uint32_t entries_off;// Offset to entries table
} woff_header_t; // 36 bytes
각 항목은 Windows 빌드를 해당 빌드에서의 함수 위치에 매핑합니다.```c // Packed as: "<IIQQII" (little-endian)
typedef struct { uint32_t major; // Windows major version (e.g., 19041) uint32_t minor; // Windows minor version (e.g., 1234) uint64_t rva; // Relative Virtual Address in the DLL uint64_t file_offset; // Raw file offset in the DLL on disk uint32_t matched_off; // Offset to matched symbol name string uint32_t matched_len; // Length of matched symbol name } woff_entry_t; // 32 bytes
##### 사용 참고 사항
* **RVA**는 DLL이 선호 기본 주소에 로드될 때의 메모리 오프셋입니다.
* **File offset**은 디스크의 PE 파일에서 원시(raw) 위치입니다.
* **Matched symbol**은 요청된 함수와 다를 수 있습니다(예: 전달된 내보내기(forwarded exports)).
문자열은 문자열 풀에 한 번 저장되며, `matched_off`/`matched_len`이 이를 참조합니다.
* 항목은 빠른 조회를 위해 **빌드 버전**(`major`, 그다음 `minor`) 기준으로 정렬됩니다.
---
#### 아키텍처 코드
두 바이너리 형식 모두 동일한 아키텍처 인코딩(`ARCH_CODE_MAP` 사용)을 사용합니다:
| Code | Architecture | Description |
| ---- | ------------ | ---------------------------- |
| 1 | x64 | 64비트 AMD64/Intel64 |
| 2 | ARM64 | 64비트 ARM(AArch64) |
| 3 | WoW64 | 64비트 Windows의 32비트 x86 |