Skip to content

Commit e165535

Browse files
committed
feat(PE): parse TLS directory
1 parent 823b6b7 commit e165535

4 files changed

Lines changed: 144 additions & 1 deletion

File tree

‎mz/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ target_sources(${PROJECT_NAME}
3535
pe/dirs/exports.c
3636
pe/dirs/imports.c
3737
pe/dirs/resources.c
38+
pe/dirs/tls.c
3839
pe/classifier.c
3940
pe/format.c
4041
pe/pe.c

‎mz/pe/dirs/tls.c‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include "tls.h"
2+
#include <inttypes.h>
3+
4+
// Sanity cap on the callback array walk: real binaries have a handful of
5+
// callbacks at most.
6+
// This only exists to keep a corrupted/malicious
7+
// AddressOfCallBacks from spinning far past anything plausible before the
8+
// reader's own EOF handling would eventually stop it.
9+
#define PE_TLS_MAX_CALLBACKS 256
10+
11+
static bool _pe_read_tls_dir32(RDReader* r, PETlsDirectory32* v) {
12+
rd_reader_read_le32(r, &v->StartAddressOfRawData);
13+
rd_reader_read_le32(r, &v->EndAddressOfRawData);
14+
rd_reader_read_le32(r, &v->AddressOfIndex);
15+
rd_reader_read_le32(r, &v->AddressOfCallBacks);
16+
rd_reader_read_le32(r, &v->SizeOfZeroFill);
17+
rd_reader_read_le32(r, &v->Characteristics);
18+
return !rd_reader_has_error(r);
19+
}
20+
21+
static bool _pe_read_tls_dir64(RDReader* r, PETlsDirectory64* v) {
22+
rd_reader_read_le64(r, &v->StartAddressOfRawData);
23+
rd_reader_read_le64(r, &v->EndAddressOfRawData);
24+
rd_reader_read_le64(r, &v->AddressOfIndex);
25+
rd_reader_read_le64(r, &v->AddressOfCallBacks);
26+
rd_reader_read_le32(r, &v->SizeOfZeroFill);
27+
rd_reader_read_le32(r, &v->Characteristics);
28+
return !rd_reader_has_error(r);
29+
}
30+
31+
static void _pe_read_tls_callbacks(RDContext* ctx, PEFormat* pe, RDReader* r,
32+
RDAddress callbacks_va) {
33+
if(!callbacks_va) return; // directory present, no callbacks: not an error
34+
35+
rd_reader_seek(r, callbacks_va);
36+
u32 index = 0;
37+
38+
while(index < PE_TLS_MAX_CALLBACKS) {
39+
RDAddress slot_va = (RDAddress)rd_reader_tell(r);
40+
RDAddress cb_va;
41+
bool ok;
42+
43+
// pe->thunk_size/thunk_type already encode this module's natural
44+
// pointer width (set in pe_set_bits, accounting for the ARM64
45+
// special case)
46+
if(pe->thunk_size == sizeof(u64)) {
47+
u64 v = 0;
48+
ok = rd_reader_read_le64(r, &v);
49+
cb_va = (RDAddress)v;
50+
}
51+
else {
52+
u32 v = 0;
53+
ok = rd_reader_read_le32(r, &v);
54+
cb_va = (RDAddress)v;
55+
}
56+
57+
if(!ok || !cb_va)
58+
break; // read failure or null terminator: done either way
59+
60+
rd_library_type(ctx, slot_va, pe->thunk_type, 0, RD_TYPE_PTR);
61+
62+
// cb_va is already a raw VA (see the note in tls.h)
63+
// pe_norm only, never pe_from_rva.
64+
RDAddress fn_va = pe_norm(ctx, pe, cb_va);
65+
rd_set_function(ctx, fn_va);
66+
rd_placeholder_name(ctx, fn_va,
67+
rd_format("tls_callback_%" PRIu32, index));
68+
69+
index++;
70+
}
71+
}
72+
73+
bool pe_read_tls_dir(RDContext* ctx, PEFormat* pe) {
74+
PEDataDirectory d = pe->data_dirs[PE_DIRECTORY_ENTRY_TLS];
75+
76+
RDAddress va;
77+
if(!pe_from_rva(pe, d.VirtualAddress, &va)) return false;
78+
79+
RDReader* r = rd_get_reader(ctx);
80+
rd_reader_seek(r, va);
81+
82+
RDAddress callbacks_va;
83+
84+
if(pe->bits == 32) {
85+
PETlsDirectory32 tlsdir;
86+
if(!_pe_read_tls_dir32(r, &tlsdir)) return false;
87+
88+
rd_library_type(ctx, va, "PE_TLS_DIRECTORY32", 0, RD_TYPE_NONE);
89+
90+
if(tlsdir.AddressOfIndex) {
91+
rd_library_type(ctx, tlsdir.AddressOfIndex, "u32", 0, RD_TYPE_NONE);
92+
rd_library_name(ctx, tlsdir.AddressOfIndex, "_tls_index");
93+
}
94+
95+
callbacks_va = tlsdir.AddressOfCallBacks;
96+
}
97+
else {
98+
PETlsDirectory64 tlsdir;
99+
if(!_pe_read_tls_dir64(r, &tlsdir)) return false;
100+
101+
rd_library_type(ctx, va, "PE_TLS_DIRECTORY64", 0, RD_TYPE_NONE);
102+
103+
if(tlsdir.AddressOfIndex) {
104+
// The TLS slot index itself is always a 32-bit value, even in
105+
// the 64-bit directory -- that's the OS's per-module TLS
106+
// array index, not a pointer-width quantity.
107+
rd_library_type(ctx, tlsdir.AddressOfIndex, "u32", 0, RD_TYPE_NONE);
108+
rd_library_name(ctx, tlsdir.AddressOfIndex, "_tls_index");
109+
}
110+
111+
callbacks_va = tlsdir.AddressOfCallBacks;
112+
}
113+
114+
_pe_read_tls_callbacks(ctx, pe, r, callbacks_va);
115+
return true;
116+
}

‎mz/pe/dirs/tls.h‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "pe/format.h"
4+
5+
typedef struct PETlsDirectory32 {
6+
u32 StartAddressOfRawData;
7+
u32 EndAddressOfRawData;
8+
u32 AddressOfIndex;
9+
u32 AddressOfCallBacks;
10+
u32 SizeOfZeroFill;
11+
u32 Characteristics;
12+
} PETlsDirectory32;
13+
14+
typedef struct PETlsDirectory64 {
15+
u64 StartAddressOfRawData;
16+
u64 EndAddressOfRawData;
17+
u64 AddressOfIndex;
18+
u64 AddressOfCallBacks;
19+
u32 SizeOfZeroFill;
20+
u32 Characteristics;
21+
} PETlsDirectory64;
22+
23+
bool pe_read_tls_dir(RDContext* ctx, PEFormat* pe);

‎mz/pe/pe.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "pe/dirs/exports.h"
88
#include "pe/dirs/imports.h"
99
#include "pe/dirs/resources.h"
10+
#include "pe/dirs/tls.h"
1011
#include <inttypes.h>
1112
#include <string.h>
1213

@@ -163,6 +164,7 @@ static bool pe_load(RDLoader* ldr, RDContext* ctx) {
163164
pe_read_resources_dir(ctx, pe);
164165
pe_read_exceptions_dir(ctx, pe);
165166
pe_read_debug_dir(ctx, pe);
167+
pe_read_tls_dir(ctx, pe);
166168
pe_read_config_dir(ctx, pe);
167169

168170
if(pe->fileheader.PointerToSymbolTable && pe->fileheader.NumberOfSymbols) {
@@ -189,7 +191,8 @@ static bool pe_load(RDLoader* ldr, RDContext* ctx) {
189191
break;
190192

191193
case PE_CLASS_VISUAL_STUDIO:
192-
rd_analyzer_enable(ctx, "compiler_rtti_msvc");
194+
rd_analyzer_enable(ctx, "compiler_msvc_rtti");
195+
rd_analyzer_enable(ctx, "compiler_msvc_eh");
193196
break;
194197

195198
default: break;

0 commit comments

Comments
 (0)