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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]" v" P7 i' e8 j" D" Q0 J% v# [: x" M8 c

+ H0 j7 s) v. r6 S4 L" h. @WINDOWS 2K Dll 加载过程$ f3 T: T% z$ v, f) [- o8 d+ O
jefong by 2005/03/30
" Z  D4 e! ~3 {0 h" A这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
1 A4 X; F( |% A) J1 Y在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”。6 q+ O) M! ?8 a6 H1 u& \% k0 ?
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。+ h, v0 f4 I4 w! {, M
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
; f/ ?: j7 n# T' o( K( s& D7 G# u# ]  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
$ S1 \6 |1 `3 c6 W- N  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
' _. D$ [4 e  {4 o  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
) G7 S( n1 h( D  q) G//=============================================================================
6 ^+ O/ J$ F5 U! N' P& |// Matt Pietrek, September 1999 Microsoft Systems Journal
0 u5 t7 |' S- Q- w: o// 中文注释部分为jefong翻译
; L8 I" q3 n; x7 G( w# @//: A, e0 S" t! J  P+ e( f0 i0 ^
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
- y3 g! q; N: R7 ^1 O  \2 k" G//
: V' x$ _  ~) Z$ }4 {( r// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;, V* d' I/ D* i# j
//=============================================================================$ X( m2 ~' I) J& i- K
3 e- Y/ d; V; t8 u
#include <ntexapi.h>    // For HardError defines near the end5 ^4 d6 N! {8 ?- ^4 J; f
1 U; ~# Q6 m  @' [/ [" g8 ?! K* r
// Global symbols (name is accurate, and comes from NTDLL.DBG)
! ]- z% x" j* }: w//  _NtdllBaseTag8 R3 ?0 y$ [7 i. d
//  _ShowSnaps
/ Y! k. }+ L8 L4 h//  _SaveSp& \' l& q6 R) ~7 Y
//  _CurSp) C3 S& o* f2 S
//  _LdrpInLdrInit
5 t% j' \9 V, E//  _LdrpFatalHardErrorCount
! c% G- u4 {4 Q8 `" K( }//  _LdrpImageHasTls
. k, ]5 q$ C% I1 O  \9 K- b$ Y9 T0 V) V% X$ |& f' B4 T' {* e
NTSTATUS! l. m7 n1 v. `' u: v$ ]& J
LdrpRunInitializeRoutines( DWORD bImplicitLoad )8 ^7 w- N$ Y' s) b, _1 ^& Z5 Q3 |
{
& V) j3 f5 p1 e8 B2 p9 G    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
6 v- T$ B; d: {$ _    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
" `0 E3 |" A3 V
0 n& |/ e0 l! `4 t8 B7 X    if ( nRoutinesToRun )+ a# T% w2 X6 ]8 j5 @9 U
    {
: ]) b- V) @3 ?/ T        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。2 t0 X8 R2 |: ]  a7 ~9 Y) ?7 v1 c
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
. s$ \7 U8 E/ }3 Y1 f0 ]9 h+ ]9 b                                            _NtdllBaseTag + 0x60000,
2 a& W! i5 k# Z* H" Z                                            nRoutinesToRun * 4 );
8 _7 }# z7 g- u& `  i* P9 f                            6 Z" G% `. n* G6 C% H, d
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
  H) R2 e8 h  r            return STATUS_NO_MEMORY;, `  s) h/ J* E( C  q" q
    }
0 h7 R/ D" G8 J8 L    else' ~* S  Z# q. H' z
        pInitNodeArray = 0;) ~& {5 |6 _' P3 B& x9 h

  F5 L; K3 J$ v4 r9 |/ K1 A6 J# K) {    //第二部分;0 D! N1 Q' ]( K8 W& A. b/ J
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
4 P0 G5 ^& U& {4 {. D    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
7 v/ O; D% f) U% X& Z. ?    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
9 r- f2 d) J# ?6 }5 q        
; v6 R, d% N( t7 n    if ( _ShowSnaps )
0 b6 o+ v( l% f6 _; h0 ?+ w9 c5 ^    {! t6 w# h, h0 z4 ]! c6 \4 i
        _DbgPrint( "LDR: Real INIT LIST\n" );
) V0 ~- `8 K6 y+ X. A& {6 p) S    }# s: k3 w# }7 k8 _. y% j4 W
+ Y% S# y# }( z& m9 k. w( q
    nModulesInitedSoFar = 0;
& W+ `5 S$ @& ~  J! G; M; O* I% ?! O, k1 S; d$ c" h' b
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
$ H7 ?$ g. T& W5 ^' B8 D. B    {
8 E0 d4 B. m  V6 Y' B( \        5 Y! q( E+ R  F. {, ?( q/ X
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块% M7 Y# h7 e0 H/ I2 L+ b
        {8 H! F. v( k* `
            ModuleLoaderInfo  pModuleLoaderInfo;9 f8 d! Z( J0 z7 J! n
            - X& [& ?: Q! P
            //3 L3 {, j3 O2 `4 y
            //一个ModuleLoaderInfo结构节点的大小为0X10字节  r* \+ i2 n& _: q
            pModuleLoaderInfo = &NextNode - 0x10;+ ^8 ~/ k( O7 T( E/ r
            
% S7 N% I4 _3 y# `$ i            localVar3C = pModuleLoaderInfo;         0 b5 c. I# W: C! A3 P

- {! J5 T( w: z1 t: f. n            //
0 K' a& q3 @8 B+ }) L6 J$ i            // 如果模块已经被初始化,就忽略; F! B4 f3 o8 m9 h% v. F6 I
            // X_LOADER_SAW_MODULE = 0x40 已被初始化
4 j+ O6 \) S* V2 C" v            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
1 w  `8 q, M0 u, z4 t            {4 Y. ?! i5 i4 Y% j) S7 ?5 j& f% N
                //1 C1 i; f! r- Y3 J7 A# f
                // 模块没有被初始化,判断是否具有入口函数
% Q- Z! D$ `# D) U                //. `3 b/ ], }# e1 c" p/ z' C; W
                if ( pModuleLoaderInfo->EntryPoint )
) J) P( b+ @1 U! }                {4 E% H1 c+ ]7 G: e# M' C
                    //1 k1 A$ ?! L4 F5 ]: S$ S) [& o
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
! {" V1 f* _4 u+ Z, J/ Y5 y7 A- R                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
7 L, x* h9 u+ x" x- a9 J8 D9 x& v6 x. P: A& r
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址  D5 r" L' Y6 N5 s6 N
      // 例如:9 V- C7 t+ A3 X) n  q
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000: }' s6 g4 j2 h- C1 l
                    if ( _ShowSnaps )
2 O- @  A" i9 i) R                    {$ ^3 p5 l, ?+ o' P) E" L; @4 p
                        _DbgPrint(  "%wZ init routine %x\n"," s- O4 e' J$ ]1 A7 y4 T
                                    &pModuleLoaderInfo->24,
8 P4 L0 _2 w4 R. Q3 l# K. H                                    pModuleLoaderInfo->EntryPoint );8 J8 r. p% _" J% E$ T- J9 j6 g
                    }
2 N. g1 h" [1 r4 N. o/ a
- G- h7 s% }+ ~' r4 |                    nModulesInitedSoFar++;& M2 B  L" P" i8 o1 _
                }
; f; S7 `# }% w& L& n            }/ Z( e! ?$ s* p$ i7 W0 p' e; X; _+ u
* ~% T7 p' A& \2 g9 S- @7 w
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。( B+ ^! a1 ^0 E& n
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;" n: J( a! V0 g+ X
0 v7 f2 }" F: p  B. J; F7 p
            // 处理下一个模块节点: _2 W  f# f  i: K4 J# q2 I
            pCurrNode = pCurrNode->pNext. d: W2 ~' {7 F# Q
        }
8 s3 i, k& O' |( ?    }
* l* O+ J2 j1 m: l1 C+ s    else0 V5 p7 x5 ?+ d2 N8 o: g, k9 Q* N0 m
    {
! E# G8 }% E3 v  L( ^2 J, s        pModuleLoaderInfo = localVar3C;     // May not be initialized???: y1 J; B* p+ C/ q
    }% \2 U2 K: C5 S* @
    5 Q4 W0 k6 w+ u7 _5 r
    if ( 0 == pInitNodeArray )
2 ~5 |  J% v" Y0 g        return STATUS_SUCCESS;
+ S, W1 X5 w: G
# s3 E1 v9 K+ H0 ~    // ************************* MSJ Layout! *****************
% `3 [' S4 h, P    // If you're going to split this code across pages, this is a great* p9 M- P% p5 D/ r, `0 F0 W  k5 s
    // spot to split the code.  Just be sure to remove this comment" h, R6 ~: f8 R+ \$ ~# _6 `% w
    // ************************* MSJ Layout! *****************7 H+ i; \* Z; y) \9 _  l% B) E
    6 K' q2 D7 V: y/ Q# H: `
    //
( x0 ~5 j, O* z" P5 r    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH! c7 `+ d! p  v) C' M; }& ^3 N! `
    // 第三部分,调用初始化部分
4 R% G; h. Y  X4 k$ d2 e( u    try     // Wrap all this in a try block, in case the init routine faults
  j  R' ^% ^+ b    {
* |3 b  R% N9 X8 [5 x/ [( m        nModulesInitedSoFar = 0;  // Start at array element 06 [+ P  W& C. m. |
5 D% Y" ^& q/ l8 P* z$ l* O, A
        //
8 ~3 o% U* r% u! v9 ~% u        // 遍历模块队列; F% [  R/ a" v* S2 l& Z$ _6 E
        //% _* Y, m2 G) {
        while ( nModulesInitedSoFar < nRoutinesToRun )
9 k, ?3 f8 ~0 h# _6 z' `: x        {
/ l2 [8 f" ^4 V  E7 M# Q: _4 A            // 获得模块指针
1 ?* L+ K" y3 M  u: R% v            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];+ L# {8 @% q; K* `" {6 f
4 M# }9 z0 I4 f! B8 Q8 n
            // This doesn't seem to do anything...
, h% c5 G) ?7 [8 t5 J! ~5 R            localVar3C = pModuleLoaderInfo;3 v: _8 ]1 t2 P  ?: K- B
            ' l! V! j- ?/ k  T/ i" X0 I
            nModulesInitedSoFar++;
. |9 P! k. L% s4 `                ) s! v! Q6 m9 N0 O2 t$ E: w
            // 保存初始化程序入口指针
. J6 o+ k1 o$ M, a  {4 N' e            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;# Z2 q- C) j, s2 o' g
            
/ I% W. @2 y, [8 Y. _3 q            fBreakOnDllLoad = 0;    // Default is to not break on load
& `+ @! g% t7 z; G* v% v) g: l. R) I; _6 R5 L
            // 调试用/ k, t9 {0 B# f0 D4 Z
            // If this process is a debuggee, check to see if the loader
* ]. y# p, w5 w2 T9 [( ~4 f            // should break into a debugger before calling the initialization." U, }1 m4 _: k: j9 E. n
            //
+ B* o, W0 S7 i. _# x4 N            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
" M, D7 M' V' Q- g4 g            // returns. IsDebuggerPresent is an NT only API.
( s) }+ l  j: K2 e6 m* L            //
3 q3 i* n+ ?$ [            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
4 K+ u7 c! E9 J% N( U  e            {+ b% Z; {2 z/ j
                LONG retCode;
! Z) Q! j7 d$ u
3 V+ e/ E2 q! {; @                //              
. Y& J. }" t# Q  p' ]: _& f1 }+ v% Y. }3 f                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
) D; W4 J/ O. \8 e$ }/ S  P                // Windows NT\CurrentVersion\Image File Execution Options"
1 `* F0 w0 l0 m% o- Z, j& F                // registry key.  If a a subkey entry with the name of
3 C7 g7 g! c3 v. F4 Q                // the executable exists, check for the BreakOnDllLoad value.
2 q6 K% {; W' k+ R1 ^1 ?- P4 }                //# W# V* b* b! l2 Y4 x
                retCode = 7 {5 R5 H! }; r1 T; X
                    _LdrQueryImageFileExecutionOptions(
' R* \" D9 [, n                                pModuleLoaderInfo->pwszDllName,1 a1 t  j, b9 C% O5 v" p0 A6 [
                                "BreakOnDllLoad",pInitNodeArray5 T; X3 A5 T. J1 i6 ]
                                REG_DWORD,
$ N9 D# m- z% Z6 h2 }3 S                                &fBreakOnDllLoad,
0 b' c& ]* e  u% D3 w" |  v5 D                                sizeof(DWORD),
4 K8 v/ v1 g. E1 B                                0 );
+ J, r' N. X( x
' P0 F) @6 T7 q( e' F) \                // If reg value not found (usually the case), then don't
! h7 L8 t# A# \3 q+ R: B8 j- v                // break on this DLL init
6 |( y: j$ f# n8 Q                if ( retCode <= STATUS_SUCCESS )' H! X/ y6 G6 z# g- m
                    fBreakOnDllLoad = 0;pInitNodeArray
8 j& z  f5 J) V& M0 o+ u1 \            }
. K5 }) D. Z2 H. L; q0 B, u4 F            4 s4 v/ p& l. z* `9 {& e0 c
            if ( fBreakOnDllLoad )
  h+ b$ M; v2 C# a  k5 E! E. X7 c            {           
3 A. ?. u% T- Y: M                if ( _ShowSnaps )
' e  l! c) y- s% I! E9 _% M8 h                {
: M- a0 ^  m' O7 a% y( P7 ]                    // Inform the debug output stream of the module name; ?- s/ g) v+ l  b+ K5 `; w2 r; U- s( u9 l
                    // and the init routine address before actually breaking; |% ]) P8 `8 f# N3 o9 h% {, Z
                    // into the debugger7 g. K2 ^1 F  g% _( w, k' j
7 T+ v# g6 I* x; a4 R# Z, S+ o" ^
                    _DbgPrint(  "LDR: %wZ loaded.",
' Y1 K! q: \+ j7 H, b                                &pModuleLoaderInfo->pModuleLoaderInfo );% e& V2 ^' i+ D- a
                    
' X! x  t, i: ]4 S- W* X# i                    _DbgPrint(  "- About to call init routine at %lx\n",0 A3 L7 Y, Z2 \
                                pfnInitRoutine )
$ x5 `& r; q$ ?% n4 ~$ ]# w                }0 t% r: ~4 g/ G1 S
                5 y4 E6 @, K5 f" O  w/ u
                // Break into the debugger                              
& M* ?# f% q6 s! D: t                _DbgBreakPoint();   // An INT 3, followed by a RET
  H+ M' N8 K4 W; J# K8 c4 M            }
6 ~/ F: f4 W- g; E7 K            else if ( _ShowSnaps && pfnInitRoutine )
/ S# Y* r' T$ A            {
/ O9 z; q+ I! D4 c. f( ?( H                // Inform the debug output stream of the module name% \" p. T( o3 e. U% K
                // and the init routine address before calling it               " y7 v! _" V" F) t+ H* l
                _DbgPrint(  "LDR: %wZ loaded.",
: o& Q1 X, {- _/ P( f                            pModuleLoaderInfo->pModuleLoaderInfo );; W1 w) }( O6 d" T" u5 _3 r0 s$ k
) p7 \4 C+ j6 k
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
1 t: x! C8 z+ V( q% s            }0 Q9 l6 |0 t5 f) ^' B! T% Z( x
                    . m. U/ K' o+ B# s/ _! D
            if ( pfnInitRoutine )
2 A) ~  I6 `) Y) m/ n            {
; H# ~6 o7 `6 V/ ?% ~0 J% r                // 设置DLL_PROCESS_ATTACH标志
$ Q. B* y4 M/ H& h; z7 \                //
# o/ U, k9 m$ Q1 ]2 K* L8 y: i7 H, b; U                // (Shouldn't this come *after* the actual call?)* _8 c5 p4 u: R+ n% H
                //4 r0 ~" i- ]& c( d' c2 @1 ], z
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             - m- L! l. N8 a* u* o
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
  v6 N& k6 I7 A& k$ B+ M& l" N8 E: w6 C% t+ N  D0 R" U( v
                //
/ u: W9 o! {$ O3 K2 a& ^+ R2 e                // If there's Thread Local Storage (TLS) for this module,
5 a8 X. X3 h5 @% r1 z6 G$ _* L                // call the TLS init functions.  *** NOTE *** This only
/ v: g# j# P+ l0 [+ @. p                // occurs during the first time this code is called (when2 w! K: R3 g  Z9 _) w) E
                // implicitly loaded DLLs are initialized).  Dynamically
, I/ K7 A, D1 {- ?: {# K8 m2 Z                // loaded DLLs shouldn't use TLS declared vars, as per the
8 I: X' F% w. n. i                // SDK documentation8 |$ J' \1 h: W$ m" R
                // 如果模块需要分配TLS,调用TLS初始化函数3 ^& }% A4 K3 D1 F0 i0 p  _0 o; c
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
# ^1 w; E! E2 F9 q1 _  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
/ {% E& z( t/ |. y/ S  B7 |/ D                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )- y$ i" U4 U& |; F  ]8 {
                {8 Q8 [/ Q; U9 Q; |- l9 j
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,5 C) d% C" P0 G4 l  P
                                                DLL_PROCESS_ATTACH );
, R( ?4 H+ @2 F  Z' I4 K! a                }
- ?; `6 d) G0 F* p               
- X1 p: I& h( {) @5 G* G9 F1 }  q
                hModDLL = pModuleLoaderInfo->hModDLL
% z+ b" S# h. n0 X3 M$ G. g% N0 {) u
' Z! r1 i1 R. a; v$ G9 X                MOV     ESI,ESP // Save off the ESP register into ESI
1 |7 t7 [) v: V1 [; k  o6 p6 w3 }6 U  & i+ ]3 a4 S! H! f3 [1 v4 b4 H
  // 设置入口函数指针               
. w- V8 P1 {" V% p4 O                MOV     EDI,DWORD PTR [pfnInitRoutine]                     5 J9 _- G3 W4 ?, E

7 F% o* J# Q3 S                // In C++ code, the following ASM would look like:4 \3 ~  G$ R  S# Y# u( C
                //
/ l. Z" Y7 m, d5 A1 u; f                // initRetValue =; m; b5 q) H8 q
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);5 r; r, [- q! M# T
                //- h6 h* \. U  u0 u2 @2 g3 y8 f3 `
  e3 B- m' Y9 m. ^
                PUSH    DWORD PTR [bImplicitLoad]! L! W9 M2 J# P" C% K" ~
               
6 z0 H+ Q+ q+ K/ u' D. k                PUSH    DLL_PROCESS_ATTACH
( ~* O4 g$ W' O, E) D" \               
" P( k+ K% V. G) g! U+ O  F8 p                PUSH    DWORD PTR [hModDLL]
6 U9 M% @5 ]! i: y                8 \. e" |) {& B' F- P, b
                CALL    EDI     // 调用入口函数
( o0 n* E* x( ^) i, \1 Z( H/ i               
; X+ B: P& F& U4 z6 q                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值% M# E$ N: T6 r& \( U
- P0 n. M# |. [+ |4 \. d2 B7 P
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
; L" U5 D" t8 |; a" T  d                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns5 V9 _0 r$ g% W. K) @; W: Y) s, F! q. |
3 z) g1 \* x6 g1 a
                MOV     ESP,ESI     // Restore ESP to value before the call. W8 ~6 W+ W, H2 l% L! S
1 ~& g" q8 J  }0 D  s+ ~) \
                //
# s, V+ t" z# w2 m% G/ o' E9 p& Q                // 检查调用前后的ESP值是否一至
# V* v; U; b8 Z( I1 B2 s' Q  // # E8 W8 O" P% Z* |  j) u) P' s4 C
                if ( _CurSP != _SavSP )
6 T7 `/ X" D  }; V3 S& h                {
; M, ]" ?- ~* F8 j                    hardErrorParam = pModuleLoaderInfo->FullDllPath;% V1 A8 Q$ E* N  e
2 Z. ~/ s3 K+ M9 \4 r9 V7 _) l
                    hardErrorRetCode =
7 L3 Y* x* ~' r. c                        _NtRaiseHardError(
3 F5 y3 Z6 O4 H8 a2 O4 w5 {                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
- y) C" T2 n4 N3 L$ n7 M# X                            1,  // Number of parameters
6 {+ {, j( K% S6 d                            1,  // UnicodeStringParametersMask,
& u! l& Q! Q# G% h& H3 k+ X                            &hardErrorParam,
, [& m& n9 W4 U3 P8 @& t: U                            OptionYesNo,    // Let user decide' j. W+ C; \6 J1 w8 d' C/ Q
                            &hardErrorResponse );, `9 M- E. x, M% l" B- g. z6 r% @
                                            7 F0 k' j- z) p, M3 g
                    if ( _LdrpInLdrInit )
% H' L0 N* F5 p( f+ b& _                        _LdrpFatalHardErrorCount++;8 o) j  \. j/ b' f4 l' O0 I

( n6 Q1 T7 E0 O& l' h- n                    if (    (hardErrorRetCode >= STATUS_SUCCESS)9 L$ V$ D) b/ C4 e' ?1 A8 K5 o
                        &&  (ResponseYes == hardErrorResponse) )' h# e: y: A* G2 a, w" u$ Z7 |" D
                    {$ U6 E- Y4 ^; `7 }" b, Z- I: x
                        return STATUS_DLL_INIT_FAILED;
0 W9 ?  [/ O0 ~8 K# T                    }
+ f* O# I: b% O% Q# h9 P- U                }
# R7 {. `. }/ j% Q& |" q" F
& N2 _$ g/ x' a/ Z# G/ V4 N( U                //
: L% d* O3 h6 w                // 入口函数返回0,错误; T) _' O5 p7 `: _$ }% n( X" X, _
                //
  X2 B: E) O2 e                if ( 0 == initRetValue )
$ L! C. U( D& D0 o, \: s                {
  o9 m7 S4 |9 @- `. a5 G+ S                    DWORD hardErrorParam2;7 y5 h% I4 w4 W3 J' h
                    DWORD hardErrorResponse2;
8 a3 b: Q  R5 A+ u                                        - V; A( f8 i3 M* A" h
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
" X6 ^! c. ]& i" {. l- x                    
7 x' p! g' v5 j- x7 _4 l+ z7 U                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
3 R: G) _/ m0 w                                        1,  // Number of parameters# k" p# }# J4 j
                                        1,  // UnicodeStringParametersMask
( _  u" A! H6 b                                        &hardErrorParam2,
6 \5 C, J6 d8 U# P4 [                                        OptionOk,   // OK is only response7 N/ c6 d+ [$ w0 q' {
                                        &hardErrorResponse2 );
7 {6 n/ `) |6 [/ l                                                            
  m7 e) v! }" f- n3 P9 }                    if ( _LdrpInLdrInit )4 n5 h5 ?% N. Z9 Y
                        _LdrpFatalHardErrorCount++;% F6 w1 o- O1 V. o+ c: I
: C* I# W* q' j0 L% |" ]9 B3 i
                    return STATUS_DLL_INIT_FAILED;, y+ F. u+ ?( O! o' Q
                }
7 c6 n& m) ]' Y) U5 V            }
7 n6 ^: F, ~* I9 M        }
& e3 V  x& A% ?# ^% G6 O4 C& F8 p6 [8 A  |- R2 n8 h
        //3 h7 w+ J& K* ?6 Q% P8 K
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
5 e* i3 o: \7 j( s" q        //      
1 v7 o! O7 h$ E        if ( _LdrpImageHasTls && bImplicitLoad )
3 a# N# S+ ?' m. b4 S& x        {* c2 \: U3 o" S( P7 ^( E+ b) P9 v
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
3 j* \7 `6 ~' s0 ?) P* S; S' k                                        DLL_PROCESS_ATTACH );* P  k$ }- b$ g$ P8 D
        }6 U8 F; \' d' `/ b
    }; v5 u/ [& F$ w! @3 f0 m- o
    __finally
% P% H, Z, S; Q( U    {( r' p$ g1 v4 G* V, ~
        //
0 |, g: P6 v% i3 |, Q. ?4 f        // 第四部分;- W9 r" [% k+ y' P) @' k' ?5 f
        // 清除分配的内存
4 D' J, O5 k- X8 |# h        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
1 n& Q- E' u, H! N  b/ o    }
. n; K1 g2 Z) b/ f1 \
6 W2 ?' \' ?7 @( s) Z: S    return STATUS_SUCCESS;1 x; S3 z0 q8 |* b6 t: b
}   
5 [" G6 W2 p" l" i2 ]' y1 a+ Q
2 l8 q6 {6 ?1 T1 K7 L这个函数分为四个主要部分:
6 _/ d5 C3 t4 e. W: ], l- {% ~2 W一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。% t2 G  v0 Z/ s- b- g& ~8 V( S
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
1 ]) y* J  _8 t& A8 U0 S1 \三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。$ a9 \( Q- d2 I$ v( Y; ?
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。) v. x) ^/ j. @
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。& H6 i; j2 ?: i) f( V
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-8-11 17:05 , Processed in 0.049993 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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