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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]5 u3 \$ V: n% I2 Z* \

4 u# _, e3 w! |0 UWINDOWS 2K Dll 加载过程
- L: F! s  K& B( ?jefong by 2005/03/30
! y, U; z7 ^2 i, r4 P这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
1 L8 S+ P  M8 l! p在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”。: F, k* G) l. \' D
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。1 c! }5 D, d: c% H  F
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。5 D5 D+ r  {2 r" G$ P; F
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。. f0 d! L+ y  Y
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
0 B( k3 S- M9 I; G6 Q8 @  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
& ^: |3 p$ S* v5 X: C" A//=============================================================================3 j  v1 S, o9 y. o- `9 \" X
// Matt Pietrek, September 1999 Microsoft Systems Journal
: B. o! u) L- H  Q* K! [// 中文注释部分为jefong翻译
7 v; E7 L& h' ]+ I//
) [9 n! o+ `6 l+ X4 u// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
, l7 ^# }" _/ C//( b6 ~2 M' H6 y  X4 h/ U# W
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;8 i8 j' p+ ?7 Q5 n9 s
//=============================================================================- C( p* ~0 m1 a% _, d
0 u& j6 q# H1 {
#include <ntexapi.h>    // For HardError defines near the end; V" W5 b& p: S8 J3 |
* @+ B3 J, e! J, ~0 e
// Global symbols (name is accurate, and comes from NTDLL.DBG)
, G& m2 J* |3 U" N1 U2 u//  _NtdllBaseTag
/ M0 F" N/ b/ Z//  _ShowSnaps2 E# ]9 i( g. c/ I/ E8 L- z% G
//  _SaveSp+ r- I7 r. e& h3 C. W9 ?7 Z
//  _CurSp
* W( P) C8 J* J- Q" F$ r" F5 W//  _LdrpInLdrInit
8 z8 K& }0 `% D; }9 Y  r- s//  _LdrpFatalHardErrorCount
8 k' E6 F# N: Q# J$ [+ {2 o//  _LdrpImageHasTls
5 v  `* |5 s! u( G* O1 i) i8 O8 j4 O; i' O( p. Y/ p
NTSTATUS- W' L0 y6 |4 l
LdrpRunInitializeRoutines( DWORD bImplicitLoad )) I- _; o1 C+ V6 ?* X8 y8 N
{: `% G) \  ?: r5 O
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
& Q- x* T8 v6 Y. e+ H( \( X    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();% P" B# _) K9 k5 h& e5 S3 S* J
" {, x4 B& \% v- C1 c) d
    if ( nRoutinesToRun )6 Q* w2 \; l- \; _9 o
    {4 E4 q( C* t2 j- G
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
7 H# f: U- ^4 E        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),0 w$ X, N/ X* g6 I. H2 w
                                            _NtdllBaseTag + 0x60000,
0 I% `2 u' o0 Z$ [. w5 t/ \                                            nRoutinesToRun * 4 );, P" d( R' _  T0 i
                              b, X- u7 h" n' q' R* i7 T4 C6 L
        if ( 0 == pInitNodeArray )    // Make sure allocation worked, F- C/ T$ }8 c2 c$ K. E6 J: f
            return STATUS_NO_MEMORY;
6 V! {: b! S2 G" X/ V$ T    }
, _/ ]  a$ D$ ?6 o' L    else4 V$ N' ^! h/ `7 L5 n
        pInitNodeArray = 0;: V1 a+ i. R4 R# l6 l0 e3 G3 F

4 P; B* k# \! R, P( l    //第二部分;& k8 R9 `. O, J# z/ T: c: A3 `, I
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。2 E4 O$ s, y8 D% n4 @% ~, U
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);7 g6 V0 J7 z6 F, _( d+ {7 L: f( l5 f  G
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
+ y+ u8 ~' b; \, R        5 {/ N" W" u% J# l
    if ( _ShowSnaps ), v9 V' g+ Y9 g; Q$ Q5 f
    {" M; z& S7 r- o0 o5 ~; Z" Z
        _DbgPrint( "LDR: Real INIT LIST\n" );
# X" D) _/ F1 O0 ~4 o' _. G    }2 x+ U4 f/ @: J' k  c- u
) {6 u* }+ ?( x2 S5 M; b& l
    nModulesInitedSoFar = 0;
$ \# Y, I7 t$ x/ ]: v, b$ q* V5 Q% E, f& y% E0 W
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块9 ?: l: p: W# l& H
    {
- L$ U) `3 w( P7 H, b+ W        9 n! ^! F- U) x5 U
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块! W! q! g' ]) y9 \3 P* r# n
        {6 B& A' J0 K% y) z$ o0 Q
            ModuleLoaderInfo  pModuleLoaderInfo;
& O7 ]8 V6 \) J! P3 v0 a& v- `, `) O            3 K1 `% O" ?! m* y
            //
) T8 K. G/ ]8 A+ g            //一个ModuleLoaderInfo结构节点的大小为0X10字节
. q5 k3 S4 ?% G, E            pModuleLoaderInfo = &NextNode - 0x10;
5 ?; v" t8 n5 A1 ]! \- m            , Q3 F) J3 L, I( t# d5 K* |" V1 @
            localVar3C = pModuleLoaderInfo;         $ g+ W( m- H% k$ X& y0 w* l( K

# o/ N: q  b; b% M% d            //
2 k- U1 M  V! n3 @            // 如果模块已经被初始化,就忽略
1 ~4 Z) D% k) w5 b( x6 M2 j            // X_LOADER_SAW_MODULE = 0x40 已被初始化
0 t  \+ a. S) P2 V$ O) W1 w            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )( C5 U4 U( e7 o! R9 ^3 b. u
            {
# F. h4 ]) z  b# F, E" X6 e                //% \1 p8 C( B. o9 h
                // 模块没有被初始化,判断是否具有入口函数" q0 O" ~& ]5 J: S
                //
2 j2 w5 Y# A5 a! A+ V' U! b                if ( pModuleLoaderInfo->EntryPoint )7 {: ~+ u3 k; A+ D# A! U7 q1 w* S
                {" t/ a* s9 c& @( P- D& g
                    //, i: g' F, E8 Q, \
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
6 s) [. p+ o6 g3 o" S! V                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
* z3 l% w& c- c( f' E: J% S: r4 O: `* E& s! I
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址- y) O' U( F, F6 g
      // 例如:
# w2 J- q) ]7 A9 r& r2 b. N2 e* F                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000: o2 Z: X! P8 W  K
                    if ( _ShowSnaps )
- m- k3 k' [* n9 j0 f* M' r: C9 s                    {
* ]  N: S2 k+ d5 W  j9 J                        _DbgPrint(  "%wZ init routine %x\n",0 @2 L, g: m! D! F7 M: D
                                    &pModuleLoaderInfo->24,
, L8 A! R9 Y3 ~) h4 I/ r                                    pModuleLoaderInfo->EntryPoint );2 {* ]6 e4 ]/ L" A7 O. p" l- t
                    }4 a- k% j* V/ h5 k* O% C4 s5 ~

( G4 P& c8 B8 p* N/ P- Z* U) T8 |                    nModulesInitedSoFar++;
! _6 B4 [/ \) d- J- A) [, ]                }$ G& C- ~, F, ~3 m
            }
( ?5 |. R  I, j0 H2 K4 @: y$ @" L/ u6 V$ e, l3 z9 L. b. t0 z% G
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。2 O1 K  S* M, P5 M8 I$ ~
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
2 o2 p: [8 Z; O$ Z* U. Q1 c5 ], r- _$ M1 _$ L- }
            // 处理下一个模块节点" f8 M7 z1 z; p+ s/ V9 C
            pCurrNode = pCurrNode->pNext( p. u* q$ u& s, [( E9 `: B
        }0 K* w6 L# _; O' @( ?
    }7 I; s7 E  q2 p& B$ ?* U
    else
+ \& m" I- F" F( N, m- {7 A/ Q    {. e7 j$ A' v( o* U' k$ g' K" Y
        pModuleLoaderInfo = localVar3C;     // May not be initialized???  m5 H7 F  y0 g6 N+ i
    }
) e# F. d* k1 `+ u* {9 E+ d      ?( s, A( C% O) y9 `
    if ( 0 == pInitNodeArray )
* a; B# `+ B8 L% W/ _( z/ r$ i        return STATUS_SUCCESS;
3 J' L% e. G5 V' F
+ a# {# s* O$ V( R" h    // ************************* MSJ Layout! ****************** Q! M% e/ }7 g% E
    // If you're going to split this code across pages, this is a great
& ~' C* @7 \8 z( A0 {    // spot to split the code.  Just be sure to remove this comment
! q7 x/ x, K8 ~( I. d) `    // ************************* MSJ Layout! *****************
2 M  N1 n0 q& u8 p: O* p    / ~" }: g, t5 ?; O2 P, e! G& o
    //+ V0 s. o& E" s, T* Z" M
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
9 l; _( |2 D5 @7 [  B  P: Z2 e    // 第三部分,调用初始化部分
' h! O& e% x1 D& G    try     // Wrap all this in a try block, in case the init routine faults" n0 M2 s1 S0 M2 r
    {
  g5 _4 H! Y( y# I! U6 z5 l        nModulesInitedSoFar = 0;  // Start at array element 0
& B% u0 U# W" G+ }9 d, ]: A: F: O/ \
        //
8 o7 ]( x9 @0 {* Z$ y        // 遍历模块队列: ?  b  R7 ~9 [! m- j0 n; f
        //6 C8 |8 i0 v( b& d3 K
        while ( nModulesInitedSoFar < nRoutinesToRun )
3 C, z' v0 f2 b, b% M        {9 C; b! V% P9 u) }+ c. J6 ^
            // 获得模块指针
+ x/ `( @0 d# N4 Q; c3 c8 `  }. a            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];; U' W! s5 r/ h. e( D# ?: c# S2 I

$ x4 _0 T/ g4 V+ ]% A$ x            // This doesn't seem to do anything...# E0 i4 G* r' J% [3 U
            localVar3C = pModuleLoaderInfo;
# T: B; ^% s2 D$ r2 I; j            
% k  U/ \. ~( {2 r; Y            nModulesInitedSoFar++;
- @+ d  V- z3 T' @3 Z$ B3 c6 M               
  \1 X+ R/ C. L; ?- N8 k            // 保存初始化程序入口指针( t/ @1 l( J" Q5 w7 r4 G
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;0 y+ Y6 s$ g2 p
            1 m1 w+ n: L- `; E5 z
            fBreakOnDllLoad = 0;    // Default is to not break on load
  [1 f# b  S( d9 ^; Q3 O$ m) l4 z
            // 调试用; r1 c" B. \; X& `6 Z* T% j  q
            // If this process is a debuggee, check to see if the loader+ g/ U! t5 d2 C* y' d
            // should break into a debugger before calling the initialization.' B8 m9 l' G/ y7 @2 C
            //
. j% v7 G! d' Z            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
- d0 W$ z% U2 n- y/ z* P            // returns. IsDebuggerPresent is an NT only API.
  z% D: l1 p1 W! j! w; R  M; E            //
, r! w# h+ @6 ?' z, o4 e& [; s            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )9 u1 {$ G% p; I& @: }% B. ?
            {8 c5 S( [2 `) _& n
                LONG retCode;7 h9 m' h* @- C. ^8 I7 O

' ~8 B$ B9 t, Z( S8 |                //                Y# h! Q$ j9 L) I) k
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\9 B7 U, n* g4 g1 ~
                // Windows NT\CurrentVersion\Image File Execution Options"
; Y. t5 }, p& M$ o, Z. {                // registry key.  If a a subkey entry with the name of/ h6 ~& w, h8 h- i3 z0 V
                // the executable exists, check for the BreakOnDllLoad value.
' E5 i. z( B1 o                //- j: y4 t0 P, i% y
                retCode = - Z. `( w4 t) E; h" k
                    _LdrQueryImageFileExecutionOptions(* M- `5 }$ v! p. Q
                                pModuleLoaderInfo->pwszDllName,
: J- F8 Z6 I5 p* E3 _/ v                                "BreakOnDllLoad",pInitNodeArray
' q  s0 @, O" m% Y) o" T1 z' t                                REG_DWORD,
' I: J. J% s7 e% N                                &fBreakOnDllLoad,$ Y, y( f$ ?$ K
                                sizeof(DWORD)," x/ R9 _3 T" F
                                0 );+ J, G% R6 H% j0 i7 W, b

6 T3 c( \6 ]  W: q; D                // If reg value not found (usually the case), then don't6 ^$ T8 p- O; z; p7 |) v! W0 p* n
                // break on this DLL init
! v. P: b% D( v* |( h5 z                if ( retCode <= STATUS_SUCCESS )9 G! l% k6 g. N& z# p
                    fBreakOnDllLoad = 0;pInitNodeArray
9 s# k; n3 t3 x7 l$ v& W1 Q3 Z            }- O8 T5 @! d9 L  [' A7 g
            % v# W1 u: u0 m7 ~) ^* W
            if ( fBreakOnDllLoad )  h; t; ]3 @. h' q9 {
            {           
& L: Q* B& i# Z5 W' g% G                if ( _ShowSnaps )! l9 R& g. `% o9 E; W
                {3 _4 d: o7 h3 k' ^
                    // Inform the debug output stream of the module name
9 u  @/ J' m3 K: e% v) f                    // and the init routine address before actually breaking* q2 ?$ V1 @+ C% \2 A
                    // into the debugger$ E2 w) H5 N9 R8 [) m
6 K6 }  N7 _* ]1 {3 V
                    _DbgPrint(  "LDR: %wZ loaded.",' q; s, r+ x( k" y! Z! r2 |
                                &pModuleLoaderInfo->pModuleLoaderInfo );
7 v& B6 Y  P! A% `( J/ Q( [6 K                    - C" V" f) p: f4 f
                    _DbgPrint(  "- About to call init routine at %lx\n",
" [% `' O  w6 e8 y; i                                pfnInitRoutine )
' X  o6 r+ w% a" D; V$ k  d+ T                }2 ~  q) C, O0 ~* H6 H
                - w+ P1 {1 |: e
                // Break into the debugger                              / ]& Y" q: B" D1 s1 i8 z
                _DbgBreakPoint();   // An INT 3, followed by a RET3 ?- r" o( ?% C. D3 x, }
            }- d6 E7 {+ @, ^' Y  X4 m
            else if ( _ShowSnaps && pfnInitRoutine )
0 D1 Z( y1 h6 v0 \4 j, P7 N  w            {
6 l0 e3 |' b. B6 @) |                // Inform the debug output stream of the module name1 _, O. N5 C8 ~0 Q$ s
                // and the init routine address before calling it               & Z. R" p) d: K4 n  C' h
                _DbgPrint(  "LDR: %wZ loaded.",; ~6 Q+ j6 O8 ]3 n! Y- A& b7 Y
                            pModuleLoaderInfo->pModuleLoaderInfo );' r& d& l. n! ]4 k# N5 `

  B, V$ H0 J  G8 G: H                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
  T' \8 {! J- g) s. T1 f, v; e+ |9 x            }
* x' _* ]  U% v                    
9 ^# W- R- f2 V            if ( pfnInitRoutine )
' |/ C& ?. ]9 @9 F" B, R3 J6 d            {
/ P% c0 i2 f) W  b4 E                // 设置DLL_PROCESS_ATTACH标志) @* D/ u1 x/ |' T0 `( x7 Z
                //2 W* z: M- N* \/ m( y( P4 d
                // (Shouldn't this come *after* the actual call?)' M0 ?- k& |8 ]1 L
                //6 ]* \( L! o0 N4 @% w! X3 f- W7 o
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
" @7 J) L2 F' n) U                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;+ }5 o( k3 X* L  i
% k" Z$ p( a/ b/ K. S
                //
# A9 u  \. x7 J0 A) [- O4 E% u                // If there's Thread Local Storage (TLS) for this module,
1 _; q4 I% \- n5 [: K7 v                // call the TLS init functions.  *** NOTE *** This only  \) |, Z! Y+ q8 U8 |
                // occurs during the first time this code is called (when
- O; O# Y: i8 w7 X+ Z7 h                // implicitly loaded DLLs are initialized).  Dynamically
6 ]) G7 K" `1 Z* R" r( W                // loaded DLLs shouldn't use TLS declared vars, as per the
9 S: m) H6 N; C! d& F) G/ [                // SDK documentation" g; T6 G- K! i8 K5 H  T
                // 如果模块需要分配TLS,调用TLS初始化函数: D! V! C5 P) y: W* C- H- p
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
$ Y$ g) g$ c9 G3 l  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
% Y6 F& A2 `1 l; B* b, o' h                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
) w/ G: J9 u7 Y+ p' u                {5 z8 v# \/ D6 Z6 F
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,: W8 {' p. L/ q4 r' \% d% @% \
                                                DLL_PROCESS_ATTACH );
% `  L  @: D! H                }, h) g2 a/ H- a4 r
               
, X- K* B. g( U8 b5 Q( m- ~2 e
+ I! r1 i7 m4 f) j7 [: N                hModDLL = pModuleLoaderInfo->hModDLL
. \7 P2 m: F* H; m4 j  M& M+ N4 u7 A; B
                MOV     ESI,ESP // Save off the ESP register into ESI
5 n; c; z/ R' v  
9 ?3 g! \* w" g' z& x% O1 g+ j  // 设置入口函数指针                6 e; O8 ^1 N- Z7 a1 ], b2 a
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
& M8 |; j9 U: x8 v1 ^, N' u2 i
0 m1 m* q0 Y+ y+ t/ A2 X                // In C++ code, the following ASM would look like:) f2 o2 {2 j0 N2 B4 k
                //; e2 _. X8 U; I1 Y
                // initRetValue =
' l  E, e& V; I$ `$ F                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);/ J3 `5 ^2 z* i* j$ B4 ^
                //6 E* l& U$ M" ~  r, F# Z; h% u  Z6 d

  ]% K9 J  h9 e0 ^4 T                PUSH    DWORD PTR [bImplicitLoad]6 j- ]& M6 w' c
                5 a# A0 B- m/ Y, G: ~6 N$ \
                PUSH    DLL_PROCESS_ATTACH2 W! e$ r, [$ @/ d" W2 J
                4 U5 \" L6 B' K* P3 c% H; N
                PUSH    DWORD PTR [hModDLL]4 b. ]1 j6 ~, d4 I4 D  P& L9 f
               
& F% k' _. ~  I% v, \/ j                CALL    EDI     // 调用入口函数
/ v0 `& m5 _0 q- a* L# X                % m# H& t& `$ f# ?5 r
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
% A6 T5 M, c& y; S5 Y" |, I  x6 ~+ p- S7 N7 b
1 A% Q8 `- |+ o' S* ?2 H0 |) v0 C                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the3 w* E8 B- C) p2 p
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns! @0 b+ f5 _( c! C) N. {  u

: a* \. Z3 r+ L, y                MOV     ESP,ESI     // Restore ESP to value before the call# Y7 ^2 T. d# s- _5 [$ Z' y8 m4 o

( n  J# l7 T/ E0 O                //
/ x5 A  M3 ~3 y. l0 I: d                // 检查调用前后的ESP值是否一至' N5 `+ R" p* |
  // 2 W" V9 E# E/ m3 r/ }
                if ( _CurSP != _SavSP )
/ G8 S0 l+ ^8 c. W! q& e# O                {
8 ]; V2 B5 C3 [( n8 w9 K                    hardErrorParam = pModuleLoaderInfo->FullDllPath;/ N) c( M4 O1 M( o* x9 o  a5 Y0 d. B
2 p3 Y5 A* f' g1 M3 {  G1 p
                    hardErrorRetCode = 6 U1 A6 y* j/ s2 D( ^# \
                        _NtRaiseHardError(
3 f8 ]* i- g& G/ P2 B  C                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
1 o8 P( _9 U, M( D                            1,  // Number of parameters3 s4 |6 ~" ^4 N4 I9 M) d
                            1,  // UnicodeStringParametersMask,
0 f  G) I( o. U; P* }- S; ~                            &hardErrorParam,
$ [% z, y1 @8 K9 x" b8 X: Y# Z                            OptionYesNo,    // Let user decide
+ x- d, A: g; G" @                            &hardErrorResponse );
- t% C6 K8 }1 \* `- c                                            9 ]; _4 [. \0 t, o
                    if ( _LdrpInLdrInit )3 |! z' c4 K3 s1 Y7 M
                        _LdrpFatalHardErrorCount++;! L/ D) q" s! k7 ?8 Y9 A: E, ?  r3 r

) V. t' X; N1 G: ^7 A7 e9 H- t* r! o                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
. f+ e) h# g4 n                        &&  (ResponseYes == hardErrorResponse) )/ W! u1 D) H1 s& U
                    {
: A9 t& K% G) g                        return STATUS_DLL_INIT_FAILED;, E! S- H; A' Z6 W
                    }7 B& w5 C% y/ Z% U7 i! q
                }& w9 X- k, }/ T4 i: C* q* l
' O' N/ ~0 ^5 S/ B$ ^5 h
                //; [6 e& N- {) B0 h* L2 X
                // 入口函数返回0,错误- V) r! s! ]1 o( X* \4 ?
                //
$ k0 w7 ^- d! r8 G+ Y& z                if ( 0 == initRetValue )- z6 k6 ^7 c0 l3 \4 a4 f
                {
% ~& ~0 K9 ?8 @/ R6 [                    DWORD hardErrorParam2;
0 N. c0 G/ D. u                    DWORD hardErrorResponse2;
3 \' [% o+ m, L5 p- _9 F5 |9 J' n                                       
( M8 u7 s$ a" s0 h3 v                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
+ z, }: x- z. p. x: q                    
2 _$ T* E( p4 ?/ @; F                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,9 p2 c1 C" H4 f( b! e2 Q. L
                                        1,  // Number of parameters
+ x6 ^2 S( _$ \9 ^                                        1,  // UnicodeStringParametersMask
: n( d  ~) _! N# |- z) o# M' @                                        &hardErrorParam2,! D: z! D8 [, A5 {
                                        OptionOk,   // OK is only response4 b) ]* N% F  F7 h8 n
                                        &hardErrorResponse2 );7 m, R% t2 s5 p* y; n7 ~, \
                                                            
1 ^4 k1 ?9 ^8 i* h; V- t: l) \                    if ( _LdrpInLdrInit )
! H$ \1 E! c2 ?2 X" l/ d3 P                        _LdrpFatalHardErrorCount++;
' R0 S% h" o# T  V. K7 e3 r2 k. E* C6 X
                    return STATUS_DLL_INIT_FAILED;
& L- J" e0 \$ b# p6 \8 p) P( ~- J                }. R, F% l. v$ A& i
            }7 t: D# e, u, h' n; |' ]8 ?
        }3 }% x- q# S9 m$ B& B% |% h5 A+ ^

2 h8 s( [8 `; m+ y7 v        //
2 v2 T  ~4 K# r, {        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时/ P* @  G- R5 m+ S: a
        //      
7 d8 W) Z8 D, j: R' W' V  W        if ( _LdrpImageHasTls && bImplicitLoad )8 Z! m& ]2 j6 @
        {$ v; ^4 u7 m7 z( n  H' m! m0 f1 j8 J
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,$ I. G, U: \& w+ h2 c
                                        DLL_PROCESS_ATTACH );
6 G- l4 s6 R! a# a) q0 g3 E        }: Y+ q; ^* y" K3 W* Z5 P0 k
    }! x; p4 n+ P  O& [2 v& X
    __finally  X! T6 E+ X( F$ d7 q/ |% Q
    {
% X4 s( R' t2 F        //
0 f! B: K/ h* l; k        // 第四部分;
3 m* M1 T, K7 C9 v  b. f' d        // 清除分配的内存
$ j( }- t( U/ r( Q- Y# N        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );9 u9 \! ]# R5 f% F  {% c7 |
    }) H' n! d, u: f* I2 ^) V

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

本版积分规则

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

GMT+8, 2026-6-8 07:24 , Processed in 0.419119 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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