|
| 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 | +} |
0 commit comments