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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]9 _# |- ~8 [; u2 d* B1 M' f+ r

% \& V5 Q" G$ RWINDOWS 2K Dll 加载过程8 Y9 v$ C. k8 ?( y& l
jefong by 2005/03/304 @) l" ^0 m2 R6 d8 L
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
3 Q+ I1 m: L/ b5 N7 ?7 h: l. h5 K在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”。& Y0 f# {% R+ I; T: i4 t) m$ g: x
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
; q: v- L. ?: N7 u' F1 _# b初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。/ c& F  D$ [3 S8 l" D5 v* Q4 }6 [
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。3 O3 k% Q7 q& O- q0 c4 F4 w
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。. y9 {* m$ b6 ^  p% R: @" J
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
& F$ M, Q( T# R( `0 w//=============================================================================- ^+ B+ A6 m; {
// Matt Pietrek, September 1999 Microsoft Systems Journal
6 ]4 k9 O) F) U, I# |1 h  [// 中文注释部分为jefong翻译
- |' x+ z) A& h1 J3 D9 @$ a//
0 g1 I4 X  s$ h4 o0 _/ x// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)2 \- N# r& K3 M4 f  k5 {
//
4 F0 N, R3 R: ]' n, _" z// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
. T" ]8 E# E& O& D' J9 j6 q//=============================================================================2 e; D: t% ?. I# s

. C) e- e( @4 w' q( p#include <ntexapi.h>    // For HardError defines near the end
' Y0 u( }- w! F9 Z
: W$ Z/ ~1 B- g) l5 q/ k( N// Global symbols (name is accurate, and comes from NTDLL.DBG)
$ |8 |4 R$ V) H//  _NtdllBaseTag+ u5 M# A* f7 M/ U: n
//  _ShowSnaps
- {- h4 w  `' I- O9 ~//  _SaveSp% g9 Y. W; e& g- U: {% n- U
//  _CurSp
/ c/ q$ _- ?5 @//  _LdrpInLdrInit/ t" A( [) ]* Z; M
//  _LdrpFatalHardErrorCount
% z1 `% X; c; D7 D- V7 t! G7 K5 l8 {' K0 n//  _LdrpImageHasTls
$ `+ A9 y; Y$ e' C" h  Z3 |  g
" }3 u& s; ^# H' LNTSTATUS: Q- c& Y7 ?" _& s- n  `
LdrpRunInitializeRoutines( DWORD bImplicitLoad )% ^: {- J8 B4 \" i. T% J
{% S0 r2 u2 [; b- Z3 _
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了5 q( p% T4 W9 J5 C: n
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();* R! T/ n' a: i  n. ]7 @
3 p2 Z! c9 b9 l: b* N2 [
    if ( nRoutinesToRun )& e* }' ^/ @4 w7 I4 l7 u# z
    {# E; ^0 `1 {/ O5 N7 v
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。1 [$ M" l. s2 N
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),' r. }, @; |9 k6 V2 g" m8 j6 u
                                            _NtdllBaseTag + 0x60000,7 ^. g! d1 x0 y9 ^& v
                                            nRoutinesToRun * 4 );, X; G" |& Q5 t9 Q4 I
                           
( L( H  Y+ O$ ^        if ( 0 == pInitNodeArray )    // Make sure allocation worked
5 z  x8 v7 N  O            return STATUS_NO_MEMORY;
9 n) L3 k5 |! [" _    }& ^9 J. k2 j* V
    else5 @8 u" i5 W: J. o) Y; b, g
        pInitNodeArray = 0;
" Q3 T( J1 w5 l# n
& l* y3 H+ Y9 Z8 l" Z: _3 y- a6 _    //第二部分;
4 ]# [7 M. l- [    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。7 A+ ]# {& S, V3 j9 n' A
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
9 y0 ?* B9 c' i  I8 P/ e" i' M8 z. L    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;' H( s. f' q0 H! p9 ?
        1 B/ q' n& o  V7 C! r. x2 P
    if ( _ShowSnaps )9 W: f9 E* b! K
    {
5 p; j' x: t# q7 h6 A        _DbgPrint( "LDR: Real INIT LIST\n" );
# j$ R$ g( a; N    }. F1 o) l( P( F) ]* z

0 e, R8 C) r  ?) F/ [    nModulesInitedSoFar = 0;
7 @- j9 g" Z+ d/ R) K( G2 h/ X8 d
$ s+ K1 b/ N5 h5 S1 W* H    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
( x5 X8 e. d, ]) t! l    {
+ I/ D. K. `8 m5 |# |        0 j- k* m6 G! R7 s  }& L1 Q
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
; v* X$ N) f' g! f. ]& Z  g        {, N, B+ P/ p( c* V; }* ]$ S, n
            ModuleLoaderInfo  pModuleLoaderInfo;
7 y5 n7 C$ E' Z7 l7 x8 n) O8 u            " S# Y' o. G2 c% p; U
            //
2 L# a, [# ?$ |' `" O            //一个ModuleLoaderInfo结构节点的大小为0X10字节
" E5 |, K; \& r% _/ h            pModuleLoaderInfo = &NextNode - 0x10;$ G7 C' p1 h( ~3 j" d( B& Y
            % ~, s3 s) w! J  s+ w5 }; a
            localVar3C = pModuleLoaderInfo;         
4 a# w% X" `' `) t+ h2 j# r/ p9 }3 ^" n+ e; e) y0 n0 }* i5 j. _9 ]+ P# \1 Q
            //
" a; G& n9 E, H2 [            // 如果模块已经被初始化,就忽略. P/ v0 l" |. R: Q# v, |
            // X_LOADER_SAW_MODULE = 0x40 已被初始化) i. ]4 ~1 D! G. |# h6 @
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
  @* }& b8 e9 F0 l6 H) t            {
0 \/ ^0 e) G. b, q2 n6 ]                //8 c4 `) f4 J) N) ^4 e* H
                // 模块没有被初始化,判断是否具有入口函数
- _; x3 p6 i) I2 d6 `- ]                //
8 j5 B7 s1 F# }4 z! l                if ( pModuleLoaderInfo->EntryPoint )
# v" M- b) S. H7 }9 c! C! }9 w  z                {
" }3 N# i( f2 t! N7 M                    //
5 V( J: i; S% {' y8 P# `! P" u& B( V                    // 具有初始化函数,添加到模块列表中,等待进行初始化+ s" L- C& ?6 K" Z
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
3 z* J( t0 `4 {; l) S, ~
' z( n8 I% y+ k; ^5 Z9 O& w0 h                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址1 i  k2 i6 y9 e0 N- `' D$ ~/ o
      // 例如:
& m- [5 l  T6 {                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
! p1 x6 C" @' Y7 s  F, z1 F                    if ( _ShowSnaps ); T, b( L0 O3 Z* \1 u4 X0 A
                    {  P5 Z  X/ ~3 c! @
                        _DbgPrint(  "%wZ init routine %x\n",
4 W: F/ y6 q" O8 r6 Q. c                                    &pModuleLoaderInfo->24,
/ f9 k, a6 Z" H- q; M                                    pModuleLoaderInfo->EntryPoint );
4 q0 ]) Q, U& @, h8 f                    }7 P$ p! P4 F# z  i
8 G! S6 o& s2 d- X5 K
                    nModulesInitedSoFar++;4 T( ~: M  q3 M' J  `# m) z
                }  o" q9 Q+ ~2 g* V7 [! w, K& S% x
            }2 _4 ^( F# E" c" i: K# P0 o

, S; E; L' @6 o: k( j4 a. x. r            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。/ v6 D5 _) H0 |* v
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
8 k1 N* G; s3 n8 e1 E3 g) W; `6 m( y# J( `- @; {4 m
            // 处理下一个模块节点% h$ M1 E2 ^2 x+ Q/ o  V* d! `9 q
            pCurrNode = pCurrNode->pNext
. W/ ?$ d2 R6 @3 h" O! p0 B9 z- N        }
6 k! G* A( u2 v) R    }1 b- s0 H! ^( _/ S9 I/ f: Q" |
    else; U3 @: N4 }1 U+ V& ]& n- T
    {# N. U. N- ^+ I) v4 V. j
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
; T7 M- z4 |2 q  w9 r/ A/ f5 Z4 e    }
8 q6 h$ R5 r6 \6 @6 @% C# I& [8 L   
7 p6 X! s( B0 `! i( m7 J9 C    if ( 0 == pInitNodeArray )6 P# r4 k% J+ Y3 E. G+ a
        return STATUS_SUCCESS;! l& ?! o- \- t; r  W7 h

* q" n# f5 V; h' `    // ************************* MSJ Layout! *****************+ }) w1 ^) L! ^: ~& r
    // If you're going to split this code across pages, this is a great
3 n7 b( f! U, P! s2 [/ Q    // spot to split the code.  Just be sure to remove this comment
5 A' ^9 {( N/ A    // ************************* MSJ Layout! *****************
2 I% N5 Z' ?" T! T5 S# ]0 \    9 Z) K; Q9 ]6 @' N! ~' q, w% j6 q; r
    //# u" e1 z% a8 B  B; o% I
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH; t1 \1 i0 q2 L  X) s% V4 T  g$ G
    // 第三部分,调用初始化部分
$ H1 b$ k" b3 F4 C5 a1 |# Q    try     // Wrap all this in a try block, in case the init routine faults
2 {3 D9 N8 [' E: O1 Z! D( r% j    {  i6 a& u8 t4 m' K
        nModulesInitedSoFar = 0;  // Start at array element 0
0 n* n, F& O0 ^0 ~8 G/ P* Q! L1 d9 R9 E) e* m
        //
% n3 v" W/ d" h% y  R. s        // 遍历模块队列
) X: x) s: l( F6 l7 z        //( d8 F% a( p5 I, W4 R
        while ( nModulesInitedSoFar < nRoutinesToRun )* ~# V& t  P. g1 U
        {* l. s. w$ R/ P, A- }7 Z
            // 获得模块指针. k4 H! R( s$ ^) Z4 ^% z
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
5 p8 v2 v% K) F
( Y; t6 E- w. {            // This doesn't seem to do anything...
' H* C% D# t) q" p8 J- p" }            localVar3C = pModuleLoaderInfo;
# Q, \9 T+ Y9 ?4 c+ n0 S            4 j  Z, {3 M8 t3 u5 |+ m
            nModulesInitedSoFar++;( b& `7 j: Q- {
                4 N$ S% \% [! N
            // 保存初始化程序入口指针
1 R$ Q8 }/ Y' T6 m: ^            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
0 J( f! x" k5 g6 D/ I0 `2 t: m6 O            
; F9 \: N* e" O            fBreakOnDllLoad = 0;    // Default is to not break on load
$ U7 f- D9 P: ]+ `
8 b% W1 g3 Y2 P9 p            // 调试用
; O! v5 Z0 q6 _8 r            // If this process is a debuggee, check to see if the loader! Q2 L- i0 _9 o7 R* O  b0 ]3 ^; Y
            // should break into a debugger before calling the initialization.& u# V1 L$ h! @& \! n: z
            //
/ I, B  n/ C  d; O9 C. M            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
" I- C0 r& i& K: a( w$ P* t            // returns. IsDebuggerPresent is an NT only API.4 ?. _( h0 C% L; W8 J8 e* `2 v, n. f
            //, h, [6 X. |! b
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )& l! z+ t, a6 B! N
            {. A2 T! X9 T1 j5 t) ~
                LONG retCode;' M5 q: J8 [9 f& G$ t2 @

/ o  E6 u* C& B9 e                //              
1 ~5 u7 {% x. w, I) J                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
2 V7 y- `4 N' N+ I                // Windows NT\CurrentVersion\Image File Execution Options"& S- U1 P. i, r$ t7 F) J" D/ a
                // registry key.  If a a subkey entry with the name of7 P  s; q8 m* [! \' o
                // the executable exists, check for the BreakOnDllLoad value.7 }# l* [0 H; K8 \% u$ _
                //
6 H6 j4 c" P5 U$ ?: [                retCode = 2 N- T0 Y1 P9 Y3 T5 F0 A
                    _LdrQueryImageFileExecutionOptions(
6 ~  U8 J- B1 F6 i* I+ N* X                                pModuleLoaderInfo->pwszDllName,. ?. h( h0 c/ Q$ O' X
                                "BreakOnDllLoad",pInitNodeArray
6 k" S0 ]9 m( P' ]3 f( K                                REG_DWORD,
9 F& t1 w# c+ s, o; F+ O4 D* k                                &fBreakOnDllLoad,
" S, L0 s; u. }; O# I                                sizeof(DWORD),: J  K: a0 P3 z: ?0 z8 f
                                0 );
6 \0 ?8 Y( i( M; x9 H2 r  E, a& B6 E' [+ |
                // If reg value not found (usually the case), then don't7 b0 Y# G, Z5 I- e
                // break on this DLL init
' p1 |- @8 G* [7 \& v, _                if ( retCode <= STATUS_SUCCESS )
. G' x2 M+ L1 u                    fBreakOnDllLoad = 0;pInitNodeArray
5 Q, t5 w) F' O2 _# D/ _8 Q3 b            }
% M. x' }/ @6 W* s0 {: f+ J7 b            
+ O. k4 n: B5 Y5 t# R4 V            if ( fBreakOnDllLoad )& L( A5 J5 @8 w  R) v
            {           
! u# _; j/ C$ u+ W7 b' A                if ( _ShowSnaps )& B& ?- _; y4 {% u- c' n
                {
0 K' y; B5 G* q7 E; t  |                    // Inform the debug output stream of the module name
+ c. P! k# {/ Q6 m                    // and the init routine address before actually breaking
6 O9 }8 r  d0 }5 T, C: j* u5 V7 j                    // into the debugger
1 S& d' ~# K5 B. N
  }4 r6 d. S' S: }& J: t  q                    _DbgPrint(  "LDR: %wZ loaded.",
: I  w# }7 h8 A# L& ]5 [                                &pModuleLoaderInfo->pModuleLoaderInfo );
; M$ [* |* {, O; O0 l+ H: f0 `                    - d- P- c) s" c2 W1 I
                    _DbgPrint(  "- About to call init routine at %lx\n",7 i$ o+ q2 z1 a+ ]" |6 ?
                                pfnInitRoutine )$ T& w- T  U7 E9 i, [5 M( }  t
                }
( ?6 ~- Q' q& t/ `. O( r, m# l               
! S* n( {! I# b+ ~3 u6 ?0 \' A" W% m                // Break into the debugger                              , K- F1 a% U* x: T
                _DbgBreakPoint();   // An INT 3, followed by a RET8 D& X! I* a8 G4 C: V
            }
. [0 [. ]4 U  d& r7 M) ^            else if ( _ShowSnaps && pfnInitRoutine )" w8 e  |( f  ]' ~0 H9 K; _
            {
  L# L; ^# X& x2 m) y9 L& _                // Inform the debug output stream of the module name
$ b0 `- k5 I9 P. ]' l( G' |) z                // and the init routine address before calling it               , ]$ u8 e; x& m9 {( z
                _DbgPrint(  "LDR: %wZ loaded.",
! K; F# s, N+ f0 f/ e- Z                            pModuleLoaderInfo->pModuleLoaderInfo );
, T# ]1 |- o) r0 q$ S& I! k: i; P  M7 v
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
  s" Q! T( D5 h  V( O8 U# q            }
. H* I$ r, s% }7 k8 v. u* h! J                    5 M$ P0 E. M+ S" i& V6 h' Q! M- b
            if ( pfnInitRoutine ). }& T) ^3 A! ~2 u
            {2 h" N8 h! T* ]8 w3 V
                // 设置DLL_PROCESS_ATTACH标志
, o' L( k, M# N4 b/ z( W+ w7 P                //# d2 V' H* X& a% G. K
                // (Shouldn't this come *after* the actual call?)
/ o  `% ^5 o/ v                //2 d1 h5 ^# \) e' A  f
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             & F6 z0 @9 {% L# ]. c: C5 ]9 t" o
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
! Y+ c  j4 w( y3 r
4 ~, [% Q; @: B$ W) O                //
1 B% F! K! u( Q% z                // If there's Thread Local Storage (TLS) for this module,, g$ ?6 |2 o& x( w
                // call the TLS init functions.  *** NOTE *** This only
. ]/ V$ b5 H9 A( n9 f! E! q                // occurs during the first time this code is called (when. d+ D' [- \' t8 e0 x. I) {3 ?
                // implicitly loaded DLLs are initialized).  Dynamically
* m7 J8 k: O4 w' F4 _                // loaded DLLs shouldn't use TLS declared vars, as per the
% c, P* Q. t9 Y: Y' W                // SDK documentation
- ]' M& n, p8 t2 K: ~: L                // 如果模块需要分配TLS,调用TLS初始化函数
) v4 A, b* P, j0 \8 A' j  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时- B- Z: s) R6 [  F- K
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
3 q; f, v1 ?1 \6 I: K& `! P7 C# u                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )% E! Y+ r6 [% x8 D. E: S% v1 Z) O
                {
) @; n1 k0 f. J5 C/ t& u6 `# R- V                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
% r  S! ?* v9 X2 J. j; S                                                DLL_PROCESS_ATTACH );, ^5 t7 f& v5 W; K$ X1 }
                }
. R! m# }6 e( t" s! D# }5 |               
$ y& L6 x) k  |5 M9 {# a
: e1 i" `2 W  b6 _                hModDLL = pModuleLoaderInfo->hModDLL
9 ]% y$ Q+ m9 Y9 w& l. I3 }4 `; B: o- W
                MOV     ESI,ESP // Save off the ESP register into ESI& a! x# R9 d; ~% Y
  
& J+ z# a( V, F3 e) ^9 M  // 设置入口函数指针               
; V  x% h/ A/ d                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
8 z0 v# f6 W4 s
# ~% A+ a+ Q3 n' p+ a                // In C++ code, the following ASM would look like:1 @$ N# F  R  l0 x- p* Y0 F# {- o# d
                //
4 ~2 \  C7 Q! o7 M# I7 r                // initRetValue =1 `! z* _& q! Q& e) i
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
1 D6 X" u5 p. O1 [                //; i! D1 L! l# n5 e& A2 Y9 H9 W
) U1 V' v$ {% C! v0 k
                PUSH    DWORD PTR [bImplicitLoad]2 \! E: k' i, G
                ; z! `+ t  }$ D
                PUSH    DLL_PROCESS_ATTACH$ h" B: a" O# z2 q+ N+ U/ u; f
               
7 M4 f1 B! {" A' }0 Y9 D                PUSH    DWORD PTR [hModDLL]. ]* {7 h3 q  l- t; S9 k6 H
                ( S  Q; S$ B- {5 g
                CALL    EDI     // 调用入口函数- x8 U/ A& H! Q: z4 E! z% V
               
. w3 J- w, H! \2 ?& a                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
/ Z# @; ~! W- f/ F4 X# V0 e# k; P/ Y4 T
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the3 J$ }, k0 b' ?/ \/ F+ R" g
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns2 E7 H) Y7 b, j: ?

) m, x* B# n  e/ N* ?; ]( X2 _                MOV     ESP,ESI     // Restore ESP to value before the call0 z3 t- e( B' T5 Z9 z

5 f( J) v. v% |& Z8 u6 o3 C                //4 l5 L3 i9 Q" k7 t; r& k
                // 检查调用前后的ESP值是否一至! J/ q- o! ]( @5 A
  //
9 |2 k6 F/ K; ?& i5 U& a- i                if ( _CurSP != _SavSP )
) [: Y2 g* }8 w# ?0 v& o                {, }9 ~0 }; K$ P1 B% {' d
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
" M/ J8 ~" j4 @/ j: E
; K- E9 h. R+ ?2 \/ a                    hardErrorRetCode = : P9 K' r% @$ r$ c/ t0 R% }. U( I
                        _NtRaiseHardError(, `6 S& e( p* k
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
# I( n3 o3 H5 q+ `. @                            1,  // Number of parameters
7 [4 F$ r% ?+ V1 {! t+ v8 Q6 a) {* S                            1,  // UnicodeStringParametersMask,. [- b% O1 Q0 v$ S
                            &hardErrorParam,
- }3 o6 n+ H% s! i; R                            OptionYesNo,    // Let user decide+ w2 R) ?+ A( A; {% f2 m' \) l
                            &hardErrorResponse );' H4 J/ Z$ r+ v+ @9 p: N. Z
                                            6 c& q* I5 o: L& }
                    if ( _LdrpInLdrInit )! `: H5 Z- S: C' V
                        _LdrpFatalHardErrorCount++;
8 Z* n4 ^; ?" V( W- W$ n( U) `( P% N* C2 ^$ k
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
% D& F2 X6 @; w1 S# r+ u                        &&  (ResponseYes == hardErrorResponse) )- F; p3 @  a, N* m% l) G
                    {2 \/ p4 @3 z7 z; G
                        return STATUS_DLL_INIT_FAILED;
* H( w3 h5 C0 D. A4 K$ l1 `                    }
, M" c% ~& v# `, P4 D0 l( j                }$ K6 ^/ [: S/ l9 \6 L% c) j- Z. X/ s

- ^; t  W0 C7 g                //
7 C4 G1 X3 Y( N                // 入口函数返回0,错误
# r8 D: [: s3 }7 `0 |( F" Q% z                //
7 p9 B0 Z- j. t, P                if ( 0 == initRetValue )7 w# p) K# p# Z
                {
: J4 P4 X' i1 g9 V1 \                    DWORD hardErrorParam2;6 X' S7 C" W  K5 [) T" i4 A
                    DWORD hardErrorResponse2;
3 @4 K, l7 f' ]. D$ G! j                                        ! t3 l/ a  E7 S+ i$ `7 B. _- W
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;( z* Y- C$ u2 S+ C% Z+ t8 z  u( a
                    
4 g9 H. q% q; H2 f* O                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
7 n6 v! u2 j' G# T                                        1,  // Number of parameters# W% }6 i3 y' y# j3 v7 E/ g6 ~
                                        1,  // UnicodeStringParametersMask( W- N# ~/ j+ F% `1 D: U
                                        &hardErrorParam2,9 ?% @: D' n) f& ~
                                        OptionOk,   // OK is only response
8 E- z! |% Y" V- y8 J! i1 c+ N                                        &hardErrorResponse2 );
3 J7 w5 ^$ q$ v6 i) N6 T                                                            
( E( b6 j3 Y# q' \+ X7 ~7 b                    if ( _LdrpInLdrInit )# S% e8 z5 ~+ W4 p# d
                        _LdrpFatalHardErrorCount++;
& W* h! `* _8 m* n- e- P4 {1 D8 @9 c/ q/ \6 t
                    return STATUS_DLL_INIT_FAILED;
6 _' ?' W( _( z( w, t7 R) }9 K                }: i) C8 y1 ^8 v9 c3 |' u
            }9 m2 L/ I8 R$ N! e
        }6 K7 W! N+ T: V, w% }

4 |& n5 ^8 c8 s; e8 `+ r4 U        //+ }" d( j) j1 z0 }! C
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时" M- F- `8 r6 B0 X' s3 H
        //      9 D3 K& e- q1 e
        if ( _LdrpImageHasTls && bImplicitLoad )
$ f6 |3 ?4 D$ S& Y# J        {# C% T, a7 ?8 R+ Q- k4 _" g
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
2 B; ~+ h6 M6 q0 q+ G( b                                        DLL_PROCESS_ATTACH );
9 J& B  L) C5 r% t        }
) I8 ]* |- i& e    }1 N6 l( o) p1 @1 e! G
    __finally0 z0 {9 |# w0 |, l& Z
    {
0 e0 T# x  u; J* V- ^) O        //
' V3 I6 E+ s& l: n" l& E        // 第四部分;) Z5 E& x& Q. `, J
        // 清除分配的内存* V: a3 E* f/ @, J7 Z" b1 F% L
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
( r6 E" g* H" _, e    }6 N4 w8 X+ e$ x. U4 Z
  u  E3 k& F- k4 c; Q5 l
    return STATUS_SUCCESS;
, m; W- S9 g9 q, W}   0 z6 _: Y( \% G; `; E' w. `6 G6 \
, x7 ?  U; h* R$ g; a
这个函数分为四个主要部分:# w9 R" j' ]$ [9 c! Z+ p
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
: Z+ e' W) v, M  }3 F二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
$ N# S2 j# W7 G2 X三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。) N+ R2 U; q: a6 |. H4 B9 g$ b  @9 c! y
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。9 u- G3 a( I/ b% ^
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
) H! {, d. V$ [" z4 I四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-6-13 07:12 , Processed in 0.063282 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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