Rev 6614 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 6614 | Rev 6767 | ||
---|---|---|---|
Line -... | Line 1... | ||
- | 1 | ; Processings of PE format. |
|
- | 2 | ; Works in conjunction with modules.inc for non-PE-specific code. |
|
- | 3 | ||
- | 4 | ; PE-specific part of init_module_struct. |
|
- | 5 | ; Fills fields of MODULE struct from PE image. |
|
- | 6 | macro init_module_struct_pe_specific |
|
- | 7 | { |
|
- | 8 | ; We need a module timestamp for bound imports. |
|
- | 9 | ; In a full PE, there are two timestamps, one in the header |
|
- | 10 | ; and one in the export table; existing tools use the first one. |
|
- | 11 | ; A stripped PE header has no timestamp, so read the export table; |
|
- | 12 | ; the stripper should write the correct value there. |
|
- | 13 | cmp byte [esi], 'M' |
|
- | 14 | jz .parse_mz |
|
- | 15 | cmp [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_EXPORT |
|
- | 16 | jbe @f |
|
- | 17 | mov edx, [esi+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.VirtualAddress] |
|
- | 18 | @@: |
|
- | 19 | mov [eax+MODULE.timestamp], edx |
|
- | 20 | mov edx, esi |
|
- | 21 | sub edx, [esi+STRIPPED_PE_HEADER.ImageBase] |
|
- | 22 | mov [eax+MODULE.basedelta], edx |
|
- | 23 | mov edx, [esi+STRIPPED_PE_HEADER.SizeOfImage] |
|
- | 24 | mov [eax+MODULE.size], edx |
|
- | 25 | ret |
|
- | 26 | .parse_mz: |
|
- | 27 | mov ecx, [esi+3Ch] |
|
- | 28 | add ecx, esi |
|
- | 29 | mov edx, [ecx+IMAGE_NT_HEADERS.FileHeader.TimeDateStamp] |
|
- | 30 | mov [eax+MODULE.timestamp], edx |
|
- | 31 | mov edx, esi |
|
- | 32 | sub edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase] |
|
- | 33 | mov [eax+MODULE.basedelta], edx |
|
- | 34 | mov edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage] |
|
- | 35 | mov [eax+MODULE.size], edx |
|
- | 36 | ret |
|
- | 37 | } |
|
- | 38 | ||
1 | ; Check whether PE module has been loaded at preferred address. |
39 | ; Check whether PE module has been loaded at preferred address. |
2 | ; If not, relocate the module. |
40 | ; If not, relocate the module. |
3 | ; |
41 | ; |
4 | ; in: esi = PE base address |
42 | ; in: esi = PE base address |
5 | ; in: [esp+4] = module name for debug print |
43 | ; in: [esp+4] = module name for debug print |
6 | ; out: CF=1 - fail |
44 | ; out: CF=1 - fail |
7 | proc fixup_pe_relocations uses edi ebp |
45 | proc fixup_pe_relocations c uses ebp, modulename |
8 | ; 1. Fetch some data from PE header or stripped PE header. |
46 | ; 1. Fetch some data from PE header or stripped PE header. |
9 | ; We need: |
47 | ; We need: |
10 | ; * ImageBase - preferred address, compare with esi = actual load address; |
48 | ; * ImageBase - preferred address, compare with esi = actual load address; |
11 | ; ebp will keep the delta |
49 | ; ebp will keep the delta |
12 | ; * RVA and size of fixups directory |
50 | ; * RVA and size of fixups directory |
Line 17... | Line 55... | ||
17 | ; * either the directory has not been created |
55 | ; * either the directory has not been created |
18 | ; * or the module has no fixups (data-only module, for example). |
56 | ; * or the module has no fixups (data-only module, for example). |
19 | ; In the first case, IMAGE_FILE_RELOCS_STRIPPED is set, and this is an error. |
57 | ; In the first case, IMAGE_FILE_RELOCS_STRIPPED is set, and this is an error. |
20 | ; In the second case, IMAGE_FILE_RELOCS_STRIPPED is not set; do nothing. |
58 | ; In the second case, IMAGE_FILE_RELOCS_STRIPPED is not set; do nothing. |
21 | mov ebp, esi |
59 | mov ebp, esi |
22 | cmp word [esi], 'MZ' |
60 | cmp byte [esi], 'M' |
23 | jz .parse_mz |
61 | jz .parse_mz |
24 | sub ebp, [esi+STRIPPED_PE_HEADER.ImageBase] |
62 | sub ebp, [esi+STRIPPED_PE_HEADER.ImageBase] |
25 | jnz @f |
63 | jnz @f |
26 | .nothing: |
64 | .nothing: |
27 | ret |
65 | ret |
Line 31... | Line 69... | ||
31 | cmp [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BASERELOC |
69 | cmp [esi+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BASERELOC |
32 | ja .common |
70 | ja .common |
33 | .norelocs: |
71 | .norelocs: |
34 | test dl, IMAGE_FILE_RELOCS_STRIPPED |
72 | test dl, IMAGE_FILE_RELOCS_STRIPPED |
35 | jz .nothing |
73 | jz .nothing |
- | 74 | ccall loader_say_error, msg_noreloc1, [modulename], msg_noreloc2, 0 |
|
36 | stc |
75 | stc |
37 | ret |
76 | ret |
38 | .parse_mz: |
77 | .parse_mz: |
39 | mov eax, [esi+3Ch] |
78 | mov eax, [esi+3Ch] |
40 | add eax, esi |
79 | add eax, esi |
41 | sub ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.ImageBase] |
80 | sub ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.ImageBase] |
42 | jz .nothing |
81 | jz .nothing |
43 | mov dl, byte [esi+IMAGE_NT_HEADERS.FileHeader.Characteristics] |
82 | mov dl, byte [eax+IMAGE_NT_HEADERS.FileHeader.Characteristics] |
44 | cmp [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BASERELOC |
83 | cmp [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BASERELOC |
45 | jbe .norelocs |
84 | jbe .norelocs |
46 | add eax, IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY |
85 | add eax, IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BASERELOC*sizeof.IMAGE_DATA_DIRECTORY |
47 | .common: |
86 | .common: |
- | 87 | cmp [eax+IMAGE_DATA_DIRECTORY.isize], 0 |
|
- | 88 | jz .norelocs |
|
48 | mov edi, [eax+IMAGE_DATA_DIRECTORY.VirtualAddress] |
89 | mov edi, [eax+IMAGE_DATA_DIRECTORY.VirtualAddress] |
- | 90 | push -1 |
|
- | 91 | push -1 |
|
49 | push [eax+IMAGE_DATA_DIRECTORY.isize] |
92 | push [eax+IMAGE_DATA_DIRECTORY.isize] |
50 | virtual at esp |
93 | virtual at esp |
51 | .sizeleft dd ? |
94 | .sizeleft dd ? |
- | 95 | .next_page_original_access dd ? |
|
- | 96 | .next_page_addr dd ? |
|
52 | end virtual |
97 | end virtual |
53 | add edi, esi |
98 | add edi, esi |
54 | cmp [.sizeleft], 0 |
- | |
55 | jz .norelocs |
- | |
56 | ; 2. We need to relocate and we have the relocation table. |
99 | ; 2. We need to relocate and we have the relocation table. |
57 | ; esi = PE base address |
100 | ; esi = PE base address |
58 | ; edi = pointer to current data of relocation table |
101 | ; edi = pointer to current data of relocation table |
59 | ; 2a. Relocation table is organized into blocks describing every page. |
102 | ; 2a. Relocation table is organized into blocks describing every page. |
60 | ; End of table is defined from table size fetched from the header. |
103 | ; End of table is defined from table size fetched from the header. |
61 | ; Loop 2b-2g over all blocks until no more data is left. |
104 | ; Loop 2b-2i over all blocks until no more data is left. |
62 | .pageloop: |
105 | .pageloop: |
63 | ; 2b. Load the header of the current block: address and size. |
106 | ; 2b. Load the header of the current block: address and size. |
64 | ; Advance total size. |
107 | ; Advance total size. |
65 | ; Size in the block includes size of the header, subtract it. |
108 | ; Size in the block includes size of the header, subtract it. |
66 | ; If there is no data in this block, go to 2g. |
109 | ; If there is no data in this block, go to 2g. |
Line 68... | Line 111... | ||
68 | mov ecx, [edi+IMAGE_BASE_RELOCATION.SizeOfBlock] |
111 | mov ecx, [edi+IMAGE_BASE_RELOCATION.SizeOfBlock] |
69 | sub [.sizeleft], ecx |
112 | sub [.sizeleft], ecx |
70 | add edi, sizeof.IMAGE_BASE_RELOCATION |
113 | add edi, sizeof.IMAGE_BASE_RELOCATION |
71 | sub ecx, sizeof.IMAGE_BASE_RELOCATION |
114 | sub ecx, sizeof.IMAGE_BASE_RELOCATION |
72 | jbe .pagedone |
115 | jbe .pagedone |
- | 116 | push esi |
|
- | 117 | fpo_delta = fpo_delta + 4 |
|
- | 118 | ; 2c. Check whether we have mprotect-ed the current page at the previous step. |
|
- | 119 | ; If so, go to 2e. |
|
- | 120 | cmp [.next_page_addr+fpo_delta], edx |
|
- | 121 | jz .mprotected_earlier |
|
73 | ; 2c. We are going to modify data, so mprotect the current page to be writable. |
122 | ; 2d. We are going to modify data, so mprotect the current page to be writable. |
74 | ; Save the old protection, we will restore it after the block is processed. |
123 | ; Save the old protection, we will restore it after the block is processed. |
75 | ; Ignore any error. |
124 | ; Ignore any error. |
- | 125 | ; Go to 2f after. |
|
76 | PROT_READ = 1 |
126 | PROT_READ = 1 |
77 | PROT_WRITE = 2 |
127 | PROT_WRITE = 2 |
78 | PROT_EXEC = 4 |
128 | PROT_EXEC = 4 |
79 | push esi ecx |
129 | push ecx |
80 | mov eax, 68 |
130 | mov eax, 68 |
81 | mov ebx, 30 |
131 | mov ebx, 30 |
82 | mov ecx, PROT_READ+PROT_WRITE |
132 | mov ecx, PROT_READ+PROT_WRITE |
83 | add edx, esi |
133 | add edx, esi |
84 | mov esi, 0x1000 |
134 | mov esi, 0x1000 |
85 | call FS_SYSCALL_PTR |
135 | call FS_SYSCALL_PTR |
86 | pop ecx |
136 | pop ecx |
- | 137 | jmp .mprotected |
|
- | 138 | ; 2e. We have already mprotect-ed the current page, |
|
- | 139 | ; move corresponding variables. |
|
- | 140 | .mprotected_earlier: |
|
- | 141 | mov [.next_page_addr+fpo_delta], -1 |
|
- | 142 | mov eax, [.next_page_original_access+fpo_delta] |
|
- | 143 | .mprotected: |
|
87 | push eax |
144 | push eax |
- | 145 | fpo_delta = fpo_delta + 4 |
|
88 | ; 2d. Block data is an array of word values. Repeat 2e for every of those. |
146 | ; 2g. Block data is an array of word values. Repeat 2h for every of those. |
89 | .relocloop: |
147 | .relocloop: |
90 | sub ecx, 2 |
148 | sub ecx, 2 |
91 | jb .relocdone |
149 | jb .relocdone |
92 | ; 2e. Every value consists of a 4-bit type and 12-bit offset in the page. |
150 | ; 2h. Every value consists of a 4-bit type and 12-bit offset in the page. |
93 | ; x86 uses two types: 0 = no data (used for padding), 3 = 32-bit relative. |
151 | ; x86 uses two types: 0 = no data (used for padding), 3 = 32-bit relative. |
94 | movzx eax, word [edi] |
152 | movzx eax, word [edi] |
95 | add edi, 2 |
153 | add edi, 2 |
96 | mov ebx, eax |
154 | mov ebx, eax |
97 | and ebx, 0xFFF |
155 | and ebx, 0xFFF |
98 | shr eax, 12 |
156 | shr eax, 12 |
99 | jz .relocloop |
157 | jz .relocloop |
100 | cmp al, IMAGE_REL_BASED_HIGHLOW |
158 | cmp al, IMAGE_REL_BASED_HIGHLOW |
101 | jnz .badreloc |
159 | jnz .badreloc |
- | 160 | ; If the target dword intersects page boundary, |
|
- | 161 | ; we need to mprotect the next page too. |
|
- | 162 | cmp ebx, 0xFFC |
|
- | 163 | jbe .no_mprotect_next |
|
- | 164 | push ebx ecx edx |
|
- | 165 | fpo_delta = fpo_delta + 12 |
|
- | 166 | lea eax, [.next_page_original_access+fpo_delta] |
|
- | 167 | call .restore_old_access |
|
- | 168 | mov eax, 68 |
|
- | 169 | mov ebx, 30 |
|
- | 170 | mov ecx, PROT_READ+PROT_WRITE |
|
- | 171 | mov esi, 0x1000 |
|
- | 172 | add edx, esi |
|
- | 173 | call FS_SYSCALL_PTR |
|
- | 174 | mov [.next_page_original_access+fpo_delta], eax |
|
- | 175 | mov [.next_page_addr+fpo_delta], edx |
|
- | 176 | pop edx ecx ebx |
|
- | 177 | fpo_delta = fpo_delta - 12 |
|
- | 178 | .no_mprotect_next: |
|
102 | add [edx+ebx], ebp |
179 | add [edx+ebx], ebp |
103 | jmp .relocloop |
180 | jmp .relocloop |
104 | .relocdone: |
181 | .relocdone: |
105 | ; 2f. Restore memory protection changed in 2c. |
182 | ; 2i. Restore memory protection changed in 2d. |
106 | pop ecx |
183 | pop ecx |
- | 184 | fpo_delta = fpo_delta - 4 |
|
107 | cmp ecx, -1 |
185 | cmp ecx, -1 |
108 | jz @f |
186 | jz @f |
109 | mov eax, 68 |
187 | mov eax, 68 |
110 | mov ebx, 30 |
188 | mov ebx, 30 |
111 | mov esi, 0x1000 |
189 | mov esi, 0x1000 |
112 | call FS_SYSCALL_PTR |
190 | call FS_SYSCALL_PTR |
113 | @@: |
191 | @@: |
114 | pop esi |
192 | pop esi |
- | 193 | fpo_delta = fpo_delta - 4 |
|
115 | .pagedone: |
194 | .pagedone: |
116 | cmp [.sizeleft], 0 |
195 | cmp [.sizeleft+fpo_delta], 0 |
117 | jnz .pageloop |
196 | jnz .pageloop |
- | 197 | lea eax, [.next_page_original_access+fpo_delta] |
|
118 | pop eax ; pop .sizeleft |
198 | call .restore_old_access |
- | 199 | add esp, 12 |
|
119 | ; 3. For performance reasons, relocation should be avoided |
200 | ; 3. For performance reasons, relocation should be avoided |
120 | ; by choosing an appropriate preferred address. |
201 | ; by choosing an appropriate preferred address. |
121 | ; If we have actually relocated something, yell to the debug board, |
202 | ; If we have actually relocated something, yell to the debug board, |
122 | ; so the programmer can notice that. |
203 | ; so the programmer can notice that. |
- | 204 | ; It's a warning, not an error, so don't call loader_say_error. |
|
123 | mov ecx, msg_relocated1 |
205 | mov ecx, msg_relocated1 |
124 | call sys_msg_board_str |
206 | call sys_msg_board_str |
125 | mov ecx, [esp+4] |
207 | mov ecx, [modulename] |
126 | call sys_msg_board_str |
208 | call sys_msg_board_str |
127 | mov ecx, msg_relocated2 |
209 | mov ecx, msg_relocated2 |
128 | call sys_msg_board_str |
210 | call sys_msg_board_str |
129 | clc |
211 | clc |
130 | ret |
212 | ret |
131 | .badreloc: |
213 | .badreloc: |
132 | pop eax |
214 | pop ecx |
133 | mov ecx, msg_bad_relocation1 |
- | |
134 | call sys_msg_board_str |
- | |
135 | mov ecx, [esp+4] |
215 | pop esi |
136 | call sys_msg_board_str |
- | |
137 | mov ecx, msg_newline |
216 | add esp, 12 |
138 | call sys_msg_board_str |
217 | ccall loader_say_error, msg_bad_relocation, [modulename], 0 |
139 | stc |
218 | stc |
140 | ret |
219 | ret |
- | 220 | ||
- | 221 | .restore_old_access: |
|
- | 222 | cmp dword [eax+4], -1 |
|
- | 223 | jz @f |
|
- | 224 | mov ecx, [eax] |
|
- | 225 | mov edx, [eax+4] |
|
- | 226 | mov eax, 68 |
|
- | 227 | mov ebx, 30 |
|
- | 228 | mov esi, 0x1000 |
|
- | 229 | call FS_SYSCALL_PTR |
|
- | 230 | @@: |
|
- | 231 | retn |
|
- | 232 | endp |
|
- | 233 | ||
- | 234 | ; Resolves static dependencies in the given PE module. |
|
- | 235 | ; Recursively loads and initializes all dependencies. |
|
- | 236 | ; in: esi -> MODULE struct |
|
- | 237 | ; out: eax=0 - success, eax=-1 - error |
|
- | 238 | ; modules_mutex should be locked |
|
- | 239 | proc resolve_pe_imports |
|
- | 240 | locals |
|
- | 241 | export_base dd ? |
|
- | 242 | export_ptr dd ? |
|
- | 243 | export_size dd ? |
|
- | 244 | import_module dd ? |
|
- | 245 | import_dir dd ? |
|
- | 246 | import_descriptor dd ? |
|
- | 247 | bound_import_dir dd ? |
|
- | 248 | bound_import_cur_module dd ? |
|
- | 249 | relocated_bound_modules_count dd ? |
|
- | 250 | relocated_bound_modules_ptr dd ? |
|
- | 251 | cur_page dd -0x1000 ; the page at 0xFFFFF000 is never allocated |
|
- | 252 | cur_page_old_access dd ? |
|
- | 253 | next_page dd -1 |
|
- | 254 | next_page_old_access dd ? |
|
- | 255 | endl |
|
- | 256 | ||
- | 257 | ; General case of resolving imports against one module that is already loaded: |
|
- | 258 | ; binding either does not exist or has mismatched timestamp, |
|
- | 259 | ; so we need to walk through all imported symbols and resolve each one. |
|
- | 260 | ; in: ebp -> IMAGE_IMPORT_DESCRIPTOR |
|
- | 261 | macro resolve_import_from_module fail_action |
|
- | 262 | { |
|
- | 263 | local .label1, .loop, .done |
|
- | 264 | ; common preparation that doesn't need to be repeated per each symbol |
|
- | 265 | mov eax, [import_module] |
|
- | 266 | mov eax, [eax+MODULE.base] |
|
- | 267 | call prepare_import_from_module |
|
- | 268 | ; There are two arrays of dwords pointed to by FirstThunk and OriginalFirstThunk. |
|
- | 269 | ; Target array is FirstThunk: addresses of imported symbols should be written |
|
- | 270 | ; there, that is where the program expects to find them. |
|
- | 271 | ; Source array can be either FirstThunk or OriginalFirstThunk. |
|
- | 272 | ; Normally, FirstThunk and OriginalFirstThunk in a just-compiled binary |
|
- | 273 | ; point to two identical copies of the same array. |
|
- | 274 | ; Binding of the binary rewrites FirstThunk array with actual addresses, |
|
- | 275 | ; but keeps OriginalFirstThunk as is. |
|
- | 276 | ; If OriginalFirstThunk and FirstThunk are both present, use OriginalFirstThunk |
|
- | 277 | ; as source array. |
|
- | 278 | ; However, a compiler is allowed to generate a binary without OriginalFirstThunk; |
|
- | 279 | ; it is impossible to bind such a binary, but it is still valid. |
|
- | 280 | ; If OriginalFirstThunk is absent, use FirstThunk as source array. |
|
- | 281 | mov ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk] |
|
- | 282 | mov ebp, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk] |
|
- | 283 | test ebx, ebx |
|
- | 284 | jnz .label1 |
|
- | 285 | mov ebx, ebp |
|
- | 286 | .label1: |
|
- | 287 | ; FirstThunk and OriginalFirstThunk are RVAs. |
|
- | 288 | add ebx, [esi+MODULE.base] |
|
- | 289 | add ebp, [esi+MODULE.base] |
|
- | 290 | ; Source array is terminated with zero dword. |
|
- | 291 | .loop: |
|
- | 292 | cmp dword [ebx], 0 |
|
- | 293 | jz .done |
|
- | 294 | mov ecx, [ebx] |
|
- | 295 | get_address_for_thunk ; should preserve esi,edi,ebp |
|
- | 296 | test eax, eax |
|
- | 297 | jz fail_action |
|
- | 298 | mov edi, eax |
|
- | 299 | mov edx, ebp |
|
- | 300 | call .ensure_writable ; should preserve edx,ebx,esi,ebp |
|
- | 301 | mov [edx], edi |
|
- | 302 | add ebx, 4 |
|
- | 303 | add ebp, 4 |
|
- | 304 | jmp .loop |
|
- | 305 | .done: |
|
- | 306 | } |
|
- | 307 | ||
- | 308 | ; Resolve one imported symbol. |
|
- | 309 | ; in: ecx = ordinal or RVA of thunk |
|
- | 310 | ; out: eax = address of exported function |
|
- | 311 | macro get_address_for_thunk |
|
- | 312 | { |
|
- | 313 | local .ordinal, .common |
|
- | 314 | ; Ordinal imports have bit 31 set, name imports have bit 31 clear. |
|
- | 315 | btr ecx, 31 |
|
- | 316 | jc .ordinal |
|
- | 317 | ; Thunk for name import is RVA of IMAGE_IMPORT_BY_NAME structure. |
|
- | 318 | add ecx, [esi+MODULE.base] |
|
- | 319 | movzx edx, [ecx+IMAGE_IMPORT_BY_NAME.Hint] |
|
- | 320 | add ecx, IMAGE_IMPORT_BY_NAME.Name |
|
- | 321 | call get_exported_function_by_name |
|
- | 322 | jmp .common |
|
- | 323 | .ordinal: |
|
- | 324 | ; Thunk for ordinal import is just an ordinal, |
|
- | 325 | ; bit 31 has been cleared by btr instruction. |
|
- | 326 | call get_exported_function_by_ordinal |
|
- | 327 | .common: |
|
- | 328 | } |
|
- | 329 | ||
- | 330 | ; We have four main variants: |
|
- | 331 | ; normal unbound import, old-style bound import, new-style bound import, |
|
- | 332 | ; no import. |
|
- | 333 | ; * Normal unbound import: |
|
- | 334 | ; we have an array of import descriptors, one per imported module, |
|
- | 335 | ; pointed to by import directory. |
|
- | 336 | ; We should loop over all descriptors and apply resolve_import_from_module |
|
- | 337 | ; for each one. |
|
- | 338 | ; * Old-style bound import: |
|
- | 339 | ; we have the same array of import descriptors, but timestamp field is set up. |
|
- | 340 | ; We should do the same loop, but we can do a lightweight processing |
|
- | 341 | ; of modules with correct timestamp. In the best case, "lightweight processing" |
|
- | 342 | ; means just skipping them, but corrections arise for relocated modules |
|
- | 343 | ; and forwarded exports. |
|
- | 344 | ; * New-style bound import: |
|
- | 345 | ; we have two parallel arrays of import descriptors and bound descriptors, |
|
- | 346 | ; pointed to by two directories. Timestamp field has a special value -1 |
|
- | 347 | ; in import descriptors, real timestamps are in bound descriptors. |
|
- | 348 | ; There can be different strategies; we loop over bound descriptors |
|
- | 349 | ; and scan for corresponding import descriptors only if needed, |
|
- | 350 | ; this accelerates the fast path where all timestamps are correct and |
|
- | 351 | ; dependencies are not relocated. |
|
- | 352 | ; * No import: not really different from normal import with no descriptors. |
|
- | 353 | ; There are two large parts in this function: |
|
- | 354 | ; step 2 handles unbound and old-style bound import, where we loop over import descriptors; |
|
- | 355 | ; step 3 handles new-style bound import, where we loop over bound descriptors. |
|
- | 356 | ; 1. Fetch addresses of two directories. We are not interested in their sizes. |
|
- | 357 | ; ebp = import RVA |
|
- | 358 | ; ebx = bound import RVA |
|
- | 359 | xor ebx, ebx |
|
- | 360 | xor ebp, ebp |
|
- | 361 | ; PE and stripped PE have different places for directories. |
|
- | 362 | mov eax, [esi+MODULE.base] |
|
- | 363 | cmp byte [eax], 'M' |
|
- | 364 | jz .parse_mz |
|
- | 365 | cmp [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_IMPORT |
|
- | 366 | jbe .common |
|
- | 367 | mov ebp, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_IMPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 368 | cmp [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_BOUND_IMPORT |
|
- | 369 | jbe .common |
|
- | 370 | mov ebx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BOUND_IMPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 371 | jmp .common |
|
- | 372 | .parse_mz: |
|
- | 373 | add eax, [eax+3Ch] |
|
- | 374 | cmp [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_IMPORT |
|
- | 375 | jbe .common |
|
- | 376 | mov ebp, [eax+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_IMPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 377 | cmp [eax+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT |
|
- | 378 | jbe .common |
|
- | 379 | mov ebx, [eax+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 380 | .common: |
|
- | 381 | mov [import_dir], ebp |
|
- | 382 | ; If bound import is present, go to 3. |
|
- | 383 | ; If both directories are absent, no import - nothing to do. |
|
- | 384 | ; Otherwise, advance to 2. |
|
- | 385 | test ebx, ebx |
|
- | 386 | jnz .bound_import |
|
- | 387 | test ebp, ebp |
|
- | 388 | jz .done |
|
- | 389 | ; 2. Unbound import or old-style bound import. |
|
- | 390 | ; Repeat 2a-2h for all descriptors in the directory. |
|
- | 391 | add ebp, [esi+MODULE.base] ; directories contain RVA |
|
- | 392 | .normal_import_loop: |
|
- | 393 | ; 2a. Check whether this descriptor is an end mark with zero fields. |
|
- | 394 | ; Look at Name field. |
|
- | 395 | mov edi, [ebp+IMAGE_IMPORT_DESCRIPTOR.Name] |
|
- | 396 | test edi, edi |
|
- | 397 | jz .done |
|
- | 398 | ; 2b. Load the target module. |
|
- | 399 | add edi, [esi+MODULE.base] ; Name field is RVA |
|
- | 400 | call load_imported_module ; should preserve esi,ebp |
|
- | 401 | test eax, eax |
|
- | 402 | jz .failed |
|
- | 403 | mov [import_module], eax |
|
- | 404 | ; 2c. Check whether the descriptor has a non-stale old-style binding. |
|
- | 405 | ; Zero timestamp means "not bound". |
|
- | 406 | ; Mismatched timestamp means "stale binding". |
|
- | 407 | ; In both cases, go to 2g. |
|
- | 408 | mov edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.TimeDateStamp] |
|
- | 409 | test edx, edx |
|
- | 410 | jz .resolve_normal_import |
|
- | 411 | cmp edx, [eax+MODULE.timestamp] |
|
- | 412 | jnz .resolve_normal_import |
|
- | 413 | ; 2d. The descriptor has a non-stale old-style binding. |
|
- | 414 | ; There are two cases when we still need to do something: |
|
- | 415 | ; * if the target module has been relocated, we need to add |
|
- | 416 | ; relocation delta to all addresses; |
|
- | 417 | ; * if some exports are forwarded, old-style binding cannot bind them: |
|
- | 418 | ; there is only one timestamp field, we can't verify timestamps |
|
- | 419 | ; of forward targets. |
|
- | 420 | ; Thunks for forwarded exports contain index of next forwarded export |
|
- | 421 | ; instead of target address, making a single-linked list terminated by -1. |
|
- | 422 | ; ForwarderChain is the head of the list. |
|
- | 423 | ; If both problems are present, we resort to 2g as if binding is stale, |
|
- | 424 | ; it shouldn't be encountered normally anyway: relocations should be avoided, |
|
- | 425 | ; and forwarded exports should be new-style bound. |
|
- | 426 | ; If the target module is not relocated, go to 2f. |
|
- | 427 | ; If the target module is relocated and there are no forwarded exports, |
|
- | 428 | ; advance to 2e. |
|
- | 429 | cmp [eax+MODULE.basedelta], 0 |
|
- | 430 | jz .normal_import_check_forwarders |
|
- | 431 | cmp [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain], -1 |
|
- | 432 | jnz .resolve_normal_import |
|
- | 433 | ; 2e. Binding is correct, but we need to add MODULE.basedelta |
|
- | 434 | ; to all imported addresses in FirstThunk array. |
|
- | 435 | ; For consistency with generic-case resolve_import_from_module, |
|
- | 436 | ; check for end of thunks by looking at OriginalFirstThunk array. |
|
- | 437 | ; After that, go to 2h. |
|
- | 438 | mov edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk] |
|
- | 439 | add edx, [esi+MODULE.base] |
|
- | 440 | mov ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk] |
|
- | 441 | add ebx, [esi+MODULE.base] |
|
- | 442 | mov edi, [eax+MODULE.basedelta] |
|
- | 443 | .normal_import_add_delta: |
|
- | 444 | cmp dword [ebx], 0 |
|
- | 445 | jz .normal_import_next |
|
- | 446 | call .ensure_writable ; should preserve esi,edi,ebp,ebx,edx |
|
- | 447 | add dword [edx], edi |
|
- | 448 | add edx, 4 |
|
- | 449 | add ebx, 4 |
|
- | 450 | jmp .normal_import_add_delta |
|
- | 451 | .normal_import_check_forwarders: |
|
- | 452 | ; 2f. The target module is not relocated. |
|
- | 453 | ; Exports that are not forwarded are correct. |
|
- | 454 | ; Go through ForwarderChain list and resolve all exports from it. |
|
- | 455 | ; After that, go to 2h. |
|
- | 456 | mov edi, [ebp+IMAGE_IMPORT_DESCRIPTOR.ForwarderChain] |
|
- | 457 | cmp edi, -1 |
|
- | 458 | jz .normal_import_next ; don't prepare_import_from_module for empty list |
|
- | 459 | mov eax, [import_module] |
|
- | 460 | mov eax, [eax+MODULE.base] |
|
- | 461 | call prepare_import_from_module |
|
- | 462 | .normal_import_forward_chain: |
|
- | 463 | mov ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk] |
|
- | 464 | add ebx, [esi+MODULE.base] |
|
- | 465 | mov ecx, [ebx+edi*4] |
|
- | 466 | get_address_for_thunk ; should preserve esi,edi,ebp |
|
- | 467 | test eax, eax |
|
- | 468 | jz .failed |
|
- | 469 | mov ebx, eax |
|
- | 470 | mov edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk] |
|
- | 471 | add edx, [esi+MODULE.base] |
|
- | 472 | lea edx, [edx+edi*4] |
|
- | 473 | call .ensure_writable ; should preserve edx,ebx,esi,ebp |
|
- | 474 | mov edi, [edx] ; next forwarded export |
|
- | 475 | mov [edx], ebx ; store the address |
|
- | 476 | cmp edi, -1 |
|
- | 477 | jnz .normal_import_forward_chain |
|
- | 478 | jmp .normal_import_next |
|
- | 479 | .resolve_normal_import: |
|
- | 480 | ; 2g. Run generic-case resolver. |
|
- | 481 | mov [import_descriptor], ebp |
|
- | 482 | resolve_import_from_module .failed ; should preserve esi |
|
- | 483 | mov ebp, [import_descriptor] |
|
- | 484 | .normal_import_next: |
|
- | 485 | ; 2h. Advance to next descriptor and continue the loop. |
|
- | 486 | add ebp, sizeof.IMAGE_IMPORT_DESCRIPTOR |
|
- | 487 | jmp .normal_import_loop |
|
- | 488 | .bound_import: |
|
- | 489 | ; 3. New-style bound import. |
|
- | 490 | ; Repeat 3a-3o for all descriptors in bound import directory. |
|
- | 491 | mov [bound_import_dir], ebx |
|
- | 492 | add ebx, [esi+MODULE.base] |
|
- | 493 | .bound_import_loop: |
|
- | 494 | ; 3a. Check whether this descriptor is an end mark with zero fields. |
|
- | 495 | movzx edi, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName] |
|
- | 496 | mov [bound_import_cur_module], edi |
|
- | 497 | test edi, edi |
|
- | 498 | jz .done |
|
- | 499 | ; Bound import descriptors come in groups. |
|
- | 500 | ; The first descriptor in each group corresponds to the main imported module. |
|
- | 501 | ; If some exports from the module are forwarded, additional descriptors |
|
- | 502 | ; are created for modules where those exports are forwarded to. |
|
- | 503 | ; Number of additional descriptors is given by one field in the first descriptor. |
|
- | 504 | ; 3b. Prepare for loop at 3c-3f with loading targets of all exports. |
|
- | 505 | ; This includes the target module and all modules in chains of forwarded exports. |
|
- | 506 | movzx ebp, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.NumberOfModuleForwarderRefs] |
|
- | 507 | mov [relocated_bound_modules_count], 0 |
|
- | 508 | mov [relocated_bound_modules_ptr], 0 |
|
- | 509 | mov [import_module], 0 |
|
- | 510 | .bound_import_forwarder_loop: |
|
- | 511 | ; 3c. Load a referenced module. |
|
- | 512 | ; Names in bound import descriptors are relative to bound import directory, |
|
- | 513 | ; not RVAs. |
|
- | 514 | add edi, [bound_import_dir] |
|
- | 515 | call load_imported_module ; should preserve ebx,esi,ebp |
|
- | 516 | test eax, eax |
|
- | 517 | jz .bound_import_failed |
|
- | 518 | ; The target module is first in the list. |
|
- | 519 | cmp [import_module], 0 |
|
- | 520 | jnz @f |
|
- | 521 | mov [import_module], eax |
|
- | 522 | @@: |
|
- | 523 | ; 3d. Check whether timestamp in the descriptor matches module timestamp. |
|
- | 524 | ; If not, go to 3h which after some preparations will resort to generic-case |
|
- | 525 | ; resolve_import_from_module; in this case, we stop processing the group, |
|
- | 526 | ; resolve_import_from_module will take care about additional modules anyway. |
|
- | 527 | mov edx, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.TimeDateStamp] |
|
- | 528 | test edx, edx |
|
- | 529 | jz .bound_import_wrong_timestamp |
|
- | 530 | cmp edx, [eax+MODULE.timestamp] |
|
- | 531 | jnz .bound_import_wrong_timestamp |
|
- | 532 | ; 3e. Collect all referenced modules that have been relocated. |
|
- | 533 | cmp [eax+MODULE.basedelta], 0 |
|
- | 534 | jz .bound_import_forwarder_next |
|
- | 535 | mov edi, eax |
|
- | 536 | ; We don't want to reallocate too often, since reallocation |
|
- | 537 | ; may involve copying our data to a new place. |
|
- | 538 | ; We always reserve space that is a power of two; in this way, |
|
- | 539 | ; the wasted space is never greater than the used space, |
|
- | 540 | ; and total time of copying the data is O(number of modules). |
|
- | 541 | mov eax, [relocated_bound_modules_ptr] |
|
- | 542 | mov edx, [relocated_bound_modules_count] |
|
- | 543 | ; X is a power of two or zero if and only if (X and (X - 1)) is zero |
|
- | 544 | lea ecx, [edx-1] |
|
- | 545 | test ecx, edx |
|
- | 546 | jnz .bound_import_norealloc |
|
- | 547 | ; if the current size is zero, allocate 1 item, |
|
- | 548 | ; otherwise double number of items. |
|
- | 549 | ; Item size is 4 bytes. |
|
- | 550 | lea edx, [edx*8] |
|
- | 551 | test edx, edx |
|
- | 552 | jnz @f |
|
- | 553 | mov edx, 4 |
|
- | 554 | @@: |
|
- | 555 | stdcall realloc, [relocated_bound_modules_ptr], edx |
|
- | 556 | test eax, eax |
|
- | 557 | jz .bound_import_failed |
|
- | 558 | mov [relocated_bound_modules_ptr], eax |
|
- | 559 | .bound_import_norealloc: |
|
- | 560 | mov edx, [relocated_bound_modules_count] |
|
- | 561 | inc [relocated_bound_modules_count] |
|
- | 562 | mov [eax+edx*4], edi |
|
- | 563 | .bound_import_forwarder_next: |
|
- | 564 | ; 3f. Advance to the next descriptor in the group. |
|
- | 565 | add ebx, sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR |
|
- | 566 | movzx edi, [ebx+IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName] |
|
- | 567 | dec ebp |
|
- | 568 | jns .bound_import_forwarder_loop |
|
- | 569 | ; 3g. All timestamps are correct. |
|
- | 570 | ; If all targets are not relocated, then we have nothing to do |
|
- | 571 | ; with exports from the current module, so continue loop at 3a; |
|
- | 572 | ; ebx already points to the next descriptor. |
|
- | 573 | ; Otherwise, go to 3i. |
|
- | 574 | cmp [relocated_bound_modules_count], 0 |
|
- | 575 | jz .bound_import_loop |
|
- | 576 | jmp .bound_import_fix |
|
- | 577 | .bound_import_wrong_timestamp: |
|
- | 578 | ; 3h. We have aborted the loop over the group; |
|
- | 579 | ; advance ebx so that it points to the first descriptor of the next group, |
|
- | 580 | ; make a mark so that 3l will know that we need to reimport everything. |
|
- | 581 | ; We don't need [relocated_bound_modules_count] in this case anymore, |
|
- | 582 | ; use zero value as a mark. |
|
- | 583 | lea ebx, [ebx+(ebp+1)*sizeof.IMAGE_BOUND_IMPORT_DESCRIPTOR] |
|
- | 584 | mov [relocated_bound_modules_count], 0 |
|
- | 585 | .bound_import_fix: |
|
- | 586 | ; 3i. We need to do something with exported addresses. |
|
- | 587 | ; Find corresponding import descriptors; there can be more than one. |
|
- | 588 | ; Repeat 3j-3n for all import descriptors. |
|
- | 589 | mov ebp, [import_dir] |
|
- | 590 | add ebp, [esi+MODULE.base] |
|
- | 591 | .look_related_descriptors: |
|
- | 592 | ; 3j. Check whether we have reached end of import table. |
|
- | 593 | ; If so, go to 3o. |
|
- | 594 | mov edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.Name] |
|
- | 595 | test edx, edx |
|
- | 596 | jz .bound_import_next |
|
- | 597 | ; 3k. Check whether the current import descriptor matches the current |
|
- | 598 | ; bound import descriptor. Check Name fields. |
|
- | 599 | ; If so, advance to 3l. |
|
- | 600 | ; Otherwise, advance to the next import descriptor and return to 3j. |
|
- | 601 | add edx, [esi+MODULE.base] |
|
- | 602 | mov edi, [bound_import_cur_module] |
|
- | 603 | @@: |
|
- | 604 | mov al, [edx] |
|
- | 605 | cmp [edi], al |
|
- | 606 | jnz .next_related_descriptor |
|
- | 607 | test al, al |
|
- | 608 | jz .found_related_descriptor |
|
- | 609 | inc edx |
|
- | 610 | inc edi |
|
- | 611 | jmp @b |
|
- | 612 | .next_related_descriptor_restore: |
|
- | 613 | mov ebp, [import_descriptor] |
|
- | 614 | .next_related_descriptor: |
|
- | 615 | add ebp, sizeof.IMAGE_IMPORT_DESCRIPTOR |
|
- | 616 | jmp .look_related_descriptors |
|
- | 617 | .found_related_descriptor: |
|
- | 618 | ; 3l. Check what we should do: |
|
- | 619 | ; advance to 3m, if we need to reimport everything, |
|
- | 620 | ; go to 3n, if we just need to relocate something. |
|
- | 621 | mov [import_descriptor], ebp |
|
- | 622 | cmp [relocated_bound_modules_count], 0 |
|
- | 623 | jnz .bound_import_add_delta |
|
- | 624 | ; 3m. Apply resolve_import_from_module and return to 3j. |
|
- | 625 | resolve_import_from_module .bound_import_failed ; should preserve ebx,esi |
|
- | 626 | jmp .next_related_descriptor_restore |
|
- | 627 | .bound_import_add_delta: |
|
- | 628 | ; 3n. Loop over all imported symbols. |
|
- | 629 | ; For every imported symbol, check whether it fits within one of relocated |
|
- | 630 | ; modules, and if so, apply relocation to it. |
|
- | 631 | ; For consistency with generic-case resolve_import_from_module, |
|
- | 632 | ; determine end of thunks from OriginalFirstThunk array. |
|
- | 633 | mov edx, [ebp+IMAGE_IMPORT_DESCRIPTOR.FirstThunk] |
|
- | 634 | add edx, [esi+MODULE.base] |
|
- | 635 | mov ebx, [ebp+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk] |
|
- | 636 | add ebx, [esi+MODULE.base] |
|
- | 637 | .bound_import_add_delta_loop: |
|
- | 638 | cmp dword [ebx], 0 |
|
- | 639 | jz .next_related_descriptor_restore |
|
- | 640 | mov ecx, [relocated_bound_modules_ptr] |
|
- | 641 | mov ebp, [relocated_bound_modules_count] |
|
- | 642 | push esi |
|
- | 643 | .find_delta_module: |
|
- | 644 | mov esi, [ecx] |
|
- | 645 | mov eax, [edx] |
|
- | 646 | sub eax, [esi+MODULE.base] |
|
- | 647 | add eax, [esi+MODULE.basedelta] |
|
- | 648 | cmp eax, [esi+MODULE.size] |
|
- | 649 | jb .found_delta_module |
|
- | 650 | add ecx, 4 |
|
- | 651 | dec ebp |
|
- | 652 | jnz .find_delta_module |
|
- | 653 | pop esi |
|
- | 654 | .bound_import_add_delta_next: |
|
- | 655 | add ebx, 4 |
|
- | 656 | add edx, 4 |
|
- | 657 | jmp .bound_import_add_delta_loop |
|
- | 658 | .found_delta_module: |
|
- | 659 | mov ebp, [esi+MODULE.basedelta] |
|
- | 660 | pop esi |
|
- | 661 | call .ensure_writable ; should preserve esi,ebp,ebx,edx |
|
- | 662 | add [edx], ebp |
|
- | 663 | jmp .bound_import_add_delta_next |
|
- | 664 | .bound_import_next: |
|
- | 665 | ; 3o. Free the data we might have allocated and return to 3a. |
|
- | 666 | cmp [relocated_bound_modules_ptr], 0 |
|
- | 667 | jz .bound_import_loop |
|
- | 668 | stdcall free, [relocated_bound_modules_ptr] |
|
- | 669 | jmp .bound_import_loop |
|
- | 670 | .done: |
|
- | 671 | call .restore_protection |
|
- | 672 | xor eax, eax |
|
- | 673 | ret |
|
- | 674 | .bound_import_failed: |
|
- | 675 | cmp [relocated_bound_modules_ptr], 0 |
|
- | 676 | jz .failed |
|
- | 677 | stdcall free, [relocated_bound_modules_ptr] |
|
- | 678 | .failed: |
|
- | 679 | call .restore_protection |
|
- | 680 | xor eax, eax |
|
- | 681 | dec eax |
|
- | 682 | ret |
|
- | 683 | ||
- | 684 | ; Local helper functions. |
|
- | 685 | fpo_delta = fpo_delta + 4 |
|
- | 686 | ; Import table may reside in read-only pages. |
|
- | 687 | ; We should mprotect any page where we are going to write to. |
|
- | 688 | ; Things get interesting when one thunk spans two pages. |
|
- | 689 | ; in: edx = address of dword to make writable |
|
- | 690 | .ensure_writable: |
|
- | 691 | ; 1. Fast path: if we have already mprotect-ed one page and |
|
- | 692 | ; the requested dword is in the same page, do nothing. |
|
- | 693 | mov eax, edx |
|
- | 694 | sub eax, [cur_page] |
|
- | 695 | cmp eax, 0x1000 - 4 |
|
- | 696 | ja .cur_page_not_sufficient |
|
- | 697 | .ensure_writable.nothing: |
|
- | 698 | retn |
|
- | 699 | .cur_page_not_sufficient: |
|
- | 700 | ; 2. If the requested dword begins in the current page |
|
- | 701 | ; and ends in the next page, mprotect the next page and return. |
|
- | 702 | push ebx esi edx |
|
- | 703 | fpo_delta = fpo_delta + 12 |
|
- | 704 | cmp eax, 0x1000 |
|
- | 705 | jae .wrong_cur_page |
|
- | 706 | cmp [next_page], -1 |
|
- | 707 | jnz @f |
|
- | 708 | mov eax, 68 |
|
- | 709 | mov ebx, 30 |
|
- | 710 | mov ecx, PROT_READ+PROT_WRITE |
|
- | 711 | mov edx, [cur_page] |
|
- | 712 | mov esi, 0x1000 |
|
- | 713 | add edx, esi |
|
- | 714 | mov [next_page], edx |
|
- | 715 | call FS_SYSCALL_PTR |
|
- | 716 | mov [next_page_old_access], eax |
|
- | 717 | @@: |
|
- | 718 | pop edx esi ebx |
|
- | 719 | retn |
|
- | 720 | .wrong_cur_page: |
|
- | 721 | ; The requested dword does not intersect with the current page. |
|
- | 722 | ; 3. Restore the protection of the current page, |
|
- | 723 | ; it is unlikely to be used again. |
|
- | 724 | cmp [cur_page], -0x1000 |
|
- | 725 | jz @f |
|
- | 726 | mov eax, 68 |
|
- | 727 | mov ebx, 30 |
|
- | 728 | mov ecx, [cur_page_old_access] |
|
- | 729 | mov edx, [cur_page] |
|
- | 730 | mov esi, 0x1000 |
|
- | 731 | call FS_SYSCALL_PTR |
|
- | 732 | @@: |
|
- | 733 | ; 4. If the next page has been mprotect-ed too, |
|
- | 734 | ; switch to it as the current page and restart the function. |
|
- | 735 | cmp [next_page], -1 |
|
- | 736 | jz @f |
|
- | 737 | mov eax, [next_page] |
|
- | 738 | mov [cur_page], eax |
|
- | 739 | mov eax, [next_page_old_access] |
|
- | 740 | mov [cur_page_old_access], eax |
|
- | 741 | mov [next_page], -1 |
|
- | 742 | pop edx esi ebx |
|
- | 743 | jmp .ensure_writable |
|
- | 744 | @@: |
|
- | 745 | ; 5. This is the entirely new page to mprotect. |
|
- | 746 | mov edx, [esp] |
|
- | 747 | and edx, not 0xFFF |
|
- | 748 | mov eax, 68 |
|
- | 749 | mov ebx, 30 |
|
- | 750 | mov ecx, PROT_READ+PROT_WRITE |
|
- | 751 | mov [cur_page], edx |
|
- | 752 | mov esi, 0x1000 |
|
- | 753 | call FS_SYSCALL_PTR |
|
- | 754 | mov [cur_page_old_access], eax |
|
- | 755 | pop edx esi ebx |
|
- | 756 | fpo_delta = fpo_delta - 12 |
|
- | 757 | retn |
|
- | 758 | ||
- | 759 | ; Called at end of processing, |
|
- | 760 | ; restores protection of pages that we have mprotect-ed for write. |
|
- | 761 | .restore_protection: |
|
- | 762 | push esi |
|
- | 763 | fpo_delta = fpo_delta + 4 |
|
- | 764 | cmp [next_page], -1 |
|
- | 765 | jz @f |
|
- | 766 | mov eax, 68 |
|
- | 767 | mov ebx, 30 |
|
- | 768 | mov ecx, [next_page_old_access] |
|
- | 769 | mov edx, [next_page] |
|
- | 770 | mov esi, 0x1000 |
|
- | 771 | call FS_SYSCALL_PTR |
|
- | 772 | @@: |
|
- | 773 | cmp [cur_page], -0x1000 |
|
- | 774 | jz @f |
|
- | 775 | mov eax, 68 |
|
- | 776 | mov ebx, 30 |
|
- | 777 | mov ecx, [cur_page_old_access] |
|
- | 778 | mov edx, [cur_page] |
|
- | 779 | mov esi, 0x1000 |
|
- | 780 | call FS_SYSCALL_PTR |
|
- | 781 | @@: |
|
- | 782 | pop esi |
|
- | 783 | fpo_delta = fpo_delta - 4 |
|
- | 784 | retn |
|
- | 785 | endp |
|
- | 786 | ||
- | 787 | ; Part of resolving symbol from a module that is the same for all symbols. |
|
- | 788 | ; resolve_pe_imports calls it only once per module. |
|
- | 789 | ; Fetches export directory from the module. |
|
- | 790 | ; Non-standard calling convention: saves results to first 2 dwords on the stack. |
|
- | 791 | ; in: eax = module base |
|
- | 792 | proc prepare_import_from_module c, export_base, export_ptr, export_size |
|
- | 793 | ; The implementation is straightforward. |
|
- | 794 | mov [export_base], eax |
|
- | 795 | cmp byte [eax], 'M' |
|
- | 796 | jz .parse_mz |
|
- | 797 | cmp [eax+STRIPPED_PE_HEADER.NumberOfRvaAndSizes], SPE_DIRECTORY_EXPORT |
|
- | 798 | jbe .noexport |
|
- | 799 | mov edx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.VirtualAddress] |
|
- | 800 | add edx, eax |
|
- | 801 | mov [export_ptr], edx |
|
- | 802 | mov edx, [eax+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY+IMAGE_DATA_DIRECTORY.isize] |
|
- | 803 | mov [export_size], edx |
|
- | 804 | ret |
|
- | 805 | .parse_mz: |
|
- | 806 | mov ecx, [eax+3Ch] |
|
- | 807 | add ecx, eax |
|
- | 808 | cmp [ecx+IMAGE_NT_HEADERS.OptionalHeader.NumberOfDirectories], IMAGE_DIRECTORY_ENTRY_EXPORT |
|
- | 809 | jbe .noexport |
|
- | 810 | mov edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress+IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 811 | add edx, eax |
|
- | 812 | mov [export_ptr], edx |
|
- | 813 | mov edx, [ecx+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.isize+IMAGE_DIRECTORY_ENTRY_EXPORT*sizeof.IMAGE_DATA_DIRECTORY] |
|
- | 814 | mov [export_size], edx |
|
- | 815 | ret |
|
- | 816 | .noexport: |
|
- | 817 | mov [export_ptr], 0 |
|
- | 818 | mov [export_size], 0 |
|
- | 819 | ret |
|
- | 820 | endp |
|
- | 821 | ||
- | 822 | ; PE format supports export by name and by ordinal. |
|
- | 823 | ; Any exported symbol always have an ordinal. |
|
- | 824 | ; It may have a name, it may have no name. |
|
- | 825 | ; A symbol can even have multiple names, usually this happens |
|
- | 826 | ; when several functions with the same body like 'ret' are merged. |
|
- | 827 | ; |
|
- | 828 | ; Addresses of all exported symbols are contained in one array AddressOfFunctions. |
|
- | 829 | ; Ordinal of a symbol is an index in this array + Base. |
|
- | 830 | ; Base is defined in export directory, usually it equals 1. |
|
- | 831 | ; |
|
- | 832 | ; Export by name is more complicated. There are two parallel arrays |
|
- | 833 | ; AddressOfNames and AddressOfNameOrdinals with the same length. |
|
- | 834 | ; This length can be less or greater than length of AddressOfFunctions. |
|
- | 835 | ; AddressOfNames is a sorted array with all exported names. |
|
- | 836 | ; AddressOfNameOrdinals, contrary to the title, gives index in AddressOfFunctions. |
|
- | 837 | ; Looking up a name means |
|
- | 838 | ; * scanning AddressOfNames array to find the index of the corresponding name |
|
- | 839 | ; * looking in AddressOfNameOrdinals at the index found above to get another index; |
|
- | 840 | ; index in AddressOfNames/AddressOfNameOrdinals has no other meaning |
|
- | 841 | ; * finally, looking in AddressOfFunctions with that second index. |
|
- | 842 | ||
- | 843 | ; Resolve symbol from a module by name. |
|
- | 844 | ; prepare_import_from_module should be called beforehand. |
|
- | 845 | ; in: ecx -> name, edx = hint for lookup in name table |
|
- | 846 | ; out: eax = exported address or NULL |
|
- | 847 | ; if [module] is zero, modules_mutex should be unlocked |
|
- | 848 | ; if [module] is nonzero, modules_mutex should be locked |
|
- | 849 | proc get_exported_function_by_name c uses ebx esi edi, export_base, export_ptr, export_size, module |
|
- | 850 | locals |
|
- | 851 | forward_export_base dd ? |
|
- | 852 | forward_export_ptr dd ? |
|
- | 853 | forward_export_size dd ? |
|
- | 854 | forward_export_module dd ? |
|
- | 855 | endl |
|
- | 856 | ; 1. Find length of the name, including terminating zero. |
|
- | 857 | mov esi, ecx |
|
- | 858 | @@: |
|
- | 859 | inc ecx |
|
- | 860 | cmp byte [ecx-1], 0 |
|
- | 861 | jnz @b |
|
- | 862 | sub ecx, esi |
|
- | 863 | ; 2. Validate that export directory is present at all. |
|
- | 864 | mov eax, [export_ptr] |
|
- | 865 | test eax, eax |
|
- | 866 | jz .export_name_not_found |
|
- | 867 | ; 3. Check whether the hint is correct. |
|
- | 868 | ; The hint is a zero-based index in name table. |
|
- | 869 | ; Theoretically, zero is a valid hint. |
|
- | 870 | ; Unfortunately, in practice everyone uses zero if the hint is unknown, |
|
- | 871 | ; which is a quite typical situation, so treating zero as a valid hint |
|
- | 872 | ; would waste processor cycles much more often than save. |
|
- | 873 | ; So only check the hint if it is between 1 and NumberOfNames-1 inclusive. |
|
- | 874 | ; 3a. Validate the hint. |
|
- | 875 | mov ebx, [eax+IMAGE_EXPORT_DIRECTORY.AddressOfNames] |
|
- | 876 | add ebx, [export_base] |
|
- | 877 | cmp edx, [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames] |
|
- | 878 | jae .ignore_hint |
|
- | 879 | test edx, edx |
|
- | 880 | jz .ignore_hint |
|
- | 881 | ; 3b. Check the hinted name. |
|
- | 882 | ; If it matches, go to 5. If not, we're out of luck, use normal lookup. |
|
- | 883 | mov edi, [ebx+edx*4] |
|
- | 884 | add edi, [export_base] |
|
- | 885 | push ecx esi |
|
- | 886 | repz cmpsb |
|
- | 887 | pop esi ecx |
|
- | 888 | jz .found |
|
- | 889 | .ignore_hint: |
|
- | 890 | ; 4. Binary search over name table. |
|
- | 891 | ; Export names are sorted with respect to repz cmpsb. |
|
- | 892 | ; edi <= (the target index) < edx |
|
- | 893 | xor edi, edi |
|
- | 894 | mov edx, [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames] |
|
- | 895 | .export_name_search.loop: |
|
- | 896 | ; if there are no indexes between edi and edx, name is invalid |
|
- | 897 | cmp edi, edx |
|
- | 898 | jae .export_name_not_found |
|
- | 899 | ; try the index in the middle of current range |
|
- | 900 | lea eax, [edi+edx] |
|
- | 901 | shr eax, 1 |
|
- | 902 | ; compare |
|
- | 903 | push ecx esi edi |
|
- | 904 | fpo_delta = fpo_delta + 12 |
|
- | 905 | mov edi, [ebx+eax*4] |
|
- | 906 | add edi, [export_base] |
|
- | 907 | repz cmpsb |
|
- | 908 | pop edi esi ecx |
|
- | 909 | fpo_delta = fpo_delta - 12 |
|
- | 910 | ; exact match -> found, go to 5 |
|
- | 911 | ; string at esi = target, string at edi = current attempt |
|
- | 912 | ; (string at esi) < (string at edi) -> current index is too high, update upper range |
|
- | 913 | ; (string at esi) > (string at edi) -> current index is too low, update lower range |
|
- | 914 | jz .found |
|
- | 915 | jb @f |
|
- | 916 | lea edi, [eax+1] |
|
- | 917 | jmp .export_name_search.loop |
|
- | 918 | @@: |
|
- | 919 | mov edx, eax |
|
- | 920 | jmp .export_name_search.loop |
|
- | 921 | ; Generic error handler. |
|
- | 922 | .export_name_not_found: |
|
- | 923 | mov ebx, esi |
|
- | 924 | mov esi, [module] |
|
- | 925 | test esi, esi |
|
- | 926 | jnz @f |
|
- | 927 | mutex_lock modules_mutex |
|
- | 928 | mov ecx, [export_base] |
|
- | 929 | call find_module_by_addr |
|
- | 930 | mutex_unlock modules_mutex |
|
- | 931 | @@: |
|
- | 932 | mov eax, msg_unknown |
|
- | 933 | test esi, esi |
|
- | 934 | jz @f |
|
- | 935 | mov eax, [esi+MODULE.filename] |
|
- | 936 | @@: |
|
- | 937 | ccall loader_say_error, msg_export_name_not_found, ebx, msg_export_not_found, eax, 0 |
|
- | 938 | .return0: |
|
- | 939 | xor eax, eax |
|
- | 940 | ret |
|
- | 941 | .found: |
|
- | 942 | ; 5. We have found an index in AddressOfNames/AddressOfNameOrdinals arrays, |
|
- | 943 | ; convert it to index in AddressOfFunctions array. |
|
- | 944 | mov edx, [export_ptr] |
|
- | 945 | mov ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals] |
|
- | 946 | add ebx, [export_base] |
|
- | 947 | movzx eax, word [ebx+eax*2] |
|
- | 948 | ; 6. Fetch the exported address from AddressOfFunctions array. |
|
- | 949 | cmp eax, [edx+IMAGE_EXPORT_DIRECTORY.NumberOfFunctions] |
|
- | 950 | jae .export_name_not_found |
|
- | 951 | mov ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions] |
|
- | 952 | add ebx, [export_base] |
|
- | 953 | mov eax, [ebx+eax*4] |
|
- | 954 | test eax, eax |
|
- | 955 | jz .export_name_not_found |
|
- | 956 | .check_forwarded: |
|
- | 957 | ; This part of code is also used by get_exported_function_by_ordinal. |
|
- | 958 | ; 7. Check whether the address is inside the export directory. |
|
- | 959 | ; If not, we are done. |
|
- | 960 | add eax, [export_base] |
|
- | 961 | mov esi, eax |
|
- | 962 | sub esi, edx |
|
- | 963 | cmp esi, [export_size] |
|
- | 964 | jb .export_is_forwarded |
|
- | 965 | ret |
|
- | 966 | .export_is_forwarded: |
|
- | 967 | ; The export is forwarded to another module. |
|
- | 968 | ; The address we have got points to the string " |
|
- | 969 | ; 8. Get the target module name. It is everything before the first dot, |
|
- | 970 | ; minus DLL extension. |
|
- | 971 | ; 8a. Find the dot. |
|
- | 972 | mov ebx, eax |
|
- | 973 | @@: |
|
- | 974 | inc eax |
|
- | 975 | cmp byte [eax-1], '.' |
|
- | 976 | jz .dot_found |
|
- | 977 | cmp byte [eax-1], 0 |
|
- | 978 | jnz @b |
|
- | 979 | jmp .export_name_not_found |
|
- | 980 | .dot_found: |
|
- | 981 | ; 8b. Allocate the memory. |
|
- | 982 | sub eax, ebx |
|
- | 983 | mov edi, eax |
|
- | 984 | add eax, 4 ; dll + terminating zero |
|
- | 985 | stdcall malloc, eax |
|
- | 986 | test eax, eax |
|
- | 987 | jz .return0 |
|
- | 988 | ; 8c. Copy module name. |
|
- | 989 | mov esi, ebx |
|
- | 990 | mov ecx, edi |
|
- | 991 | mov edi, eax |
|
- | 992 | rep movsb |
|
- | 993 | mov dword [edi], 'dll' |
|
- | 994 | mov ebx, esi ; save pointer to |
|
- | 995 | mov edi, eax ; module name |
|
- | 996 | ; 9. Load the target module. |
|
- | 997 | ; 9a. Get the pointer to MODULE struct for ourselves. |
|
- | 998 | mov esi, [module] |
|
- | 999 | test esi, esi |
|
- | 1000 | jnz @f |
|
- | 1001 | mutex_lock modules_mutex |
|
- | 1002 | mov ecx, [export_base] |
|
- | 1003 | call find_module_by_addr |
|
- | 1004 | test esi, esi |
|
- | 1005 | jz .load_forwarded_failed |
|
- | 1006 | @@: |
|
- | 1007 | ; 9b. Call the worker. |
|
- | 1008 | call load_imported_module |
|
- | 1009 | test eax, eax |
|
- | 1010 | jz .load_forwarded_failed |
|
- | 1011 | mov esi, eax |
|
- | 1012 | ; 9c. We don't need module name anymore, free the memory allocated at 8b. |
|
- | 1013 | stdcall free, edi |
|
- | 1014 | ; 10. Resolve the forwarded export recursively. |
|
- | 1015 | ; 10a. Prepare for importing. |
|
- | 1016 | mov [forward_export_module], esi |
|
- | 1017 | mov eax, [esi+MODULE.base] |
|
- | 1018 | call prepare_import_from_module |
|
- | 1019 | ; 10b. Check whether we are importing by ordinal or by name. |
|
- | 1020 | ; Forwarded export by ordinal has ebx -> "# |
|
- | 1021 | cmp byte [ebx], '#' |
|
- | 1022 | jnz .no_ordinal |
|
- | 1023 | lea edx, [ebx+1] |
|
- | 1024 | xor ecx, ecx ; ordinal |
|
- | 1025 | @@: |
|
- | 1026 | movzx eax, byte [edx] |
|
- | 1027 | sub eax, '0' |
|
- | 1028 | cmp eax, 10 |
|
- | 1029 | jae .no_ordinal |
|
- | 1030 | lea ecx, [ecx*5] |
|
- | 1031 | lea ecx, [ecx*2+eax] |
|
- | 1032 | inc edx |
|
- | 1033 | cmp byte [edx], 0 |
|
- | 1034 | jnz @b |
|
- | 1035 | ; 10c. We are importing by ordinal. Call the worker. |
|
- | 1036 | call get_exported_function_by_ordinal |
|
- | 1037 | jmp @f |
|
- | 1038 | ret |
|
- | 1039 | .no_ordinal: |
|
- | 1040 | ; 10d. We are importing by name. Call the worker. |
|
- | 1041 | mov ecx, ebx |
|
- | 1042 | or edx, -1 |
|
- | 1043 | call get_exported_function_by_name |
|
- | 1044 | @@: |
|
- | 1045 | cmp [module], 0 |
|
- | 1046 | jnz @f |
|
- | 1047 | push eax |
|
- | 1048 | mutex_unlock modules_mutex |
|
- | 1049 | pop eax |
|
- | 1050 | @@: |
|
- | 1051 | ret |
|
- | 1052 | .load_forwarded_failed: |
|
- | 1053 | cmp [module], 0 |
|
- | 1054 | jnz @f |
|
- | 1055 | mutex_unlock modules_mutex |
|
- | 1056 | @@: |
|
- | 1057 | stdcall free, edi |
|
- | 1058 | xor eax, eax |
|
- | 1059 | ret |
|
- | 1060 | endp |
|
- | 1061 | ||
- | 1062 | ; Resolve symbol from a module by name. |
|
- | 1063 | ; prepare_import_from_module should be called beforehand. |
|
- | 1064 | ; in: ecx = ordinal |
|
- | 1065 | ; out: eax = exported address or NULL |
|
- | 1066 | ; if [module] is zero, modules_mutex should be unlocked |
|
- | 1067 | ; if [module] is nonzero, modules_mutex should be locked |
|
- | 1068 | proc get_exported_function_by_ordinal c uses ebx esi edi, export_base, export_ptr, export_size, module |
|
- | 1069 | locals |
|
- | 1070 | forward_export_base dd ? |
|
- | 1071 | forward_export_ptr dd ? |
|
- | 1072 | forward_export_size dd ? |
|
- | 1073 | forward_export_module dd ? |
|
- | 1074 | endl |
|
- | 1075 | ; 1. Validate that export directory is present at all. |
|
- | 1076 | mov edx, [export_ptr] |
|
- | 1077 | test edx, edx |
|
- | 1078 | jz .export_ordinal_not_found |
|
- | 1079 | ; 2. Convert ordinal to index in AddressOfFunctions array. |
|
- | 1080 | mov eax, ecx ; keep ecx for error message |
|
- | 1081 | sub eax, [edx+IMAGE_EXPORT_DIRECTORY.Base] |
|
- | 1082 | ; 3. Validate the index. |
|
- | 1083 | cmp eax, [edx+IMAGE_EXPORT_DIRECTORY.NumberOfFunctions] |
|
- | 1084 | jae .export_ordinal_not_found |
|
- | 1085 | ; 4. Fetch the exported address from AddressOfFunctions array. |
|
- | 1086 | ; On success, continue to check for forwarded exports in get_exported_function_by_name. |
|
- | 1087 | mov ebx, [edx+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions] |
|
- | 1088 | add ebx, [export_base] |
|
- | 1089 | mov eax, [ebx+eax*4] |
|
- | 1090 | test eax, eax |
|
- | 1091 | jnz get_exported_function_by_name.check_forwarded |
|
- | 1092 | ; Generic error handler. |
|
- | 1093 | .export_ordinal_not_found: |
|
- | 1094 | sub esp, 16 |
|
- | 1095 | fpo_delta = fpo_delta + 16 |
|
- | 1096 | ; Convert ordinal to string. |
|
- | 1097 | lea edi, [esp+15] |
|
- | 1098 | mov byte [edi], 0 |
|
- | 1099 | @@: |
|
- | 1100 | mov eax, 0xCCCCCCCD |
|
- | 1101 | mul ecx |
|
- | 1102 | shr edx, 3 ; edx = quotient of ecx / 10 |
|
- | 1103 | lea eax, [edx*5] |
|
- | 1104 | add eax, eax |
|
- | 1105 | sub ecx, eax ; ecx = remainder of ecx % 10 |
|
- | 1106 | add cl, '0' |
|
- | 1107 | dec edi |
|
- | 1108 | mov byte [edi], cl |
|
- | 1109 | mov ecx, edx |
|
- | 1110 | test edx, edx |
|
- | 1111 | jnz @b |
|
- | 1112 | ; Get module name. |
|
- | 1113 | mov esi, [module] |
|
- | 1114 | test esi, esi |
|
- | 1115 | jnz @f |
|
- | 1116 | mutex_lock modules_mutex |
|
- | 1117 | mov ecx, [export_base] |
|
- | 1118 | call find_module_by_addr |
|
- | 1119 | mutex_unlock modules_mutex |
|
- | 1120 | @@: |
|
- | 1121 | mov eax, msg_unknown |
|
- | 1122 | test esi, esi |
|
- | 1123 | jz @f |
|
- | 1124 | mov eax, [esi+MODULE.filename] |
|
- | 1125 | @@: |
|
- | 1126 | ccall loader_say_error, msg_export_ordinal_not_found, edi, msg_export_not_found, eax, 0 |
|
- | 1127 | add esp, 16 |
|
- | 1128 | fpo_delta = fpo_delta - 16 |
|
- | 1129 | xor eax, eax |
|
- | 1130 | ret |
|
141 | endp |
1131 | endp>>=> |