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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
: P) Y/ @' G  _! x7 w+ \/ e* T3 w
5 q6 q- f, i( B' D9 E# EWINDOWS 2K Dll 加载过程: z& ^" U2 W& ~$ [' ]
jefong by 2005/03/30
8 Q! U2 a5 \' q& h; ?3 d7 D这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。8 F# l0 @5 c6 |" E3 j7 x
在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”。% a' q! n5 V$ b" K6 w/ e1 W
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
* V6 m: _# Z3 o6 D  R初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
$ T3 S/ p0 T) s) l! _! g  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。: H) R8 L1 r- F; K* y6 q+ e
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
' Y% H7 U3 ~  s2 \) f, O  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
1 p9 B/ H! n4 K  ^5 J( d" Z$ ?//=============================================================================
5 d) t+ d9 _* m8 J! y// Matt Pietrek, September 1999 Microsoft Systems Journal( F; V" M% W, R8 e: K/ Y1 n7 G$ J
// 中文注释部分为jefong翻译
) J' d, Z" g% m( b) I" O//% L8 [1 Z6 z+ t
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)/ {& U' w- |9 k6 u, r
//0 K3 ]( n8 a6 K
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;9 E9 n. A% q- ~. X# u
//=============================================================================1 \( j% B% x) S
: R& y$ I  e! N7 M- S# f$ w" h* @
#include <ntexapi.h>    // For HardError defines near the end* C  Y# O6 H" g$ |
" E& |( ]& t+ r; r! h& n5 p+ ~
// Global symbols (name is accurate, and comes from NTDLL.DBG)
6 L; L" `8 @9 J  k* n6 \//  _NtdllBaseTag
+ O- k: p+ h6 c+ A0 }/ e//  _ShowSnaps0 t) d0 G' u$ n, H
//  _SaveSp5 n' Z) s* o8 _( u( {" Q3 e" q
//  _CurSp
4 ^$ D4 q% L3 E- l# t  y//  _LdrpInLdrInit& P, ^/ a* ~5 y4 {# l. W1 v8 C
//  _LdrpFatalHardErrorCount) Q" b( M8 b) _; o# b$ t2 e7 e
//  _LdrpImageHasTls8 f! _! ^* K# U! B: j7 _, G0 h, e2 Z
7 X  E' h- M, s
NTSTATUS! C$ ]( f! W. I0 R9 o9 ^! G  @' W$ ~
LdrpRunInitializeRoutines( DWORD bImplicitLoad )+ x! q% G2 ]( N9 f+ K
{
% M8 a+ u. v! |- A* {! R$ t8 [    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
9 |* t  w1 n. U% O, _) E+ k' u    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();4 s% n9 o& m  a8 P% w
& T9 [! C3 I3 T0 I, c
    if ( nRoutinesToRun )
5 k3 @# k6 ?8 _3 V$ A, g$ N4 i    {) Q3 w5 l% K7 N. q1 c. L
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。4 d7 M( _- L9 Z$ v) N: x
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),- m5 Y( c1 j9 G* Y+ E
                                            _NtdllBaseTag + 0x60000,
7 w/ S' Z2 k; w, h' x                                            nRoutinesToRun * 4 );
7 i0 p/ C9 `  L5 G4 ]$ a6 p2 W                           
7 V+ G. I7 t9 g        if ( 0 == pInitNodeArray )    // Make sure allocation worked9 V9 `/ X, W- m5 W/ a
            return STATUS_NO_MEMORY;3 u/ E" b( e4 \2 b3 q5 s
    }
. W- N4 o: Q* H    else
1 g0 `3 ]2 D! d* p8 C        pInitNodeArray = 0;
4 r. g9 n+ N0 ?: M( g
# i# m% Q$ A0 |+ N' K2 X% p, Z    //第二部分;
2 ~( h( u! g3 f! _5 @    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。  @  w3 q6 c0 l% Z
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
3 B, h+ L& K  f4 ~    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;" }) g0 F+ y; ?( l
        ( e8 C) S- B9 t; q, c
    if ( _ShowSnaps )6 ]( A$ L' C3 [3 i) k& l7 F
    {
# X1 y2 Q0 W' |- R( U- Y; w- i& D        _DbgPrint( "LDR: Real INIT LIST\n" );
+ A4 p# h+ t" @- H( M4 Q2 z    }+ }; a9 @2 H$ z2 o/ `
+ s  C; `; j1 w; }! y6 l/ y
    nModulesInitedSoFar = 0;
8 R9 p2 C% ~: i8 @! P
/ O3 v2 @  K! h8 h$ |9 p6 D    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
( _" x* A8 p$ D" Q, d    {( a* U2 m. W5 w& X6 ?' C
        
8 g# j, e+ c1 ]5 [: ^4 I2 G$ U) ?: l% C        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块0 D* q- p1 R2 `& O3 x( X$ z
        {. Y/ l6 p* D; E& _2 _3 P
            ModuleLoaderInfo  pModuleLoaderInfo;
; a' V9 M1 w% a* l5 z) ?  T. ^5 T            9 i$ N4 ~* r/ x0 ?/ K3 h# U
            //
- p: y8 p7 {* w) ?: h6 X            //一个ModuleLoaderInfo结构节点的大小为0X10字节# J+ Y! Q; A0 Q1 r' Y
            pModuleLoaderInfo = &NextNode - 0x10;
- r2 A9 Y: g3 @! k            
7 g* w7 Q& w$ V1 R) n7 r            localVar3C = pModuleLoaderInfo;         + [2 X% g  s7 l# n1 D" U
! c) c2 `1 q* L0 `6 D" E
            //
% p/ J3 _, ~! w5 J            // 如果模块已经被初始化,就忽略
7 a  s  M3 h+ Q! I            // X_LOADER_SAW_MODULE = 0x40 已被初始化
6 O8 M' v+ F  g) S            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
! o2 g, j2 }' x" k$ U5 v            {
1 f- q6 `$ i/ |0 u+ t                //
. K. r0 ], x0 c- `0 X                // 模块没有被初始化,判断是否具有入口函数+ t1 w- J# I2 ^0 v
                //
$ \* N% o# r- g4 g3 b                if ( pModuleLoaderInfo->EntryPoint )
7 t. u7 H: x# @# V/ V% }                {- [& K5 i7 n' K* n) i8 q- a. P0 ^, d
                    //( q0 ]7 ^* I! E& a. F) ~& J" I
                    // 具有初始化函数,添加到模块列表中,等待进行初始化& s, ]: p" I% x
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
( T4 F6 {6 e: }
: K; l0 c; h+ G, A) `                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址& Q' g  X3 t( ^) {" _
      // 例如:
8 Z" s1 L& D1 z: s                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
( L% `9 ^' g8 J: }                    if ( _ShowSnaps )8 ~6 [; R; L- v
                    {# s* R8 I6 J% G( p5 E) h2 q
                        _DbgPrint(  "%wZ init routine %x\n",# Y6 k. o$ G! I+ f
                                    &pModuleLoaderInfo->24,
" \' r: v7 }7 O/ w/ D$ C# I                                    pModuleLoaderInfo->EntryPoint );  l' w7 f) ~) i! d  v4 p
                    }$ n! y4 p3 T9 V$ n0 P

  r$ f; ]# {( @6 d$ l4 d                    nModulesInitedSoFar++;! p# T, Y' i& T& B" t2 k. l) {
                }% f) G$ N; s5 l+ b1 ]5 i) Z
            }
3 d/ ~  S4 r! o7 s. Q! S' M" n3 y9 J$ l6 U2 L2 h6 \
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
: l1 O, o6 l* ]6 T) h3 {* R8 l            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
. g* g' `" U+ S, \) R9 C* G) ^3 d( g: o8 j( ~
            // 处理下一个模块节点
! k. U/ v# {6 @6 H            pCurrNode = pCurrNode->pNext' S. B5 b/ y# w6 G
        }  V; |- F' P- N5 s( `- K
    }
& V) T2 P4 K. ]- h8 h6 a& e    else1 N+ T) S0 ~8 p# @: }1 Q
    {
& O2 X" n/ o0 K  M7 C        pModuleLoaderInfo = localVar3C;     // May not be initialized???
; Y1 d. T- x% ?" J; B    }
4 `5 f3 y( l" q: s- T    / p! c4 U/ p) D  D
    if ( 0 == pInitNodeArray )
/ R3 t; E7 d+ T9 v+ k        return STATUS_SUCCESS;/ a% m$ Y8 U( c6 e! ]& }

0 c+ m7 {4 G* F8 P: N  s( l    // ************************* MSJ Layout! *****************. P: H5 z/ l2 X- d) o
    // If you're going to split this code across pages, this is a great% I# `) s4 b) t1 D
    // spot to split the code.  Just be sure to remove this comment
% X, T& f+ F* S, G% l    // ************************* MSJ Layout! *****************
% [) c% _$ y& I$ c& S1 y9 p8 l   
  [+ E" Z* s! k    //- F) H0 u% v. v" U- d7 E, i" O
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
" }! V8 J) B1 C# g; t1 B" e    // 第三部分,调用初始化部分
, L# I* p" p& s7 y* a$ O2 n    try     // Wrap all this in a try block, in case the init routine faults
, ~; e1 c3 L9 i# y/ u# [2 u$ |9 _    {$ _/ u1 _# o, j) D; @0 Z
        nModulesInitedSoFar = 0;  // Start at array element 0
& V" x5 e- o3 W7 r
) J& C! ?6 F: y5 L: H        //
" r$ t( l+ ?6 v$ G6 V        // 遍历模块队列$ M# L: M! m/ ?( z  X  \& u
        //
7 q0 i! T( C# p0 P        while ( nModulesInitedSoFar < nRoutinesToRun )
  L" r9 D0 u" Z! ^0 W        {
! b- M- [+ K( ^$ s            // 获得模块指针
7 o+ W- M1 h' ?8 m+ \( d+ N0 [5 X1 e& _% X            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
5 k2 n  k' Z1 {& j& T6 Y* u( e5 t0 _
            // This doesn't seem to do anything...
; s" M# w% c: S7 l. Z: V# u5 }, y( k            localVar3C = pModuleLoaderInfo;& q; q5 @" E2 P: w
            . ]1 p' I3 M  r1 W$ Q7 ~
            nModulesInitedSoFar++;
. v) k+ s5 a* w, g5 r& j2 @                + |+ W9 o' W# Y. N# b
            // 保存初始化程序入口指针
$ D" W# {5 A  S/ ]9 H9 u# x            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
/ j: ?9 T+ {, A- i7 |+ z            $ d5 b1 O. [3 ~
            fBreakOnDllLoad = 0;    // Default is to not break on load
4 E' s( u% N0 I7 d1 Q3 w# g- U% `& {8 K5 e
            // 调试用3 L# d: P7 a  [, J' x8 C0 w' V' ]0 ~
            // If this process is a debuggee, check to see if the loader
% Y; S/ l% J# n9 N; C$ ]            // should break into a debugger before calling the initialization.$ V( ~" j" R2 ~1 S& b8 `* @
            /// I% Z7 w9 H+ d9 Z9 C
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()# Z$ m1 P3 C# {. k/ R  g- m
            // returns. IsDebuggerPresent is an NT only API.0 z  a! F5 r7 |$ q$ s6 F
            //$ _4 E& }5 L" `  K- f1 n
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )7 k, ^2 P7 u0 Q  s* B
            {& a! ^7 h. @4 d6 W$ k- G. H
                LONG retCode;6 a3 J! |0 _, A
3 \, t( Z# Z6 f, X. Z" @
                //              ( [/ Y) N5 V7 @1 H8 p: z) G
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\. q3 ]) f- [7 F$ K( K
                // Windows NT\CurrentVersion\Image File Execution Options"
8 E# P1 v& f% A- u$ {* J* V                // registry key.  If a a subkey entry with the name of
7 `% d, p+ [4 R9 }$ W                // the executable exists, check for the BreakOnDllLoad value.( `; z4 b4 I1 m3 b6 l3 d- V
                //
/ j$ n! M; k+ j' R                retCode = # T$ T# H. G: r, j& @4 A5 I6 A6 ?
                    _LdrQueryImageFileExecutionOptions(  c( K- J% A0 C! s; O9 k5 p% C
                                pModuleLoaderInfo->pwszDllName,
: F- O- b  s. }( ^. p                                "BreakOnDllLoad",pInitNodeArray
' l9 p  M) X/ I$ E5 s5 O. B                                REG_DWORD,
; _8 J1 M# [5 w6 S( H6 g* b4 L  S                                &fBreakOnDllLoad,
/ P5 f2 J5 i. \: o5 o                                sizeof(DWORD),3 J7 P- K+ @. V+ r0 U
                                0 );
* ]4 _8 R3 G& R$ _* J. l
2 T' v) @# w' k/ W4 m% e  Z; a                // If reg value not found (usually the case), then don't
4 d9 E- e& ~1 B6 N: T) s( F' a                // break on this DLL init
( H* t$ y2 L4 b) U/ F8 S$ s/ l7 F* U                if ( retCode <= STATUS_SUCCESS )- j; `$ n2 e5 E# H0 c: ~
                    fBreakOnDllLoad = 0;pInitNodeArray
' k- y" `. A: v1 P" g            }+ x5 h' a: {: H
            
5 w/ h: j/ x2 p: x            if ( fBreakOnDllLoad )5 C1 J# D- }8 S2 m5 C
            {           5 D1 p! \" M1 M: \% g, h4 i
                if ( _ShowSnaps )# l5 N& P# H: U: I5 c4 l. [
                {
5 W1 L" N1 d" d: f                    // Inform the debug output stream of the module name
5 p: G7 t* E5 f* {8 ~6 Z/ o3 o                    // and the init routine address before actually breaking
5 ~. v7 e* q5 _% {                    // into the debugger
8 ^% C" o6 z8 U$ O6 r" W  [: i0 r) d% U0 S5 j5 `
                    _DbgPrint(  "LDR: %wZ loaded.",6 ~, M; [* Y- D. A  d4 Q3 [5 s
                                &pModuleLoaderInfo->pModuleLoaderInfo );( [7 ^9 c& j: I" N! o
                    % c* n0 I; `# X
                    _DbgPrint(  "- About to call init routine at %lx\n",8 b8 c# w3 c1 e
                                pfnInitRoutine )& E' j* _& Y9 H$ A  ~5 G! g2 }
                }
7 v" J3 X  V  u( M' N7 C               
- T2 r3 ^$ ~9 f% A( A' [1 B& e                // Break into the debugger                              
( U. L7 b: J% Q& ?2 ~/ I                _DbgBreakPoint();   // An INT 3, followed by a RET4 E) ]3 F' Q' C5 o
            }+ {5 _7 L- f: \& K" \5 e
            else if ( _ShowSnaps && pfnInitRoutine )5 u" H! L. b# b* w8 \: B
            {5 F- q# @8 D7 T
                // Inform the debug output stream of the module name
0 G; J9 v) e4 y9 E2 e                // and the init routine address before calling it               3 A* x! b. _: v6 @. c1 q$ z+ P  \* m3 T
                _DbgPrint(  "LDR: %wZ loaded.",8 l/ R$ \% w1 c( [3 c" h% x) V
                            pModuleLoaderInfo->pModuleLoaderInfo );$ u* K9 D* J! a, V5 o) Q4 L

% O( j  Q" G8 _0 v                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
' R* A5 a+ H7 k            }
( x+ ?- M# _4 R9 I+ D5 _( {                    5 Q( w) J; V8 t+ V# K. R
            if ( pfnInitRoutine )- i* U" V/ W  V
            {4 @; b# ?3 L! ]& A) h/ S  l
                // 设置DLL_PROCESS_ATTACH标志+ s- `) g" `; b" [8 d1 N9 Z0 p5 m( m
                //
0 x3 E6 B* Z$ x8 r* M# Q                // (Shouldn't this come *after* the actual call?)
* }. l# [% e9 ?. W5 S% M) t                //( p! M: ], x! i4 y' p
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
: H. H" U  V4 B4 ?/ E) t                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;9 N4 l# ]2 j0 r5 ]: O3 ]2 q7 r
9 |, G  b! \6 X& e2 Z& p) B
                //
! A  @# u+ j" w  \                // If there's Thread Local Storage (TLS) for this module,5 y* ]* J4 V; k+ j- h% b) p. R7 |; v
                // call the TLS init functions.  *** NOTE *** This only
. u4 j1 w2 t, M4 ~' D& k                // occurs during the first time this code is called (when
7 h" q- Q$ @1 Y- V& x7 S                // implicitly loaded DLLs are initialized).  Dynamically
' l2 i0 h$ p$ A0 y5 l$ w+ S( B                // loaded DLLs shouldn't use TLS declared vars, as per the( @: N4 I1 f+ l$ i7 c: K) U
                // SDK documentation
. w9 }' _+ o$ j! C/ j4 z! k                // 如果模块需要分配TLS,调用TLS初始化函数# z& [, T" N( s. H
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
: t; n" X! h/ A- e  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量. V2 O, d1 ?: O
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
$ \: h6 h: C0 {+ s+ r0 a- f$ n* T                {
% ?6 e) s4 n* o) v$ y! I  n& I                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
8 `5 }" u  M+ s* P" \6 j                                                DLL_PROCESS_ATTACH );6 }7 V8 M4 K! N6 i+ a
                }4 ?) z" i, l5 A  v. @
               
, O+ u6 b7 w# o# t; ?9 y+ h" y& i7 ^
) b" |9 U; p) k, Z$ G0 M) \                hModDLL = pModuleLoaderInfo->hModDLL4 D- d9 X! H8 u* `' V* _( X* u

: q# M# B) R+ c                MOV     ESI,ESP // Save off the ESP register into ESI* |. N4 A7 l- g% t3 }
  
" M: b, t9 q+ B# T* M  // 设置入口函数指针               
, G: s: M6 o( a$ D  F9 C$ V8 j  H- `8 F                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
4 }) Y) s8 Q/ Q7 X% T! E$ a9 W; A4 c. o+ u4 e
                // In C++ code, the following ASM would look like:
* k; a, B/ i8 `6 P; R1 _                //! T0 M9 A9 g/ d7 t9 C; B$ Y
                // initRetValue =
7 \) ?4 X' t" \( Q                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);# G) Q3 z& N2 x, l( |
                //
: }5 m' d: c5 D# g4 a; m# A, x  z1 v# j+ C3 s9 K( ^( S
                PUSH    DWORD PTR [bImplicitLoad]0 D' Y1 J% [/ r4 ]" h1 S# E4 |
                : p  q/ Z' l* @. a$ X. t# }
                PUSH    DLL_PROCESS_ATTACH
- I9 l- R# S4 j                ( h2 p# G1 Y$ G0 r! q7 v5 ]
                PUSH    DWORD PTR [hModDLL]
& t: ~2 ]7 H, h" ?5 y! K+ H+ h               
. R; t; Z% W# f) Q                CALL    EDI     // 调用入口函数
& k) f: E/ Q  X( D1 k$ a: Y" j# K               
0 V/ k  i, F' I                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
2 [4 [/ L& @, n6 n  a7 h1 v" D( i7 O2 W' L8 R
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
; ^) P, Z1 X6 N& ]" z: x- O+ ?                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
2 B! l. j1 x8 d1 Q8 o2 S7 c7 }7 r8 x( ?+ T1 V7 ?/ p2 g
                MOV     ESP,ESI     // Restore ESP to value before the call
* O# z4 w( A4 d5 _! i# h4 k9 N# F, ]& `. ^/ @
                //+ N% j' e8 a2 Z; M3 _
                // 检查调用前后的ESP值是否一至8 m0 b4 e! V& O) u* `9 [% Z
  //
/ @, E& R; ~# G7 R$ W                if ( _CurSP != _SavSP )1 J) g. H* D9 m9 g5 s
                {' K6 O7 L+ a; b, y  ^
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;/ L: u3 B$ A0 [6 @

4 d; O3 X8 `( a+ h                    hardErrorRetCode = 0 ^  a- n$ }" n2 N2 D. x
                        _NtRaiseHardError(
5 L/ I5 u  Y  [% w; l                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
4 E! `2 e# a4 E: _7 C$ Y                            1,  // Number of parameters
& p4 V# {$ `" ]; J1 [                            1,  // UnicodeStringParametersMask,) x$ E. j2 I" V/ G1 w6 A: c
                            &hardErrorParam,
; u; t  F2 Q- m) J- L; ?                            OptionYesNo,    // Let user decide+ p8 m* B0 |6 f5 N7 O9 C
                            &hardErrorResponse );
) A) c! N7 V% K& _0 g                                            ( n% k7 I( j+ V4 f/ I
                    if ( _LdrpInLdrInit )
9 l4 |0 U0 ~* v% N6 L& a8 A8 V                        _LdrpFatalHardErrorCount++;' }% L) F. K4 E
7 B+ K) u/ X  L( |; U7 w" R0 g
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
+ d8 T! A5 l) G' D$ w/ M* b                        &&  (ResponseYes == hardErrorResponse) )
) Y; H! m" }: i% A+ j/ C  W                    {
0 g, O0 u, x- D8 H9 y% }4 u, ^                        return STATUS_DLL_INIT_FAILED;
" d7 j: }; _* m                    }  [2 Q' `: ]; ?
                }% l7 a9 t, R1 l7 c/ R
6 [. G3 _7 [4 \4 B
                //
4 S  H- k& Q$ `$ h, E$ b0 B5 w% Y" W6 q                // 入口函数返回0,错误9 @/ c8 X7 {7 v( B" e) j  J
                //) a" d+ C& `% g* V: _7 _
                if ( 0 == initRetValue )
3 |" b. `0 V; ^! Y                {$ Y6 n, y- W7 D% g) t2 [/ W1 t8 D
                    DWORD hardErrorParam2;' T# V8 ~7 |$ M7 c! |
                    DWORD hardErrorResponse2;. m5 S# H( z+ M! o/ F) @, F1 O' B
                                        1 [1 p# ?6 x9 N5 y4 m) r
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
0 |4 h: P, V# n0 h4 q& w                    
1 q# J: d& `5 p3 T9 o# R                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,/ \( m( M4 O/ Y1 I; k; f6 K- u
                                        1,  // Number of parameters+ h$ q5 a2 d! r2 S0 |- b( K' U4 A0 J
                                        1,  // UnicodeStringParametersMask
/ j$ E8 h' E  S# C3 g                                        &hardErrorParam2,+ S& S- ~5 ?- y& Q3 p2 A: M
                                        OptionOk,   // OK is only response# v3 e- @+ q9 T) h
                                        &hardErrorResponse2 );
: Z  C, Q1 _& V6 W; _% l                                                            ( l" G& E5 z5 E. U
                    if ( _LdrpInLdrInit )
/ x' M" D% C2 L" M$ [$ `2 m                        _LdrpFatalHardErrorCount++;
2 r! x$ a5 x2 n" t8 v) K
- _  h4 U* ?% k& T) K1 U                    return STATUS_DLL_INIT_FAILED;
+ K3 P- T8 E1 ?2 e) e                }7 }  W4 W7 w" B* m
            }
, s& t3 @9 G( ?, _+ a4 O9 ]/ r$ I        }
" l" Y5 J' m: W" N, c/ F" a4 s  W* K( J
        //+ |/ t' v6 ?& B0 [0 U$ n4 a
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时5 I6 c/ R" d7 e2 p1 _; L) P6 y4 w& V
        //      
$ \  k3 r4 m; b) V6 L. z, m9 ~        if ( _LdrpImageHasTls && bImplicitLoad )
: r/ j  s5 {6 D6 s        {
5 x1 V; C" t1 M; Y            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
  V# G( m1 j1 |2 ^, A. h                                        DLL_PROCESS_ATTACH );
, ~) h- o6 c+ \" Z        }8 L0 n5 l; M! y( A. i# \4 m! k7 X
    }$ z- ^2 M5 u& V7 Y" K
    __finally- N$ W8 K& v" C/ \5 }
    {/ c8 w; N4 H# K. h; U$ w
        //
3 Y6 t0 F2 S) g2 x7 m        // 第四部分;* G: ^+ B" ^* }6 v! J. z
        // 清除分配的内存2 S# B9 k) y4 Z
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
- }) Y( Y, p  \/ w4 A- U    }
/ L! Z/ t- P1 O9 O3 U8 ~# G- f- ]1 Z( \
    return STATUS_SUCCESS;4 o# ~4 M7 Z3 B* B- ?# q
}   
# F" m* F. w& s
1 o( t- N7 a5 Z5 k1 |0 [* m: f这个函数分为四个主要部分:
) f1 ~3 x/ k; p# l2 G) K; g一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。1 m) C+ X1 q7 {+ A8 G
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。2 E1 ~! j) j' z' W  ]
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。) p1 {5 s4 e6 e- p& v! B" [9 R
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。  y2 g9 n& x1 y3 c* ]) B+ S
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
; R) _" B# i. J+ ^) V7 b# g5 z" g四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-4-4 14:31 , Processed in 0.136543 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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