Is an exe's import table built built by the linker or assembler?

조회수 12회
읽지 않은 첫 메시지로 건너뛰기

ThoughtCriminal

읽지 않음,
2004. 1. 2. 오전 3:06:4804. 1. 2.
받는사람
I know the PE loader fills in the table.

I'm using MASM.

I found an easy way to run time optimize call from to usual:

call->jump table->function to

call->function E8+offset

My code work fine, but I needed a way to pick the right import to
assign to the correct function. My solution was a hand-made table:
0,-4,-8,etc into a copy of the import table to select the correct one.
The table is hand made because I have not found a way to garantee the
order of the imports.

Is there a way to get MASM or LINK to garantee the order? Or...

Is there any way to get a vtable interface to kernel32.dll?

Or I could make a name table and use LoadLibaray and GetProcaddress,
but I would prefer to avoid doing this.

Thanks.

Ivan Korotkov

읽지 않음,
2004. 1. 2. 오후 2:18:2504. 1. 2.
받는사람
<Subject>

By linker. It's not compiler's business whether a function resides in a DLL
of in another module.

> I know the PE loader fills in the table.

There are many import tables. Loader fills in the import address table.

> I'm using MASM.

Great.

> I found an easy way to run time optimize call from to usual:
>
> call->jump table->function to
>
> call->function E8+offset

Pardon? What's that? E8 is opcode of a near relative call. You perhaps mean
you call a function directly via IAT entry? That's FF /2.

> My code work fine, but I needed a way to pick the right import to
> assign to the correct function. My solution was a hand-made table:
> 0,-4,-8,etc into a copy of the import table to select the correct one.
> The table is hand made because I have not found a way to garantee the
> order of the imports.
>
> Is there a way to get MASM or LINK to garantee the order? Or...

Never heard about. I guess there isn't any. Imported functions are not
forced to be sorted either by name or by ordinal.

> Is there any way to get a vtable interface to kernel32.dll?
> Or I could make a name table and use LoadLibaray and GetProcaddress,
> but I would prefer to avoid doing this.

That's the easiest way if you don't want to lay all calling details on
compiler/linker. It's not a big optimization, though.

Ivan


Matt Taylor

읽지 않음,
2004. 1. 3. 오전 12:46:3104. 1. 3.
받는사람
"ThoughtCriminal" <karl...@hotmail.com> wrote in message
news:2e7c19fd.04010...@posting.google.com...

> I know the PE loader fills in the table.
>
> I'm using MASM.
>
> I found an easy way to run time optimize call from to usual:
>
> call->jump table->function to
>
> call->function E8+offset
<snip>

As Ivan pointed out, a direct jump is not a big advantage. Modern CPUs are
very good at branch prediction. The degenerate case here is code that is
only executed once and makes an indirect function call. If it is executed
more than once, the BTB will cache the destination address and the call will
be predictable after the first execution. Since the degenerate case is both
rare and trivial, it is of little consequence.

If you were designing a loader or perhaps working on a compiler/linker, then
perhaps it would be a neat optimization. Tracking all of your calls to
imported functions would generate a lot of data. You may be able to use the
reloc table for this purpose if it exists in your binary. Any other way is
too much of a pain to be worthwhile.

-Matt


ThoughtCriminal

읽지 않음,
2004. 1. 3. 오전 8:06:2204. 1. 3.
받는사람
"Matt Taylor" wrote in message news:

> <snip>
>
> As Ivan pointed out, a direct jump is not a big advantage. Modern CPUs are
> very good at branch prediction. The degenerate case here is code that is
> only executed once and makes an indirect function call. If it is executed
> more than once, the BTB will cache the destination address and the call will
> be predictable after the first execution. Since the degenerate case is both
> rare and trivial, it is of little consequence.
>
> If you were designing a loader or perhaps working on a compiler/linker, then
> perhaps it would be a neat optimization. Tracking all of your calls to

This optimization can only be done at runtime. Compiler/linker doesn't
know anything about API function entry points. I need the PE loader to
do its job first.

> imported functions would generate a lot of data. You may be able to use the
> reloc table for this purpose if it exists in your binary. Any other way is
> too much of a pain to be worthwhile.
>
> -Matt

I found a simple way to do it in 22 instructions some data tables and
labels.

The basic idea is I put a label after the call or invoke. Get the
address of that label and adjust the address to the dword after E8.
That address is also stored in a table that will use that address to
figure out the new offset for E8. I also need a table to store the
correct function offset since I cannot count on the oreder of
functions in the IAT. I also make a copy of the IAT, which I may not
need. I'll post my code. Then you can step thru a debugger and see
whats going on. Its first draft however, so it works, but probably
not for every case. I'm thinkig of taking Iczelions tutorial #3(make a
window)are a real example. I'll be using more than kernel32 then.

There are some imports that are never used to help in testing.

The map file:
0001:00000000 __imp__CreateFileA@28 00401000 -12
kernel32:KERNEL32.dll
0001:00000004 __imp__ExitProcess@4 00401004 -8
kernel32:KERNEL32.dll
0001:00000008 __imp__VirtualAlloc@16 00401008 -4
kernel32:KERNEL32.dll
0001:0000000c __imp__Beep@8 0040100c -0
kernel32:KERNEL32.dll
my edit^
.XMM
.686p
.MMX
.model flat,stdcall
option casemap:none
option proc:public
option dotname

assume cs:FLAT,ds:FLAT,ss:FLAT,fs:NOTHING

_TEXT SEGMENT PUBLIC PARA FLAT 'CODE'
_TEXT ENDS

_DATA SEGMENT PUBLIC PARA FLAT 'DATA'
_DATA ENDS

public mainCRTStartup

externdef _imp__Beep@8:NEAR
externdef _imp__CreateFileA@28:NEAR
externdef _imp__ExitProcess@4:NEAR
externdef _imp__VirtualAlloc@16:NEAR

LCALL@4 TYPEDEF proto :dword
FCALL@4 TYPEDEF PTR LCALL@4
LCALL@8 TYPEDEF proto :dword,:dword
FCALL@8 TYPEDEF PTR LCALL@8

$Beep TEXTEQU <LCALL@8 PTR __imp__Beep@8>;E8 optimzed call
Beep TEXTEQU <FCALL@8 PTR _imp__Beep@8> ;FF function pointer call
$ExitProcess TEXTEQU <LCALL@4 PTR __imp__ExitProcess@4>;E8 optimzed
call
ExitProcess TEXTEQU <FCALL@4 PTR _imp__ExitProcess@4>;FF function
pointer call

;///////////////////////////////////////////////////////////////////////////////
;// The IAT segment stores the import table address of declared
;// functions. This is used by GetImportEntry to get the function
entry
;// points and store these in a buffer in the same order as in the
;// import table.
;///////////////////////////////////////////////////////////////////////////////
_DATA SEGMENT
align 16
INDEXSIZE=4
NUMINDEX=(IATend/INDEXSIZE)
IATINDEXE=(NUMINDEX*INDEXSIZE)-4
IAT:

__imp__CreateFileA@28:
dd _imp__CreateFileA@28
__imp__ExitProcess@4:
dd _imp__ExitProcess@4
__imp__VirtualAlloc@16:
dd _imp__VirtualAlloc@16
__imp__Beep@8:
dd _imp__Beep@8

IATend=$-IAT
_DATA ENDS
;///////////////////////////////////////////////////////////////////////////////
;// The RELOC segment creates a table to store the function entry
points
;// obtained by GetImportEntry in a buffer in the same order as the
import
;// table. It is filled highest to lowest address.
;///////////////////////////////////////////////////////////////////////////////
_DATA SEGMENT
align 16
RELOC:
Ientry dd 32 dup (0) ;Default 32 redirections
_DATA ENDS
;///////////////////////////////////////////////////////////////////////////////
;//The MAP segment defines a table that stores the call address to be
;//modified and an index to the proper entry point in the IMPORT
segment.
;//The MAP format is:
;//
;// dd address to be changed-1
;// dd function index
;//
;//Order of MAP elements does not matter. The index resolves the
correct
;//function. The index is maually entered.
;///////////////////////////////////////////////////////////////////////////////
_DATA SEGMENT
align 16
MAPITEMSIZE=8 ;Size in bytes of a MAP entry
MAPITEMNUM=(MAPend/MAPITEMSIZE) ;Number of functions to modify
MAPSIZE=(MAPITEMSIZE*MAPITEMNUM) ;Total size of MAP
MAP: ;Function address MAP+0
dd offset .1_Beep-1
TARGET: ;Function index address MAP+4
dd -0
dd offset .1_ExitProcess-1 ;Function address MAP+8
dd -8 ;Function index address MAP+12
MAPend=$-MAP
_DATA ENDS
;///////////////////////////////////////////////////////////////////////////////
;// The code:
;///////////////////////////////////////////////////////////////////////////////
_DATA SEGMENT
align 16
mainCRTStartup:
;////GetImportEntry/////////////////////////////////////////////////////////////
mov ecx,SIZEOF Ientry/4 ;Size of RELOC buffer in DWORDs
mov edx,RELOC[SIZEOF Ientry/4] ;Start with highest address of RELOC
mov eax,IAT[IATINDEXE] ;IATINDEXE sets eax to the end of IAT
mov eax,[eax] ;Get real import table address
GetImportEntry:
mov ebx,[eax] ;Get entry point
mov [edx],ebx ;Store address in RELOC, high to low address
sub eax,4 ;Decrement import table pointer
sub edx,4 ;Decrement RELOC table pointer
sub ecx,4 ;Decrement loop count
jnz GetImportEntry

;////FixCallTarget/////////////////////////////////////////////////////////////
mov ebx,SIZEOF MAPSIZE
lea eax,RELOC[SIZEOF Ientry/4] ;Load highest address of RELOC

FixCallTarget:
mov ecx,dword ptr MAP[ebx-MAPITEMSIZE] ;Address to modify
mov edx,dword ptr TARGET[ebx-MAPITEMSIZE];Index to correct target
function

add edx,eax ;Add RELOC address offset to index
mov esi,[edx] ;Index resolved entry point target

mov edi,ecx ;Address to modify
sub edi,esi
not edi ;Need negative offset
mov [ecx-3],edi ;Put relative offset DWORD after the E8
sub ebx,8
jnz FixCallTarget
;//////////////////////////////////////////////////////////////////////////////
invoke $Beep,0,0
.1_Beep::

invoke Beep,0,0

invoke $ExitProcess,0
.1_ExitProcess::

ret
_DATA ENDS

end


!!!8 space tabs make formatting a nightmare!!!

Any advice to make this better appreciated.

Thanks.

Matt Taylor

읽지 않음,
2004. 1. 3. 오후 8:48:4004. 1. 3.
받는사람
"ThoughtCriminal" <karl...@hotmail.com> wrote in message
news:2e7c19fd.04010...@posting.google.com...
> "Matt Taylor" wrote in message news:
<snip>
> > If you were designing a loader or perhaps working on a compiler/linker,
then
> > perhaps it would be a neat optimization. Tracking all of your calls to
>
> This optimization can only be done at runtime. Compiler/linker doesn't
> know anything about API function entry points. I need the PE loader to
> do its job first.

Compiler/linker can insert a stub that executes after the loader and before
your code executes. In C/C++ they normally do anyway.

> > imported functions would generate a lot of data. You may be able to use
the
> > reloc table for this purpose if it exists in your binary. Any other way
is
> > too much of a pain to be worthwhile.
> >
> > -Matt
>
> I found a simple way to do it in 22 instructions some data tables and
> labels.

<snip>

How are you populating the RELOC table? I didn't see any macros/code that
managed that. This is the problem I spoke of. I guess you could do it with
macros, but that only works for a single asm file.

-Matt


ThoughtCriminal

읽지 않음,
2004. 1. 4. 오전 5:31:3404. 1. 4.
받는사람
"Matt Taylor" <pa...@tampabay.rr.com> wrote in message news:<HgKJb.109453

<snip>
>
> How are you populating the RELOC table? I didn't see any macros/code that
> managed that. This is the problem I spoke of. I guess you could do it with
> macros, but that only works for a single asm file.
>
> -Matt

Look for: Here->

;////GetImportEntry////////////////////////////////////////////////////////////////


mov ecx,SIZEOF Ientry/4 ;Size of RELOC buffer in DWORDs
mov edx,RELOC[SIZEOF Ientry/4] ;Start with highest address of RELOC

mov eax,IAT[IATINDEX] ;IATINDEX sets eax to the end of IAT


mov eax,[eax] ;Get real import table address
GetImportEntry:

mov ebx,[eax] ;Get entry point
mov [edx],ebx ;Store address in RELOC, high to low address

Here-> ^^^^^^^^^^^^^
eax has the address of the real IAT. Grab the entry point and copy to
RELOC.

sub eax,4 ;Decrement import table pointer
sub edx,4 ;Decrement RELOC table pointer
sub ecx,4 ;Decrement loop count
jnz GetImportEntry

I must admit I'm having a hard time thinking of good descriptive names
for all the data structures. I don't use macros. Since the whole IAT
is available at run time that should not be a multi-file problem. I'm
not sure how the linker will treat global:: labels. I think I will
need to find a way to export that info for multi-file support.

전체답장
작성자에게 답장
전달
새 메시지 0개