转自:http://www.0xaa55.com/thread-1385-1-1.html

之前做过ldr遍历的操作,发现第一项竟然是空,也就是大部分元素都是0,下面来揭示一下原理:

经过研究,其实Ldr链表得第一项为头结点,为PEB_LDR_DATA结构,而其他所有项均为LDR_DATA_TABLE_ENTRY结构
Ldr的创建:ldrinit.c -> LdrpInitializeProcess

PEB_LDR_DATA PebLdr

LdrpInitializeProcess   初始化进程时用空项PebLdr创建Ldr
    Peb->Ldr = &PebLdr;
    InitializeListHead(&PebLdr.InLoadOrderModuleList);
    InitializeListHead(&PebLdr.InMemoryOrderModuleList);
    InitializeListHead(&PebLdr.InInitializationOrderModuleList);
    PebLdr.Length = sizeof(PEB_LDR_DATA);
    PebLdr.Initialized = TRUE;
        
LdrUnloadDll和LdrpLoadDll分别会进行对Ldr这3个链表卸载和增添节点操作,而顺序不同:
Load时如果发现未加载dll则会增加节点,会先添加InMemoryOrderModuleList  InLoadOrderModuleList  两个链表增加节点,之后操作InInitializationOrderModuleList,之后调用DllMain初始化
而Unload的时候若发现引用计数为0则会删除节点,会先对InMemoryOrderModuleList InInitializationOrderModuleList 两个链表删除节点,之后调用DllMain清理,最后删除InLoadOrderModuleList节点
#define RemoveEntryList(e) do { PLIST_ENTRY f = (e)->Flink, b = (e)->Blink; f->Blink = b; b->Flink = f; (e)->Flink = (e)->Blink = NULL; } while (0)
可见删除链表操作为将该项后一个节点直接连接到前一个节点,并且将当前节点的首尾指向NULL,因此通过判断Flink=0 可以判断某DLL正在被卸载
        
正确的遍历Ldr LIST_ENTRY方法:
    ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
    Next = ListHead->Flink;
    while (Next != ListHead)//跳过头结点即可
        {
                 Next = Next->Flink;
        }

而ldr结构图如下:
typedef struct _PEB_LDR_DATA
{
    ULONG               Length;
    BOOLEAN             Initialized;
    PVOID               SsHandle;
    LIST_ENTRY          InLoadOrderModuleList;
    LIST_ENTRY          InMemoryOrderModuleList;
    LIST_ENTRY          InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
        
当时我遍历的时候将Head当成LDR_DATA_TABLE_ENTRY,自然数据是不对的~~

为何遍历Ldr会得到空项?的更多相关文章

  1. Java的poi技术遍历Excel时进行空Cell,空row,判断

    /** * 导入信息 */ @Override public List<Object> add(HttpServletRequest request) { // TODO Auto-gen ...

  2. js中遍历删除数组中的项(项目中遇到的问题解决)

    代码如下: for (var key=0;key<$scope.pageContent.messages.length;key++){ if($scope.pageContent.message ...

  3. checkbox遍历操作, 提交所有选中项的值

    <div class="content_list pad_10 hidden" > <h3>修改可配送地区</h3> <input typ ...

  4. Winform 遍历 ListBox中的所有项

    foreach(DataRowView row in listBox.Items ) { MessageBox.Show(row["displayMember"].ToString ...

  5. PHP数组去空项

    $strDelCodes = "A;B;;C;;C;D;;;D;D";$rsArray = array_values (array_unique (array_diff (spli ...

  6. 先序遍历创建二叉树,对二叉树统计叶子节点个数和统计深度(创建二叉树时#代表空树,序列不能有误)c语言

    #include "stdio.h" #include "string.h" #include "malloc.h" #define NUL ...

  7. C++树的插入和遍历(关于指针的指针,指针的引用的思考)

    题目 写一个树的插入和遍历的算法,插入时按照单词的字典顺序排序(左边放比它"小"的单词,右边放比它"大"的单词),对重复插入的单词进行计数. 程序源码 #inc ...

  8. PEB及LDR链

    PEB地址的取得在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,PEB_LDR_DATA+0x1c处存放一些指向动态链接 ...

  9. 二叉树遍历入门 Lebal:research

    解决二叉树遍历的画法 对于二叉树的基本概念,一般学生都知道,但对于二叉树的遍历,在实际运用中可以发现很多问题,这里提供一次性彻底解决这个问题的方法. 二叉树的遍历 二叉树的遍历是指不重复地访问二叉树中 ...

随机推荐

  1. Google C++ style guide——头文件

    1.#define保护 使用#define防止头文件被多重包括.命名格式为:<PROJECT>_<PATH>_<FILE>_H_ 比如,foo中的头文件foo/sr ...

  2. mesos博客集

    http://dongxicheng.org/category/apache-mesos/

  3. zepto 获取select选中的值

    jQuery中典型的方法: $('option[selected]') 不管用,因为selected并不是CSS标准.   因此,在zepto中想要获取select元素中选中的option,需采取如下 ...

  4. L9-1-安装Apache

    一.安装Apache服务器: 安装apr [root@OCP soft]# tar -zxf apr-1.5.1.tar.gz [root@OCP apr-1.5.1]# ./configure -- ...

  5. C# 启动EXE文件及带启动参数EXE

    (一).先制作一个带启动参数的EXE文件. 步骤: 1.定义全局私有变量:private string[] s = new string[1];  //这里为了简单起见,只做一个参数 2.  在窗体的 ...

  6. Node.js学习系列1

    概述 最近在刷javascript的技能,觉着nodejs是个不错的入口,作为一个.Net平台的前端工程师学习使用js开发服务端,想想都有点小激动哈哈^_^^_^. 入门 之前开发过ionic,所以对 ...

  7. JUnit报空指针错误,控制台不报任何错误

    解决方法:1. 在测试类的beforeClass方法上加try-catch块 2. 添加main方法,里面添加beforeClass();

  8. bootstrap简章

    系统整理一遍bootstrap 的东西 1/  设置页面为H5文档类型 <!DOCTYPE html> <html lang="zh-CN"> ... &l ...

  9. 在Linux手动把文件转码的方法,防止乱码出现

    iconv -f utf-8 -t gb2312 report.html  >  report_test.html

  10. Datagridview控件实现分页功能

    可以进行sql语句进行设置:      1.先新建一个窗体,一个DataGridView控件.两个label控件.两个Button控件   2.代码如下: using System; using Sy ...