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

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

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

8 H( P7 J/ V; E( g// Global symbols (name is accurate, and comes from NTDLL.DBG)+ q# \- N5 i% `1 M
//  _NtdllBaseTag
2 S" z- |6 @  k! E* d* g$ [; \//  _ShowSnaps
; c- Y1 I) \, S$ {) M8 Y//  _SaveSp
" Y3 [: s  [' Y& W3 V" o//  _CurSp
9 }" n; X- F' o% S3 e//  _LdrpInLdrInit5 ?  [/ S7 X3 E* b* z/ ]
//  _LdrpFatalHardErrorCount, M$ k8 m* O( V+ e% ^8 R& [
//  _LdrpImageHasTls7 l( n( v6 {, s" K+ _- I8 p

) j; ^( p6 W; n" XNTSTATUS1 w4 }: n5 X1 L# e8 Y# r+ h
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
$ H: a( e" q$ U{0 U* m8 p2 ^) B; P2 Q
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了  q3 \# P2 o& W0 ~
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
! ]  q% `0 @7 q  X. O9 D6 \0 |+ A' j
    if ( nRoutinesToRun )
) z4 n0 F1 G0 I5 b9 W6 ]' {. Z    {- B6 I3 ~- G* [8 b+ t. I- I6 ^
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。, M- p9 u3 @5 l: F; J
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap()," h: g, E$ h% P
                                            _NtdllBaseTag + 0x60000,) F$ L) i/ ?1 H+ k- J& ?4 x
                                            nRoutinesToRun * 4 );
: J+ \5 G9 g1 ^. g/ v                           
/ W- c; ?' U% P9 n7 ^        if ( 0 == pInitNodeArray )    // Make sure allocation worked. |7 _0 O6 s! f0 w" f5 S& h( S
            return STATUS_NO_MEMORY;8 x1 V& N" l4 t+ a- l0 y: K
    }) B/ F( Z5 b4 [- E! |( B
    else' @+ K+ a! }" c9 y  [* Q
        pInitNodeArray = 0;2 D+ M# z2 Q1 S% W$ n7 H( b7 U

- u& f; [0 s% g4 c    //第二部分;* L& f: q: A" T7 u
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
2 L8 K- C8 E/ p/ \. i  v    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);; o' g8 q. z) u- Q0 I6 u6 X1 Z1 m5 C; _
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;, i) y6 Y$ D' I3 R& q% L
        
$ m7 H$ q  R  u" U5 s2 q    if ( _ShowSnaps )6 }9 D5 A' c; F) {9 M, {1 y5 [
    {  S1 i; U. |% [4 D2 @, E" v1 N4 l
        _DbgPrint( "LDR: Real INIT LIST\n" );6 l5 E8 r8 T) v
    }
6 m0 t: X6 d1 s; t0 O" h% p& d! V8 H' n9 Z3 d1 n
    nModulesInitedSoFar = 0;
  u+ ^7 m: T% ?' ?0 k6 b9 V+ I+ z' m
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块9 E8 a; F9 Q+ Z' X  C: o
    {
' ^# z+ o- d7 C7 T7 E" k7 N        7 W* m) c9 }4 b$ @
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块5 h& u/ v( B% v3 s- l4 m
        {
$ H1 M0 ^' b% T" `: ]6 z            ModuleLoaderInfo  pModuleLoaderInfo;
4 ~; B( `, g) a            
+ u6 z4 p- Z+ Z7 ~( e, E; F& ?6 i            //4 l, n4 ]) m' K+ k& t7 e
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
* P, I/ P$ P) `! p            pModuleLoaderInfo = &NextNode - 0x10;* f- R: M/ G% j( M8 L  i& e  d9 C: y# }
              X+ `  y( f+ G
            localVar3C = pModuleLoaderInfo;         
$ D/ ^, F- e2 x2 z% [
1 G& j- S  ]: ]" ~% g3 j$ d& N            //
# W! {0 P0 z- u$ G8 d            // 如果模块已经被初始化,就忽略
) i( A; J# d! r, p. x8 v' Z            // X_LOADER_SAW_MODULE = 0x40 已被初始化, u; z% I$ Y9 p, u8 `
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )0 P* n5 K5 e3 `* ]7 D3 y% L( r4 F
            {* C, Z; U$ E; Y9 N
                //7 Y  U9 s2 C  x# ?
                // 模块没有被初始化,判断是否具有入口函数
$ z: }, x8 |- _1 N& Z1 E( D8 x                //
* x2 i  c0 W: `# q9 i( E                if ( pModuleLoaderInfo->EntryPoint )/ B: U* c( J( ^" ?, E# v3 F
                {4 L4 b- m1 F5 f% |2 H9 W
                    //1 P* \: J1 p: S5 }6 ]
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
6 [& r3 r. [$ D                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
& \7 A6 j  Q) m5 i2 M6 Q0 o4 m% {  Z8 u5 j2 ]9 r. k
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
. ^+ P. O: @% V) V      // 例如:
" m% c; ^" U1 ^! I2 f8 q1 A  x' H                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
( T: U- y5 o' n; m6 ?2 t                    if ( _ShowSnaps )4 V% V/ x6 E7 T+ {/ `6 @+ p
                    {
5 K3 i- o: N& k4 r. n+ M                        _DbgPrint(  "%wZ init routine %x\n",
) j* |6 |+ J1 ?9 d                                    &pModuleLoaderInfo->24,
# D% {& C+ ?& t# j7 O                                    pModuleLoaderInfo->EntryPoint );
( z6 L* g* t' ~" F                    }) ^3 j) D5 d/ N" z6 ~0 P

3 h, a6 W  I& c+ ?& z( V- w                    nModulesInitedSoFar++;% t2 y/ I6 P5 ]  A4 L
                }
0 H4 T7 e5 [0 L            }
) o. \5 F( Q2 U! u5 o) h) L' W( g/ f- {, M9 G
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
, z6 Y# |7 U: F* l7 u            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
  h+ \. V. u4 Q" E! y
8 \" g9 H$ l) u$ k3 y            // 处理下一个模块节点
# J+ l# Z- ]+ {- l% X1 e* C            pCurrNode = pCurrNode->pNext
1 p# ]  I7 V! a' H        }
7 ^$ ]5 C8 g, h" v    }
2 A- [: h$ C6 {  J/ _% d    else
' i  L/ c; Q9 I) ^    {
$ ~- k3 d. {* Q# {# s. j        pModuleLoaderInfo = localVar3C;     // May not be initialized???
' G. {, |% \  b) F8 P& o# w    }- ]2 R: _3 O+ e' @/ C
   
, I1 d2 R5 h" Q% b1 T9 ^+ \7 Q/ B; n    if ( 0 == pInitNodeArray )* |; a0 P$ u7 m' C& r" D5 Z$ e
        return STATUS_SUCCESS;
. B1 q7 }3 R+ j" l
. n% F' U9 i" R) c$ a    // ************************* MSJ Layout! *****************
4 o* `0 [" I( |. Q    // If you're going to split this code across pages, this is a great  o  v+ A1 R( O: v0 o# \, u
    // spot to split the code.  Just be sure to remove this comment5 I. H- P7 [0 E' ~
    // ************************* MSJ Layout! *****************
" _! n* o- p2 Z8 `   
" n- n- h& [( x4 u0 p! j# `; i    //
( E' L% c! ^6 B    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
6 O6 G2 w) X0 A0 i    // 第三部分,调用初始化部分
' h7 A$ C, T! B; J7 L/ f    try     // Wrap all this in a try block, in case the init routine faults
' p  g  i% ]  _# G8 z2 d9 l" U    {
5 S, z$ Q' F3 L1 U        nModulesInitedSoFar = 0;  // Start at array element 0
& i* B  H( Z4 C0 t
6 `- |+ j" a5 u8 ?4 y; X* h        //9 O2 m& H8 q+ ]7 l: W+ q6 _) M' e
        // 遍历模块队列; j7 ]& f' T3 n# w% N9 f' @
        //
' Y$ Z" X2 H2 ^& G0 i        while ( nModulesInitedSoFar < nRoutinesToRun )! |$ w! ]! r- m$ o: {+ Q( I
        {& o. T. H5 J/ k" M
            // 获得模块指针! R3 c" n7 f; E& d
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
5 }% e# _; }8 _1 X0 N8 y2 l% t/ i1 [9 I2 ~" H+ p% ]
            // This doesn't seem to do anything...
/ {8 {/ |8 P+ M0 |            localVar3C = pModuleLoaderInfo;
8 Q! J- N) ^$ b0 S, ]: X            
. \' M& I  i% C- h/ ?, K# |- W            nModulesInitedSoFar++;' v( Q8 K! ?  g# ]/ u% h+ U
               
+ l7 P! G+ M- s" ~. [3 j& I            // 保存初始化程序入口指针
8 Y1 V; q+ K/ X$ ~  N6 S            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
" u) V  Z0 K$ y            
! H5 g* D5 h: y' d/ m8 F            fBreakOnDllLoad = 0;    // Default is to not break on load9 J0 ?) ^- q1 a& u) m; y' @1 G0 e

; i1 H' Z2 o# P; w& k7 E            // 调试用' T1 R* C2 U- f
            // If this process is a debuggee, check to see if the loader+ q; e! O3 u$ @) J7 f
            // should break into a debugger before calling the initialization.
) Q. N& J. }) j7 ~6 u& A/ _            //* M+ N7 w6 R# _/ {1 S. @3 |$ ~% `
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
, F; _4 s8 t  s$ `' g8 n% M            // returns. IsDebuggerPresent is an NT only API.
" e2 p: q; k! a# b6 ^; ~" a2 m, D; x, ^            //: L- c: Y2 j1 _2 q& O9 ?! \* d' D) m  C
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
) u  h0 c6 s8 r- t            {
# d, |& m5 L/ @9 h% P  t                LONG retCode;5 a# V! r) s9 x
; ~$ B' I' F" f6 f
                //              " B' u- Y, I/ c; h1 O
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
# {) C, ]8 m. H# E' Q  P                // Windows NT\CurrentVersion\Image File Execution Options", k. D" R. `# q; U' n5 d
                // registry key.  If a a subkey entry with the name of- P1 Q% C/ ]" R: {7 \
                // the executable exists, check for the BreakOnDllLoad value.6 Q" U$ D$ L, q# y3 V$ b9 u. K5 v0 |
                //
, Y0 y( ~7 w: J! F6 e1 X, o- }7 A; \" D                retCode = 5 @8 u& S$ B( q. s
                    _LdrQueryImageFileExecutionOptions(
# X4 u, Q4 ^2 Q: b% d! l                                pModuleLoaderInfo->pwszDllName,
9 t4 g7 {8 F5 b# i                                "BreakOnDllLoad",pInitNodeArray
+ b5 Q$ Q( L- c                                REG_DWORD,
3 |. q& v0 }+ K8 v; u                                &fBreakOnDllLoad,
% u7 f3 R! M* ^, z+ p( g                                sizeof(DWORD),
2 \! {; ]( u1 l7 Q% h                                0 );: I5 |  z# k, A
( g- @: p; a; E# U: ]2 d6 u& r
                // If reg value not found (usually the case), then don't% ?+ a% A2 e. g8 n3 N# |9 O. u5 l% Q
                // break on this DLL init# N5 {5 X( S, a; o* C: e* Q: h3 o$ b
                if ( retCode <= STATUS_SUCCESS )
& ]) V) ~7 t$ d1 l* [$ K                    fBreakOnDllLoad = 0;pInitNodeArray- j4 q: _- K, I$ g' e9 p
            }
& a- T& X2 v* W' p% k            
8 Q, A  v; Z+ R9 n; A' x; T5 q7 B9 S, b            if ( fBreakOnDllLoad )
, x; w' K' s+ k3 f+ I            {           6 |" T4 e; ~* f/ ?: |' L
                if ( _ShowSnaps )4 t( i! J! D. {  ^$ k: R$ V
                {6 p" e- G. M- h" n5 G4 [
                    // Inform the debug output stream of the module name
# V" U2 {! e' ~                    // and the init routine address before actually breaking
- ]" B& `2 D" |, R3 j. d# }                    // into the debugger: d8 x$ H+ i/ G6 B$ _5 S
( f5 w5 g4 L$ R! h: l( ^
                    _DbgPrint(  "LDR: %wZ loaded.",
( ~4 f) T. Z  F* y1 J                                &pModuleLoaderInfo->pModuleLoaderInfo );- v8 Y- K$ c( M, {5 ]
                    
4 t* {) H( z  d4 v" R8 Q% r                    _DbgPrint(  "- About to call init routine at %lx\n",
+ @" O# f& a! R, e                                pfnInitRoutine )/ v4 }& q) @; D8 K2 C9 ?( k) X
                }
& g( `2 o6 v! U               
9 @2 x, n1 {$ {( d" C  W+ y                // Break into the debugger                              
( G( `0 L' r5 k0 N- J/ g& N: Y                _DbgBreakPoint();   // An INT 3, followed by a RET
9 j( ^9 P0 ~7 @3 _9 p. ^+ l            }) u1 i/ j* B+ C3 J  O$ e
            else if ( _ShowSnaps && pfnInitRoutine )
) A1 @0 e0 k: E4 W" m8 }            {, o2 P3 e0 Y; P# d9 K& w0 D
                // Inform the debug output stream of the module name2 Z3 t) ?/ P2 H; Z! ^
                // and the init routine address before calling it               
. ^* P; _, b& B' F/ I                _DbgPrint(  "LDR: %wZ loaded.",/ z: Y7 B3 t) \! O
                            pModuleLoaderInfo->pModuleLoaderInfo );
4 X1 n( f& r$ x7 R5 Q+ U
: f! S: }, F, f                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);8 q9 u/ Z5 F5 s+ G
            }
$ c* v$ B! s! z                    
* C7 v3 o0 R  X/ Q( ?) i  H4 y            if ( pfnInitRoutine )% y- G# N" q% o
            {; p% q  q6 H  h$ {
                // 设置DLL_PROCESS_ATTACH标志- B+ p" t- O1 h$ x
                //
/ \; }% h  w6 s% q  l6 a                // (Shouldn't this come *after* the actual call?)! O( ^, X: f. K" b2 \! b0 i
                //
) |# u! s# @8 d0 f  L- T$ d8 V                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
- C8 \1 r9 B7 x1 ^0 [# |2 {                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;3 Q) w5 Q. ^* t; s1 u0 R/ D
" F( w9 r6 U" M% p; _: l% `
                /// S( f- n' X4 ~/ ~
                // If there's Thread Local Storage (TLS) for this module,8 |# |4 n# D' ^
                // call the TLS init functions.  *** NOTE *** This only/ w7 \7 ^% a1 Z
                // occurs during the first time this code is called (when
) Z* N! ^2 x* E3 ?6 P  {; L                // implicitly loaded DLLs are initialized).  Dynamically" x7 K5 h' \$ ~6 s! `0 D+ u: ^( l
                // loaded DLLs shouldn't use TLS declared vars, as per the
* t+ A. i; w  L& k  k1 {                // SDK documentation
' F2 V; K' y3 G8 t+ p0 Y                // 如果模块需要分配TLS,调用TLS初始化函数
0 m4 M2 p( m. I( [( F7 a$ F  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
2 a4 n$ {/ s6 l3 Z  u! P( b  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量; d# Z3 J; ~% Y, n7 [8 x2 |
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
) Z7 {9 }/ u9 x3 @4 i/ ?* m                {
. X, _4 ^2 b  v/ {( v, X0 l1 V2 j2 |7 m3 z                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,& c0 s0 Q5 x2 {$ ]. x
                                                DLL_PROCESS_ATTACH );
+ l1 p" a, g0 A  f+ G                }
9 Z4 R% e2 ]7 s3 x                2 j4 W( ?) m& l+ \8 d7 N+ B
- A  N. X+ V# C5 Q
                hModDLL = pModuleLoaderInfo->hModDLL
1 A3 d+ W0 r! b0 H. r- ~1 a' M. `* A0 e; }7 l
                MOV     ESI,ESP // Save off the ESP register into ESI( S1 F0 z7 F2 d4 x% L  V! \. i7 D4 ]
  1 \* R% ^9 u. x
  // 设置入口函数指针                : h3 L: {% O! a+ ~. n
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
" n& ~8 Q/ q% ?- v* B6 d
% ~) r4 F# v' ^9 ^" P1 Z* {                // In C++ code, the following ASM would look like:
3 j# [. q, B1 f1 J2 v1 b( K                //- w( r! K6 C# p& C0 y7 v, B
                // initRetValue =
( \) m( T7 Z5 T3 P                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
4 f! c, E1 M8 l; k, c% S                //
8 w; ]2 K( x9 N+ ]$ i* H1 d( g0 ^
5 Z: c. e6 x+ v. V0 D# ~                PUSH    DWORD PTR [bImplicitLoad]
9 b- g( ?3 j: ?                  E; ~0 V5 `( G1 ?6 _( y
                PUSH    DLL_PROCESS_ATTACH6 P0 M2 t, j2 @' [/ i/ I
               
$ n: p) j7 B% J# I4 d                PUSH    DWORD PTR [hModDLL]8 ?4 H0 Y% t' K1 I9 M7 C$ O3 G7 @' F
               
+ M, U- Y" E: T+ w0 j' [                CALL    EDI     // 调用入口函数4 T1 n8 d# p7 a' p
               
" g6 U3 q5 P$ T  R3 P8 y( V                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值: \  R& r+ J" {9 @# t' Y

; K; o+ `! W) H7 q: y7 {                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the5 g3 [6 x5 T5 m5 n' i% ~
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns8 a: F( V5 O$ [& F* a: G" [' Y
  ^! z' y; f9 B' D0 s
                MOV     ESP,ESI     // Restore ESP to value before the call- N6 G/ A- q) o" H, @1 L* E
! T- J5 V0 S5 V, _  b" Q* m4 |
                //
8 [' c3 H3 B8 Z                // 检查调用前后的ESP值是否一至
# o$ F  Q( u4 Q  // ! s- {- F- u( E' \& ]3 _$ L/ u* C
                if ( _CurSP != _SavSP )( c4 P: s" J# l% E5 C; I: \
                {0 B, b' ]0 W* Z* O; D- h
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
. e8 D: w6 ^- t9 i" p  P/ G' `$ m/ k
                    hardErrorRetCode =
' z$ `: t/ J! H0 G8 V* u# P                        _NtRaiseHardError(
/ t: O( ?/ f6 e- ?3 i0 a                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
- z3 h3 X0 K# U# Q$ N                            1,  // Number of parameters( R; c* T" S- a- W+ r6 ~
                            1,  // UnicodeStringParametersMask,* D  ^# r. y; K) h# M9 W& ^" j
                            &hardErrorParam,5 b0 d9 W' Z3 \4 o, Z
                            OptionYesNo,    // Let user decide
( |2 G1 w0 ?3 D) q, l# r                            &hardErrorResponse );
$ t0 M7 u& O! c/ L- m6 [                                            - o% N2 J" D5 P+ O. e( l3 E
                    if ( _LdrpInLdrInit )
0 p3 O( a5 y  U7 F2 J  }, C+ z$ B6 [+ e                        _LdrpFatalHardErrorCount++;+ |1 Q" O* ~) j: a
! D% ^# i/ B1 p7 ^$ w  G/ R
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)* K- V0 A: J8 R% ~
                        &&  (ResponseYes == hardErrorResponse) )
, O& R1 K* [+ W9 G2 n/ B0 T                    {
: o/ ^, l( ?3 j7 \                        return STATUS_DLL_INIT_FAILED;
& x( L: n! t) M! H2 J, G! u                    }8 @# ^% Q9 K8 A  ^
                }
. c2 s8 R3 w! m$ Z& b4 y$ q5 W" ]/ ~5 s- b  N' O# t
                //2 @6 l+ R" a6 N2 _
                // 入口函数返回0,错误# G& x0 `/ n; Y6 X; g
                //6 i3 V; @' z+ J( n; S4 j6 D
                if ( 0 == initRetValue )4 z/ A+ b( }! ?0 D' I
                {1 @* w% i3 j3 a, _
                    DWORD hardErrorParam2;
) ]* W4 {+ ?$ }3 Z                    DWORD hardErrorResponse2;
; o1 `8 o6 ]  g: V1 C! ^! l                                        - f* u  X/ S5 `0 Q
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;# q% H( ]$ B' x! }/ E$ O" C" m/ _
                    
- ?4 A$ J9 O8 I$ |6 E) B                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
6 p' U- t- Z$ d' x                                        1,  // Number of parameters* n# ^; O) ^; K3 }: `( ?. T  l1 x0 X+ w
                                        1,  // UnicodeStringParametersMask1 P' P7 O" T# a! ]% m8 c
                                        &hardErrorParam2,% x& G3 V  R. t; v+ ^
                                        OptionOk,   // OK is only response# ?! }. `: [% n* I2 I# X% m* l5 a
                                        &hardErrorResponse2 );7 |1 V3 ]; E1 X% ^% z4 S0 Q, W
                                                            
; t9 [) C/ a0 t2 x7 g                    if ( _LdrpInLdrInit )
, i% Z5 `0 a+ i3 e" H) |4 `                        _LdrpFatalHardErrorCount++;
  q( H- }* B2 V8 @2 S" H7 X  c/ a9 c- W3 X. X$ g
                    return STATUS_DLL_INIT_FAILED;1 f+ I" A# Q; z3 F, g. \$ O% j; m
                }- J5 F9 _0 K% X5 _6 E
            }2 |; B5 D  L3 x4 L/ Q) d+ ]
        }0 o+ H% Q) b8 j+ P6 F8 A! i
% M) o8 A) p1 _, k& v# K* r. T
        //0 ~& ]! Z" F1 i: Y# t
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时, A4 W4 F" Z/ A/ W: W: T8 h
        //      - `* _5 a+ [( L2 A0 L# Q( w! b9 K
        if ( _LdrpImageHasTls && bImplicitLoad )
, ?7 G6 I$ L4 ?" W        {$ ^: T5 z( \# I  J9 q
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
8 a. h9 l* [8 H! Y( _                                        DLL_PROCESS_ATTACH );
( X5 X( ^) ~  X1 h0 t' w" c- h# T        }
0 Z/ [9 ^' r* B- D9 ^1 N( O4 h    }
9 L0 x. c" k8 d# s. A6 O6 b    __finally. [' K  r! z8 r" L6 `" ^& D
    {
0 k+ c& R  g3 c  }0 n) ^# t        //% M. ~4 A* R) S# J
        // 第四部分;
7 E5 ~& {; e. \4 e  G0 O        // 清除分配的内存" P. T9 X: Y# Y0 g7 A, c/ G& o
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
: m1 L/ O7 k* m9 K    }6 o# M) M4 y( C7 v7 c

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

本版积分规则

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

GMT+8, 2026-10-12 06:52 , Processed in 4.085470 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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