找回密码
 加入计匠网
搜索
热搜: BIOS ACPI CPU Windows
查看: 13368|回复: 0

[转载]WINDOWS 2K Dll 加载过程

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]1 B8 B6 e0 W% W. F$ S  w
/ f" B+ w: `3 X2 v8 Z3 E' s
WINDOWS 2K Dll 加载过程
5 A) c8 q4 d/ z4 [jefong by 2005/03/30
) {8 o" b( }; w. |0 i7 x$ S这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
- m* J2 x# j& s+ Q在windows中exe可执行程序运行时都会调用一些DLL,例如KERNEL32.DLL和USER32.DLL等系统的dll。但是dll是怎么被加载的呢?通常,大家都知道在编写dll时会有一个DLLMain的入口函数,但是实际上这个函数并不是调用dll时最先的工作。首先dll需要被加载,然后要进行初始化分配,再之后才进入DLLMain。还有可能你的一个dll中还会调用另一各dll。那么dll到底是怎样加载和初始化的呢,我们来参考一下Platform SDK中的“Dynamic-Link Library Entry-Point Function”。" x* Y4 \& A  z0 u. M# A# R
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
% q! @3 y( i7 Z) k4 _% X* D1 @初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。) |* C- \" n4 A! ]$ `! y
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
  o0 ?; I1 }- p  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。  X; d5 h+ `# j' L$ D
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
- h. P+ o, `9 O) n- h//=============================================================================, K# I' k. s. I9 O3 b
// Matt Pietrek, September 1999 Microsoft Systems Journal' O2 E0 z9 L+ {  S& T8 `9 T/ z* h8 ~
// 中文注释部分为jefong翻译
& q* F; E+ ]4 v+ P//. O$ {/ q4 w+ D- t# q
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
  q8 G) ]$ h* s" g; p//
8 ]- ~4 ]; m* e+ a$ ^' H// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
9 n4 w6 i/ K3 n6 @% l/ o//=============================================================================
! }# f+ A) O6 n, b4 n$ i, q2 h+ ?
/ l* V+ f. `5 `  M8 A6 A#include <ntexapi.h>    // For HardError defines near the end
. X5 B* W4 r1 l0 ]/ t7 P( Y. ^
+ M* y5 `1 c. ^/ t1 {5 D- m// Global symbols (name is accurate, and comes from NTDLL.DBG)
# K/ _& T8 X2 c' F//  _NtdllBaseTag
: ~, G; M+ H# A/ I: h* s//  _ShowSnaps
7 k* m6 J( N. j5 [3 {/ t2 O//  _SaveSp
( {8 F: A2 B/ E' v5 q) t1 C( b//  _CurSp
4 D! [; @8 R$ |) w# b4 e1 x: t//  _LdrpInLdrInit- y$ {. l3 n* |  L1 `
//  _LdrpFatalHardErrorCount6 f/ G) Y' Q, N/ ]. \/ Y7 n# U3 l
//  _LdrpImageHasTls
5 {" h  ^! }/ |+ w8 H3 P4 M5 n
3 l8 ?; {9 C0 _4 n  [5 l; g1 XNTSTATUS0 V% F: r1 A  k% N
LdrpRunInitializeRoutines( DWORD bImplicitLoad )' H" q1 S& Q  p2 \# s
{
! _, u" E5 U0 z    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
! x4 B8 H. C$ u" e    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();$ k% |/ V. e; M* \) V
. J* R1 M- S' k6 M2 O# h0 ^$ }. d
    if ( nRoutinesToRun )$ U! }' j1 E; d& I7 t! `
    {% P: _6 q- h7 Q  |# }' F
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。% @' v$ {! r/ S* w# W
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
9 k/ R1 a$ @3 @1 ^+ d, a                                            _NtdllBaseTag + 0x60000,
" A: ~6 {9 t; U( @0 K                                            nRoutinesToRun * 4 );
# r8 c3 C9 I' S7 q) H$ N* Q0 K3 b                            3 w9 _2 ^2 L2 S' c. u
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
1 A" u2 {4 H$ d* @+ H            return STATUS_NO_MEMORY;
6 R' [, w) p% e( ~; W; I    }
8 V7 \( c+ w& K    else
4 D9 b! m( @- A* ~  N& }, m$ l' j; ~        pInitNodeArray = 0;2 d7 j4 H# h& P0 b3 i! O

* Q0 {& A, w$ o% `    //第二部分;
, b! F9 t' c: v/ p- x) T    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
9 D) J7 l0 b. G* s$ d' d    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);( w8 B% |  I3 i. K* B0 Q0 u- M
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
) F5 R0 K# {& T! t" d( d, h; ~        
7 f- w8 v' c& E; V/ B9 N+ R$ D1 s    if ( _ShowSnaps )
: g% d+ j5 ~) B3 Z. T    {
; N* Y2 f2 J) Y3 ?% i# x        _DbgPrint( "LDR: Real INIT LIST\n" );$ f! u  T# L7 Z; h" K7 c& |: p
    }: [; E% Y$ ]$ C1 |% ~' \, x
  s0 `5 e$ t# s
    nModulesInitedSoFar = 0;3 D: r: e  |" Z+ t5 h  x, @
) x2 v% q/ u8 F( H9 N3 i* k
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
3 |$ t* i0 k$ X$ t! y/ F1 ~! E: p    {& e5 L4 T! V* o0 A4 `7 H
        
; f- Q$ @" x% y. N! s        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
' z' \( V- \* d. ^9 c        {; c) F  h3 k+ q$ a
            ModuleLoaderInfo  pModuleLoaderInfo;6 c. e* j( q/ V
            
8 E; _3 l7 w! u% u            //2 q. O" k3 b7 v4 A0 p1 k
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
1 [+ S$ V$ Q( R& {7 p7 i3 X            pModuleLoaderInfo = &NextNode - 0x10;* E* d& |8 Z7 h' |
            6 I+ I9 \; V8 {' U+ @1 B
            localVar3C = pModuleLoaderInfo;         
6 n; p3 f% f2 l# n# X, |1 F+ ]7 U: H9 w7 S
            //
% I, V2 }& Z- }            // 如果模块已经被初始化,就忽略: ?, H$ V4 X3 h0 }
            // X_LOADER_SAW_MODULE = 0x40 已被初始化# t$ y2 K1 q6 m- j4 U# P0 K6 w$ D; P
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
: P! N- t: w( \1 M2 F3 C" ?, V- C' Y            {7 B7 o8 k1 `9 o9 k6 J* |4 j9 @
                //: B0 b: v6 Z4 y% h9 y  Z
                // 模块没有被初始化,判断是否具有入口函数
7 T7 q% d- S, z4 P' s4 T. k                //
" g/ S& `. \$ O                if ( pModuleLoaderInfo->EntryPoint )
2 ?2 x! Y4 j1 n7 H8 W1 w0 y% j                {# i3 N4 D+ N  z5 u" K8 z
                    //5 m$ p4 i3 W3 w4 R1 s
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
) H8 l/ D- e' \                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
9 q! x0 X: M2 p/ C: `, a6 M6 l3 V* y+ L/ q
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
% G0 X# L) I& `* F4 K      // 例如:
0 u( m' P6 c5 Y6 l& }, |( W9 T                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000* Y) \/ w7 @8 x  N: s' V
                    if ( _ShowSnaps )
# {6 k1 U  [3 L$ l$ n4 T                    {
2 |  Y6 d0 H/ k3 \& m                        _DbgPrint(  "%wZ init routine %x\n",
0 Z3 ]" f0 W, y                                    &pModuleLoaderInfo->24,+ L0 T2 v; ~) O. |: K$ O
                                    pModuleLoaderInfo->EntryPoint );: M) L4 ~2 e- T; N9 W# q
                    }) o$ J! G- Y. I% \6 |/ G" T

* t# ?  L  W- M" o1 ^- Z                    nModulesInitedSoFar++;
. [8 ^0 L6 W3 g1 j1 v( O                }, X' Y2 o. A5 u9 _( k
            }
! E  D+ n" `9 r6 _9 H7 c) a  z6 M0 j' Y; E) z+ O+ O% y
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。' o" G: \0 S/ i. E
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;+ O# B; D1 F: m$ X

( F* U# p6 e9 p& `% d' ~            // 处理下一个模块节点( j/ v, Y+ e. H& n
            pCurrNode = pCurrNode->pNext$ D/ b- j" D& B; j9 g& ~. m
        }' R$ t7 F" b; `
    }& p. H" _: b# M
    else
! l! z) f% ^! W# ?& _8 `9 O+ L1 l+ q    {7 h: s' w! f8 G- s; o: X& [. ?
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
+ |1 I8 c! G6 P" t* Q    }
6 q6 q* l( o5 h+ _3 m$ H, g# r. j8 h/ o   
* e+ ~% J: F+ q4 ?    if ( 0 == pInitNodeArray )$ f. _# K) R) p3 y# |) O
        return STATUS_SUCCESS;) J0 x" H, o, J3 s
% c0 Z/ l8 F# x1 k  d2 x/ x7 b
    // ************************* MSJ Layout! *****************
& X6 b- Z+ B2 [    // If you're going to split this code across pages, this is a great
8 I" k& F' F( R    // spot to split the code.  Just be sure to remove this comment, y# N, V2 O; n+ Q" e3 q
    // ************************* MSJ Layout! *****************
  k& c! x0 G! K& K8 S   
8 E* h1 x+ d% L) n    //
# Y( O! B  f! f    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
& y. M" S$ x( X; V$ ~    // 第三部分,调用初始化部分
  p% Q' J$ B4 u9 M$ Y    try     // Wrap all this in a try block, in case the init routine faults
1 @% N8 H2 o: [- M0 ?! s) h6 U    {& c1 w' j9 u+ q8 \3 L
        nModulesInitedSoFar = 0;  // Start at array element 0
7 b8 f- L/ H$ J5 W' Y: P# H; x0 y
        //
% ~- v2 Y3 h( c  e  Z        // 遍历模块队列4 L7 q+ B% v8 Z" h/ s) X
        //1 Z: h' b" @0 |4 y* J7 q4 K
        while ( nModulesInitedSoFar < nRoutinesToRun )
4 ?7 h" Q0 y- z% e        {+ F* B6 B7 z% p: R& t* C' A! @
            // 获得模块指针
, n! g2 b" _& G. P            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];0 V- k. T+ q# f7 ^* c

: Y& ]( A4 H! F            // This doesn't seem to do anything...
/ N% K, ]2 a6 P& c8 Y8 w            localVar3C = pModuleLoaderInfo;
1 ]  K! t/ y& f% g            
4 |% z& ]( c, U* @0 V            nModulesInitedSoFar++;. d4 G4 e( o) S# a; I/ w0 W
               
+ N9 n/ T! x9 }5 [! c) V. l+ G8 t' l            // 保存初始化程序入口指针6 ?9 N! X4 U' G+ C& S
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;! ]% h. Y9 i2 ~8 ]" V
            # a( X" n# F! `& u; o2 Y( @
            fBreakOnDllLoad = 0;    // Default is to not break on load5 v+ {$ P, y5 d) S% l6 H

+ e  x7 _% g7 i* V+ }+ H: p            // 调试用
) u" u, Z) x# N, d            // If this process is a debuggee, check to see if the loader
( t8 G+ G0 @$ c; c            // should break into a debugger before calling the initialization.. b' K4 _0 A1 S0 [! O* C* E" `. z
            //9 q; x; ]6 }- c* x; Q
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()# a' E& N8 I; ]) D; N# j( {- K
            // returns. IsDebuggerPresent is an NT only API.
; V7 }( W- _4 |: i            //* t4 F6 p3 F! M5 y( M1 C4 n! w0 S
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )2 I5 L7 m8 H7 k4 L% L# f
            {3 J3 Q# F+ R' Y* j* ?1 b) u
                LONG retCode;1 N" H. x% I6 T8 M6 L1 r2 K' l; J

7 j: u, G4 i( }2 I" f4 l# T                //              
/ P) x! W: Z! V) E: S0 B: l                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\% n/ R! r; w+ J# o- S: }/ T: U
                // Windows NT\CurrentVersion\Image File Execution Options": C0 g1 H6 f4 I! c: P4 ~; t1 s1 A
                // registry key.  If a a subkey entry with the name of
- q% I$ _  e$ t- M                // the executable exists, check for the BreakOnDllLoad value.
0 u2 k4 |# v9 P( s. E. V9 {  e/ p                //
. H+ c6 q( k3 i2 u                retCode =
6 R3 n' l5 Q. e3 [2 v+ R                    _LdrQueryImageFileExecutionOptions(+ B8 \4 m9 y; @+ @
                                pModuleLoaderInfo->pwszDllName,$ x" N4 d9 C. a$ u; e# O) I
                                "BreakOnDllLoad",pInitNodeArray$ z7 Z' K1 M2 h0 y9 B
                                REG_DWORD,7 |, g/ W$ n! |; U8 v
                                &fBreakOnDllLoad,+ r" I/ v( d9 o( T0 \, W- k
                                sizeof(DWORD),* }5 ]5 @+ V/ ^! L7 ?+ b8 O
                                0 );3 H, @+ u3 [, h( z! O3 b9 t

2 P* p0 t( n' O                // If reg value not found (usually the case), then don't' }* c3 H% B; Z7 B, b1 Y+ X/ ^
                // break on this DLL init
& w2 u+ k: }7 x8 _& {" v& A; h# f                if ( retCode <= STATUS_SUCCESS )7 u7 q8 [0 e  g
                    fBreakOnDllLoad = 0;pInitNodeArray% S+ R4 i7 z9 `% M2 ?! t6 [
            }, v4 ], J2 g5 x& E( E5 F
            6 L' W% O# h; @' H
            if ( fBreakOnDllLoad )5 w$ I% N2 _  ^7 y- m' \- h
            {           1 o0 [* N% M) z& {2 Y- k9 r
                if ( _ShowSnaps )
. ]% u/ e+ |8 ^& `8 I# X                {
4 p& s3 y$ i3 }# Y7 {- Y                    // Inform the debug output stream of the module name4 I* H9 R$ b! x5 S
                    // and the init routine address before actually breaking
1 h1 b, `  g/ @4 v9 j$ w" i6 F% s                    // into the debugger/ O$ |8 I! y) Y; @
/ [0 C/ J* e. P* q  D
                    _DbgPrint(  "LDR: %wZ loaded.",
; {& O) G% b  m* K4 C! u                                &pModuleLoaderInfo->pModuleLoaderInfo );
' Q. a1 {& P" w. g5 s0 U! z8 K4 h                    , w0 H0 N) ~" R) F* E, l
                    _DbgPrint(  "- About to call init routine at %lx\n",
7 [4 y  _2 ]& J3 r2 n                                pfnInitRoutine )
; K9 ]7 g. R8 |                }3 S- T) i5 ^! f  e0 y/ P
                8 E$ i# u1 t" ?7 l* a6 [! F0 g
                // Break into the debugger                              2 B4 i4 ]' q6 ?* N" o! _( \) x
                _DbgBreakPoint();   // An INT 3, followed by a RET
! N: i4 T/ F& i2 T            }
. k$ f- n5 f1 v% }: i4 ^            else if ( _ShowSnaps && pfnInitRoutine )# w! M/ v; W1 i( m, u
            {
6 Z0 V! N$ E5 ], m& a0 Q$ U                // Inform the debug output stream of the module name
. |. ^2 B8 e( l4 }/ B4 Q8 b- B  n* u1 M                // and the init routine address before calling it               
( i0 ~- J3 O* b- }                _DbgPrint(  "LDR: %wZ loaded.",: B2 G) ^9 r: {' d, g
                            pModuleLoaderInfo->pModuleLoaderInfo );" q% h! V6 f9 Y4 Z& k: ~

: w. C7 b" F& b: e# `$ k                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
+ S; n: @; E: z& S( S6 e            }
: R  D2 p) Q7 O                    
; F* E" i( A) h2 S+ }+ O            if ( pfnInitRoutine )
& T' l& I. F2 S+ H7 ~            {( H3 v& X% u/ c. O+ P
                // 设置DLL_PROCESS_ATTACH标志
1 Q, ?7 e# ?- d  I4 d                //
% [' q: t: K( w5 Q1 x- H                // (Shouldn't this come *after* the actual call?)
: x1 n0 z& y4 v2 X1 F" G( g$ }  u                //
9 A$ p& S3 D& t) k                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
; [3 I* y8 z! m5 Z                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
) W0 E( t( n/ {7 K  P0 T
: |- r/ A: M2 ]& }                //& b3 ]2 v6 e! N$ Y  e4 h$ p# h
                // If there's Thread Local Storage (TLS) for this module,
+ X  X$ _" Q* S  b1 ]+ m                // call the TLS init functions.  *** NOTE *** This only: y! v% }& T5 s3 ]: b
                // occurs during the first time this code is called (when
2 N2 V- E" p5 B+ E                // implicitly loaded DLLs are initialized).  Dynamically5 R7 z3 @( `5 i8 v/ a% N  z7 N3 V" a
                // loaded DLLs shouldn't use TLS declared vars, as per the
6 X0 L6 ~3 s. a0 z" }) ]& s                // SDK documentation* c# ?! V& j3 J& X9 T7 w; j6 a9 o
                // 如果模块需要分配TLS,调用TLS初始化函数
1 {) C# A  z& J. {) p: [  T# U  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时3 T( g, X$ C: N) X8 @6 ]
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量# @+ `$ Z3 X1 V% Q- `8 ~4 t
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
, u: p4 Y7 q+ _1 I! _1 @                {6 H- V# ^! D4 f: o( }- k1 y
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
: }; i# b  G. i1 t                                                DLL_PROCESS_ATTACH );
3 ]; Z! |3 ~7 W4 b% o7 G9 v                }5 o0 K% \; m8 w  f0 r
                ' l8 N( A& Q! e
; g7 X* V5 x+ B6 p' t) v0 g
                hModDLL = pModuleLoaderInfo->hModDLL4 R& E+ |1 u" t/ H* q- H

5 p8 k5 h1 Q- |7 ~% z4 i& R8 p                MOV     ESI,ESP // Save off the ESP register into ESI
% A+ B7 B- G3 D1 t- {; v  
3 D! a9 t1 B* V5 h  // 设置入口函数指针               
" Z, `! t1 `( N& Z                MOV     EDI,DWORD PTR [pfnInitRoutine]                     6 i% y. r5 Y( R. x/ e

/ d3 e5 j0 ?  T                // In C++ code, the following ASM would look like:
1 _+ c, _# e$ V7 p. y                //% m% b0 k3 L+ Q
                // initRetValue =
9 C7 v! P1 j4 C" l" r4 T' y5 A                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);8 x; f/ b% _8 Q+ F
                //# V5 U, s% @* T3 r$ Y

& a, ?. T8 e/ a% B                PUSH    DWORD PTR [bImplicitLoad]
" g0 u# w& ^5 ^) O# \                + v* v5 ^, m+ V# I3 S# n& k/ Q, E* N
                PUSH    DLL_PROCESS_ATTACH
( }4 s$ S. Y! t) ~9 p9 _# g, t                ( e/ T1 t* ]6 J5 k/ ?1 E: x
                PUSH    DWORD PTR [hModDLL]3 Y) S3 O. y/ N- {  i: `
                0 ^- I, e( l( x4 \; F% m3 P% }, x
                CALL    EDI     // 调用入口函数
+ e/ ]1 m9 v$ L               
3 X) a3 ?6 e2 {" d                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值& u- t. P: I  r

0 ?3 E& O4 ^8 |8 X8 C  c0 P; ]                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the7 H5 N. }! J% l7 ?
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns- B' V( Z; S& m& N5 H
+ ]! q, O  t) U; ]0 [" U% p! I
                MOV     ESP,ESI     // Restore ESP to value before the call8 r8 l7 z0 {0 b- n) F- m2 v1 w7 c# v
. i# F7 A: E7 p+ }9 N" \& `
                //8 y& r. M- ]( g+ |
                // 检查调用前后的ESP值是否一至! N% ~9 X' X- B* v: V: F' ^
  // , o. E( N7 R! c0 k4 N/ Y/ q: m
                if ( _CurSP != _SavSP )
6 Q, Y- }2 P* A! z" t                {# @8 t/ A( W( j. V
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;5 ^6 F, W3 l! e
. Y1 x4 e" B/ D: p# }2 J
                    hardErrorRetCode = 3 K# v( x% E- g9 a
                        _NtRaiseHardError() J8 f. K% l- m8 e$ d% H
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
  H5 [! K& C" ]( b# r                            1,  // Number of parameters; O$ z5 T# T6 A
                            1,  // UnicodeStringParametersMask,
- U  |/ u' @, b. M2 v( n  Z                            &hardErrorParam," j6 I5 p4 \& f
                            OptionYesNo,    // Let user decide% l! F3 u3 ]1 L- J3 u- D* ?
                            &hardErrorResponse );
# }( [& t" c/ L. D                                            
7 p4 {) z; ~- T9 f" c1 }- n                    if ( _LdrpInLdrInit )1 L' ~4 D* N3 A" e* f( I5 l0 W
                        _LdrpFatalHardErrorCount++;' p0 r  I5 ~' g0 R# x5 N1 W) G
' `( ]' J$ h1 r! O0 j
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)! p% ^, F7 K) L# g
                        &&  (ResponseYes == hardErrorResponse) )
" ~! _# t1 l5 y# e0 Q; i* s                    {
0 C. g% E& P" |# t' M                        return STATUS_DLL_INIT_FAILED;& Q4 ?' [9 s5 N8 I5 n  V
                    }
' @5 k& E$ M9 m                }: s% t, Z+ M. G
, G1 h1 R* Q; r8 a1 Q" o3 R
                //
  j7 Q: v: ]# }! A                // 入口函数返回0,错误1 H% q4 e9 @5 A$ N8 d' Q/ o* {$ b! y
                //& O* c: m, {$ |1 h; B' Q
                if ( 0 == initRetValue )
3 C7 |/ y! Y+ t& j# d9 M' y5 g! I                {( c/ M: `( A! I. F- c! v
                    DWORD hardErrorParam2;
$ e( o* v1 _$ H6 {8 W                    DWORD hardErrorResponse2;0 z5 a: r8 A) g' N
                                        0 }; ~- ?" W9 n9 v. R. M" c8 e
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
4 b/ ]/ [' _5 A- D! _% Z, j                    + ~6 X& }8 N! B! i
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
' ~7 T  K% I: c3 p/ T                                        1,  // Number of parameters% K$ h. I( x! l  M) }5 [
                                        1,  // UnicodeStringParametersMask
$ z) }+ h2 [, T! j9 [$ X- Y8 e# z                                        &hardErrorParam2,
3 K+ D9 r7 \* I                                        OptionOk,   // OK is only response
( \  |2 P- P) R6 z6 a* T8 F  a                                        &hardErrorResponse2 );, R( _6 m4 b' }! s+ J4 E
                                                            ; i: E  ^% \! B/ T, z
                    if ( _LdrpInLdrInit ). M3 Q  U, p; R& _4 \9 d
                        _LdrpFatalHardErrorCount++;9 ?9 M& ]  d3 B  h  i5 V2 b

( o$ r3 m$ q: w& T% }6 C                    return STATUS_DLL_INIT_FAILED;% |8 k4 `" V! Q/ n. s3 d
                }; \/ b# a4 o, ]/ I6 x9 q
            }
, w  c6 Q+ i5 T9 c: F: `        }- n" }/ h, w- a! b

* c/ n1 N% o6 c        //
- e( A9 A' m! z$ v3 Q+ B: E. q        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时) e- m% d8 G# f' a
        //      + R$ b$ t  v1 t
        if ( _LdrpImageHasTls && bImplicitLoad )
8 f+ V3 ^& V& ]( P1 O        {
0 f- V, S# e2 R) V. S: q1 {            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
+ _7 K6 k7 u  t" F                                        DLL_PROCESS_ATTACH );0 ?2 N0 [4 X- N3 ?  G
        }1 O" w: S/ P, D- O0 k* p
    }5 f5 N9 `  A; D' T" a& R
    __finally
) \, o& s: G( x5 \6 t3 j! {) r" e3 o    {" e* w  V5 q$ u+ b1 Y! F6 a/ k
        //3 d3 a5 h' G# O. r- h9 i2 A
        // 第四部分;
0 ?% y5 D/ x: x' A: d& f3 u        // 清除分配的内存
$ a% }; K! u$ O3 P# B% h        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );  ?/ U- q* l7 p: j: _' S% n$ B* Q
    }
: E( o0 a2 h/ |2 N/ s. k7 s* w9 k5 P" W4 w
    return STATUS_SUCCESS;
8 n3 x" v/ X# K" m3 A9 i: h}   " m2 D, f: Y& W: x4 n

# i2 \8 v: U; j  M这个函数分为四个主要部分:& s. l! k6 K! t& W6 ?, p
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。' m# m4 Y$ N" J, A* S5 }  {- }! M$ e
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。8 H. l7 ~1 f7 ^& x1 l+ u
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。! d" W) d1 X% `' @
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
9 y3 k: i( w& q/ r在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
- I# h, Z- ?4 c  h& X四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

Archiver|手机版|小黑屋|计匠网

GMT+8, 2024-5-19 07:53 , Processed in 0.036572 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表