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

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

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

( p' q3 {! ]/ A; l* W#include <ntexapi.h>    // For HardError defines near the end
" f" S; }* D1 u& C5 A  ^/ q0 n  I* p
// Global symbols (name is accurate, and comes from NTDLL.DBG)
) @: x+ j% ~) N//  _NtdllBaseTag
' e5 j) e* n- C$ x//  _ShowSnaps% O6 a! X1 E2 J& ]& ^
//  _SaveSp
, U4 D3 U% J1 {& D& n  Y//  _CurSp
& T6 f& `  J' U9 b//  _LdrpInLdrInit2 ?9 C1 _) x0 u+ \6 `( e% j
//  _LdrpFatalHardErrorCount) C) W) ]' D" o9 h( F! v) y
//  _LdrpImageHasTls3 H/ P) _- e, y
% W3 I" @' ~4 |& Q) l) R' `0 V
NTSTATUS
" h& P) M0 p0 T" e1 L4 Q7 J: i  HLdrpRunInitializeRoutines( DWORD bImplicitLoad )% Q% y+ x, t8 V* S* B1 B' e
{: q6 x; F# p! M- k
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
( w# e9 y& w3 n    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
# u) P( P1 U% j+ y
5 Y( X' S% p+ x7 T    if ( nRoutinesToRun )
9 e& r* m! w( Z) h* ^( R    {
4 `. c* ^2 U- h6 c! m, _        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
) R) }0 i6 v* X' D2 P, v        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
- {: t6 N) I! }1 F4 K                                            _NtdllBaseTag + 0x60000,
( R6 S& o9 u' d8 L# w" L, F: o                                            nRoutinesToRun * 4 );
' G; f+ T( ~7 U9 i9 Q                           
* b" C/ M2 Z; g3 G3 L        if ( 0 == pInitNodeArray )    // Make sure allocation worked
2 ^2 `+ W  |1 Z  a            return STATUS_NO_MEMORY;, `  ]+ e; i  k& N2 l
    }' q5 {2 s! B  a. K; j- Y+ H
    else- V6 j% s; s0 T1 k; J4 D6 I
        pInitNodeArray = 0;8 w3 `0 S, Y* E/ j, O3 Q6 P
0 `9 l' p7 c/ x% R0 }
    //第二部分;
1 h" g* D% Z* Y' h! Q    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。9 K& J# b7 {5 E0 |
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);! L$ p4 H- v) c9 i/ B3 q
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;/ W+ {! i$ `0 Z% i0 E( R
        ) B, d) W" c  r
    if ( _ShowSnaps )& g3 T; a/ I) {; J; W
    {
) f3 ?) K  G$ S1 |3 a2 {1 n        _DbgPrint( "LDR: Real INIT LIST\n" );+ {9 g9 A( S( W3 M- F
    }2 t0 R# B1 k  j" e  `

+ E6 W6 ~5 H  T: g: F$ T# ?4 H& c; z    nModulesInitedSoFar = 0;
8 j) |4 r* k. h! G% |. ]. J: g9 q  {0 p
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块) Y# \: M) E8 q' m8 ]0 B
    {
9 @: X* n: U5 ?7 I- d5 y        8 |3 g  J7 L* [
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
3 C' b" _. c/ _, ^# z7 {        {- U+ [# \8 E* `  f  M
            ModuleLoaderInfo  pModuleLoaderInfo;3 D/ q( K4 C" m  x/ _& k9 I
            
( Z4 K$ i* H: a+ Q) b) O; D            //" S$ h! |/ W4 a( {+ i  R3 z5 F( |) q
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
! ]; q( l7 h/ U            pModuleLoaderInfo = &NextNode - 0x10;
$ v* p7 O( F, Y6 L8 t            2 v! p7 i/ k5 `6 Q( v9 Z8 y3 u
            localVar3C = pModuleLoaderInfo;         ' x5 [" J2 a9 z4 N

' S5 a4 P1 f% ]0 n0 ^8 n            //& ?8 n- a) G; _! }
            // 如果模块已经被初始化,就忽略
1 Z5 e* M- r& @& W. y% `6 [            // X_LOADER_SAW_MODULE = 0x40 已被初始化& S' p0 m: Q1 `# h  y  e
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
; N! H# Q9 k  W            {8 ~% X$ t' j' y7 E3 U8 d
                //3 k# a/ d: M" L
                // 模块没有被初始化,判断是否具有入口函数, q0 [) n& }* H, k: R# P$ f& M
                //! m- {/ ^+ y1 i% K& ~
                if ( pModuleLoaderInfo->EntryPoint )
. C9 M2 w4 c8 B) k# N- U' t; t                {
0 u6 c  i! }; Q2 a% ~" C/ I                    //
% X) F  x/ ^9 J; U- k" s( n                    // 具有初始化函数,添加到模块列表中,等待进行初始化, k! X2 `1 D1 @0 A0 H! \
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;' P' d7 q, e' B7 b; Y/ q
( O7 u5 p' P  ]) ^. N6 z" Q2 n
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址% M& a6 n% j# @1 F8 D8 O6 o
      // 例如:; u  @& ?8 z' B/ G2 M6 n# g+ P( v
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
  y/ s% k! P8 m6 C, d/ H                    if ( _ShowSnaps )( A% x+ r- X) {' r& x$ m9 s
                    {
6 O& N  z! e$ ]! V: V. P$ [                        _DbgPrint(  "%wZ init routine %x\n",4 u0 U8 k& R+ m% F# L4 D$ u
                                    &pModuleLoaderInfo->24,6 |& v8 I2 k9 S9 |( B3 m$ ^9 S
                                    pModuleLoaderInfo->EntryPoint );( b3 ?/ d; D' s) a7 v
                    }( w6 o. z+ J4 B( [' H  g

& w9 I$ b  ^# X8 d/ |                    nModulesInitedSoFar++;
5 k9 a2 R6 z7 Z7 N  N% b' ?3 M* I                }6 V) T6 l) D( v& d. g+ w
            }6 Y1 p. M' j3 W& g; U( R/ G
8 l; g2 ^5 P. i2 d( K* T/ }
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。9 X/ u6 o  u( h5 v- t6 r
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
, g9 {& p/ ]; c1 E/ Y& [5 O
. e9 g, @/ j3 ?9 W/ c5 j+ i7 [            // 处理下一个模块节点% n# Q, Z: B4 j1 W  C6 T- @2 v
            pCurrNode = pCurrNode->pNext7 @' U; a5 x/ c$ s* C
        }# O! Q6 y6 x, y' m
    }
/ X# {& G& e: }2 T    else0 _/ e* l& r* h, @& |+ p8 W
    {0 O9 \0 U9 W. B; f# G; O
        pModuleLoaderInfo = localVar3C;     // May not be initialized???  m7 M1 _  K* `
    }2 Q" W) T' N( c8 _$ G( r0 S" }% G& t
   
. _' }9 R3 L* ?: I) r" W    if ( 0 == pInitNodeArray )
# F$ `( k) i  J" ]        return STATUS_SUCCESS;
% H2 S+ O, W8 n" @) ?) c, L, H
( f8 T0 Y; e8 S3 b4 ?1 U    // ************************* MSJ Layout! *****************3 [& T( ^* ^* J% l
    // If you're going to split this code across pages, this is a great: j! r# B4 J: _! q7 W& H
    // spot to split the code.  Just be sure to remove this comment+ c) M7 _- Z7 Q( y
    // ************************* MSJ Layout! *****************
" Z( E! V5 p, O- n- L' f- P, [      k6 f3 G: S3 |/ s8 {6 w4 i
    //
( `$ M3 A8 }2 C$ N8 ~# r    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH: w7 i& K0 w6 k8 i5 q7 m
    // 第三部分,调用初始化部分% i! P5 n5 R) [' U
    try     // Wrap all this in a try block, in case the init routine faults& Y$ [& j+ u- ^. S
    {) o. ]+ A! Z5 ~0 \- i0 z  ]# K
        nModulesInitedSoFar = 0;  // Start at array element 0
  p2 i5 o( E" j3 w
8 b0 c) ~% p" h! a$ T        //) C! l& }7 P# d9 B8 [
        // 遍历模块队列
1 a/ a/ U: k6 u% [9 ^, N, i: y7 M        //6 c0 h* Q# ]8 `5 [9 |/ {6 ?" n
        while ( nModulesInitedSoFar < nRoutinesToRun )! d' m) B9 s' @/ ], D) Q
        {% }0 b7 C% V$ a( Z) E9 b
            // 获得模块指针1 Z" W0 Q, r* o) [! [4 X7 j
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
7 G3 O5 T9 V" I6 z$ `
# S7 [3 d3 L( w% y$ s5 u0 e            // This doesn't seem to do anything...
" b9 e# h3 P' U$ ^/ m            localVar3C = pModuleLoaderInfo;& ?2 f3 o% K* p
            
* [" |3 P4 y$ F: `6 T            nModulesInitedSoFar++;* b+ H7 g4 Y3 e! F* ^1 u6 @
                ( X" r# m6 `7 L3 \5 }6 O- W6 U
            // 保存初始化程序入口指针0 {0 S1 [' G# N) {, U% t
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
0 r( G! S6 \' i! b* y% r7 ~            
# c! p9 j, r# x4 P0 A            fBreakOnDllLoad = 0;    // Default is to not break on load1 S! D7 |5 r3 j! ^

. c6 a( Y+ p; B- f( V( V& h2 k/ d            // 调试用
: v0 ~$ S5 m4 k+ w2 U  d" Y3 V            // If this process is a debuggee, check to see if the loader% U% N2 j" e  e5 g" Q
            // should break into a debugger before calling the initialization.1 w, L8 q/ R0 j1 d
            //- N8 R, j" w$ i
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
% E5 D- f7 C2 ~- i+ X+ D            // returns. IsDebuggerPresent is an NT only API.
$ _% C( }1 p. B$ _            //
+ L$ K3 N/ B" \5 ?5 v            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
. o: @3 T/ f2 ~- o7 G: E+ l; L            {3 ~: r( @: d/ k
                LONG retCode;' p$ j" z. [8 f: h6 u
1 J5 i  v' V8 x. z+ \2 D
                //              ; B3 G, Q7 y( F, [( \! u
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
) I' X6 @4 z3 Y4 e" m                // Windows NT\CurrentVersion\Image File Execution Options", w# `: K: }/ X
                // registry key.  If a a subkey entry with the name of
9 \- r' K" M8 N/ {                // the executable exists, check for the BreakOnDllLoad value.) B( W2 A: r  @" Z9 v; s5 {
                //
3 _, W" Q4 l7 w  n                retCode = / ?* z/ E& u/ H: ^+ S( k/ A
                    _LdrQueryImageFileExecutionOptions(" c5 }" ]0 i7 u1 G6 V- r  y
                                pModuleLoaderInfo->pwszDllName,. C  l4 n5 m7 w" n/ o
                                "BreakOnDllLoad",pInitNodeArray
- \! o  U$ B0 M) E2 T' A                                REG_DWORD,* B6 G& v: D& S" T' h" [9 O9 e
                                &fBreakOnDllLoad,5 r; o1 m, y/ w
                                sizeof(DWORD),0 l3 r4 }. Z) `0 ?3 C. r0 Z' V- n
                                0 );
) w5 m! @; S  @' g. x1 N2 ~5 D; u# S' O8 B* Q  Y! n6 s/ c
                // If reg value not found (usually the case), then don't
5 y- M- S9 _4 j1 O6 u& C5 W                // break on this DLL init0 V7 ?! l6 E6 M7 ~/ H; {
                if ( retCode <= STATUS_SUCCESS )/ l1 N- H% |: a, `& X8 A5 h
                    fBreakOnDllLoad = 0;pInitNodeArray
( X" u0 S6 c+ M* y) c* o            }8 f" E& [2 J" T5 @, v* Y
            
- C' f/ B' l0 {/ k; _) R            if ( fBreakOnDllLoad )
" }! J2 u2 w2 w9 g- G+ i, ?            {           , o9 q  C4 @5 f$ r9 {
                if ( _ShowSnaps )
4 I) w2 _$ _2 H2 h0 T                {
+ D+ b5 e9 Z* H: O' T) @" m/ z                    // Inform the debug output stream of the module name% \9 i& `: l! ]6 `8 [
                    // and the init routine address before actually breaking
, w  N7 _2 r7 M4 D- _5 n. s                    // into the debugger' m8 M  p# _: [6 t. x

$ u: h- K3 M  p% P3 F1 n                    _DbgPrint(  "LDR: %wZ loaded.",$ d& A) O. }3 ]; K# q
                                &pModuleLoaderInfo->pModuleLoaderInfo );
) a1 [! K. ~4 q9 T1 A; m& i                    % P0 ^2 N( u- V; g+ i8 B2 x$ @4 o6 p
                    _DbgPrint(  "- About to call init routine at %lx\n",
  n1 T2 w( Z/ S                                pfnInitRoutine ). c+ d' i; `" I* T
                }; _6 t8 u, ?" d- V1 d& \9 B
                . @: ]! ?9 |% b! g2 z9 Q) I( @$ X6 C
                // Break into the debugger                              * e- ?$ z8 B% v/ ]
                _DbgBreakPoint();   // An INT 3, followed by a RET0 D& v2 a+ s, l# V* O. a
            }
( d2 i; K+ }# `            else if ( _ShowSnaps && pfnInitRoutine )  P' w. u3 B* y& f% `7 m
            {. Y$ q* E3 f3 f! f$ `3 g4 }
                // Inform the debug output stream of the module name
- E5 R9 g5 V+ J. l' f/ r  {- U                // and the init routine address before calling it               
; I7 w! g3 j; Q8 g9 y) U                _DbgPrint(  "LDR: %wZ loaded.",
  L1 x: w4 r8 \/ U$ M9 H                            pModuleLoaderInfo->pModuleLoaderInfo );
# }3 r# D6 }4 M& R  b0 P/ [2 u7 q! N0 W5 m
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);/ D# D# S& e. w! s& ?
            }: O1 k; s; H  r' M
                    & i3 T* \! U$ T: f& O  H
            if ( pfnInitRoutine )
6 i3 c8 n' J4 I0 j            {. {( a5 e% f  p4 I  i- U# q" u2 v
                // 设置DLL_PROCESS_ATTACH标志$ O6 `+ _5 [; Q5 z
                //) W: ?6 L5 d+ T$ g. |
                // (Shouldn't this come *after* the actual call?)
) g1 \* K# N/ [* r                //
3 p0 L8 C' }8 v# f9 `                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
. b3 M7 v) A, o9 n+ b& L6 |$ p/ l                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
- b) P) Z" o# O+ j6 H- B
) I) W8 P8 j, R* b: }; ^  b9 U                //
# P9 [; x( U- B+ K( n' |                // If there's Thread Local Storage (TLS) for this module,
0 x( x" Y& t7 u! M6 B8 ^                // call the TLS init functions.  *** NOTE *** This only9 y* s* W2 o2 W6 ~; h# H
                // occurs during the first time this code is called (when) i& t# n, U  c2 I: w" v
                // implicitly loaded DLLs are initialized).  Dynamically( X2 e! o* A) |; \: x
                // loaded DLLs shouldn't use TLS declared vars, as per the9 m% F+ y: U  ?& W7 \8 Q3 T
                // SDK documentation& Z: `4 R( A. w) N2 u) b& I
                // 如果模块需要分配TLS,调用TLS初始化函数
; T9 P. F4 B3 }) n! |! U  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
. Y9 ?  `; z, d. @' r% `) W  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
, Y3 I& J8 q3 C" g+ F) S                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )' l- A+ m& i& k. o( i
                {
3 L( w" l$ f2 g) L) w' G                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
3 o, L2 U" G" w5 \/ y                                                DLL_PROCESS_ATTACH );
4 J: t- s  }* M4 ]2 j" Y                }; j- n! q+ v3 D2 |- o7 x, |( r
                / B4 e1 |+ h2 |  y* o# F
9 `3 t: j. M  ^8 d2 h4 w1 k1 ^, G
                hModDLL = pModuleLoaderInfo->hModDLL" W3 P0 ]; H, B) J8 x: S
; m1 J: R& @# y: {, F+ s" S; O
                MOV     ESI,ESP // Save off the ESP register into ESI
6 T+ V  y2 l; n; ]+ Q  @4 u/ {8 b: t# y  7 {: @8 t# z' f$ }2 ?. g
  // 设置入口函数指针                1 u# Z" Y8 q, i
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     9 r( p, U* U% [7 u  w
( X. a3 z* Z% X9 U% i
                // In C++ code, the following ASM would look like:
, c7 s  ~/ f5 L$ S9 }+ h) D& Q5 z, C! F                //. a4 |. q6 C- b- P
                // initRetValue =) R! e; f) J* `5 B
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
' W, I6 B8 d) w2 y4 U                //' l; R- V) ?0 B" s# U
# b6 m9 s& Y. w* X8 T7 D5 l, L
                PUSH    DWORD PTR [bImplicitLoad]8 r# s) q4 E. l# _/ ]5 M0 {6 h
               
, X# u$ C& J  K2 H$ O4 V9 v0 `                PUSH    DLL_PROCESS_ATTACH
/ K2 d% ?' D0 S. B               
9 u9 U6 Q& m2 T2 b& E                PUSH    DWORD PTR [hModDLL]8 |( ]2 z; c1 H+ q- G; U8 y
                8 v$ T! n7 ~# f4 J6 P& M
                CALL    EDI     // 调用入口函数
# S2 G5 H5 f: u4 q: U                3 h. ~- Q( M) c& L# C% s; C
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
5 C- a+ p- C2 V
2 F3 v+ C4 P" v0 ]7 s6 b% b                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the+ e# F8 X5 l: ]% \  d8 i
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
  `: N3 n' G  x  l+ C8 s9 |- X# g
! b( ~3 `5 S4 ]% {$ x, |0 `* u. {                MOV     ESP,ESI     // Restore ESP to value before the call% u  ^: c, s$ q( s+ l
$ j! ^- q% @7 r. r! c$ c$ q
                //
- m" E8 N) _2 Y- I5 N                // 检查调用前后的ESP值是否一至
6 M$ ~3 h: ~( ~' u4 p4 c' `$ n  //
. ~  _! P% h; V' n                if ( _CurSP != _SavSP )
" V% a, b% u& v                {
! P1 c' w3 R  Y6 }+ K3 p                    hardErrorParam = pModuleLoaderInfo->FullDllPath;+ S* E7 j0 t# J' z- m
+ U- Y" H' C' X7 @' S# Z' w5 W
                    hardErrorRetCode =
/ @6 v7 D  I0 w; v4 w                        _NtRaiseHardError(
* f( B. g. P) N4 e                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
! G# v4 i8 \1 w% X) @% a7 ]; k                            1,  // Number of parameters5 F6 W3 u0 o8 U% d7 Z" z4 c
                            1,  // UnicodeStringParametersMask,
  @; |( u' {) ?  U, E                            &hardErrorParam,
" r4 s3 P4 r4 \, A                            OptionYesNo,    // Let user decide: A5 I4 l, J# V& J7 Q% v8 Z
                            &hardErrorResponse );
. v3 I6 o, Y6 H, P0 `                                            
. m- Q  W2 ~0 j% ]+ h& o+ n; ~                    if ( _LdrpInLdrInit )
  _5 b" c; U7 u4 ]% Q8 J                        _LdrpFatalHardErrorCount++;1 Q% H" f0 p  a
- s) A2 d; p4 q1 {# k
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)  T) S& e' \9 ^
                        &&  (ResponseYes == hardErrorResponse) )
. l" Y! l7 N# D3 K* h" N  q                    {
( }" B2 @. s% t8 Q                        return STATUS_DLL_INIT_FAILED;. q' x4 R5 L4 l; ]  P
                    }; t" x+ k4 C7 {# H$ q3 g7 q
                }0 y9 P  ]1 X% z- T. v
8 G2 A: h+ ]) j/ s( G7 F9 P" R
                //
3 g# k' @/ m$ g% Z' a0 U2 |# i0 \) j                // 入口函数返回0,错误
' b7 j! y3 o7 p- f& k3 }                //0 N/ s' s  O4 V) ?. i) Q: t- D
                if ( 0 == initRetValue )
- r# A4 Q# M) a' {/ S3 T                {
( W: G! S4 `3 O( T                    DWORD hardErrorParam2;
+ V# n6 p0 R: ^: A& C) ~                    DWORD hardErrorResponse2;
$ o  \4 _6 j& O3 {, Z& Y1 |                                        7 B; w! G# X  A& n
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;, u1 k: A/ x8 K5 l9 L
                    $ J0 d& x5 M8 F; D* e. n, l! e  }
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
+ `/ m0 G1 _; Q( _& ?$ R                                        1,  // Number of parameters) w5 i- p' x+ @
                                        1,  // UnicodeStringParametersMask
2 V3 k" s; O+ y/ w3 O$ g0 q                                        &hardErrorParam2,
+ L) F" k# j3 l                                        OptionOk,   // OK is only response" q6 P* g0 P+ R( }
                                        &hardErrorResponse2 );& W6 e1 Q  k* z0 C# [- o" d
                                                            
) ^5 g7 O7 f7 n# W7 |6 \                    if ( _LdrpInLdrInit )
$ _$ x( f2 E0 X4 V" M+ k# p                        _LdrpFatalHardErrorCount++;6 ~, N4 k, c8 R" B4 B5 X4 V+ u
7 e" Y$ Q% S# g7 |5 i
                    return STATUS_DLL_INIT_FAILED;" W$ z, ]. ?4 s1 f" Z' z% n
                }+ b* ]4 k1 h( G2 h/ P3 s& S2 U9 o
            }
4 y7 {. }. S- f: y4 F/ r' g( Y        }' L. M2 ?) k$ C1 X

6 d3 X7 V! }1 i' m        //
- N0 W- I2 {# {4 H# m% p) ]' n$ w        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
; `7 j/ D% p9 Q" e2 S' k% q6 C9 [        //      
6 v8 \& W% M, c, G6 Q4 Q        if ( _LdrpImageHasTls && bImplicitLoad )
! D7 i! d) `4 V' h: C+ T        {" N9 g# X4 Y% Z& c7 c: q
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
7 b, _! H/ S9 B1 E1 q1 g' b                                        DLL_PROCESS_ATTACH );
9 g! x) s) W# Q+ N5 u% a% r' o        }
% e' l( Q+ P! R3 J9 J/ p    }& E; s3 v. A. k4 A7 |/ U% q; U+ ~
    __finally5 s* B9 ^' t# ^7 ?
    {4 ^+ ^/ Q7 P, v+ U+ g# W
        //
5 @& D5 Z& b2 A) V        // 第四部分;1 C' n1 v" X3 C
        // 清除分配的内存
0 }/ F$ x; X7 E# V; }6 S' t7 B        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );( }# `% e) \& h) Y
    }
! J2 z: g& O! ?+ t1 c! P* K' e" ]- [& _+ K
    return STATUS_SUCCESS;5 V/ O+ R* I% W4 S! K- b7 J
}   
8 q+ u3 Y) s- e  c4 t4 P8 L+ X/ F4 i( m% }+ l( }
这个函数分为四个主要部分:  M# s; Y5 i" y" A' H2 `+ y
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
- H3 p7 ?# R4 j* c2 d3 [, M& U" ~! u二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。5 W% a$ V4 y6 E9 R( C% M  Z! y7 @
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。7 |* ^$ c; I: h4 M+ G: C
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。$ J. q& t8 J' u, R/ c- E; d  O
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
$ t: g; |4 F7 C0 Y- H) g四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-9-21 21:25 , Processed in 0.240855 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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