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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
0 R7 I  E3 b4 D! h* o1 c9 r7 F6 ]9 _+ f5 U
WINDOWS 2K Dll 加载过程
* G" K" d! h1 y7 ~1 j' \( v% sjefong by 2005/03/30, r: D# Q- `9 d+ G
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
1 z. s' \, l$ b) C; c在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”。/ J) V5 b3 s: i, f/ m/ x% L
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。$ t0 x& U$ e* r3 ?
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
$ W' G( m- o0 ~4 m; w' ]  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。* O2 z* U2 a3 m+ \( c
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。2 A9 q2 [7 ]- U/ L+ {3 z
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):! E/ r' ^0 W+ X# L5 a# J
//=============================================================================* c! d8 u- C  J. }6 Z8 J6 @
// Matt Pietrek, September 1999 Microsoft Systems Journal
: m% K5 N" H, ^' V+ X8 @// 中文注释部分为jefong翻译
) @' s$ t  ~0 o% }7 ?7 N( G//
  M+ ~7 r+ f2 b3 C// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3). _7 @* k0 K1 M/ F: A0 P2 U
//  \; R, R- a$ W' ~: Y" z2 _
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
6 J. F* A, L# O+ i6 H, m//=============================================================================
( d2 ]+ c8 N6 i: g$ a9 A
; Q' D* B6 k: P% r#include <ntexapi.h>    // For HardError defines near the end
3 p0 W1 m+ ?' m4 t4 ]" w5 {
+ h' Q: W& G% y( V. ^: J) f// Global symbols (name is accurate, and comes from NTDLL.DBG)
3 a. }# W0 w# w5 L- y//  _NtdllBaseTag% v) B7 \5 v$ e% Z% x
//  _ShowSnaps
- a& {+ L$ j' Z( m//  _SaveSp
; T+ ]. q, D9 u. O( t! a4 m//  _CurSp
$ b, y0 M. ]' H5 i, V- P$ F0 W; j; b//  _LdrpInLdrInit
; V: P) |4 Q& J9 Q2 e/ j$ j//  _LdrpFatalHardErrorCount+ S# P, {# ?8 u6 L4 o
//  _LdrpImageHasTls
: I' \6 @! t" U
! F$ {4 Y$ F2 L/ u" x! ^1 iNTSTATUS
% w" G9 o. i1 K) u8 _LdrpRunInitializeRoutines( DWORD bImplicitLoad )
4 {' u- t/ [0 t: K{0 q1 @* j/ ^# f+ s
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了& A# o: Q9 b2 a5 @" L) K2 [
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
: y4 g9 J8 q7 h0 x" y0 u/ G- `) Q. ?/ ^& z+ \5 W# p
    if ( nRoutinesToRun )0 S' p+ ~# l$ \* w. {$ F+ `
    {( \; g. W' s5 @
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。/ a1 y( G) V8 h; W+ I" l9 {
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
6 V' Y8 e: F/ Y6 l0 V- q9 a; @; D                                            _NtdllBaseTag + 0x60000,4 L' m7 t& C0 u# Z( B
                                            nRoutinesToRun * 4 );
0 v4 U8 t, M. Y* M: `                            & a' e% R! ], X/ N4 e: k2 P; R
        if ( 0 == pInitNodeArray )    // Make sure allocation worked+ W" D. N0 E2 |% H; R
            return STATUS_NO_MEMORY;/ `  z0 l0 M( v$ f4 Q! D3 Y+ |
    }% T, i& x" J& b+ W* K% b
    else: c# \  j" ]) ~, w+ T  H3 p* g
        pInitNodeArray = 0;
$ f4 s4 f1 o' S2 F6 B& z- p* S6 o4 k( d8 E& n+ r+ k; n8 P5 v
    //第二部分;
1 P: P& P- b/ y6 u, O    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
  E/ u0 U+ B: D* x0 E# V    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
2 I5 e$ ~# _: m$ [    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;: x  i7 e, \: a: _' i( B
        % i) L( S% H- v. `
    if ( _ShowSnaps )
& \, q# k1 E9 b" y) \; F' a* G" W+ y    {
. H; U$ @7 a. ~( h: I        _DbgPrint( "LDR: Real INIT LIST\n" );6 c1 g$ D! {$ e( ^. E
    }
5 B0 {* J7 |% g  W3 O/ D7 y
0 @) R2 J5 E# g# ^& _2 p) H    nModulesInitedSoFar = 0;
( a7 e+ _& O+ m! D! P
6 N; Z4 P: K. z- ?  d2 x" l    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块8 a6 R+ G2 ~: }8 ]! Y$ \! g
    {, Z- T7 d5 ^! d1 |4 d
        5 |& I( S: W- t4 C8 P
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块1 |8 K# c- B3 C( m7 i- d
        {
& L* ^, \8 V: @, S" o6 B7 Z7 b            ModuleLoaderInfo  pModuleLoaderInfo;
* X. [9 s& o2 @. A( o            - r! u6 @. I5 Y7 z" D0 X3 Z
            //
' t! ]6 @/ Y( _: f) M! X            //一个ModuleLoaderInfo结构节点的大小为0X10字节
0 Z- l- M+ G, O: D% ?5 ~- n# W            pModuleLoaderInfo = &NextNode - 0x10;- `8 t- z' ~- Z1 G4 \
            
8 M+ Z2 i4 q9 t# h7 c  A$ l            localVar3C = pModuleLoaderInfo;         
& Y' t% Y' i* f  _* G* `+ P2 f& m0 E4 S& q7 s5 A# G
            //; r$ c& B, z3 f; U' J1 A- ?
            // 如果模块已经被初始化,就忽略
: P4 a) @# J5 r2 N9 @% s$ W* w& h            // X_LOADER_SAW_MODULE = 0x40 已被初始化+ z- z4 I& J+ Z9 X3 E: p
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
0 F  N% ]2 M! B4 ~& Z            {
5 E' M* b& F: V9 e                //. h( E, ^  Q/ S$ z1 ?% |
                // 模块没有被初始化,判断是否具有入口函数
3 W+ g4 t5 e" F$ o) L" a                //
7 N! @& k1 d/ d8 J                if ( pModuleLoaderInfo->EntryPoint )2 z+ |7 A; t6 ~7 B( x  p( k5 t
                {5 I8 w! {7 j1 I: ?6 a
                    //
9 b+ P$ [: c3 b% C3 n  m' x                    // 具有初始化函数,添加到模块列表中,等待进行初始化: c/ q0 G# H6 a' p* g
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;2 b: u9 M, t9 e9 h4 n5 }
# J+ }; R, i2 z% F
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址; o. \( \  p# N1 \0 a! {
      // 例如:
; e0 Y, u0 F( q+ C! p5 Y1 D                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
7 J! o  h7 K1 E! I$ N! {; F- G/ ]                    if ( _ShowSnaps )
/ ~6 D9 ^9 t  n                    {+ m: T! D: z5 p3 T, g7 L
                        _DbgPrint(  "%wZ init routine %x\n",
$ D7 W9 L1 i& P; o# s                                    &pModuleLoaderInfo->24,+ k9 |$ A6 B3 v$ @4 I! N7 n+ h; \- {
                                    pModuleLoaderInfo->EntryPoint );
6 P+ [, U) h% L6 B3 [/ j0 F0 W, B                    }
6 S6 }! d. Y% m# u+ m) y9 D/ d' O$ g+ F, u
                    nModulesInitedSoFar++;7 X+ n' \$ ~6 k2 j0 s3 ?  K8 ?5 M
                }
( F5 [4 i  A3 [4 F4 Z: X. B3 N            }- N8 E  h+ r, ?* b5 n9 q" s9 V
0 t; u/ f, o0 K, l" G& ^
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。$ N- G3 L! b8 R& j
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
6 Z+ D: `! F" ^, s/ }  r* m9 d7 y* w4 n* E) l( h3 M
            // 处理下一个模块节点
" H- z1 c5 F3 M% m* x  W            pCurrNode = pCurrNode->pNext) O8 g/ \0 c  B+ C, D6 P- a; q
        }
2 w  ]  x. P+ \% Q" ?/ Y2 n    }, F1 Y. B! b0 i* B
    else6 e) h  F# ]4 v" X' U7 F! `- h0 d
    {
: \& n6 I, _& u* E        pModuleLoaderInfo = localVar3C;     // May not be initialized???
1 @0 F0 v) f; i! c& G    }
) A' r4 X- u3 A9 u- x8 ]   
) Q' _  W$ Z3 y" z( M7 Q4 j  W    if ( 0 == pInitNodeArray )
3 j4 w9 Z! [2 G/ E* U        return STATUS_SUCCESS;
7 Z& a/ p+ C; R! k* v# |, E  u
% Q* @  N, O3 [* f' B$ ?    // ************************* MSJ Layout! *****************5 h# L) r$ d" a$ e1 b
    // If you're going to split this code across pages, this is a great, ^; r& @9 o- r7 Z0 u
    // spot to split the code.  Just be sure to remove this comment
" Y# ]' \6 w6 B% Y! P    // ************************* MSJ Layout! *****************
+ y, b/ |& }5 g" u8 x- \- T% w; j! |' I9 u   
6 R% u2 T: l) O" C8 ?, P    //
2 W+ m6 P7 ?! ~: z    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
3 v  b3 i; k& R- O  Z/ r1 d0 B. z    // 第三部分,调用初始化部分2 `' z( _  u' |, H$ z
    try     // Wrap all this in a try block, in case the init routine faults
4 e/ O0 }" p0 p& @    {1 \/ n9 j9 v9 v, `! p
        nModulesInitedSoFar = 0;  // Start at array element 0
- m. M9 s5 }+ E. \3 _6 z
! C. o2 F* ]: R% J0 z        //
7 c$ C( {9 F0 O0 f2 m3 w        // 遍历模块队列* F. }: q$ e# u" F; Z. |
        //" d# q* e4 M' E1 M3 b
        while ( nModulesInitedSoFar < nRoutinesToRun )
8 ^; `; w8 I' f  K8 o        {
! x0 S6 T. i, b            // 获得模块指针
2 V/ s2 H4 Q5 _- w* n% a  g            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];. o+ D( g! \) _
8 d' O5 [# I& R, }- E  i  ~5 }5 v
            // This doesn't seem to do anything...) f6 ^. A" I% X5 B! t. Z% X2 C& h+ x
            localVar3C = pModuleLoaderInfo;
; B1 p+ k; Y9 z  }8 M- r; y0 x% l            * i7 u6 x: j  m4 e$ N  D/ j
            nModulesInitedSoFar++;; d8 A* c' ~# N) y! g" V
                8 {! G# y, Q* `4 ?3 u0 s9 D% ?$ A
            // 保存初始化程序入口指针
/ }+ B4 z, P. d  J% C3 e3 c$ x            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
' o% f9 L1 _- G/ f            
# R5 R( Y# v3 [# R$ |  c; ^            fBreakOnDllLoad = 0;    // Default is to not break on load
0 K8 |/ V" O- W; i0 s6 Y3 }8 T$ [8 D" ~, I$ F. A/ c
            // 调试用
" {( {. d: n8 m            // If this process is a debuggee, check to see if the loader3 r! X( U! J# E9 D5 a. W2 Q1 D
            // should break into a debugger before calling the initialization.
6 [+ h; b) e) y0 G. ~5 e* O            //
& h% S/ X' U+ w            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()& l/ I& O, M, m. O, d
            // returns. IsDebuggerPresent is an NT only API.
. P! I4 z. o+ D8 r" U; N            //
2 F% W2 N1 J0 |+ @+ x; Y            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
6 w4 O- P, a6 j) R5 [1 l            {
0 ~8 ^" K& g7 n7 `                LONG retCode;  \7 I( S  w( B' N; m: W  }

0 q5 N4 T0 u2 r6 d6 `3 d2 ^0 v                //              
. ^5 t4 K7 @3 P                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
5 S+ r( v( U6 j: t$ {                // Windows NT\CurrentVersion\Image File Execution Options"( [  t2 n7 `4 j1 s$ t
                // registry key.  If a a subkey entry with the name of, w1 ~( h( t6 e' k! f1 J% h9 G
                // the executable exists, check for the BreakOnDllLoad value.9 M9 d0 t; E8 M( `  C% ~' N
                //0 |( h. R' R! H7 }/ i3 q0 u
                retCode =
: \  \8 U9 F% Z& K  s                    _LdrQueryImageFileExecutionOptions(
) T, d$ K+ L. |0 k                                pModuleLoaderInfo->pwszDllName,
7 P3 v9 r  s) S' x2 N4 w                                "BreakOnDllLoad",pInitNodeArray
% D9 P* T- [" Y, \, {/ K- D3 ^                                REG_DWORD,
. \  ]% o- B- D9 U                                &fBreakOnDllLoad,3 `8 l8 O; n6 R+ Q% g4 g2 B8 T
                                sizeof(DWORD),4 m6 W$ J5 q! I
                                0 );
' {( V4 q$ N9 ]0 e2 u
. _  A: i5 q/ _6 \3 @/ U, D                // If reg value not found (usually the case), then don't
! o' Q, ~2 ?. _2 v, j                // break on this DLL init: d( n7 D3 a, ~! T% E
                if ( retCode <= STATUS_SUCCESS )1 \9 W& _; f! `  ^- Q
                    fBreakOnDllLoad = 0;pInitNodeArray
; i9 G# l- A* K  }4 E5 @. A            }7 F4 q& F5 i8 K2 y! W
            . {; F, u. j6 d* J
            if ( fBreakOnDllLoad )/ f% ^" D6 `+ X) Y# Q, k, r3 |
            {           # ?5 ], t& w' [% J) T/ d9 P
                if ( _ShowSnaps ), W. Q1 `+ Q) ]  c) k
                {
. Q( N# M, H) h( ?  j                    // Inform the debug output stream of the module name
4 f/ P5 o+ k* A6 j' q' z0 b                    // and the init routine address before actually breaking8 g! B' q5 S- E# R. L, v
                    // into the debugger
4 s% Y  V0 t4 i9 }% n' L4 i7 [
/ B9 H" n7 e- {) L                    _DbgPrint(  "LDR: %wZ loaded.",$ S1 B4 T) t4 ^9 I$ k/ ^; V1 m( u
                                &pModuleLoaderInfo->pModuleLoaderInfo );
% n7 p6 d* H3 m                    
: ~& c0 H% J' ~6 k                    _DbgPrint(  "- About to call init routine at %lx\n",# a5 @% j. L) s, E5 B" ^+ q2 f
                                pfnInitRoutine )  [( |( O9 {  d  s. q8 a; h
                }
9 j0 {+ Y5 X7 \: C3 P                . ~7 F3 D. W7 o
                // Break into the debugger                              
! t! |: W" M1 ~                _DbgBreakPoint();   // An INT 3, followed by a RET
9 K7 a/ z' S4 o. k  w            }8 _" ~, B) @3 i
            else if ( _ShowSnaps && pfnInitRoutine )! ~* A. ^/ I# n) C/ k: Y1 I
            {
7 f% V  d3 J9 A                // Inform the debug output stream of the module name) I7 Z7 B5 o0 x
                // and the init routine address before calling it               
4 A* Y  L4 f& W4 y! O1 C9 r                _DbgPrint(  "LDR: %wZ loaded.",2 @$ a' Y) L4 u/ d9 I3 m
                            pModuleLoaderInfo->pModuleLoaderInfo );
* G. S3 _! O6 s+ A. d/ f4 h3 r5 u- i0 r
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);# `9 N' S# d0 O, u
            }% @& v# b$ E5 e) n7 f6 s% @+ P; D
                    
  _: L' _" C( ?- q7 J+ ?            if ( pfnInitRoutine )5 x1 E% h9 W! f) R5 ?0 X
            {( H& b0 t1 A& e; y) Q+ L4 f% j
                // 设置DLL_PROCESS_ATTACH标志* K8 D3 Z+ Y4 j6 h* Q
                //  v$ X1 r6 H4 e. V+ p# r- m
                // (Shouldn't this come *after* the actual call?)6 X- Z* r5 c, |5 ?& @7 V2 [
                //
- p# H) z5 @) ?( ]  T1 K                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             : K( g% Z6 z, }( C
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;. w1 g9 H: I- c3 x. [; W

1 G2 u2 u1 ?9 X/ k4 Y; Z1 i                //
2 t7 F# |# l0 u0 p5 d# F: i                // If there's Thread Local Storage (TLS) for this module,- N- E. [1 @; U, y' I
                // call the TLS init functions.  *** NOTE *** This only
# N0 u8 W: j5 [6 C                // occurs during the first time this code is called (when! h5 K, k$ \) l) ~4 H
                // implicitly loaded DLLs are initialized).  Dynamically* H, g- C& O- T# V
                // loaded DLLs shouldn't use TLS declared vars, as per the
2 v# i% t- q) k/ k& ]9 u4 M' `( L                // SDK documentation$ X: u6 ?8 o0 r; k# R
                // 如果模块需要分配TLS,调用TLS初始化函数2 i. ?9 D* O) g7 {: I
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时+ v# R) u7 m: F; x/ \- Y
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量( q3 v% {* b& X' \# Z  ~; h; \
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )! K  m& F. G2 [2 N( {6 A
                {3 U' S% `) W+ z; s& x
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,: F9 y& p% l% m" a4 k' R
                                                DLL_PROCESS_ATTACH );  o+ c9 W0 Z4 G6 [1 C
                }, A9 H% A/ V. Z1 E. s- o5 S# \
                7 s2 F0 k6 f% e; x3 n
$ W8 L7 j: D7 B7 a3 t3 s
                hModDLL = pModuleLoaderInfo->hModDLL
/ Q" d4 R) U5 G: H7 D, g' {
# z$ d5 Q# }- h$ _. b7 a" v1 ?                MOV     ESI,ESP // Save off the ESP register into ESI
$ \+ ~) E! ]; `0 l  D  * m# }7 D3 Y0 T8 T; r% |
  // 设置入口函数指针               
4 _9 o  m; w" T                MOV     EDI,DWORD PTR [pfnInitRoutine]                     . C  ~5 d& Y+ x' _8 r

, {/ V) Z* h% p  H. B1 h& V                // In C++ code, the following ASM would look like:8 Q3 X0 d7 B5 t( k3 M+ m  ~
                //
8 A& z0 U. ^+ Z1 Z                // initRetValue =: o, Y4 h) r0 e+ M5 G% J! e
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);1 y! c! ^" S( D+ O
                //3 Y# Z. s  ~" g: x& n- z$ Q+ J
! b, e4 ~. F2 W' H# O- d
                PUSH    DWORD PTR [bImplicitLoad]
6 t5 q- X# z- `. ]; I                ! s4 U. d' J7 \/ Y; G& |
                PUSH    DLL_PROCESS_ATTACH
  U% v( R6 Q$ h  X8 E+ C8 ?9 B               
9 A) f, M/ e% m# k                PUSH    DWORD PTR [hModDLL]
  I8 p5 O0 T1 N8 n               
. T( o) ]/ a& m  T                CALL    EDI     // 调用入口函数
2 x2 R9 i+ O3 `2 V" o$ i                * Q4 j2 R1 g, o+ P! \, t# n
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值7 b0 ^' g; Z. F' c

- J& n9 I; o. a; P( _                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
9 B1 p, B& Z8 ?: \4 h: \! ]                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
8 W% w- G+ w; y7 e  j2 x; P
; {0 _4 b8 z: T! [5 E8 i7 v                MOV     ESP,ESI     // Restore ESP to value before the call
/ ~, x. a3 V) G0 ^" l# ^# E
' N1 v" e! o! r; @                //2 O! Y# n8 X' j# Z
                // 检查调用前后的ESP值是否一至
, j7 {& I# b5 Y" v+ |/ t' A) e  R  // 1 r+ C( F) f- ^% z, ]
                if ( _CurSP != _SavSP )
% B9 u" ~) |/ q' F- D                {
. o: Z! C4 Z2 ^+ r                    hardErrorParam = pModuleLoaderInfo->FullDllPath;6 K1 [! F! j/ d* b0 o

4 a) |, \: G" f9 u                    hardErrorRetCode = % t1 ^! Q+ j. e. D7 n
                        _NtRaiseHardError(' X3 W9 B/ Y& \5 ^
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,0 I+ ~8 l# n/ B
                            1,  // Number of parameters( k/ f1 |: a9 a
                            1,  // UnicodeStringParametersMask,
! u- C7 l+ c! z& `; \+ Y9 V                            &hardErrorParam,
5 o" v/ s& X& ~" q+ k/ S- x! V                            OptionYesNo,    // Let user decide. g# y0 i7 O1 q( E
                            &hardErrorResponse );
8 x7 P2 P/ W3 Q% m, g3 z                                            
$ `8 J1 ?2 P( M                    if ( _LdrpInLdrInit )
1 @3 J: L  c8 x+ e5 J: J" o1 r                        _LdrpFatalHardErrorCount++;2 }  m& N- @  R6 A5 d' E

; A9 ~# v* i& V4 S' p0 l  X/ _+ T                    if (    (hardErrorRetCode >= STATUS_SUCCESS)& r% h% s  c% @$ p
                        &&  (ResponseYes == hardErrorResponse) )
: N' \9 h, W1 Y7 E4 u) E8 A                    {
! f8 Q2 `  X$ f9 }- ~                        return STATUS_DLL_INIT_FAILED;
+ D/ G& G( e6 v% N8 n! |                    }0 q& i, y4 p3 |5 E8 C6 f
                }
! S& E0 D2 U1 S$ i7 z( m5 L+ e% B4 E: H5 R- E! k4 J* h
                //% H2 e% _, b. r3 {# i3 @
                // 入口函数返回0,错误0 K- B* D$ p# T& R2 `+ q7 l! x
                //, _; V9 m# T0 o; \- i$ f
                if ( 0 == initRetValue )
% K$ b3 Z, f* [# K2 G                {
$ M$ M1 n/ f' A* n! M7 Z/ K                    DWORD hardErrorParam2;
( d  \' X/ T+ k1 T# {4 t3 H                    DWORD hardErrorResponse2;
! h1 G2 W% n% g  C/ }( K                                        ! `; M/ z2 N" O% }7 D/ \
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
6 v! z, A& @) k/ [7 M/ c& p8 I8 d                    
( T+ J+ B( ~) I; _  u' m                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
8 f/ D5 M; S' c6 |! u9 B                                        1,  // Number of parameters. d  A/ _3 B( v0 H0 N% f( W; L
                                        1,  // UnicodeStringParametersMask  u6 ?+ G% ?! h" e+ \$ Q
                                        &hardErrorParam2,3 e; `) E% [. o7 y/ o7 W5 `
                                        OptionOk,   // OK is only response
$ S) w: Q- o0 l! R* g- @                                        &hardErrorResponse2 );' V' n, Y2 S- b* q0 j
                                                            
( s) H! P! U/ q" w( q! f2 s                    if ( _LdrpInLdrInit )
; K6 S( H9 K7 T8 N                        _LdrpFatalHardErrorCount++;
( Q! x% |' q  q) c- U5 ~$ l
1 X, G: B) _/ W% p1 A5 p2 a                    return STATUS_DLL_INIT_FAILED;
, s; L6 r8 a2 I( s$ M8 a* Q                }0 y! V; ^4 a. ~! o
            }
4 s4 [7 p. b! ?. w+ p% |        }1 H+ Q9 }/ A6 d0 v
0 L9 p8 h) _" I" n: X
        //
& t( }' Q& X* P0 m, v4 i( i$ a        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
& f* K! P" G4 }1 G) n# P* s+ A        //      & n8 i+ M* z, f- U
        if ( _LdrpImageHasTls && bImplicitLoad )3 U$ A" J4 H) I* V5 k' Q, B6 w7 A
        {
6 r$ J. P' _' W4 w1 `5 q/ h            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,( M+ M3 l0 {2 w7 n  G" K6 D. ~) R
                                        DLL_PROCESS_ATTACH );6 l. I3 S& k' X  h9 M6 }/ t; T3 O4 F
        }& `4 ]; ]& F( E4 P' P. e) s
    }
0 e$ s5 {+ w) w3 f& k    __finally
* A7 Z: m0 ]1 O* q& ]    {
' a$ l- ]/ [2 p' ^& i0 ~0 L        //  |* ~% g) K* [1 ]; ]3 }4 B4 b6 |9 g
        // 第四部分;, c7 b* ?6 t% t* `. ^& k
        // 清除分配的内存9 }0 ]; k5 z3 Y5 w5 W
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );# S; m2 v" S6 B, g# Y" _" j
    }
5 w8 l% F# I2 H- t# e5 c
+ x2 S* Q1 L* `" A+ O; L    return STATUS_SUCCESS;, _+ @# g) z  X6 x
}   
8 u( K; P% D: v+ X; o7 u- z5 m
+ f+ W0 m. @. Z1 U+ \5 f- P这个函数分为四个主要部分:# E( j9 x3 J: K! z# q
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。+ s& p9 j+ k  s
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
& \' i/ y* W/ g4 c# @% ~三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。: H. I5 I6 ], x: M
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。
4 n8 n. s- Y/ L9 u在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。. |$ S# {3 g- s- G2 Q* S* j  `
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-12-1 06:42 , Processed in 0.072044 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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