LdrInitializeThunk 解析

  1. LdrInitializeThunk()
        Windows 的 DLL 装入(除 ntdll.dll 外)和连接是通过 ntdll.dll 中的一个函数LdrInitializeThunk()实现的.
        在进入这个函数之前,目标 EXE 映像已经被映射到当前进程的用户空间,系统 DLL ntdll.dll 的映像也已经被映射, 但是并没有在 EXE 映像与 ntdll.dll 映像之间建立连接(实际上EXE 映像未必就直接调用 ntdll.dll 中的函数)。
        LdrInitializeThunk()是 ntdll.dll 中不经连接就可进入的函数,实质上就是 ntdll.dll 的入口。除 ntdll.dll 以外,别的 DLL 都还没有被装入(映射)。此外,当前进程(除内核中的“进程控制块”EPROCESS 等数据结构外)在用户空间已经有了一个“进程环境块”PEB,以及该进程的第一个“线程环境块”TEB。这就是进入__true_LdrInitializeThunk()前的“当前形势”。
    VOID STDCALL
    __true_LdrInitializeThunk (ULONG Unknown1, ULONG Unknown2,
                             ULONG Unknown3, ULONG Unknown4)
    {
       . . . . . .

       DPRINT("LdrInitializeThunk()/n");
       if (NtCurrentPeb()->Ldr == NULL || NtCurrentPeb()->Ldr->Initialized == FALSE)
       { 
            Peb = (PPEB)(PEB_BASE);            //进程环境块
           DPRINT("Peb %x/n", Peb); 
            ImageBase = Peb->ImageBaseAddress; //EXE 映像在用户空间的起点
            . . . .
           /* Initialize NLS data *           //语言本地化有关
           RtlInitNlsTables (Peb->AnsiCodePageData, Peb->OemCodePageData,
                             Peb->UnicodeCaseTableData, &NlsTable);
           RtlResetRtlTranslations (&NlsTable);

           NTHeaders = (PIMAGE_NT_HEADERS)(ImageBase + PEDosHeader->e_lfanew);
           . . . . . .
           /* create process heap */ 
            //创建一个堆、 及其第一个区块,其初始的大小来自映像头部的建议值,其中 SizeOfHeapReserve 是估
             计的最大值,SizeOfHeapCommit是初始值,这是在编译/连接时确定的。
           RtlInitializeHeapManager();
           Peb->ProcessHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 
                                NTHeaders->OptionalHeader.SizeOfHeapReserve, 
                                NTHeaders->OptionalHeader.SizeOfHeapCommit, 
                                NULL, NULL); 
            /* create loader information */ 
           //PEB 中的 ProcessHeap 字段指向本进程用户空间可动态分配的内存区块“堆”
            Peb->Ldr = (PPEB_LDR_DATA)RtlAllocateHeap (Peb->ProcessHeap,
                                                      0,
                                                      sizeof(PEB_LDR_DATA));
           . . . . . . 
           /*    PEB 中的另一个字段 Ldr是个 PEB_LDR_DATA 结构指针,所指向的数据结构用来为本进程维持三个“模块”
             队列、即 InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList。
                 所谓“模块”就是 PE 格式的可执行映像,包括 EXE映像和 DLL 映像.
                 两个模块队列的不同之处在于排列的次序,一个是按装入的先后,一个是按装入的位置(实际上目前ReactOS
              的代码中并未使用这个队列)。
                 每当为本进程装入一个模块、即.exe 映像或 DLL 映像时,就要为其分配/创建一个LDR_MODULE 数据结构,
             并将其挂入 InLoadOrderModuleList。然后,完成对这个模块的动态连接以后,就把它挂入
              InInitializationOrderModuleList 队列.LDR_MODULE 数据结构中有三个队列头,因而可以同时挂在三个队列
             中。
           Peb->Ldr->Length = sizeof(PEB_LDR_DATA);
           Peb->Ldr->Initialized = FALSE;
           Peb->Ldr->SsHandle = NULL;
           InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);
           InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);
           InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList);

           . . . . . .
           /* add entry for ntdll */
           NtModule = (PLDR_MODULE)RtlAllocateHeap (Peb->ProcessHeap,
                                                            0,
                                                            sizeof(LDR_MODULE));
           . . . . . .
           InsertTailList(&Peb->Ldr->InLoadOrderModuleList,
                                 &NtModule->InLoadOrderModuleList);
           InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
                                 &NtModule->InInitializationOrderModuleList);
           . . . . . . 
            /* add entry for executable (becomes first list entry) */
           ExeModule = (PLDR_MODULE)RtlAllocateHeap (Peb->ProcessHeap,
                                                            0,
                                                            sizeof(LDR_MODULE));
           . . . . . .
           InsertHeadList(&Peb->Ldr->InLoadOrderModuleList,
                          &ExeModule->InLoadOrderModuleList);
           . . . . . . 
           /*当 CPU从 LdrPEStartup()返回时,EXE 对象需要直接或间接引入的所有 DLL 均已映射到用户空间并已完成连
             接,对 EXE 模块的“启动” 、即初始化也已完成。
              注意在调用 LdrPEStartup()时的参数 ImageBase 是目标 EXE 映像在用户空间的位置

           EntryPoint = LdrPEStartup((PVOID)ImageBase, NULL, NULL, NULL);
           . . . . . .
       }
       /* attach the thread */
       RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); 
       //目的是调用各个 DLL 的初始化过程,以及对 TLS、即“线程本地存储(Thread Local Storage)”的初始化
        //TLS:有时候又确实需要让每个线程都对于同一个全局变量有一份自己的拷贝,TLS就是为此而设的
        LdrpAttachThread();
       RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
    }


    //注意在调用 LdrPEStartup()时的参数 ImageBase 是目标 EXE 映像在用户空间的位置
    PEPFUNC LdrPEStartup (PVOID ImageBase, HANDLE SectionHandle,
                          PLDR_MODULE* Module, PWSTR FullDosName)
    {
         //PE 映像的 NtHeader 中有个指针,指向一个 OptionalHeader。说是“Optional”,实际上却是关键性的。在 
         //OptionalHeader中有个字段 ImageBase,是具体映像建议、或者说希望被装入的地址
       . . . . . .
       DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
       NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew);

       /*
        * If the base address is different from the
        * one the DLL is actually loaded, perform any
        * relocation.
        */
       if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
       {
           DPRINT("LDR: Performing relocations/n");
            //ImageBase 是目标 EXE 映像在用户空间的位置
           Status = LdrPerformRelocations(NTHeaders, ImageBase);
           . . . . . .
        }

       if (Module != NULL)
       {
           *Module = LdrAddModuleEntry(ImageBase, NTHeaders, FullDosName);
           (*Module)->SectionHandle = SectionHandle;
        }
       else
       {
           Module = &tmpModule;
           Status = LdrFindEntryForAddress(ImageBase, Module);
           . . . . . .
       }

       . . . . . .

       /*
        * If the DLL's imports symbols from other
        * modules, fixup the imported calls entry points.
        */
    //它所处理的就是当前模块所需DLL模块的装入(如果尚未装入的话)和连接。如前所述,这个函数递归地施行于所有的模块,直至最底层的“叶节点”ntdll.dll为止。
       DPRINT("About to fixup imports/n");
       Status = LdrFixupImports(NULL, *Module);
       if (!NT_SUCCESS(Status))
         {
           DPRINT1("LdrFixupImports() failed for %wZ/n", &(*Module)->BaseDllName);
           return NULL;
         }
       DPRINT("Fixup done/n");

       . . . . . .
       Status = LdrpInitializeTlsForProccess();
       . . . . . .

       /*
        * Compute the DLL's entry point's address.
        */
       . . . . . .
       if (NTHeaders->OptionalHeader.AddressOfEntryPoint != 0)
        {
            EntryPoint = (PEPFUNC) (ImageBase + NTHeaders->OptionalHeader.AddressOfEntryPoint);
        }
       DPRINT("LdrPEStartup() = %x/n",EntryPoint);
       return EntryPoint;
    }
  2. //调用关系[__true_LdrInitializeThunk > LdrPEStartup() > LdrPerformRelocations()]

    typedef struct _IMAGE_DATA_DIRECTORY

         DWORD VirtualAddress; 
         DWORD Size;
    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

    //每个 IMAGE_BASE_RELOCATION 数据结构代表着一个“重定位块” ,每个重定位块的(容器)大小是两个页面(8KB),而 SizeOfBlock 则说明具体重定位块的实际大小。这实际的大小中包括了这 IMAGE_BASE_RELOCATION数据结构本身。
    typedef struct _IMAGE_BASE_RELOCATION

         DWORD VirtualAddress; 
         DWORD SizeOfBlock;
    } IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;


    static NTSTATUS
    LdrPerformRelocations(PIMAGE_NT_HEADERS NTHeaders, PVOID ImageBase)
    {
    . . . . . .
    //PE 映像的 OptionalHeader 中有个大小为 16 的数组 DataDirectory[],其元素都是“数据目录” 、即IMAGE_DATA_DIRECTORY 数据结构:其中之一(下标为 5)就是“重定位目录” ,这是一个IMAGE_BASE_RELOCATION结构数组。

    RelocationDDir =&NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    . . . . . .

    ProtectSize = PAGE_SIZE;
    //所谓重定位,就是计算出实际装入地址与建议装入地址间的位移 Delta,然后调整每个重定位块中的每一个重定位项、即指针,具体就是在指针上加 Delta
       Delta = (ULONG_PTR)ImageBase - NTHeaders->OptionalHeader.ImageBase;
    //IMAGE_BASE_RELOCATION结构数组
    RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + RelocationDDir->VirtualAddress);
    RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + RelocationDDir->VirtualAddress + 
                                                                       RelocationDDir->Size);

    while (RelocationDir < RelocationEnd && RelocationDir->SizeOfBlock > 0)
       {
          Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
          Page = ImageBase + RelocationDir->VirtualAddress;
          TypeOffset = (PUSHORT)(RelocationDir + 1);

          /* Unprotect the page(s) we're about to relocate. */
          ProtectPage = Page;
          Status = NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage,
                                  &ProtectSize, PAGE_READWRITE, &OldProtect);
          . . . . . .

          if (RelocationDir->VirtualAddress + PAGE_SIZE < NTHeaders->OptionalHeader.SizeOfImage)
           {
              ProtectPage2 = ProtectPage + PAGE_SIZE;
              Status = NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage2,
                                  &ProtectSize, PAGE_READWRITE, &OldProtect2);
              . . . . . .
           }
          else
           {
              ProtectPage2 = NULL;
           }
    //具体的指针调整是由 LdrProcessRelocationBlock() 完成的,此前和此后的NtProtectVirtualMemory()只是为了先去除这些指针所在页面的写保护,而事后加以恢复。
          RelocationDir = LdrProcessRelocationBlock(Page, Count, TypeOffset, Delta);
          . . . . . .

          /* Restore old page protection. */
          NtProtectVirtualMemory(NtCurrentProcess(),&ProtectPage,
                                 &ProtectSize, OldProtect, &OldProtect);

          if (ProtectPage2 != NULL)
            {
              NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage2,
                                     &ProtectSize, OldProtect2, &OldProtect2);
            }
        }

    return STATUS_SUCCESS;
    }

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言最近随着计算机的发展,软件日益更新,有很多公司发布的产品遭到篡改和破解,在总众多年的历史种逐渐形成了软件保护与软件破解之间的对抗产生了软件逆向工程这本门技术将在如下的课程讲解各种软件逆向知识,软件保护知识,已经破解脱壳反调试知识,为初期学软件逆向不懂而又迷茫的同学门指明一条道路此套课堂能有效帮助同学们解决软件逆向中所遇到的大部分问题大纲软件逆向工程高级班分为反调试篇汇编篇算法篇补丁篇HOOK篇将在如上这几篇对软件逆向的各个方面进行详解,包括网络验证的分析思路,封包算法的提取,以及各种软件保护技术,无论哪一篇都会从诸多个方面的细节进行详解反调试篇:分为PEB,时间校验,CRC,NtQuery函数,内核过渡等知识要领与诸多方面的综合性详解,细节分为每一节课,每一节课目标清晰无比,每一节深入精髓进行讲解!汇编篇:一个程序编译完成之后是如何通过在计算机运行起来的,其中少不了底层知识的汇编指令,汇编篇中将深入浅出的带领同学们对MASM32SDK的一套汇编库中开发程序,熟悉汇编的原理,如何运用汇编写出一套花指令,并且去除指令,方便以后的算法学习以及为今后的学习打下坚实的基础算法篇:随着编程语言的发展,编程语言的标准也在发展,一些编译器善于运用数学的手法,对程序进行各种优化,然后我们进行分析,我们得需要一步步还原这个优化或清晰了解这个优化才有可能掌握这个数学模型优化,进一步还原代码,算法篇知识要领将在优化,技巧这方面表现的玲离尽致!此篇会带领同学们分析多个语言的算法,包括C/C++算法还原代码还原易语言代码还原 算分开库的实现,制作自己的第一个注册机等!补丁篇:说到补丁,同学们可能第一个想到的就是对方一些网络验证所用到的技巧,我的课程这一方面虽有涉及,但是补丁技术远远不止这一点,我的课程会详解更多的补丁知识原理,包括什么是补丁,补丁的真正概念,前辈们是如何善用补丁对程序的漏洞进行修补损坏的程序。将在此篇一一介绍HOOK篇:详细详解了各种HOOK的原理以及实现代码,包括网上流行所说的超级HOOK,COMHOOK,协议HOOK,代理中转等方法,怎么定位关键位置,环境的保存知识要领,hook关键的注意事项,为自己以后做hook行业打下坚实的基础课程每一个细节讲究的深入骨髓,通俗易懂的学习方式,全程贯彻原理,软件逆向中必不可少少的教程!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值