滴水三期:day29.1-节表

本文详细介绍了PE文件结构中的节表,包括节表的位置计算、节表结构、字段含义以及如何手动解析节表。通过示例分析了节表中的Name、Misc、VirtualAddress、SizeOfRawData等字段,并解释了Characteristics字段如何反映节的属性。此外,还提供了手动解析节表的代码示例。

一、节表

1.节表引入

  • 我们知道一个PE文件的结构是由DOS头 + PE标记 + 标准PE头 + 可选PE头 + 节表 + 多个节构成

    image-20211222165439642
  • 那么一个可执行文件分的多个节在硬盘上和内存中应该从哪个地址位置开始存储,到哪里结束等都需要一个东西来管理、来记录。这个东西就是节表

  • 节表相当于各个节(.text、.idata等)的一个目录;而DOS和NT头相当于对整个文件的一个描述

2.定位节表位置与计算个数

  • 节表是表,肯定不止一个数。一个PE文件有多少个节,就有多少个节表!每一个节表的大小是确定的,40字节

  • 如何确定有多少个节:可以通过标准PE头中的NumberOfsections字段的值来确定;确定了有多少个节,就确定了有多少一个节表,一个节表记录管理一个节的信息

  • 一个PE文件从哪里开始是节表(硬盘上的地址):DOS头大小 + 垃圾空位 + PE签名大小 + 标准PE头大小 + 可选PE头大小(需要查);我们知道DOS头大小固定为64字节;PE签名大小为4字节;标准PE头大小固定为20字节;可选PE头大小可以通过标准PE头中的SizeOfOptionalHeader字段的值来确定

    所以:e_lfanew + 4 + 20 + SizeOfOptionalHeader = 节表开始地址

  • 如果文件运行装载到内存中节表在4GB内存中的地址要加上imagebase的值,才是节表真正在内存中的起始地址

3.节表结构

  • 每一个节表的结构是一个结构体类型的,长度固定为0x28,即40字节。如下:

    #define IMAGE_SIZEOF_SHORT_NAME 8 //宏定义	
    typedef struct _IMAGE_SECTION_HEADER{
        BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; *   //每一个节都可以取一个名字,最大长度为8字节
        union{
        	DWORD   PhysicalAddress;
            DWORD   VirtualSize;
        }Misc; *                           //Misc就是此联合体类型的变量
        DWORD VirtualAddress; *
        DWORD SizeOfRawData; *
        DWORD PointerToRawData; *
        DWORD PointerToRelocations;
        DWORD PointerToLinenumbers;
        WORD NumberOfRelocations;
        WORD NumberOfLinenumbers;
        DWORD Characteristics; *
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
    
  • 一个节对应一个节表,即一个节表由一个结构体类型的节表记录信息。节表数据紧接着可选PE头数据后面,节表中会循环上述的结构,因为一个节就对应一个结构,且这些数据都是挨着顺序存放的

    image-20211222192413582 image-20211222192554791

  • 即一个节表中记录了此.exe所有的节的信息,每一个节都用一个结构体来存储信息,所以所有的节对应的节表组合在一起就构成了PE文件的节表结构

二、节表每个字段的含义

1.Name[IMAGE_SIZEOF_SHORT_NAME]

  • 8个字节,一般情况下是以"\0"结尾的ASCII码来表示节的名称,名字可以自定义(一般是编译器加的)。我们知道数组元素是从最后一个元素倒着入栈的,所以从低地址往高地址分别是从数组的第一个元素到最后一个元素
  • 容易出现的安全问题
    • 因为Name数组的长度最大为8字节,如果定义节的名称为.text.的ASCII码为0x2E;t的ASCII码为0x74;e的ASCII码为0x65;x的ASCII码为0x78。所以Name数组中的数据为2E 74 65 78 74 00 00 00,一共8字节,如果此时用一个指针指向Name数组首地址,即char* np = Name所在的地址,接着使用printf("%s",np);来打印此节表的名字,那么由于使用%s和char*的方式来访问一个数组会从数组首地址一直打印直到遇到\0,即0x00就结束打印,所以此时就会打印出来.text,没有任何问题
    • 但是如果我们自定义名字或者编译器帮我们定义的名字长度等于8,把这8位全占了,没有给\0留位置存储。比如.abcdefg,每个字符转换成1字节的ASCII码,最后Name数组中的数据为2E 61 62 63 64 65 66 67,一共8字节。那么如果我们还是使用char* p = Name的起始地址,使用printf("%s",p);来打印名字,就会有越界问题,因为此时数组由于没有\0结尾了,那么就会接着把Name后面的内存中的数据打印出来,直到遇到一个0x00为止才停止打印。那么打印的名字就可能是.abcdefg癑5J@.??.等乱码
    • 解决方法:自己定义一个==char arr[9] = "";==然后使用库函数strncpy(arr,name,8);,或者自己写一个循环一个一个赋值进我们自定义的数组中,最后一位补\0即可,然后使用%s的方法打印我们自定义的数组中的值即可
  • 操作系统也是通过这种方法来处理8字节长度的名字:即8字节名称并不遵守必须以"\0"结尾的规律,如果不是以"\0"结尾,系统会截取8个字节的长度进行处理

2.Misc

  • 表示该节装入内存时在对齐前的真实尺寸:联合体类型变量,大小为4字节。该值改了对程序的运行没有影响,如果一个程序没被修改过,那这个值就是准确的,如果被其他的软件加工过,就可能变的不准确,但不影响程序运行

  • Misc.VirtualSize的值准确的是:节在内存中没有对齐时的大小(为什么一定要是在内存中的,详见day30.1

  • 为什么定义成联合体,因为有些编译器或者软件喜欢用PhysicalAddress这个变量名表示,有些又喜欢用VirtualSize这个变量名表示,那么为了两个都可以使用,而且共用一个内存不占用多余的内存,就使用联合体,想使用Phys

1讲:2015-01-12(进制01) 第2讲:2015-01-13(进制02) 第3讲:2015-01-14(数据宽度-逻辑运算03) 第4讲:2015-01-15(通用寄存器-内存读写04) 第5讲:2015-01-16(内存寻址-堆栈05) 第6讲:2015-01-19(EFLAGS寄存器06) 第7讲:2015-01-20(JCC) 第8讲:2015-01-21(堆栈图) 第8讲:2015-01-21(宝马问题) 第9讲:2015-01-22(堆栈图2) 第10讲:2015-01-23(C语言01_后半段) 第10讲:2015-01-23(C语言完整版) 第11讲:2015-01-26(C语言02_数据类型) 第12讲:2015-01-27(C语言03_数据类型_IF语句) 第13讲:2015-01-28(C语言04_IF语句逆向分析上) 第14讲:2015-01-28(C语言04_IF语句逆向分析下) 第15讲:2015-01-29(C语言04_正向基础) 第16讲:2015-01-30(C语言05_循环语句) 第17讲:2015-02-02(C语言06_参数_返回值_局部变量_数组反汇编) 第18讲:2015-02-02(2015-01-30课后练习) 第19讲:2015-02-03(C语言07_多维数组) 第20讲:2015-02-03(2015-02-02课后练习) 第21讲:2015-02-04(C语言08_结构体) 第22讲:2015-02-05(C语言09_字对齐_结构体数组) 第23讲:2015-02-06(C语言10_Switch语句反汇编) 第24讲:2015-02-26(C语言11_指针1) 第25讲:2015-02-27(C语言11_指针2) 第26讲:2015-02-28(C语言11_指针3) 第27讲:2015-02-28(C语言11_指针4) 第28讲:2015-03-02(C语言11_指针5) 第29讲:2015-03-03(C语言11_指针6) 第30讲:2015-03-04(C语言11_指针7) 第31讲:2015-03-06(C语言11_指针8) 第32讲:2015-03-09(位运算) 第33讲:2015-03-10(内存分配_文件读写) 第34讲:2015-03-11(PE头解析_手动) 第35讲:2015-03-12(PE头字段说明) 第36讲:2015-03-13(PE) 第37讲:2015-03-16(FileBuffer转ImageBuffer) 第38讲:2015-03-17(代码空白区添加代码) 第39讲:2015-03-18(任意空白区添加代码) 第40讲:2015-03-19(新增添加代码) 第41讲:2015-03-20(扩大-合并-数据目录) 第42讲:2015-03-23(静态连接库-动态链接库) 第43讲:2015-03-24(导出) 第44讲:2015-03-25(重定位) 第45讲:2015-03-26(移动导出-重定位) 第46讲:2015-03-27(IAT) 第47讲:2015-03-27(导入) 第48讲:2015-03-30(绑定导入) 第49讲:2015-03-31(导入注入) 第50讲:2015-04-01(C++ this指针 类 上) 第51讲:2015-04-01(C++ this指针 类 下) 第52讲:2015-04-02(C++ 构造-析构函数 继承) 第53讲:2015-04-03(C++ 权限控制) 第54讲:2015-04-07(C++ 虚函数) 第55讲:2015-04-08(C++ 动态绑定-多态-上) 第56讲:2015-04-08(C++ 动态绑定-多态-下) 第57讲:2015-04-09(C++ 模版) 第58讲:2015-04-10(C++ 引用-友元-运算符重载) 第59讲:2015-04-13(C++ new-delete-Vector) 第60讲:2015-04-14(C++Vector实现) 第61讲:2015-04-15(C++) 第62讲:2015-04-16(C++实现) 第63讲:2015-04-16(C++二叉树) 第64讲:2015-04-17(C++二叉树实现) 第65讲:2015-04-20(Win32 宽字符) 第66讲:2015-04-21(Win32 事件-消息-消息处理函数) 第67讲:2015-04-22(Win32 ESP寻址-定位回调函数-条件断点) 第68讲:2015-04-23(Win32 子窗口-消息处理函数定位) 第69讲:2015-04-24(Win32 资源文件-消息断点) 第70讲:2015-04-27(Win32 提取图标-修改标题) 第71讲:2015-04-28(Win32 通用控件-VM_NOTIFY) 第72讲:2015-04-29(Win32 PE查看器-项目要求) 项目一:PE查看器 开发周期(5天) 需求文档 第73讲:2015-05-07(Win32 创建线程) 第74讲:2015-05-08(Win32 线程控制_CONTEXT) 第75讲:2015-05-11(Win32 临界区) 第76讲:2015-05-12(Win32 互斥体) 第77讲:2015-05-13(Win32 事件) 第78讲:2015-05-14(Win32 信号量) 第79讲:2015-05-15(Win32 线程同步与线程互斥) 第80讲:2015-05-18(Win32 进程创建_句柄) 第81讲:2015-05-20(Win32 以挂起形式创建进程) 第82讲:2015-05-21(Win32 加密壳_项目说明) 项目二:加密壳 开发周期(5天) 需求文档 第83讲:2015-05-28(Win32 枚举窗口_鼠标键盘事件) 第84讲:2015-05-29(Win32 CE练习) 第85讲:2015-06-01(Win32 OD练习) 第86讲:2015-06-03(Win32 ShellCode_远程线程注入) 第87讲:2015-06-04(Win32 加载EXE_模块隐藏) 第88讲:2015-06-09(Win32 IAT_HOOK) 第89讲:2015-06-10(Win32 InlineHook) 第90讲:2015-06-11(Win32 进程通信) 第91讲:2015-06-11(Win32 进程监控_项目说明) 项目三:进程监控 开发周期(5天) 需求文档 第92讲:2015-06-15(硬编码_01) 第93讲:2015-06-16(硬编码_02) 第94讲:2015-06-17(硬编码_03) 第95讲:2015-06-18(硬编码_04) 第96讲:2015-06-19(硬编码_05)
以下是处理文本数据并绘制CPU指标折线图的Python程序: ```python import re import matplotlib.pyplot as plt from datetime import datetime # 示例数据 data = """ Line 11: [2025-12-22 14:32:03] CPU: 12.4% usr 29.8% sys 0.0% nic 51.8% idle 0.7% io 0.0% irq 5.0% sirq Line 43: [2025-12-22 14:32:04] CPU: 11.6% usr 27.7% sys 0.0% nic 58.5% idle 0.0% io 0.0% irq 2.1% sirq Line 75: [2025-12-22 14:32:05] CPU: 30.3% usr 37.3% sys 0.0% nic 19.7% idle 0.5% io 0.0% irq 12.0% sirq Line 106: [2025-12-22 14:32:06] CPU: 38.9% usr 32.4% sys 0.0% nic 9.9% idle 0.9% io 0.0% irq 17.7% sirq Line 138: [2025-12-22 14:32:07] CPU: 36.3% usr 36.8% sys 0.0% nic 12.1% idle 0.5% io 0.0% irq 14.1% sirq Line 170: [2025-12-22 14:32:08] CPU: 17.0% usr 23.7% sys 0.0% nic 50.2% idle 0.2% io 0.0% irq 8.7% sirq Line 202: [2025-12-22 14:32:09] CPU: 16.4% usr 29.1% sys 0.0% nic 45.3% idle 3.0% io 0.0% irq 6.0% sirq """ # 解析数据 pattern = r'\[(.*?)\] CPU:(.*?)$' entries = [] for line in data.strip().split('\n'): match = re.search(pattern, line) if match: timestamp = datetime.strptime(match.group(1), '%Y-%m-%d %H:%M:%S') metrics = match.group(2).split() metrics_dict = {} for i in range(0, len(metrics), 2): if i+1 < len(metrics): value = float(metrics[i].replace('%', '')) name = metrics[i+1] metrics_dict[name] = value entries.append((timestamp, metrics_dict)) # 准备绘图数据 timestamps = [entry[0] for entry in entries] metrics_names = ['usr', 'sys', 'nic', 'idle', 'io', 'irq', 'sirq'] metric_values = {name: [] for name in metrics_names} for timestamp, metrics in entries: for name in metrics_names: metric_values[name].append(metrics.get(name, 0)) # 绘制折线图 plt.figure(figsize=(12, 6)) for name in metrics_names: plt.plot(timestamps, metric_values[name], label=name, marker='o') plt.title('CPU Usage Metrics Over Time') plt.xlabel('Time') plt.ylabel('Percentage (%)') plt.legend() plt.grid(True) plt.tight_layout() plt.show() ```
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值