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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]5 `: m( j8 {/ r
1 D, P7 ]6 u% f+ e/ k. ^6 W, L6 h
WINDOWS 2K Dll 加载过程
! M4 s4 w1 S  x/ K  s9 a- b) Ajefong by 2005/03/305 H- Z$ ~" K5 d( M) M7 S" ~
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
) D; |! w5 Y* w- x0 O% B在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”。3 E: J: z( L5 r: O1 V
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。% x/ p( H% k  O7 k3 s: W
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
4 u+ Y+ }7 a2 T6 q+ U  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
3 x# z1 H7 P" B  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。4 b3 W. l2 X; V) D2 n
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
& m$ |2 b' _5 w//=============================================================================
) w+ @0 R: S& d. Y2 N) q' v, P// Matt Pietrek, September 1999 Microsoft Systems Journal+ g/ Q4 P3 C' F# t; W  X6 n
// 中文注释部分为jefong翻译# \9 v+ r- T  j" z( V3 G( D7 W9 h* W
//: Z, m4 V- ^* Z$ a2 a* \
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
$ R# s# M; ^7 v//( c! o$ E. r% v# r. G* C7 a1 U
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
7 F3 E* S4 ?' A; F- u. H: {) v//=============================================================================
2 @3 C; ^/ j9 Q" Y
5 ^$ ]/ j" |! d' ^' T#include <ntexapi.h>    // For HardError defines near the end, ?0 m- [- h* K+ c9 j9 |- M  x  S" q
# y, @4 b2 s' r: i; {8 z0 n' k
// Global symbols (name is accurate, and comes from NTDLL.DBG)7 _0 |: n- i. H8 s# S% m: m' p3 P# I
//  _NtdllBaseTag
6 I& a) t8 O$ |8 |8 @% g//  _ShowSnaps
# T! G  e0 g9 z5 U' w# |//  _SaveSp! R' P* R/ a6 Z
//  _CurSp
) ^- d' G' y. K, w) d' @/ l//  _LdrpInLdrInit
' M; Q3 v, n/ H! W0 y3 @$ \) w7 T//  _LdrpFatalHardErrorCount3 x9 T" W, N& }- I( T/ f
//  _LdrpImageHasTls" q' b3 [. a3 X1 N7 }3 x

' D. q+ e% m% N: m( X0 jNTSTATUS
- ~$ s! I1 Q8 D( ?5 q. cLdrpRunInitializeRoutines( DWORD bImplicitLoad )
! {. N0 K; H1 n- ^5 g( B{) Y, t- L0 i; i- V/ E
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了& x3 A$ f4 S( p& l4 \4 Q
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();, T% H  @2 n! Y3 A* w
+ {+ H5 b0 d5 s7 G! i2 Z- R4 o
    if ( nRoutinesToRun )
- q5 S! p, D5 l9 v) X    {
8 @1 ]# a0 K& j        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。$ ?5 ]3 {* N: J* P  S; B' S5 n
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
( ]" R; E# n, X                                            _NtdllBaseTag + 0x60000,& n- v' X) T/ S) W, J
                                            nRoutinesToRun * 4 );# ^. e, Q; O8 t% z9 |' Y
                            5 h* v1 @  V9 J* C- |
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
6 n7 F- m2 f9 A6 J            return STATUS_NO_MEMORY;
0 \6 V  T2 G$ e' R. z6 [    }
% ?" |9 @; R+ C    else' ]  g$ }& H" J/ V  g4 E
        pInitNodeArray = 0;1 m  y/ V; o" I: U3 b

# U; v5 V$ H: R6 K4 L    //第二部分;. q! v1 z0 z1 D  ~
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
. [' {+ S  S7 m" S: J% z) O% F    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
. @1 c7 h4 B' u7 x) u    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
" b$ f# M) r: b" c6 w        ' V' F# _" b- \4 f. ^) o- }! a
    if ( _ShowSnaps )
3 ^* ?6 F1 w1 ~+ b- N    {/ q# H; T, u- H7 s7 T5 x
        _DbgPrint( "LDR: Real INIT LIST\n" );
/ c2 A9 w5 _' L$ F4 q# ~    }) y$ |* [# s0 q  P

8 c7 N( w7 h/ z    nModulesInitedSoFar = 0;
0 A* U7 g% u% i* \$ Z$ M% _1 `6 A6 f' V- o- R2 E
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
, ~, v$ C5 h6 X. O* b# x    {5 p% r1 {  t) `4 M. R- v
        
: g1 W- r* S) s- C3 D        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块* D: i6 e" m6 @; v8 M+ ]7 F# m
        {" W9 f5 a. V' h* n, n
            ModuleLoaderInfo  pModuleLoaderInfo;  j' s9 y; _  J0 M/ O; ~% S
            6 F* y; J/ n% J3 `- B7 `
            //5 Z& A- l' R+ |) C8 R* S; T
            //一个ModuleLoaderInfo结构节点的大小为0X10字节9 v9 ]. o/ v! b# o8 I. B5 T
            pModuleLoaderInfo = &NextNode - 0x10;
7 k+ ]; m5 [+ n$ k- f3 }0 P8 d/ @6 O            & \7 Q6 B% b9 Z4 C0 l' Z0 \& e: R
            localVar3C = pModuleLoaderInfo;         
3 P8 m( j; C) l" `- ?2 W/ w8 u0 h. h- h1 A( l( M- N  a5 t  j
            //
5 O# T- o$ D* l* l7 o) [/ _+ b            // 如果模块已经被初始化,就忽略. h# s/ ~) G0 D4 d4 v; T+ ]& A- e
            // X_LOADER_SAW_MODULE = 0x40 已被初始化
4 z8 ^. n7 S/ S9 k$ r            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )9 W1 W( D& _1 Y2 [& P' @+ N4 {
            {- o7 [8 _5 C# D6 e5 \! u
                //
: P: V/ p" o6 s* S1 S+ o$ X5 X                // 模块没有被初始化,判断是否具有入口函数4 i3 \* O0 {5 O( b* n" t
                //9 b% @% {* l! w, T% b' [
                if ( pModuleLoaderInfo->EntryPoint )
- _& _' o& y. _                {
) B- T- @2 |1 z! U                    //" z, u+ a, |; D4 A* E* d
                    // 具有初始化函数,添加到模块列表中,等待进行初始化! Q% k% n4 o6 j
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;" p& B( g9 T  }* o0 N' C) V: L
- A6 G5 ~* n! v, \
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址+ m' ?4 u" {3 \6 M) p. _
      // 例如:
& e0 W, P* F0 W) O* T( ~% ?                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000" }7 K( }% ~! C) r, K* Q4 X% z8 U
                    if ( _ShowSnaps )& _2 R( F5 R- v! c9 P8 y
                    {
  `* O7 N' W/ h+ ]                        _DbgPrint(  "%wZ init routine %x\n",
: a# u" g0 o  G" K! i# \9 v. o# {                                    &pModuleLoaderInfo->24,
2 U( y% o1 q" Z. ~6 q$ _( y! Q" k                                    pModuleLoaderInfo->EntryPoint );1 E6 [3 b4 ^+ s/ f8 p% a
                    }
& u% A! I8 I$ J5 s: m  Z; n2 M$ B' j; Z' k/ n) _+ D
                    nModulesInitedSoFar++;
! Y+ v: I: u+ x' k5 f# w( x                }
7 h7 D' F2 ~1 _) y5 x4 `- d            }
! h2 T& [( a6 R5 g1 l( G
$ G% Z& E4 p8 Y7 U            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。7 t) T+ T$ H* k9 G- h& a$ L0 M& J8 r
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
  G3 {2 D9 |) n9 ]/ b. [
( W0 I8 X! x) f! w( e/ f            // 处理下一个模块节点
- c% A& ?. p2 {4 i+ a1 |            pCurrNode = pCurrNode->pNext: w4 _6 o9 }9 t: x) i
        }
# o1 X/ M% T8 p5 x1 _9 C    }
6 `9 ]) T+ ?4 ?; d7 m    else
4 w! l* D- H8 g    {$ G$ Y; q" ~, A4 a! N/ U. w3 d; l
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
0 `9 X, t7 m6 j. G7 F% Q    }9 v5 `8 U% E$ S6 F
    9 f6 s8 a% R! j8 g% ]/ k
    if ( 0 == pInitNodeArray )
' s( v0 F8 B! h( K        return STATUS_SUCCESS;# w: }& p; E6 v1 J5 o) K

! W/ Q+ m8 j& O. ~8 U$ |" W    // ************************* MSJ Layout! *****************" e! p2 @& F% p" h- M( ~
    // If you're going to split this code across pages, this is a great5 I! V) M* `9 F) U
    // spot to split the code.  Just be sure to remove this comment6 s) L- G1 F6 a- g1 C- H
    // ************************* MSJ Layout! *****************
4 n) _4 k: c- W. n+ I7 o    7 c( I. `% R2 M& J, ]- P' o" X
    //8 Y9 G" x7 H" l, L* Z5 m9 x- ]  B. Q
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH( l. u3 A4 P; Q0 s
    // 第三部分,调用初始化部分
5 c8 C7 K. N# G" `2 \% U3 K, k/ F( K    try     // Wrap all this in a try block, in case the init routine faults* w) h6 D2 D! ^3 j- G# u
    {
9 C' p- L7 i% Y  T        nModulesInitedSoFar = 0;  // Start at array element 0
" u. |* V/ Z4 l  G( i) `* i; E# V/ k& ]% B  g0 A* h  d
        //9 K6 C+ x' X1 P- a0 O, v
        // 遍历模块队列
; m8 z1 H: j7 D" `        //
# \) Z: |/ z4 H* _5 {        while ( nModulesInitedSoFar < nRoutinesToRun )6 |; U) [2 v6 w1 Y
        {& \8 H8 L5 O" w" S) V
            // 获得模块指针
% h# ]$ W4 ^' Z1 ]- s; n* ]/ t            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];4 C3 ?; S, F3 z* v4 C
; |- c: M, s7 ^+ s5 {" T# @
            // This doesn't seem to do anything...
# r  r9 H4 Q" S% |            localVar3C = pModuleLoaderInfo;6 q( H. [1 [: A, K$ o
            : M  `  X- B8 D# k8 x
            nModulesInitedSoFar++;/ P* W1 s  n6 x9 V4 j2 e
               
9 R, i; A& V, T+ k            // 保存初始化程序入口指针
) i+ T/ |7 g/ K            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
8 L3 l+ a, m( y2 J. K' ]/ ^& x) ]: a            8 D1 d3 o- P, W
            fBreakOnDllLoad = 0;    // Default is to not break on load1 E4 f3 W6 R8 W, P
2 ^1 Y0 o1 u2 y" }
            // 调试用; o& ~. y4 m1 c
            // If this process is a debuggee, check to see if the loader8 c! `) w1 [0 B8 c1 J2 R
            // should break into a debugger before calling the initialization.7 C9 \3 O' K- X4 [
            //) F; {; M$ p9 a3 \
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent(); s2 h+ Z0 E( L5 }$ D
            // returns. IsDebuggerPresent is an NT only API.: @. ~* H8 O$ O6 n% \5 b
            //
; ?- v- L! }' m& P( Z2 q            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
3 m& p, {+ h7 i            {
% l8 z  n! x. a% F6 W$ o8 s1 o" i                LONG retCode;
" q" }7 C+ `6 O- l5 b
* B" v- [! x, Z- A) o3 J$ N                //              $ B, N9 S/ @0 y0 }% x* I
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
3 k4 s. |! w7 U" k& [; y) G                // Windows NT\CurrentVersion\Image File Execution Options"
9 g2 D/ B: ~) o7 g( `/ z                // registry key.  If a a subkey entry with the name of
  }: N5 k# k$ H- v; K, R                // the executable exists, check for the BreakOnDllLoad value.
2 ^- U( g4 ~# M                //
  m6 [4 v  w8 l8 [4 S9 z                retCode =
/ }5 d7 J0 v" i: k3 j8 Q! w                    _LdrQueryImageFileExecutionOptions(: p+ w" }0 X$ K/ x5 ?- R6 a
                                pModuleLoaderInfo->pwszDllName,7 D# K, z- S5 F, G( k: x/ H  o9 n
                                "BreakOnDllLoad",pInitNodeArray
3 R( T" g3 c, |7 p! {                                REG_DWORD,
! r3 y: a: \# a0 d$ N                                &fBreakOnDllLoad,
! I+ s% j* F2 ?/ `! {- ]! q                                sizeof(DWORD),
% a0 b& A" k  J5 X                                0 );/ ~: u" g: P8 k) ^( a9 S( t
, I+ x6 ~" ~: A2 j7 m6 |! L
                // If reg value not found (usually the case), then don't
5 |& @8 c, a  u1 b9 K/ {                // break on this DLL init' k2 q  v  a' p" M) ~* s0 k
                if ( retCode <= STATUS_SUCCESS )
2 r" z& x$ F# m+ G* n& a  R8 n                    fBreakOnDllLoad = 0;pInitNodeArray
8 ]0 S3 Y3 {" _            }, j" H* a( o+ a2 v- j' h2 J( i
            
: f  V" E2 F* g% L, N            if ( fBreakOnDllLoad ), _- M  c  \1 g7 Y1 h! P3 d! s0 w
            {           
8 K2 @% V  T6 K' F2 g                if ( _ShowSnaps )0 O: f$ I# A. F; v( X4 S
                {, V+ K3 S& w1 \9 R, ^( @- w
                    // Inform the debug output stream of the module name, k, }3 ?9 e+ @, A! w# H
                    // and the init routine address before actually breaking! F! }0 J  V: X6 V2 Y" ?4 m
                    // into the debugger9 e! G, h7 Q& ^9 ^- \
: u$ L1 ?# u5 V" x
                    _DbgPrint(  "LDR: %wZ loaded.",) f+ `; ^+ Y5 b4 k
                                &pModuleLoaderInfo->pModuleLoaderInfo );
4 u$ K0 G+ A/ [) h( G( ^" Y9 s                    
( S' ]; P3 Z' H. g, D                    _DbgPrint(  "- About to call init routine at %lx\n",
2 j* f* i( ?! `- U/ I) I                                pfnInitRoutine )& f" b5 q2 U2 ~
                }' Y7 |4 |& i* M2 l) A0 [
                $ j& x5 W9 J. Y7 N( v4 V
                // Break into the debugger                              
* f/ ^/ ^1 T% S% U' D( A7 k                _DbgBreakPoint();   // An INT 3, followed by a RET
4 W( M: o( ^" {" _/ x3 A5 H+ Q$ U            }8 f7 y; |! P; [& u5 z+ [0 T- `
            else if ( _ShowSnaps && pfnInitRoutine )  ~0 F/ k: G4 x/ x, J
            {8 x$ {1 K9 ~- _2 a5 Y7 C
                // Inform the debug output stream of the module name
# R  M6 q3 W' c, v3 `                // and the init routine address before calling it               , |& u( k7 d6 D9 x( J
                _DbgPrint(  "LDR: %wZ loaded.",
! y- X+ v: q5 A) M% v( M3 `  H9 G$ q                            pModuleLoaderInfo->pModuleLoaderInfo );$ T4 h( h& T$ z4 F

3 E6 b7 Y# p& w9 k                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
0 a0 b- J- ~7 U. k) |1 u/ X            }
% H4 }! ?1 j, C5 O- A7 Q$ Z                    
  r8 i9 f1 J, X2 w            if ( pfnInitRoutine )
3 T+ E2 p+ r, s# V4 c5 [            {  ^' `% ^- P  Z" D9 b
                // 设置DLL_PROCESS_ATTACH标志
* Q, p0 k$ l+ l! f0 K/ c7 Z                //) Z- h; X; f7 ]/ |$ t
                // (Shouldn't this come *after* the actual call?)
- v! ^: g3 G5 h2 X  @                //0 {# U. `1 \8 N/ ~  e) M: _+ N
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             " b" E* F8 k- q$ P( o; @4 G+ d
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;+ _7 W+ ?7 e" E8 g
* T6 ?" i4 g* Z8 {8 |! C
                //
- R6 c8 `* b0 H9 Y                // If there's Thread Local Storage (TLS) for this module,- f# L3 h7 x- R; }; ?
                // call the TLS init functions.  *** NOTE *** This only6 q- w5 K. v; y* {2 |
                // occurs during the first time this code is called (when
0 F1 i' u% K6 Y2 h( X                // implicitly loaded DLLs are initialized).  Dynamically
* L7 `9 S: q1 o5 j; m  q- v                // loaded DLLs shouldn't use TLS declared vars, as per the" m. V, B& G' Q: ~/ \3 i
                // SDK documentation# N6 v# F; J/ G$ ^) A2 L& I
                // 如果模块需要分配TLS,调用TLS初始化函数
0 m7 |* ?, }! S$ V# c  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
# e; e/ O) f+ I  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量$ ~9 W) O$ j) d" f# D0 u* K
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
. x5 k' n* p6 A0 e. n- o# K" D' @                {3 k' ?' X# Q9 ?8 e" Q
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,, L; ?- |( @8 d! K: }: L
                                                DLL_PROCESS_ATTACH );: r2 k3 a" y2 u7 r* [
                }
9 r; K# C5 C: W4 ]                ! V4 H1 u+ |  ^" q5 E$ ]. a9 V
- t7 m7 w8 @7 W! P
                hModDLL = pModuleLoaderInfo->hModDLL
3 E5 I) L+ X2 z, E/ @7 u$ B/ y1 A
* \1 j; {, ]& o% T/ }, D                MOV     ESI,ESP // Save off the ESP register into ESI# ]7 s5 q/ ?8 O& s% B( z4 N# E
  
4 y/ `4 K, A) q/ V& `  // 设置入口函数指针               
; D8 C. Y6 Z: l" }. _' }$ E                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
9 D5 B" s/ L* G5 L0 a. C. L) Q3 z0 M) F* P# T/ {# f9 v
                // In C++ code, the following ASM would look like:
7 T0 q5 L( R$ ]. ]: Z! D                //3 @" X7 {" q- N+ P8 f
                // initRetValue =
# g* {& B, ]# _% F                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
* h! S( o8 V6 F! L& \0 ]                //
0 S3 f! |) g$ N' C- z- Q6 f' r
" b' s. {8 e+ f5 P1 u) E                PUSH    DWORD PTR [bImplicitLoad]
' k$ h' {' v! y: B0 ~                / W9 x' \( N5 U7 M1 ]3 r0 B+ I
                PUSH    DLL_PROCESS_ATTACH
" m; U3 @# q) {: B1 y5 H                0 h& ~, Z. n. {/ L3 j5 ^
                PUSH    DWORD PTR [hModDLL]
4 h0 E: r9 {" ^" m* J3 \1 D                + J$ u2 Q0 K" w& x( n5 i
                CALL    EDI     // 调用入口函数) T; t5 e6 {7 D5 T- [& l% h
               
( n$ [2 Y9 c4 l! `+ h) j# k                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
1 W+ N' q+ a8 o! ]- }/ e3 |
* U2 p6 t; T! U/ r2 o                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
$ w5 j$ d' k$ \6 Y* `! a. y                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns5 X. Z  P6 |/ j2 ?

  T' ^1 F2 X3 T6 o; C                MOV     ESP,ESI     // Restore ESP to value before the call
3 s8 C( y$ a6 Y6 |
9 f+ `  q3 g+ B                //+ H4 N! _; W; R# p, V3 h' A
                // 检查调用前后的ESP值是否一至
& M" B: W, A( f" O; S2 v* A+ v% b5 J  // / B' ?+ [7 O; p
                if ( _CurSP != _SavSP )+ t/ w& M/ o8 h5 C9 x; U6 k
                {  w9 s! |# e8 v/ ?: L+ m; c1 c/ V
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
* E( n0 m4 y& ]% ?
0 {# o( j) c1 x/ |1 a( h                    hardErrorRetCode =
7 h) {( F# ^6 ~+ i! a8 a- N' Z                        _NtRaiseHardError(
2 r8 E' W: @+ k! z! \( S0 M9 I5 D8 p' o                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,% C4 n' Z2 y6 ]' f
                            1,  // Number of parameters8 C2 N8 z  x( ?; p/ N( w# }
                            1,  // UnicodeStringParametersMask,, C- N1 D; u4 |- \4 }' Y. s& F  \
                            &hardErrorParam,8 D& n. M  ?/ o. b  C
                            OptionYesNo,    // Let user decide. y! T( ?9 k% c5 n4 E  x/ R
                            &hardErrorResponse );+ U2 U9 _! l( P. X7 S& Y* r
                                            
5 u4 S' W! }# N( j  r# k( ]                    if ( _LdrpInLdrInit )
6 \. S- P9 M7 O/ s2 u5 ~+ _                        _LdrpFatalHardErrorCount++;. @% ?% H* B' ?- c

2 z: t" g5 N) Q- A% a0 `                    if (    (hardErrorRetCode >= STATUS_SUCCESS)8 @2 R# V# M& K+ C% G" Q' n) \0 x
                        &&  (ResponseYes == hardErrorResponse) )( s* |- e, d8 i
                    {0 w: F* E1 T* C7 k6 I
                        return STATUS_DLL_INIT_FAILED;( M3 J' M! l6 {
                    }/ P  Y! k" V" z5 l
                }
( u- m2 q2 ]* D* g+ z
+ n! W, Q) I, X' E                //
# L$ Q( u: V/ N) v( ^7 [+ R6 S9 N                // 入口函数返回0,错误% \% a$ |6 m  Y9 A" @& S. `* e- U
                //6 C$ p" j$ y( O
                if ( 0 == initRetValue )
+ E( z" a+ M( \" t0 D                {& T& Q4 O( ?  q( A( e
                    DWORD hardErrorParam2;) E0 b& r0 T2 n' g& ~
                    DWORD hardErrorResponse2;
3 w2 L' i& F/ `  b                                        ; d, ?# j8 T2 |0 P4 j
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
2 z7 P( q! `/ ~                    
0 ]. G, t( V' ]& V                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
5 ?) s) U; p5 R6 h                                        1,  // Number of parameters! b: K7 `( J1 ]
                                        1,  // UnicodeStringParametersMask# p7 s$ A5 \( C7 W0 t: Z
                                        &hardErrorParam2,
6 m3 o8 ^# y" F* F* \% z0 x  r                                        OptionOk,   // OK is only response/ K5 E$ Q( g" h2 V4 n
                                        &hardErrorResponse2 );% W  c, j( C3 X  T+ P2 B4 u
                                                            
& p2 R7 ~. D0 V4 }6 k3 g7 S( L                    if ( _LdrpInLdrInit )
. d6 Q! T8 p- H- J                        _LdrpFatalHardErrorCount++;
8 _; |9 x' n2 E: b0 X* ]
3 I9 }9 U8 o  R/ U. k                    return STATUS_DLL_INIT_FAILED;& z  l6 n4 h) I+ q2 }5 B" s) G5 h
                }
$ v6 T' ]8 B7 B) Z$ I" e9 s0 a; |            }
( v5 e1 y; J! y; z" A; c' J6 s        }
% @, U. y% ]/ W/ X
2 p' P$ t/ ~- X1 b        //
( S. S, o7 l) @, L4 C! d7 }9 C# W        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
% f0 e% O* e# J( s6 E8 S        //      
3 O# [/ D8 Z4 T' ~, e! ]% U. M        if ( _LdrpImageHasTls && bImplicitLoad )
6 f' f$ R2 Q9 H; A% k        {9 C  E  T& R; p7 K9 }  v8 e
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,0 H  w! ^) Z: @0 {" K
                                        DLL_PROCESS_ATTACH );
4 z" t7 \. u0 c, }" j# M/ z        }. ~  q( g+ s$ @# |+ u/ x* X8 W: K+ p
    }
! ]! D# i, {9 \    __finally+ f: `6 A4 ~' s& c
    {3 ^4 c4 {0 R2 L) T- [
        //$ d" {6 }8 S& h5 q
        // 第四部分;
3 W  d! a, F6 G        // 清除分配的内存- e4 r. c0 o' S$ X! N4 I& C) @: x
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
- J) h! S9 L! i; e* O; E( M    }$ E( x. U2 N  Q7 P3 ~! S) f% J) K3 s

; r- r: f- i3 X+ l- o    return STATUS_SUCCESS;
1 n* v( x  F$ N1 e: T2 X4 _: p}   8 g# q6 ~0 z/ h4 R# I' U

- ?/ ~, x# [  |这个函数分为四个主要部分:
% x% z; G! E* z4 J一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
6 Q9 d$ H4 a. f2 [二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
6 [: L5 c& I3 }: U2 X2 i三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。( f9 Q0 |4 K  g8 r2 t
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
3 G5 Q' ]5 g1 _1 i" Q在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。/ L5 H9 |; ?3 C5 E: v
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-7-20 08:46 , Processed in 0.263563 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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