Subversion Repositories Kolibri OS

Rev

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