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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]  q8 i( C$ {& Y

: K6 C5 u* D8 a: P5 x: UWINDOWS 2K Dll 加载过程
# v( T: Y) U' C/ i; ?4 `jefong by 2005/03/307 Z5 y) Z. d/ H! }
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。5 W4 j- k  f2 T
在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”。9 I) F& g1 B$ x! L$ s9 @
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。' [9 {+ [' b( k: n
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。; H0 c3 X3 s: j9 D* R
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。1 H* Z. L* K8 }
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
6 H' W3 ^! O+ m  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):2 C* U) Y0 a8 _0 y
//=============================================================================
  E, }, C" Q% a* T& o% M- j+ d// Matt Pietrek, September 1999 Microsoft Systems Journal
* E6 {9 ?& ?. ^" \// 中文注释部分为jefong翻译1 }/ e! w5 ?0 b3 W: o! I
//
& f0 n8 v! O- i7 P// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)! x0 ~( ?9 W2 P3 A2 z& |0 G
//6 v9 O1 o# I% U: R" w2 K
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
- o; v* n$ U" T+ v5 a! Z$ p, b3 |  [//=============================================================================
9 |: K7 \! d0 l) n# X1 S
9 K/ V$ c/ b3 J) R. `. ~( L2 m#include <ntexapi.h>    // For HardError defines near the end* Z: k9 Y# E1 P- U% s$ ^

. ~" n) j: L2 K! }2 V3 J// Global symbols (name is accurate, and comes from NTDLL.DBG)  m) a3 j' K7 F4 P( i4 J0 v
//  _NtdllBaseTag% l' C( G5 u) _  Y! V9 Q+ C" K
//  _ShowSnaps
: v0 _$ [/ q9 g6 |+ h//  _SaveSp/ T+ K$ R$ h' x, |8 @+ @9 I
//  _CurSp
  \& V9 Y0 u* n9 W- k$ @//  _LdrpInLdrInit
4 g6 O0 |- P+ ?4 h% ]7 W0 ^+ f//  _LdrpFatalHardErrorCount
: b9 G8 B6 u" ~" |- t//  _LdrpImageHasTls2 [' }  u$ N* h) _( l- q9 \
/ i+ w: j, {* F. t8 ?; a
NTSTATUS- a7 \+ v0 Q; x9 ?
LdrpRunInitializeRoutines( DWORD bImplicitLoad )* U% _- J0 Z, f6 A
{3 `+ n1 ^. [4 v( a8 {2 C! {7 l
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了# R7 t- ~2 V+ t7 ~- C8 [2 \( J
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
! U+ M: ]( n9 |6 m, t2 p  k% I1 }& h6 c- s0 }/ x% i$ Z; f
    if ( nRoutinesToRun )1 J- T6 k6 f. h# V
    {
0 @$ y" O8 y0 [: g/ A        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。9 o( ]% q  o! m; X
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
, l9 P* U! Z5 N$ E1 W$ U- r                                            _NtdllBaseTag + 0x60000,
  Y8 ?& w# m3 l                                            nRoutinesToRun * 4 );
8 h6 K$ i. ]6 V1 G; k; `                            : n" n& C2 i7 o: l
        if ( 0 == pInitNodeArray )    // Make sure allocation worked9 \/ z7 m/ F+ {4 e+ v! o7 g
            return STATUS_NO_MEMORY;! g6 C' u# l; w* U
    }- _) W+ z4 n2 x0 k
    else. h: C8 P9 Q8 b. I! [- X
        pInitNodeArray = 0;
, T1 J, @5 @& B: j% [9 f
3 |* V; c# u; e    //第二部分;5 q8 P# Y; g" P, J6 n
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。+ b  r' q% ?% K8 B7 e+ |% n% A0 o
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
5 \0 H) c7 s& w) |4 x0 e4 N    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;) q0 j8 P  P, E% B# M! K
        
6 k+ D5 p. A- o! _: ^1 n    if ( _ShowSnaps )/ [5 x  T2 T9 L: f$ I/ u
    {! @; m9 O: j. v$ S6 H! A/ {1 P
        _DbgPrint( "LDR: Real INIT LIST\n" );
5 b) }  g' \/ R    }+ h. F4 i) \1 O* m% v7 g
$ ~& H- `# |" [
    nModulesInitedSoFar = 0;
; O. A+ c! _3 t* p; v7 p
0 Y2 A) Q2 B  N0 k* K5 m0 }    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
, N* k0 q6 ~4 \+ T6 \    {0 c* v; U* F& W5 _
        
5 {$ X) \( _, h) @4 G8 q! t8 g        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块- H1 R; X2 }/ U* _
        {
1 l7 q0 A+ V+ k/ q8 |! W  D$ M8 |0 ^1 [            ModuleLoaderInfo  pModuleLoaderInfo;$ ~+ P) x/ }; Q9 h7 R
            - e: p5 P4 W5 i* x& ]/ H) `
            //+ o( j. y8 D: L/ Y6 E- }2 J+ G; ^4 E
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
* @5 M/ m0 Z+ A! u$ m            pModuleLoaderInfo = &NextNode - 0x10;% a( `5 i# v/ n& P0 _
            - n1 I) j7 [; z+ d7 U3 P) \, o
            localVar3C = pModuleLoaderInfo;         8 c2 {4 w8 r6 q
. n4 p3 i( B' N5 r" G
            /// B5 ~1 e/ t0 X7 }) X9 V
            // 如果模块已经被初始化,就忽略1 H1 Y" J# @% [# _! j" l2 @) D' r
            // X_LOADER_SAW_MODULE = 0x40 已被初始化2 p1 v% }# d8 A* e& b3 `7 c0 m5 B% _% M
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )3 g5 E- J9 Y! r! R
            {
; o* M( ?. m4 p" b- S  X* c                //" V  I' m) L- h
                // 模块没有被初始化,判断是否具有入口函数
7 q  V; Z! c6 ]# O9 x- L                //
$ G% i- e! J( }* r3 H                if ( pModuleLoaderInfo->EntryPoint )3 o. D: f- B2 {" o: g* v' q
                {
. l+ t  G  _& K* z/ K                    //" H+ g) w& x: s* o1 b
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
  z3 M3 c) V" w3 p5 L2 Y* G! ]                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
' @7 ?" N% z; O6 u) ~$ x3 ^
7 E3 A& j7 j$ k% k3 p! N! M                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
/ F" }6 j4 ]$ x' K- [0 ]6 v      // 例如:
5 t! s5 b+ G" h7 B                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
* U) |$ x; d0 m5 s8 v: r* n                    if ( _ShowSnaps )
; K$ N/ E; a# ]( Z5 I! I- p                    {
; b9 p3 }0 z9 ^& p                        _DbgPrint(  "%wZ init routine %x\n",
5 D. J! V& ~* u' o; s                                    &pModuleLoaderInfo->24,  u7 }: P0 z. y" V- s( ?6 R' r' D
                                    pModuleLoaderInfo->EntryPoint );
1 X5 ]$ o* A3 U6 D1 _& l- R; x                    }
1 e2 Y) }" S& {7 P4 [; f
3 h  o; g. V  W3 L                    nModulesInitedSoFar++;
) h+ N; g& V( \, z% I& ?                }
# O2 U3 Y) b  v3 e% N            }
8 ~. I. O, d2 h$ q4 B
# E1 l4 {4 P8 t1 A            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。* e6 k4 ]$ }* {3 ?7 U6 n* N
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;' ?' U# D- j: D; E2 s# d
, b7 Z, v$ ^' P: r
            // 处理下一个模块节点
4 V1 c% l; n9 B; O            pCurrNode = pCurrNode->pNext
" T; X1 {' I2 s$ G        }& h6 f% m) b) n& {' B* n
    }+ m# V  {* L: I( o3 H* ~
    else
, |6 m6 g& t9 D7 F( }- f4 H4 k    {
! {3 V+ l" k7 m) H7 |9 b+ M        pModuleLoaderInfo = localVar3C;     // May not be initialized???
/ U  y! i' D# @1 s$ J# k* e    }' C8 W7 `8 y# }+ T6 @
   
. w  P1 G' R, b! X, z2 ^    if ( 0 == pInitNodeArray )
# J  A6 ^. W; j: s# T        return STATUS_SUCCESS;
: m! t* z" w/ Z, ~5 ^/ K$ Y3 I) `7 ~5 B/ o) G
    // ************************* MSJ Layout! *****************
2 r0 e9 Z, N! C. g% v2 k$ h0 m    // If you're going to split this code across pages, this is a great
$ k$ T2 Y" ?/ k$ _+ p7 P, }$ p( @    // spot to split the code.  Just be sure to remove this comment
4 [0 i6 n! L, Y  E9 R8 I9 D    // ************************* MSJ Layout! *****************, s: e9 t, I- G$ n! P2 Y
   
* y# A/ A7 G. k# d" c4 E    //
' X& _; s( P) M. M    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH+ m( X. s( w' l8 R5 |/ f
    // 第三部分,调用初始化部分
* _! z  o& X' N; p4 H6 q0 G  e% |$ A    try     // Wrap all this in a try block, in case the init routine faults
, ~2 h) g% M9 u% @+ B5 Z) o    {% K0 ]- r; w3 h* t7 o: @$ c8 r
        nModulesInitedSoFar = 0;  // Start at array element 08 O, J( `# q# s9 h; u$ r
2 o' M0 \/ y0 ?" n2 G9 @
        //( y' ^- l; M* T% f" V7 @
        // 遍历模块队列
% K; P/ V8 J( r- L& ]6 m' m8 c+ K9 f        //
/ m; b" j" w/ P  v9 Y( a' o. F        while ( nModulesInitedSoFar < nRoutinesToRun )) O, r! ?3 u/ K
        {8 \  G7 o$ ]6 \# L
            // 获得模块指针
' A0 k$ y4 M6 X$ V$ D. ^( V            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];6 T% X0 x+ x8 J7 G& |8 p( d' i
' u4 z1 N0 ^% O0 E
            // This doesn't seem to do anything...
0 P# j$ W, D  H% M/ t- r0 j- K            localVar3C = pModuleLoaderInfo;- C% N/ S1 l% u
            * N; V: h. u+ R' y% {1 I
            nModulesInitedSoFar++;
8 X* E: O3 ~7 F; h6 r7 o' z3 v                  e* V# n! M% p. N
            // 保存初始化程序入口指针3 l; c8 W/ \) K" _+ \; J
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;/ _, r# g6 i3 |( E' }5 X
            
5 ~3 B/ G6 e% ~            fBreakOnDllLoad = 0;    // Default is to not break on load
! H. i0 Q7 r) \* y/ k
0 q" c5 U1 {% ~5 K$ `8 O" s            // 调试用1 N: D+ I1 y+ X: V# Y5 d" ]0 t
            // If this process is a debuggee, check to see if the loader
' W" O3 |  i, {            // should break into a debugger before calling the initialization.
; y  D& a1 Z) ^8 f: k3 O$ b            //
" K: _. p! f$ V  H            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()0 m2 o; j0 W/ f0 W6 S6 E- J
            // returns. IsDebuggerPresent is an NT only API.( [9 T; h9 E: R, R; @
            //* F( `; l, c7 Y2 w* h- Y
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )1 I! b. e/ V) z( g; g; ]
            {1 r! [5 `4 T! P3 M0 a
                LONG retCode;; v8 U% X8 ]" q- i

+ u3 |; P$ t4 ]                //              . t) f3 ?0 i& O- D+ c
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
. z2 M* E' D; u. d4 Q                // Windows NT\CurrentVersion\Image File Execution Options"! Q: b% S, n8 H+ G6 I
                // registry key.  If a a subkey entry with the name of
8 w3 |$ O! u0 ^1 ~                // the executable exists, check for the BreakOnDllLoad value.
! o9 h6 n3 q, K! s6 ^0 Q% W                //
$ {+ K1 N% y! b; q                retCode = * d" j& T, ~' I% C# ?, u
                    _LdrQueryImageFileExecutionOptions(
  _% \" c" C% M: o8 b7 b$ e                                pModuleLoaderInfo->pwszDllName,
* I( V4 p1 b7 w                                "BreakOnDllLoad",pInitNodeArray3 M- H  X% Q, a$ I8 [  |$ P
                                REG_DWORD,0 Y: V* k8 M1 u+ Y" I( U
                                &fBreakOnDllLoad,
7 j6 ]1 P. C6 t5 ~- T. b& t3 U                                sizeof(DWORD),1 X) _, j  f% E6 T
                                0 );
6 L5 Y) S/ ]6 b, |2 D( B
5 d  N2 a9 n# v( t& s                // If reg value not found (usually the case), then don't
+ e. l3 Z. e) h) q$ }9 P                // break on this DLL init
" W- n+ A& w( y/ p7 K2 z) A                if ( retCode <= STATUS_SUCCESS )/ _( }' M  `/ a( V
                    fBreakOnDllLoad = 0;pInitNodeArray
; o6 V7 O4 H! C! h' p( c            }
9 Y' U% @& h  l* ]            
! t9 E: V; Z5 k. B* I  Y7 e% D7 y            if ( fBreakOnDllLoad )/ H+ t8 g& b9 p9 O. Y) v
            {             O/ m0 _+ }/ v8 y" ?
                if ( _ShowSnaps )
* M. s/ n+ r6 [: W* n+ j                {2 t% s' Z$ D8 _; [% Q7 }
                    // Inform the debug output stream of the module name* C8 c: `' O% `; b; u) N
                    // and the init routine address before actually breaking
' E; ~% P( y  r: R* O                    // into the debugger3 x" F/ h: O+ K

& l$ `9 ?4 Z3 _6 \                    _DbgPrint(  "LDR: %wZ loaded.",9 z2 ?% o( z5 e& b+ I" G! a
                                &pModuleLoaderInfo->pModuleLoaderInfo );
2 F" ^/ h9 M1 v$ Q                    ' ~0 T* ^% q* A9 w% C  H3 @
                    _DbgPrint(  "- About to call init routine at %lx\n",
1 j1 Q# t2 l7 g4 h                                pfnInitRoutine )
& S3 c) |9 _' C, c                }& o6 F6 V6 {' V- }; j
               
( y* x$ S1 h% f1 [: Y2 |                // Break into the debugger                              
0 g2 [" O8 L7 G                _DbgBreakPoint();   // An INT 3, followed by a RET
1 R9 P/ R/ Z, i& n- a            }
, l9 _  N" y7 H$ T3 x            else if ( _ShowSnaps && pfnInitRoutine )
7 d5 }# M4 w9 g! l3 L4 x, O. W' B            {
9 ]1 y- L+ }5 Z  ~% a- x                // Inform the debug output stream of the module name
( [. o! c9 i5 X$ g                // and the init routine address before calling it               
* N. H  t6 I3 v1 [; d1 K1 s; J' ^7 S                _DbgPrint(  "LDR: %wZ loaded.",
% g. S2 I* c8 k* Z* H                            pModuleLoaderInfo->pModuleLoaderInfo );
' o( L* x: y" Y" G: Y: a# b5 \/ g; R2 X; W7 I
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);. r9 p5 t: V' g) ^2 }6 B
            }9 i3 b9 T# l7 M" O1 }4 N; U' ?
                    8 ~4 u# I6 _0 X
            if ( pfnInitRoutine )+ w! S9 `5 {2 Y5 g  m# }5 U# R6 A" g
            {
3 x& t. Y9 Z+ I1 o                // 设置DLL_PROCESS_ATTACH标志
) H7 S0 l9 C( U1 w                //( ^: B0 v9 p+ Q, C6 T; q/ q
                // (Shouldn't this come *after* the actual call?)* S, J9 k2 I3 I6 ?
                //
) H( X: @- D8 t# P( L: N                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             % k! D0 Z  ~/ |% ^' J, K: n) u7 {1 e
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;- J# X! p# ?7 U5 T7 P
7 t  L% b3 R! b. r
                //
9 u; C) G( n/ G2 X+ ]- y2 P2 q2 e                // If there's Thread Local Storage (TLS) for this module,
( i/ a$ |+ r) {* H0 r                // call the TLS init functions.  *** NOTE *** This only
& q) k2 z$ c3 ]2 |                // occurs during the first time this code is called (when
3 e  E: ], y' V+ J$ F% m8 ]                // implicitly loaded DLLs are initialized).  Dynamically
# U8 a* l! v- w: u! f. T+ E                // loaded DLLs shouldn't use TLS declared vars, as per the, [. }8 E$ u' h6 ^' j; Q6 r
                // SDK documentation4 `* |* |9 P( n# G$ z. t
                // 如果模块需要分配TLS,调用TLS初始化函数7 ^, b$ e- ^) u- E" S
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时( e6 q! u- y/ X; e- F; c
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
) `- |/ A" L3 w1 K+ F                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )7 E) g* }4 y1 E; b4 h
                {
3 @* d# H- F1 X% e, G9 F                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,5 H: T( P- }7 k% [
                                                DLL_PROCESS_ATTACH );: D% P8 M$ q8 x1 O$ z0 L/ J
                }
. Y2 \$ l" h) \9 {4 U- J                2 Q1 |2 Q8 D3 e  r1 o2 d

. u5 m2 n9 B- ?1 Z; f: {                hModDLL = pModuleLoaderInfo->hModDLL
1 u6 ^1 T" N1 `0 x1 D: I0 v! I- T# C! ~! q$ A
                MOV     ESI,ESP // Save off the ESP register into ESI
2 }' X- k9 r  P) G& Z  : b0 V+ o( t" U: @  K8 C: U
  // 设置入口函数指针               
5 u4 k  Y( M+ ~) Z+ ^                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
) N# j6 V$ c  e$ V, X6 N& y0 K5 c' Z; Z& q
                // In C++ code, the following ASM would look like:8 q$ R; s/ Q# e$ o  z
                //0 [" i1 ^' x. h. n4 w
                // initRetValue =. r/ W1 X( R% a0 r" k& F' y
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
. [6 e& c. f, y3 D' E; i                //8 s7 [5 V+ R4 y: n0 u. c4 K) I
! B( u9 I' A: l% Z+ b6 I
                PUSH    DWORD PTR [bImplicitLoad]
' f7 ]$ B/ F3 S% N( F9 {3 s/ E               
1 X% i! o" e, Q. \, j' k$ g                PUSH    DLL_PROCESS_ATTACH& p; v$ m' r" k8 u2 u. C  h2 U2 F
                  d9 v6 h8 }% T; x4 x# t: r4 i2 H
                PUSH    DWORD PTR [hModDLL]! O' i# |5 q7 s) A: l' N! ~
                + e& a. Q' {& u. j. t0 c+ L% B, R+ p
                CALL    EDI     // 调用入口函数# H% k$ k3 L$ Z1 T. X
                / d9 |/ ~6 ~$ z7 i/ W
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
4 I" u( }4 h/ y6 p
2 P; b+ |7 B5 A* y                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the3 ~8 v) |8 q2 A& d9 Y; Y9 z
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns6 a! j" Q4 k& o

7 L  e3 v" B4 t5 Z- d. d                MOV     ESP,ESI     // Restore ESP to value before the call" y! W; h$ w6 a8 o; C. S

! S- g: F' t0 I1 t                //
" Z! j7 X* q$ R; W7 g                // 检查调用前后的ESP值是否一至
! X; l* w6 Y7 w" j# B  //
* s: ]. Z3 \* m& b: w1 b$ b) P                if ( _CurSP != _SavSP )
' E( j* V4 n2 L4 ?+ @                {
" C$ ?$ `1 Z4 M' y, i                    hardErrorParam = pModuleLoaderInfo->FullDllPath;6 x2 a( C* w0 S: p$ U5 H, j
! s- U/ q' h, E1 o6 ^, ]
                    hardErrorRetCode =
6 L3 k/ T9 ]6 m6 d                        _NtRaiseHardError(& Z  q7 J4 c/ Q6 W3 X( ?  e
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,4 v( L, ^  [! i" I
                            1,  // Number of parameters
2 @: T3 E% g5 j) ~5 s9 `                            1,  // UnicodeStringParametersMask,
. D- a4 V1 J7 x' N$ [                            &hardErrorParam,
& Q% e4 K! x3 _4 A8 I/ s                            OptionYesNo,    // Let user decide
5 g( D, g7 O" w  g. ~                            &hardErrorResponse );0 s* `. X. I  F1 |7 Y. u. g
                                            
9 m! H. t2 S  r5 ?8 X+ g! g% ]8 z5 u: ~                    if ( _LdrpInLdrInit )9 D3 L/ g; `4 k! |* p
                        _LdrpFatalHardErrorCount++;! ^3 d) ~) C" c. \$ q  X; ~. {7 R

- `  y9 s3 `! ^/ D0 A; N, o                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
0 F4 B: D# t4 B" g                        &&  (ResponseYes == hardErrorResponse) )) Z2 s+ n; l4 G8 E5 y& U
                    {6 O( j6 ]0 q* |: K/ y6 |0 `
                        return STATUS_DLL_INIT_FAILED;4 X" D" N  F' K! N7 g0 r
                    }
- z8 z1 U" _" U5 u' h5 r' n                }
1 n# T1 N8 u- a# J3 y" P8 p2 d4 n; v( i$ ?/ l6 h
                //
. J( C' d/ C  ?5 v                // 入口函数返回0,错误8 V8 a9 F  z& w5 V8 b+ r
                //
* b$ c3 v" H9 o0 c% r                if ( 0 == initRetValue )" [! \! J! {8 U% h- d2 P
                {
* m7 A' R7 F7 c, s                    DWORD hardErrorParam2;8 v) B/ ^) n$ l% u+ q0 m4 v
                    DWORD hardErrorResponse2;  h7 b+ b0 l; H; U( F9 s2 i
                                        . U9 A# D8 P  e" T  i  W2 f
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;+ i; }+ S8 C" j' ~
                    5 m9 |' O( A4 Y7 F& `3 s9 F" p
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,/ C/ L' S8 X+ c  X# ]5 z* r
                                        1,  // Number of parameters
( b4 g+ }$ g9 l) a                                        1,  // UnicodeStringParametersMask, K% `. f$ [. Z$ }% O3 q' }% }
                                        &hardErrorParam2,( Q8 ]. b. W- `
                                        OptionOk,   // OK is only response
  h, B& B) j: d* e  G! y% ~                                        &hardErrorResponse2 );9 b5 e+ F- r+ M) K
                                                            
! y" d( ]9 k# y$ B" }7 K- D, @                    if ( _LdrpInLdrInit )
) u& }( u6 l5 c5 m! m6 S                        _LdrpFatalHardErrorCount++;( B2 h  b0 h: E) W) v6 r4 P
) O6 l1 }+ A4 l8 e) t3 u7 H7 e: R
                    return STATUS_DLL_INIT_FAILED;5 O" N) a& V# Y' L
                }! ~, D( y2 u" y5 Z
            }: Y$ \4 n( N% h9 K- f9 M3 w
        }8 S- o3 ^. }# Y: s
$ @- }, N4 K& e- j' H/ Z
        //
& R# Q% L% x  j! l) a! i        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时& ~! t6 {6 X* `, a
        //      " ]) K$ j9 _% C. X* B4 f; M
        if ( _LdrpImageHasTls && bImplicitLoad ); s8 b. D1 l5 R3 z
        {
. B, l' T0 l' g  ?            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,7 H1 e' Q, F0 _" F, [: y7 A5 r. R
                                        DLL_PROCESS_ATTACH );" R0 n% v8 d0 V9 u9 \
        }5 I( H; C' R* {8 _# I" _
    }  K$ r9 B' Y! f" D* ]# g' y. ^4 X7 {
    __finally
6 l# |& |9 M# q+ n% d* y, Y- B! f    {" O8 j, n  Q/ z" i! O. @% _
        //
( r" m# C1 n$ _        // 第四部分;
% K2 \, p- [& t2 d# ~        // 清除分配的内存1 U* r- m0 @) E. U0 L# t& p
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
; d8 n8 `1 J0 e; `    }
2 X$ M3 X' |0 x3 ?# I3 |% Z' O0 B* u! w9 N$ D
    return STATUS_SUCCESS;
7 e% e! g; h: d2 b}   
2 M- C% M- x+ s" @' w1 {7 H, e2 f& ^8 w: |0 B
这个函数分为四个主要部分:( I& K  ^! u" r5 t' h  }  `: s
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。. n. K5 i: T2 O, U
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。% e* m0 m; l* Z
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
0 U) @9 q0 V' u2 b0 h7 D' T1 E另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
+ t% I  c/ r$ V; a: H在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。; m6 [, {" Y: U  U" \( V
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-1-12 00:34 , Processed in 0.254556 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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