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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
* i/ M+ c/ d  l  V, S& Z& H+ X
+ ?- I& A4 r* E( a4 p. aWINDOWS 2K Dll 加载过程
. i- W2 P- g  ^5 Rjefong by 2005/03/30' Y4 t8 G6 O+ u& h
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。: o: Y, V4 {: I5 A  p! e
在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”。" ]. @2 l/ I2 @, G
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
; Q7 e; j. }+ ~5 b9 [初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
# B- P0 f0 H9 |5 n  m  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。; ?3 W% \6 D7 E* c7 w+ H
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
  ~# E% q6 ]' O  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
( e$ X$ S9 c# ]! ~1 Y" Y//=============================================================================& G& R' V/ L8 c7 J. N
// Matt Pietrek, September 1999 Microsoft Systems Journal; [  I% ~5 j# O# x( z+ S  i$ ]( U
// 中文注释部分为jefong翻译
6 _) I$ T2 E6 C4 ]//1 p8 w) {4 r, b# p/ K1 R
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
: T6 S  `, L: k! D3 Y# X2 w//  V3 Y' R$ U2 p$ m3 Y" I
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;3 [9 B& i, C. `! K4 r& X( |2 H9 {
//=============================================================================
% T- }# ?  j  [' N+ L0 s# P- K
#include <ntexapi.h>    // For HardError defines near the end
+ g# c0 b( Y3 q6 z  ^, y1 @; K+ m8 [6 e4 ]/ c* ^8 J
// Global symbols (name is accurate, and comes from NTDLL.DBG)6 r* ~2 ?5 j% B( C, \2 g1 @0 ?
//  _NtdllBaseTag
3 R+ `" ?- W. d7 Z, r//  _ShowSnaps
) ?6 w% Z. ^* I7 l- ?5 F# z% ~//  _SaveSp4 h6 D: K2 [5 K' m( G, N
//  _CurSp
6 k, w7 a7 D- q) h* ~+ g" G$ `//  _LdrpInLdrInit
& [: `6 A, i9 A: H//  _LdrpFatalHardErrorCount
8 t1 Y6 j( v8 J* ^//  _LdrpImageHasTls
7 ]5 D( L8 h* }  V: M* N! O$ W: A- W% y$ g2 R
NTSTATUS2 c" s* r4 z3 J6 h+ i& I. @  H
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
) [- c7 _$ }- q/ t3 F( `{
/ p/ M( [: n1 \3 e    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
$ l; @7 m. W6 E9 m8 c# u' d    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();6 e. `7 i: w% H( d6 c

, G9 U  G: h* {- v    if ( nRoutinesToRun )3 X( Z4 o2 ^$ P9 y/ U
    {3 }+ C& U) B+ W0 _6 M1 o, N
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
" h8 b9 l  P' x8 I8 X- x        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
8 m* y; c. }% v                                            _NtdllBaseTag + 0x60000,. I! Q* l9 T  ~. s! c  A0 W
                                            nRoutinesToRun * 4 );
1 p" r$ Q- H3 b3 \7 Q  z+ n1 {) a$ O                           
! r1 z; z- t# m        if ( 0 == pInitNodeArray )    // Make sure allocation worked
* P6 ^- v9 J% r- k- d5 l( f            return STATUS_NO_MEMORY;
4 e$ n6 H8 v) x) E8 s    }
9 K/ B* ]5 [+ f4 d8 X& f1 P    else
+ F3 h; W# O* S3 J. N. Z7 R        pInitNodeArray = 0;
) I7 c) h5 L: a: R; |; C1 ]8 d' S9 t+ b
1 `2 q3 {5 h9 l! z  a    //第二部分;
9 B; e1 D2 Y" G    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。# }% I# o& D' W6 }
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);3 B; a" n6 q0 K- |! S
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
' V( T* u+ t+ z        
! T& G- _! w# l* l" E$ l    if ( _ShowSnaps )0 a$ l1 T2 h9 K; E1 d0 `
    {9 m% q/ o' @* e% L& l5 n
        _DbgPrint( "LDR: Real INIT LIST\n" );
) Z, t! g" w5 G" |! ?+ o    }
" _+ w. @3 l# a8 K; |) a1 x0 v, E* S8 I) t, P6 w; W
    nModulesInitedSoFar = 0;1 R: C* Q+ ~2 T: P
9 A3 {6 O! T$ C
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块; t. r% e' k! ?9 r' [! m
    {/ m5 v2 V+ r& o# Z
        
6 l& d1 h% A; l0 h" F0 }        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块# Z+ u/ W% k6 m" i- _; a
        {
0 P4 o3 J, ~% s% H" X, }2 ?3 P            ModuleLoaderInfo  pModuleLoaderInfo;9 I5 f/ J% T8 X4 Y8 K- s5 b1 C$ B; B
            3 s+ s3 w; U$ F: Y$ u$ @
            //8 w+ T5 [3 D( z; z
            //一个ModuleLoaderInfo结构节点的大小为0X10字节. M% ?5 o+ d4 S- \. q
            pModuleLoaderInfo = &NextNode - 0x10;
. Y" ]: Y3 r: V* O            # ]7 Y; h' B* N$ T2 t+ i/ p
            localVar3C = pModuleLoaderInfo;         
# }+ U6 F! u% B2 y$ c5 u; g. W; Y8 J0 P! |. |
            //
1 h* s; d4 j2 i( J& I: d            // 如果模块已经被初始化,就忽略
4 ~1 n( {: F  M; {  v            // X_LOADER_SAW_MODULE = 0x40 已被初始化
' c1 X+ s1 t$ f- e4 T( o            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
4 A  y/ I* s1 V2 T* `2 m1 E            {4 H* I5 V, y& u5 E, z6 ^
                //
9 \" O; j3 y5 P3 S1 U                // 模块没有被初始化,判断是否具有入口函数" q' g3 U: U5 k# A& W, m; {
                //% Z7 i; l; y: T) {- j
                if ( pModuleLoaderInfo->EntryPoint )  X" ^2 q- o3 x# L" `. B
                {: a# p  S  g2 i+ e1 j
                    //! }% W( [% g+ b; y
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
2 k) O7 m* m$ P2 k% n4 A+ ?4 s                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
# d- o# g5 H/ Q# {5 N
) T# [* q8 V8 u# e$ t+ C                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
( n5 Q5 T% r& {* q1 _      // 例如:
& @4 b! r* o. a$ ?                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
2 h9 N5 h$ n2 p3 ~7 a                    if ( _ShowSnaps )
" l! @& G- e( [4 l" ?7 z                    {
& o9 k5 E# ?! R' L- L2 @# b$ K& p                        _DbgPrint(  "%wZ init routine %x\n",2 M+ U! V" A) |) u
                                    &pModuleLoaderInfo->24,
. K$ F/ F. Q( N1 W1 j/ W                                    pModuleLoaderInfo->EntryPoint );+ `6 C1 c& ?, z3 ~4 I
                    }! T8 D$ A' `) x' b2 P

( B; T# t2 }( ^; _+ z  R- W" ?1 U                    nModulesInitedSoFar++;
( g9 l0 g: A5 W                }5 r( O/ t3 o& |% \$ o) H
            }
6 l! H$ O* v0 l% K- c7 G6 U# M* v& z( b
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。& g' {! m3 C- P) M* K0 q# B
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
' s/ }- ?/ c' _3 q% u$ P+ ^) X6 y3 A3 k5 A6 C) w
            // 处理下一个模块节点
+ Z0 l* D7 ]; i& C! o  B            pCurrNode = pCurrNode->pNext$ \' k4 r6 W) R* b
        }
( ]+ W2 g; t4 Z; J& n    }( g/ n% U8 Q$ F# \0 V6 n4 v
    else
3 r; m8 P5 n! w; q    {
" K3 S" G6 u3 u. I3 G& |        pModuleLoaderInfo = localVar3C;     // May not be initialized???" b% x- \1 u* n9 l4 a' H4 q
    }
% Q* c; u$ n+ R  x4 D0 o" m    " w! d4 z$ ?# A# N9 X" C2 U1 Q
    if ( 0 == pInitNodeArray ). D2 I/ C, U) C# |
        return STATUS_SUCCESS;
9 v4 A0 Y. m% l/ P
6 O! s" a3 B% D! k5 `    // ************************* MSJ Layout! *****************4 C* ~  B* E2 u" {
    // If you're going to split this code across pages, this is a great$ ^0 D- I" L9 L1 q' X' S7 `1 k
    // spot to split the code.  Just be sure to remove this comment* f& u& {1 i2 T
    // ************************* MSJ Layout! *****************& ?* M; D+ h1 I+ B- f2 m
    . D! W  W7 \- ^  q8 {3 [
    //
# [& \) @$ s% r    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
- ^$ K+ e/ S1 r7 I8 S- N, I9 w/ ~    // 第三部分,调用初始化部分
8 }0 m4 U2 U. E1 p; B    try     // Wrap all this in a try block, in case the init routine faults
6 u* W9 r' Z* ~- M    {; a- u, ^' M/ O) b7 J% t$ ~
        nModulesInitedSoFar = 0;  // Start at array element 0
5 p# q- ?5 ?; w8 C' W0 D0 }; }8 ?& v: Q& r. v
        //
9 w# o) ~8 }% ^5 j4 v        // 遍历模块队列9 b5 E% ]  B% m0 m
        //" V& o$ l, `# e$ B1 o5 G
        while ( nModulesInitedSoFar < nRoutinesToRun )
$ {1 k4 p8 `: E* C2 C0 I4 b6 ~, V        {: E$ z  f% v0 W, K  U) }
            // 获得模块指针
1 e- P0 H2 L8 z" {- B) _* s            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
$ V# a$ v7 J4 y; E1 _  T/ h+ U
+ p5 {  \+ C/ `/ v+ }            // This doesn't seem to do anything...+ c6 r2 @4 i' Z$ Q
            localVar3C = pModuleLoaderInfo;
4 ^2 x& o  t+ |) F* p            
: d. A$ P* S: @$ H' _            nModulesInitedSoFar++;; R: b0 l0 e; Q8 ~9 w1 K4 r  b
               
. e7 Y1 K7 T( K# K: l            // 保存初始化程序入口指针
  [7 B9 e+ r( a' I$ ?" T: ^3 u. t            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
* Z7 e7 s, G+ J" D% P; J4 A: s* i            $ B5 w' q; X* `7 U9 _  P
            fBreakOnDllLoad = 0;    // Default is to not break on load! \% S' |# e& U0 V8 _9 e% o5 d* o1 H6 U
/ [8 v% D4 G  R( W- Q
            // 调试用
! j8 ~/ w* C* v$ N+ _3 K0 m            // If this process is a debuggee, check to see if the loader5 _6 p5 t' |0 F( p7 }
            // should break into a debugger before calling the initialization.6 i  A/ B; ?$ N3 b3 X7 T
            //
- |) f2 `6 v# L; u. ^# y* \            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
% G3 w& [' x1 }6 G% [3 |. \8 M1 ~            // returns. IsDebuggerPresent is an NT only API.
; K6 v: h: H) M$ ]* l  n            //2 B4 ^! N) I; j& ]1 u
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )3 B) j: O, Z/ G! u+ F8 j1 f
            {
$ ?* g4 X1 p' f% B                LONG retCode;; V; G$ Y2 d* y$ T2 z$ Q. M, t
6 K4 Y$ [$ V) g3 e' m
                //              
1 c1 Y  J1 H9 O1 X# W  T. |( o/ k& d                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\/ E. |5 {( a, d+ r! D
                // Windows NT\CurrentVersion\Image File Execution Options"
1 P* h, _) k2 b  e                // registry key.  If a a subkey entry with the name of
0 X% `% X, s, V0 f                // the executable exists, check for the BreakOnDllLoad value.  ?5 L  |0 q! c4 u
                //
  v% g4 w2 p% W' O* B: w                retCode = 5 O  t/ G. ?  M+ s
                    _LdrQueryImageFileExecutionOptions(
7 }1 f& {: T5 L                                pModuleLoaderInfo->pwszDllName,
( c) a3 W0 Y1 D" q                                "BreakOnDllLoad",pInitNodeArray
7 O. {8 W( ^  j: z+ F. j' [0 b; O                                REG_DWORD,
' M3 b* C2 _1 J+ ~, P* N                                &fBreakOnDllLoad,
' \3 I1 S' k' \                                sizeof(DWORD),
# @# `9 q, L& ^                                0 );  p! P. u/ G+ ?9 `& ^% f

' q- Q7 F4 }" ~% r; P                // If reg value not found (usually the case), then don't( a2 h( G# e% w( [
                // break on this DLL init( Y2 J. @, E( y
                if ( retCode <= STATUS_SUCCESS )
5 \  b8 P2 s/ W! s  g7 L                    fBreakOnDllLoad = 0;pInitNodeArray
$ U8 K2 F! b0 O" a2 j" H3 }            }! U7 H/ {9 E' ]0 u$ Y. D$ y- D1 g2 |4 S1 W
            
4 Y7 a" o3 H& x/ \/ o- U. h            if ( fBreakOnDllLoad ): l' x" K7 z* U- q6 C& K4 A; h
            {           
1 d0 o+ S8 h8 Y, c. `4 Y' l                if ( _ShowSnaps )
/ [) S, S" H. _* U+ N0 a/ M4 v6 q( v                {
* t2 F! m) |; E                    // Inform the debug output stream of the module name
- n1 `% q6 N( t# p; O: F( |5 n1 b9 T                    // and the init routine address before actually breaking6 F7 A% G* S. y) @) w% [
                    // into the debugger
0 U0 T. M( r8 K7 {- B8 L
& `( D6 t' p) B% g/ E                    _DbgPrint(  "LDR: %wZ loaded.",
" |0 ~. d5 p$ }; X                                &pModuleLoaderInfo->pModuleLoaderInfo );* g$ r' m1 `2 _7 a0 l$ w1 J
                    % y8 n0 @5 U4 A9 S) g0 Z7 N
                    _DbgPrint(  "- About to call init routine at %lx\n",
! K& a8 V& f7 \' a3 e* g9 G                                pfnInitRoutine )& K& C) P# l! j) M3 C2 R7 t
                }) B& V+ a4 k0 n& O2 F  v
               
2 g% k6 S5 \- k: S0 `2 S8 |) {                // Break into the debugger                              
0 e4 _0 u4 a' J& R5 X                _DbgBreakPoint();   // An INT 3, followed by a RET+ X- h" W$ P0 j" i; b: p. ^
            }3 l) J( `3 q& v" ]& N) v5 M
            else if ( _ShowSnaps && pfnInitRoutine )! W: v! {, k% m0 S- i8 o
            {
5 @$ b$ Y5 R. S7 |                // Inform the debug output stream of the module name/ E; U% B5 {4 N, T: d* l6 Y
                // and the init routine address before calling it               . x7 |9 w. S$ X7 E* ?* L
                _DbgPrint(  "LDR: %wZ loaded.",
8 q/ ^* g  k3 I' E0 K" t                            pModuleLoaderInfo->pModuleLoaderInfo );
) b- [, k' i" B: A+ S  W) [* T( ]) i, x1 N% c9 h" r
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);% u! Z0 M/ V8 Z8 A$ [
            }
- J; J3 ?1 j% W! u6 ]0 h  A5 G# y                    
) X- q* \# G* v; P            if ( pfnInitRoutine )
1 H0 u# D+ m/ {. h0 ~# w            {/ n! D) ~) f4 p
                // 设置DLL_PROCESS_ATTACH标志! T+ s. g, g; ]' P' K# P/ H2 g
                //
# Y# e* Z% ]( ]; ?* V* F7 W                // (Shouldn't this come *after* the actual call?)# q/ C3 z& @+ h# ~* _
                //4 s* O1 e* Z: }
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             - i# x( E8 ~+ ~1 {: S( b" S
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
- d' U1 h" T5 ?' r8 a
$ x$ r- F6 D; ]2 Y# w. }                //4 z! N7 l# V" ~1 m* `5 [
                // If there's Thread Local Storage (TLS) for this module,4 U7 @' K7 B: L! R: P
                // call the TLS init functions.  *** NOTE *** This only
& x* v" z5 e# {1 i" ~  N- Y: @5 [" W                // occurs during the first time this code is called (when) V2 g- {4 L, D8 D" q. u
                // implicitly loaded DLLs are initialized).  Dynamically
, o$ N! W2 z: `) _  t/ ^" k+ D* p, ?                // loaded DLLs shouldn't use TLS declared vars, as per the; e7 J& T# b8 |) f: ^/ }; r. X
                // SDK documentation
" h2 a6 _# G8 k& F3 _$ w                // 如果模块需要分配TLS,调用TLS初始化函数
/ Y* A$ }6 T. M# I5 o  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
2 O2 K8 Y5 `& C# s+ g  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量8 r' H; G+ s0 S9 g' `/ I
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
4 ?4 |1 d/ K4 v; h9 S! j                {. U/ U* Y0 Q" P) i  V
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,* `$ X/ v* w) K
                                                DLL_PROCESS_ATTACH );( h5 ~( B# o* O/ S+ H5 r* H8 b; u
                }3 N3 r5 J8 s$ {+ W/ Z
               
  o6 G$ A6 [4 m% Q
$ w( B- [# X6 D: D                hModDLL = pModuleLoaderInfo->hModDLL+ Q/ p0 d: H+ e3 n
$ }+ z$ t9 @  u/ g7 _# w
                MOV     ESI,ESP // Save off the ESP register into ESI$ x) ]0 l* B0 h
  : L8 U" Y0 k9 q
  // 设置入口函数指针                2 E& r1 q+ T2 d
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
( P# M" ^. p# C" M5 X5 L; T/ L7 ?* S# d! Q
                // In C++ code, the following ASM would look like:
* y- p. Z5 Q1 n3 }                //
0 d+ G! y. o, ^8 M* b/ u9 N) A7 }                // initRetValue =3 r) T" n+ W  M* I2 k2 w# H1 \
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
+ ^9 _) [- H! @6 |                //3 v- D; }+ D& z
! f  x2 ~9 ^4 O. y( ^/ `/ E
                PUSH    DWORD PTR [bImplicitLoad]
/ W$ g. V9 X7 `$ N& N+ c3 L. |               
" ]3 m; l4 m1 j+ P# ]) Z: Y2 k                PUSH    DLL_PROCESS_ATTACH
2 c* r/ \' z8 D" W                & O. y! V! W; H% m$ k" x
                PUSH    DWORD PTR [hModDLL]
9 ^* C3 k' \3 r+ [9 `               
5 w- h/ X) L/ c7 _1 t# Z$ q" e                CALL    EDI     // 调用入口函数
& |3 F5 D6 F6 Q3 M$ O+ ~                , c2 T7 v3 [. x: q5 ~8 ?
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
: S1 r9 V. {' X% N& R7 }8 \7 ^1 x) t  `
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
1 _7 i: _" h7 B9 }                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns6 v9 l/ r; ^4 f3 B: A4 i/ G
. y: R, P3 W: O# _+ p( G
                MOV     ESP,ESI     // Restore ESP to value before the call; j3 @9 |1 N% S6 Y2 C( F7 n5 m

5 p; F5 Z& w8 j" q! ~; A! i; O' e                //  E1 _& C1 {+ T7 C
                // 检查调用前后的ESP值是否一至
7 O2 K% R9 Z! z! g  // 7 j- N9 V+ F) D% w- Y" m+ O
                if ( _CurSP != _SavSP )
& n6 {0 `5 o% {0 q                {% k1 V3 }$ R: f: w* Q; d
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;9 U  g( P5 h! D# p6 N
* @$ C- T; E! I/ g1 D3 F. K: u! q
                    hardErrorRetCode =
; Y' p7 z0 |- k9 p                        _NtRaiseHardError(, g+ }9 `! l5 d* y
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,  D+ H- D/ B5 f
                            1,  // Number of parameters) `9 t: I! A* P6 M+ U( H$ l
                            1,  // UnicodeStringParametersMask,; j4 Z5 {( s( E" j+ `. I
                            &hardErrorParam,
+ U1 Q# m9 u* z                            OptionYesNo,    // Let user decide4 F5 X2 c6 n7 j! @* q5 I
                            &hardErrorResponse );$ I4 `% C/ p- a
                                            : d# T0 Y2 j. w7 V
                    if ( _LdrpInLdrInit )3 |" P/ L' c' ~/ v2 `5 S
                        _LdrpFatalHardErrorCount++;' A1 D+ V: m; Y3 K

+ R* B' P7 L1 D9 _% E3 B( X                    if (    (hardErrorRetCode >= STATUS_SUCCESS)7 F( P& y1 F1 ]2 M# w
                        &&  (ResponseYes == hardErrorResponse) )/ z0 c2 ^) O! u; N" ?, o5 q, n
                    {
# \3 m. o6 M0 u  f# d/ _6 t                        return STATUS_DLL_INIT_FAILED;" ~* k% g0 _" ~0 ?* k6 @" ^
                    }6 M2 ?* Y( f, J
                }
9 x: O% f7 m+ {2 _& U/ G3 {2 W0 [; Y2 _# s6 s2 v
                //
" f1 L- ~1 j  D# L                // 入口函数返回0,错误
( e5 X" {5 w- Z4 E1 S                //4 x" b6 y/ h1 M
                if ( 0 == initRetValue )7 @$ i& X" ~% B$ K
                {
) Z6 q; ?  S4 h* y) c. Q                    DWORD hardErrorParam2;0 H' |1 j) |- p) Q9 I
                    DWORD hardErrorResponse2;
2 J! W* Y7 u& H- f7 U  G4 g( p                                        - ]" Z( a$ v/ d* T5 a7 t
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;2 n. X9 E7 Q* `0 c( `& h* S
                    , u3 V& t1 }" ]/ Z& U
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,! b6 R' P- ?' i- E, }0 O" `
                                        1,  // Number of parameters" r' @6 h6 B; c" P* G3 u3 B
                                        1,  // UnicodeStringParametersMask3 ?3 ^; e! `8 K0 b6 Q" r8 i. X6 A
                                        &hardErrorParam2,
4 N% F5 ?2 r# E4 ~                                        OptionOk,   // OK is only response  `& [4 G! t2 e. I$ r9 |' G$ _% K
                                        &hardErrorResponse2 );! Q3 v" M4 A6 c; H4 L
                                                            . p. |. n3 O. h
                    if ( _LdrpInLdrInit )$ R9 @7 [: `  W* R  R, Y5 V' g
                        _LdrpFatalHardErrorCount++;. I  u# I/ l' h; S1 Q4 h
. i: y4 ~- b6 W$ |. U& I
                    return STATUS_DLL_INIT_FAILED;
7 z  l9 ~/ I. H$ Y& N& o2 z% S& ]% ^                }
! p8 j! [; U  J1 W2 l            }
7 ^; _0 @" n1 D/ j( S) {        }
9 S6 D* n" M/ u$ G* {6 N
. @: j* z) u& B1 R4 O& I) p        //
; K4 y& `% A1 O4 T8 o. H1 h6 k        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时' Z0 r$ \, G9 |( `) a' m6 g
        //      
. T% {9 Q. P% K% m. h' {        if ( _LdrpImageHasTls && bImplicitLoad )( `& X! e6 c, R) T& |: p
        {3 |) z5 W( m6 m' g
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
7 u5 M3 B7 c) o& O7 J. `                                        DLL_PROCESS_ATTACH );& G8 @: a- u- z5 T' o2 K, @) x
        }8 o' n& z* Q& Z' C, _0 h
    }2 i% Q+ m3 |7 J5 Z" p+ Z$ B# I
    __finally
8 O8 O  l9 c: i& ~* a% A' h6 K    {5 N) m; x/ a1 @
        //2 w- B- ?/ q$ u% Y6 q2 a
        // 第四部分;
/ v; r# u0 p* U* ~9 p        // 清除分配的内存
. H! V7 H" ?" [5 q6 z        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );$ W# r9 @5 }7 W/ k% N2 H
    }
" \, ~' N; }6 B  K5 S
: \$ l+ e5 N- K( E0 c( L( Z: ^  Q  p    return STATUS_SUCCESS;
5 H  l9 I: t# R+ r# {! w# V}   , K3 e  G2 n2 Q7 b# G4 Q. i

& h% x6 b1 d! _- l5 @这个函数分为四个主要部分:  T* T0 ]% [/ `+ s( l, Q$ J( D
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。0 O) W6 T- r* a7 {. i4 {
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。# I) H# X+ ]0 j7 s
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
8 T" ~3 M% Y% x8 k7 k( k( s, v另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
8 J: t7 `2 p. r. }5 I在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
2 C" W( y6 v+ l  ~0 n! I5 o四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-5-22 02:25 , Processed in 0.084423 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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