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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
3 [1 |5 f( v3 O) m: m7 f: r# B% `+ a. O& V
WINDOWS 2K Dll 加载过程" F7 A, C. r: s/ j+ L$ c
jefong by 2005/03/30. d3 ]: E- l& V
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。# p' e0 T- x( q; p. }  z# N2 G: i
在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”。# \* a& A8 ?! g% d
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
+ q' W4 z- y! C  ^; ?# u! M$ o: f3 }初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
* }, R7 R7 c2 Q  p9 J  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
, S3 Z! t$ C0 D& S' |' I  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。% ?8 Z/ A; M( z1 P' Z$ t, P0 f
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):9 A7 C+ G. N6 Z. J+ {8 ^; x0 Y
//=============================================================================2 s/ F6 \" g: u* m+ C: T- L" Z# ^
// Matt Pietrek, September 1999 Microsoft Systems Journal+ H8 [. {# N& k
// 中文注释部分为jefong翻译, a+ \2 @  P7 m" E: R/ {- \
//6 ~  y6 l$ A, q. B* n; b
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)# w7 e/ ^" K! M' A& V4 Q
//7 ]5 `7 j% {' @
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
4 f  W3 y; v* n% M0 h6 n//=============================================================================8 f9 I1 w$ o5 H" `7 |

3 N, V3 i( A9 t0 F4 ?#include <ntexapi.h>    // For HardError defines near the end
- L; N6 N$ n/ B/ a9 M+ k8 i/ D/ _$ T. s
// Global symbols (name is accurate, and comes from NTDLL.DBG)
2 e) k& W# @$ E0 Z7 w: V  J7 }9 ^//  _NtdllBaseTag
9 D3 Y8 c* B0 ~  }# A//  _ShowSnaps
1 L; k9 p( @( `& }' g& B//  _SaveSp, ?; j, t& H: g! F2 r( B6 H1 F, M
//  _CurSp
: s7 t2 p* S; U# c8 t+ B( X//  _LdrpInLdrInit/ n% t9 O( Q% T+ X/ [6 L
//  _LdrpFatalHardErrorCount
! m4 Y" y: V' M5 }; D/ L//  _LdrpImageHasTls
7 E: Q# J* Q* Z% o: Q8 Y! D1 k5 G/ _$ P, s2 n7 W: |8 ?$ l
NTSTATUS7 ~( n" |) F5 D8 r' y7 q
LdrpRunInitializeRoutines( DWORD bImplicitLoad )9 f7 ]- ~+ T3 o- Q4 C
{
' N% a! W- v  H# T    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
! i, b- p7 G$ S" H. d    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();' m" ]; X$ }: ?: H( F1 ?

' T3 g& f. t9 L8 L4 a8 L9 R4 S# o    if ( nRoutinesToRun )1 @! T6 z3 G3 X
    {3 A  g* Z4 U; Z" [2 q5 `+ `2 y1 }6 |
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。* s" p8 U) y7 h. y. t
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
& E8 }8 y$ ]1 K+ H6 t                                            _NtdllBaseTag + 0x60000,
. [: {9 b( Y7 L  O# h. D                                            nRoutinesToRun * 4 );" l# i# v+ c& W" u, y
                           
. _& D. `  E/ e) a/ x, S  @% a        if ( 0 == pInitNodeArray )    // Make sure allocation worked& N+ _# c& y  y0 t3 Q3 P
            return STATUS_NO_MEMORY;! r) b. q) R+ C: A/ G$ x
    }& T$ ~( y1 |$ R
    else. [+ J- i0 F* a) s6 l
        pInitNodeArray = 0;) ^  k5 Q: Q0 y- X# ~8 |2 ^
8 G+ L4 O( q" B  T0 T; b
    //第二部分;
% D3 H, ^8 }0 W+ a% v  o    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。  b) K# E) u2 a
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);7 X+ z1 c6 H* c0 m* Q% o
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
2 L) f' t) x/ f: W        * _3 s+ d2 p, @. g
    if ( _ShowSnaps )
& e1 R% c4 B( ^4 r    {
" x1 a, Q+ P+ X% v+ N2 z        _DbgPrint( "LDR: Real INIT LIST\n" );
$ J  N0 }6 p5 V9 V/ ~    }
! ^( A& Z4 }  k7 C) z: p
+ N4 H5 K  S8 I0 a1 Y    nModulesInitedSoFar = 0;
8 W  A- g* Y$ V; j8 H# o3 f+ |0 ^# P0 A8 ~# x& ]+ U4 J8 [
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块) _) R5 h! D! n4 f6 W
    {# ?) T. C& K( f5 p0 ~, A, F5 Z
        
. p! E. R! O- m' i2 z3 k0 l        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块' Y$ A' @- z; h4 V' I
        {
& C9 \5 F" i1 y1 ^1 X            ModuleLoaderInfo  pModuleLoaderInfo;
% t( Q/ K5 @  D            1 K, d( P3 v& |; Y3 l# A1 H
            //
5 l5 k+ m+ ~# W% u) x+ h  o            //一个ModuleLoaderInfo结构节点的大小为0X10字节
  R: r& M; t( h/ @7 N  t1 p            pModuleLoaderInfo = &NextNode - 0x10;" j8 r% k% M8 m  @
            % }6 e, z8 v2 G' S/ K, ^: I" X
            localVar3C = pModuleLoaderInfo;         6 b# v1 h4 f/ U! ]% |( ]

# [, Q# [, w4 e8 S; n            //
: D+ P1 m( Z# R/ \6 S            // 如果模块已经被初始化,就忽略- z: A( r9 D$ _# i' O3 k
            // X_LOADER_SAW_MODULE = 0x40 已被初始化& J) i# [& E+ {# a, [
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
  r# @' \7 ^/ V7 o) [. B, y            {
& y/ m6 X5 r8 `2 E3 \/ T0 W, G% L                //. z1 k3 E; B9 c7 Z. I+ y
                // 模块没有被初始化,判断是否具有入口函数
9 s# B1 i) J) E- O1 n/ N. k1 s2 m                //2 v# y, u- \5 s2 F$ e7 i
                if ( pModuleLoaderInfo->EntryPoint )
8 Q- J, F) g6 q9 t5 d: o5 K$ X7 e  x8 Y                {) c4 s1 c6 ]2 N+ E
                    //
3 ?3 y. i1 v" y/ Q- ]4 @. M& H8 R                    // 具有初始化函数,添加到模块列表中,等待进行初始化; W4 V  O* F* M9 f" y0 ?
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
; v( \* A8 \3 t, J2 p+ D1 T  q+ B. [1 U6 s
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
2 N% Y% Y: U% E- X- D4 ]      // 例如:
; K7 H$ S! P! Q+ v* Y9 z                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
% G6 s4 L3 f( z5 x$ O                    if ( _ShowSnaps )
; d& v) m" |5 C7 C. e6 S                    {& i0 V1 k$ O+ ]5 T/ j
                        _DbgPrint(  "%wZ init routine %x\n",
; l1 `1 i. v* ]; ~                                    &pModuleLoaderInfo->24,
" F* g. s1 ^) V' \, @                                    pModuleLoaderInfo->EntryPoint );+ f) C9 e. B7 g7 @3 K
                    }/ J5 E4 m; k! T, H9 R2 l) d
5 I3 X3 O) H8 M, n4 u9 ^) G. x$ s% s
                    nModulesInitedSoFar++;( A4 S. h- i3 t# |$ F# p
                }7 {6 y" L# V. Z0 D, c* _
            }5 R8 p( b  L, Q
9 Q- Q" j9 R1 E5 r/ e
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。8 }' @5 M7 }2 d2 z5 I
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
4 P% Q' J/ _- o
- @; \# t0 B% C& b: c& D            // 处理下一个模块节点* d" b* s& q9 d+ ~, ^
            pCurrNode = pCurrNode->pNext
$ g; e3 i- M5 S3 u* a6 ?7 f! ~( I4 D        }
6 J! A4 C& c; _' Q$ R9 g    }
- H: P0 o1 \+ T- A    else
, E& }) c4 z- _2 v+ _$ C: d; o    {  i: N' U& D# y6 z; l
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
" A& b4 w& N! d. b9 f    }- Q# }1 Y% r3 x' n6 k( O4 @/ ^
    1 ~9 j/ h) \1 ]8 p/ Z  m
    if ( 0 == pInitNodeArray ), [1 \% v( O# D) @) Y4 p. o" F% V
        return STATUS_SUCCESS;
  Q5 B; c( s+ F  p# H( a% A" X5 j( O7 j, t$ \  ~
    // ************************* MSJ Layout! *****************! `6 o! H2 E3 G& [3 q
    // If you're going to split this code across pages, this is a great
1 E6 n" W: Y9 A+ o! Z    // spot to split the code.  Just be sure to remove this comment; W( j- q' Z1 d7 v& s% B
    // ************************* MSJ Layout! *****************5 v+ b1 v3 t  p0 f, V3 P& z
    - I( N7 f9 `, O$ Z' [1 y- Y
    //
& ]  L8 g3 h: L6 |    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH' v% L8 k+ J9 i9 c; v4 h0 e7 S
    // 第三部分,调用初始化部分  p5 W' x9 l" a! z
    try     // Wrap all this in a try block, in case the init routine faults5 Y' }( ]! M! U6 b9 o
    {
/ |' t5 O$ a' _+ e        nModulesInitedSoFar = 0;  // Start at array element 06 w6 a; m- a  S% u

* r4 m5 n0 E% R        //
; e& ~# i$ ?' G- h+ U        // 遍历模块队列1 N9 `- a$ E4 U( {. [
        //
9 K; E& V$ A" J' h3 @% G/ S0 \        while ( nModulesInitedSoFar < nRoutinesToRun )
' C% n7 u; l8 a, t  m        {
/ p0 M; p) D4 W' D            // 获得模块指针
& }$ B/ [* u% s; t            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];6 v- Q" R1 @  }2 y

5 e- {6 V: P& d* C. C2 `7 [; _. _            // This doesn't seem to do anything...+ B+ f8 h5 b- ?* j* y) A; J' T
            localVar3C = pModuleLoaderInfo;
& m! q1 O) ^( N; }0 W. z& R; k            
9 h# f3 n! x7 Q& N            nModulesInitedSoFar++;% x5 d$ G: N7 c
                . t0 N1 {2 g: G8 A
            // 保存初始化程序入口指针
4 x  B. ]! B  O  X            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;: J. s: u! p- z+ O
            
3 k# `# s$ c3 x; ?/ ^            fBreakOnDllLoad = 0;    // Default is to not break on load
2 i7 ]" X0 V, z" _+ \5 l$ e" h
# o" ?9 c- i( t7 M( s            // 调试用
8 H( e5 e% d  L( v' ^            // If this process is a debuggee, check to see if the loader
5 @1 r- Z" H+ z' {" ~            // should break into a debugger before calling the initialization.  Y, ^2 Q! B- h* M( B  o
            //. m, D) {; \: E4 h8 `
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
" R) @; W& M7 x2 P* i# A            // returns. IsDebuggerPresent is an NT only API.: c+ |1 v0 r( u
            //
" A' }3 K: k; o% S            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
& W, D/ F, ^& o            {7 v9 p; y( L9 Y' Y" k+ C6 r0 H
                LONG retCode;
/ l  o3 o+ `6 F- y& B2 X/ \; |  c* t1 b
                //              
7 a( S6 a, O& h$ E7 @6 i2 g# y                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
( w& [' y; _5 N6 [                // Windows NT\CurrentVersion\Image File Execution Options"
: L1 E5 n& Q) @* ^6 `2 P                // registry key.  If a a subkey entry with the name of
. o& `. `3 J7 u! R: g                // the executable exists, check for the BreakOnDllLoad value.3 g+ b, r) o" O6 _' k% Y
                //
" i, X  d" M& K/ D                retCode =
6 w- f5 f- d6 Q# p6 q2 w7 a+ ^+ Z                    _LdrQueryImageFileExecutionOptions(9 R* I/ I3 d: r4 m! j7 _
                                pModuleLoaderInfo->pwszDllName,) w% E, Y1 @& V9 Y* [! K
                                "BreakOnDllLoad",pInitNodeArray
" s$ O# M: e  _  t' P: i/ e                                REG_DWORD,
6 v4 I  }6 V- T! Q. P/ w                                &fBreakOnDllLoad,3 j4 i9 C* O0 k
                                sizeof(DWORD),
% z3 q' e, p# g) T  w                                0 );' S! M4 i% ?) C3 ~& o
; V, l( N: ]: s8 g
                // If reg value not found (usually the case), then don't
8 g* d4 b' o- y/ _  p                // break on this DLL init6 ^, z9 c8 l9 t5 R6 N
                if ( retCode <= STATUS_SUCCESS )' o8 M: y: r8 C) u$ G. B" X
                    fBreakOnDllLoad = 0;pInitNodeArray1 t7 Z% c0 B1 M* N8 ^
            }* u2 D, W+ L0 ^8 n$ W3 w, R
            
8 s; N: Z% j6 H& S$ H! N% g, y            if ( fBreakOnDllLoad )
3 q! G' Q, E) r  |# a: u6 m            {           % y5 }1 [: f7 H: ]& l& |8 Q; W
                if ( _ShowSnaps )
* Z; \: ?) }: L; i. |& _9 R                {
0 |* x% R+ C+ @" M# k                    // Inform the debug output stream of the module name
) \. M+ s5 y) r2 H. a: Z; m( v                    // and the init routine address before actually breaking
' U5 E/ ]# a* O4 }, \  l6 C; A                    // into the debugger
& B- C# [9 Y, H0 a, N4 Q! \+ m; T, N4 [3 V9 p& P
                    _DbgPrint(  "LDR: %wZ loaded.",0 L% N  ?+ \% M2 `# A$ F6 Y4 b. `
                                &pModuleLoaderInfo->pModuleLoaderInfo );; r8 ~2 Z. n& O- W% p
                    
# ?0 \5 Y3 Z4 D/ F) g2 K) a                    _DbgPrint(  "- About to call init routine at %lx\n",% V! g. H4 f  I
                                pfnInitRoutine ). @' ]2 Q. N8 n9 Y) t# ~! a6 }# [8 D% v: k
                }
8 u) R0 Z/ a8 Q4 ?2 E                3 Q( [2 C, J  Z" |- G
                // Break into the debugger                              
" A2 e6 C* d! y" R% w" Q                _DbgBreakPoint();   // An INT 3, followed by a RET
, y# v$ }. o2 }: A0 }            }7 O- l4 i  U8 L4 d# y/ K. x
            else if ( _ShowSnaps && pfnInitRoutine )
% `% E, |4 V. j1 y' C2 v            {
$ D4 U& d& [% p. ?: ?: d                // Inform the debug output stream of the module name
9 K* i- X' T1 N5 O; x7 `, |                // and the init routine address before calling it               
& h1 e0 e8 f1 y                _DbgPrint(  "LDR: %wZ loaded.",
( i4 E; Y6 ?) |- v2 _: r                            pModuleLoaderInfo->pModuleLoaderInfo );
% B4 H( X; }+ p; K  ^' A/ R1 I4 N) X  P; S  C  w9 d
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);; O8 r, q! `) a5 O( a" s7 |
            }2 t$ F. U& D, J7 m
                    
- B3 j6 w. |( B- D+ q            if ( pfnInitRoutine )
4 f. W; O! M7 E* |0 @5 l5 |            {
$ h$ k: k1 w1 g9 W                // 设置DLL_PROCESS_ATTACH标志
# g5 J4 Q7 k$ u+ s$ |* }                //
4 R/ b8 P- d- J' K                // (Shouldn't this come *after* the actual call?)
- J7 A# d+ F& E+ z: d' w8 F                //1 e) @2 H; w' l) A
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
1 F* k* a1 t- c: e( L                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;% X/ f8 _3 u4 j2 _* l' x
* u% x9 f' v1 q& k1 b4 V7 o
                //) t$ T5 m) P3 G& K$ {+ n# d
                // If there's Thread Local Storage (TLS) for this module,
6 Z- F! L; @" C  Q3 R                // call the TLS init functions.  *** NOTE *** This only# r: c9 y) B# b
                // occurs during the first time this code is called (when$ X+ w7 l+ Z) z2 o
                // implicitly loaded DLLs are initialized).  Dynamically0 K; X5 V( Q0 g; T6 c
                // loaded DLLs shouldn't use TLS declared vars, as per the! K- q9 R6 }) o4 y0 x
                // SDK documentation
! g! @( x% d# {                // 如果模块需要分配TLS,调用TLS初始化函数$ [8 ^5 k; y5 C) B# O" y; I! b; o
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时; x5 b: l5 E6 b  c
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
1 W& b5 x2 g5 L% v% W                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )5 g- j- U: P7 m5 |2 }$ v2 C
                {% [, U) \2 L( _$ l4 @
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
( T2 C. r0 N2 g& z; R! U0 u                                                DLL_PROCESS_ATTACH );  n  |8 H2 O9 U: ^7 l' ^
                }# e& U8 z3 @- C9 ]" f; t6 Q
               
: g( L; n, q2 t; ~, f
1 `1 k6 d* n. P& c                hModDLL = pModuleLoaderInfo->hModDLL
1 N( y5 `  P% s; t& Y2 l/ [/ ?: ?3 b( I" W% c
                MOV     ESI,ESP // Save off the ESP register into ESI
' P9 M( T5 \! F* q+ c) x3 R6 Y" d  6 w% r8 x0 G7 r
  // 设置入口函数指针               
+ b6 {) S+ l9 F0 ~* Y4 c                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
9 b2 R6 n0 B, t* g1 W0 q1 }& c% v4 F9 j7 @$ ?- `& |
                // In C++ code, the following ASM would look like:
- U8 G- |* x$ v                //" K  o; G3 W" q, X' \
                // initRetValue =
/ ^0 F9 s9 n& c5 H6 o3 `                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);9 k( n* i9 U. W; Q* d
                //
" e: e* r$ j1 |$ b2 o
( i% w& w- x/ {/ U5 s" b                PUSH    DWORD PTR [bImplicitLoad]6 O2 G% E8 l6 [
                , o# U! S% }8 `- s. }
                PUSH    DLL_PROCESS_ATTACH
$ \/ ~! A+ E3 s                + C1 M8 m0 L3 }- T! ~: z+ P
                PUSH    DWORD PTR [hModDLL]/ }6 }5 ?  Z6 G( p  r2 e5 S( B4 I0 ?6 o
               
% W! O" V. v. I: H                CALL    EDI     // 调用入口函数
5 t! v7 m4 {9 e  j# C+ U                & e1 l5 S! x  I  [: T
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
8 c* m5 w3 A  J  P9 i8 V2 G- h: b4 C& ]4 k& P
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
; n/ L5 c9 i3 y                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns' c4 s' u9 d# f6 U$ p7 ?3 w- |& x

$ y. g) `1 @! s! q7 a$ Q                MOV     ESP,ESI     // Restore ESP to value before the call
# O  K/ o0 ]: @/ Z" x( e3 G$ o+ {# A: u# ?7 ?
                //
; T& w$ D5 h. e0 H                // 检查调用前后的ESP值是否一至
+ C4 w2 \2 ~3 U) y  // 0 I$ U5 Q$ I; P& E* g' s
                if ( _CurSP != _SavSP )
8 l' i; V( A9 a* C8 L                {
. A  c: v) w6 }6 L) X2 O7 L, e0 A2 i                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
$ L/ d4 y3 y+ u3 X. a3 x2 k6 s9 T7 l& U% O- K
                    hardErrorRetCode =
" l4 D5 X. s* i5 V                        _NtRaiseHardError(  o1 l$ a3 V4 O* t) B7 T' z
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,, E7 U3 E$ `) b
                            1,  // Number of parameters
9 r: i: K# G% j8 T4 K$ K                            1,  // UnicodeStringParametersMask,
  v6 @( w/ Q, c1 w                            &hardErrorParam,
7 ]* {- h/ D1 p$ \1 P+ r                            OptionYesNo,    // Let user decide9 S2 y; D: J% K" j/ B, z. T/ d
                            &hardErrorResponse );
0 s4 m) r& E, \8 \. Q1 y                                            + |7 j& e) X/ x$ p( b  Q; y
                    if ( _LdrpInLdrInit )
0 M' p2 f1 d$ ~$ }                        _LdrpFatalHardErrorCount++;( L2 j$ r( v- U, C6 C

. v4 M& O5 q1 Z& [8 J" s  q                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
/ Y0 K- j  s( G8 P9 S                        &&  (ResponseYes == hardErrorResponse) )8 i* ~+ N1 x: z+ e
                    {( q1 D- T8 f7 E- `% Z9 ^2 c
                        return STATUS_DLL_INIT_FAILED;
  ?' h' n2 p' U8 m6 M3 t                    }$ e  x* e+ T' h
                }* |: x* B" I, X! P' Z& q
  O# l$ n  a6 j  G+ w$ w
                //
4 X" t4 V$ ]- ^6 F                // 入口函数返回0,错误
1 m0 @. W, @) W                //, S( a) ]  s0 c% M' V
                if ( 0 == initRetValue )) g1 F, W9 p$ U- p% o, y
                {/ G/ L1 A/ @$ I+ T: B  A
                    DWORD hardErrorParam2;) |4 N* C+ ^# w# d$ n2 Q$ j
                    DWORD hardErrorResponse2;
3 O" |, N+ @3 }9 G' \/ F                                        ) d( C9 T/ _& b) S- w$ M4 W' |5 @+ h
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
* m6 q- M9 U$ e/ c( [                    4 x  t! x5 n* x& |$ |" ?
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
0 v  T2 H$ i. G$ r5 T% O                                        1,  // Number of parameters
- {% ?& Q1 A- E) R/ s- H                                        1,  // UnicodeStringParametersMask
0 F8 u. f; t; T                                        &hardErrorParam2,
; f4 u, N+ U' J# l% R                                        OptionOk,   // OK is only response
4 }- \7 L1 d% d; N                                        &hardErrorResponse2 );
: q( |- H: x  t. r# a% J1 j5 X                                                            
. Y5 ^* r6 E6 K* X: t8 m, Z                    if ( _LdrpInLdrInit )6 W" Q$ n3 H6 K- }# R2 e2 }2 ?) C& Q
                        _LdrpFatalHardErrorCount++;
/ R+ x( {4 K& U* C  o4 Z9 |; z, k3 Y) I9 X' M+ z' X
                    return STATUS_DLL_INIT_FAILED;
  t$ A& C& j: A) W$ Y- i6 C                }" {" I2 X; q, w
            }" f8 l+ B; H) f9 r6 T: o6 H
        }
8 l8 R& M& b" p0 i- B% f
9 R: M5 b/ |: _- s! }$ A        //5 N# C2 t8 q0 v+ ?+ J
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时8 @% I  L# V0 i# O9 m
        //      2 U3 N7 r: \% P: l, r
        if ( _LdrpImageHasTls && bImplicitLoad )
% O9 M& t# M: W* t$ s$ z7 F: ]        {4 M  c6 a. n/ o! {5 M
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
1 d2 y- Q! c( q( Y                                        DLL_PROCESS_ATTACH );: q6 R8 q5 M" X' s
        }
' m, s- O5 }4 \  X) B, y# U    }
' n( G& E2 _3 |    __finally
) a/ s& g/ X- [+ q3 f/ z8 q    {- H% s. O' w" J: ~% y* x
        //! E8 D% r8 ]. g  C1 S/ s
        // 第四部分;
. [  t- D* b; Q: T8 \9 p        // 清除分配的内存/ h2 ]& x$ ?5 H
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );! m- u8 U$ d! E4 L8 G. K, u4 k
    }
% ~2 g3 w& L- U: l; a6 m
$ r0 _5 K% A  I9 N! z7 Z; E: e! D    return STATUS_SUCCESS;" o5 E: X% t+ {
}   
$ T7 b4 x: {0 o
) k( U6 A6 `6 [4 v; f这个函数分为四个主要部分:. z, j2 e" c) B0 B
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。7 `; |: a5 x8 C. i9 B, K- T2 a
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
+ s; a2 |8 Y6 r" A3 O: }三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。# [7 i- O0 i) W/ x) k6 @: H, ?
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。% w4 m6 t/ U* V: U) E
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。7 k' D' t0 x& V6 z
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2024-5-20 19:11 , Processed in 0.018896 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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