搜档网
当前位置:搜档网 › (读)向PE文件中插入可执行代码的研究

(读)向PE文件中插入可执行代码的研究

(读)向PE文件中插入可执行代码的研究
(读)向PE文件中插入可执行代码的研究

第28卷第2期

2005年4月鞍山科技大学学报Journal of Anshan University of Science and Technology Vol.28No.2Apr.,2005

向PE 文件中插入可执行代码的研究

胡 珊

(鞍山科技大学计算机科学与工程学院,辽宁鞍山 114044)

摘 要:根据Win32PE 文件的结构特征,实现了两种不同的向PE 文件中插入可执行代码的方法:(1)在本节中的未用空间中插入代码;(2)添加新的节。指出在编写要添加的代码的过程中,要注意插入代码的变量地址的重定位和代码返回改动态获取API 入口地址等问题。

关键词:可移植的执行体文件;虚拟地址;相对虚拟地址;原始地址

中图分类号:TP31617 文献标识码:A 文章编号:167224410(2005)022*******

1 PE 文件结构介绍

PE 意为可移植的执行体(Portable executable ),是Win32环境自身所带的执行体文件格式。Portable Executable 意味着此文件格式是跨Win32平台的;即使Windows 运行在非Intel 的CPU 上,任何Win32平台的PE 装载器都能识别和使用该文件格式。在PE 文件中,可执行代码、已初始化的数据、文件资源和重定位信息等数据被按照属性的不同而放到不同的节中。掌握了PE 文件的结构就可以根据需要来实现对PE 文件数据的修改。比如,根据需要向可执行文件中加进特定的可执行代码来实现自己的目的。Ms 2dos header (IMA GE 2DOS 2HEADER 结构Dos 2stub PE 头(IMA G E 2N T 2HEADE R 结构)PE 标志PE 文件头(IMA GE 2FIL E 2HEADER 结构)PE 可选头(IMA GE 2OPTIONAL 2HEADER 结构Sections table N 个IMA GF 2SECTION 2HEADER 结构Section 1Section 2Section …SectionN 图1 PE 文件的结构Fig.1 Structure of PE files 图1是PE 文件结构的总体层次分布。所有

PE 文件必须以一个简单的DOS MZ header 开始,在偏移0处有DOS 下可执行文件的“MZ 标

志”。有了它,一旦程序在DOS 环境下执行,DOS

就能识别出这是有效的执行体,然后运行紧随

MZ header 之后的DOS stub 。DOS stub 实际是

个有效的可执行体。在不支持PE 文件格式的操

作系统中,它将简单显示一个错误提示,类似于字

符串“This program cannot run in DOS mode ”。在

此处,程序员可根据自己的意图实现完整的DOS

代码。紧接着DOS stub 的是PE 头。PE 头是PE

文件相关结构IMA GE 2N T 2HEADERS 的简称。

其中包含了许多PE 文件装载器要用到的重要

域。在PE 头中包含了PE 文件标识(pe 2sign )、PE

文件头(pe 2file 2header )和PE 可选头(pe 2optional 2

header )三部分。PE 可执行文件在支持PE 文件结构的操作系统中执行时,PE 装载器将从IMA GE 2DOS 2HEADERS 结构的e 2lfanew 变量(即3CH 偏移处)中找到PE 头的起始偏移量。(跳过了DOS 2stub 直接定位到真正的文件头PE 头)。然后读取PE

文件头和PE 可选头的信息来定位程序中不同的节在虚拟内存空间中位置。紧接PE 头的是结构型数收稿日期:2004206220。

作者简介:胡珊(1974-),男,湖北阳新人。

组———节表(Sections table )。每个节表包含对应节的属性、文件偏移量、虚拟偏移量等。如果PE 文件里有n 个节,那么此结构数组内就有n 个成员。PE 文件中的真正内容被划分成块,称之为节(Sec 2tions )。节的划分是基于各组数据的共同属性,而不是逻辑概念。如果对PE 格式的文件进行修改,理论上讲可以写入任何一个节内,并调整此节的属性就可以了。

在修改PE 文件之前必须明白原始地址(Raw address )和映像地址(Image address )的概念。在PE 文件中Image address 是映射到虚拟内存空间的地址;它涉及实际的虚拟地址(VA )和相对虚拟地址(RVA )两个概念。例如,如果PE 文件装入虚拟内存地址空间是400000h (VA )处,且进程从虚拟地址401000h (VA )开始执行,那么可以说进程执行启始相对虚拟地址在1000h (RVA )。每个RVA 都是相对于当前可执行模块的起始地址,这里所谓的相对是针对PE 文件的装载地址(Image base )而言的,即RVA =VA -ImageBase 。Raw address 是在磁盘文件中的映像地址,即在文件中的偏移量。

2 向PE 文件中添加代码的方法

在明白了PE 文件的内部结构后,就可根据需要利用两种方法来向PE 文件中插入代码。以下给出了两种方法(以下方法中的英文词组是结构体2IMA GE 2OPTIONAL 2HEADER 或2IMA GE 2SECTION 2HEADER 中的变量)。

211 在节中的未用空间中插入代码

由于PE 文件的节要根据FileAlignment 来调整大小(即每节的起始物理地址必须是FileAlignment 的整数倍),每节中都有一定的空间没有使用。如果要插入的代码较小就可以直接把代码插入没有使用的节空间中。要把特定的代码添加到节的未用空间中就必须得到以下的参数:(1)节的Pointer ToRaw 2Data ;(2)节的SizeOf RawData ;(3)要添加的代码的长度。其中主要的关系:(1)节头起始地址=e 2lfanew +sizeof (IMA GE 2N T 2HEADER );(2)各节的末尾=各节的Pointer ToRawData +各节的Size 2Of RawData.

通过遍历每一个节头,然后取出每个节的Pointer ToRawData 和SizeOf RawData 来定位每节的节尾,接着从节尾开始反向的搜寻(既从高地址向低地址搜寻)节中未用空间的大小(节中的未用空间以0填充,即搜寻末尾连0个数)。如果添加代码所占的物理空间小于被插入节的剩余空间则可以实现代码的插入,否则不行。因为要添加可执行代码,所以选择的节的Characteristics 属性应该可执行的。212 添加新的节

把特定的代码添加到新的一节中必须得到以下参数:(1)要添加节头的启始地址;(2)最后一节的末尾;(3)最后一节的RVA ;(4)最后一节的VirtualSize 。其中的主要关系:(1)要添加节头的起始地址=NumberOf Sections 30x28+e 2lfanew +sizeof (IMA GE 2N T 2HEADER )(0x28是每个节头的大小);(2)要添加节的物理起始地址=最后一节的Pointer ToRawData +最后一节的SizeOf RawData ;(3)要加节头的Poninter ToRawData =要添加节的物理起始地址;(4)要加节头的SizeOf RawData =(要加代码长度/FileAlignment +1)3FileAlignment ;(5)要加节头的RVA =最后一节的RVA +(最后一节的Virtual 2Size %SectionAlignment +1)3SctionAlignment ;(6)要加节头的VirtualSize =(要加代码的长度/FileAlignment +1)3FileAlignment 。注意:在此方法中还需要修改SizeOf InitializedData 的大小(Size 2Of InitializedData =SizeOf InitializedData +要加节的SizeOf RawData )和设置添加节的Characteristics 属性。根据实际情况可以选择不同的方式来给可PE 文件添加代码。首先利用CreateFile 函数打开要添加代码的PE 文件,然后用Set FilePointer 、ReadFile 、WriteFile 等文件操作函数来实现文件的定位和读写等功能。

?

021? 鞍山科技大学学报 第28卷

3 添加的代码所应注意的问题

311 插入代码的变量地址的重定位和代码返回

向PE 文件中添加代码之后,要使此文件首先执行所添加的代码就必须把AddressOf EntryPoint 的值设成所添加的代码的RVA 起始地址,同时保存原先的基地址和入口地址,在所添加代码执行完时再恢复宿主PE 文件的基地址和入口地址。所添加代码的形式如下:

yourcode :

call codestart

codestart :

pop ebx

sub ebx ,offset codestart ;从这添加你希望实现的功能

mov eax ,old 2base[ebx ]

add eax ,old 2entrypoint [ebx ]

push

eax

ret

varl dd 0

yourcodeEnd

添加的代码中可能要用到变量(常量)。当代码插入PE 宿主程序后,变量编译时的地址和插入PE 文件时内存地址不一致,必须调整它们之间的差值,使插入代码能在被插入的PE 文件内存空间中准确的定位到要使用的变量。call 指令实际上是push 和jmp 的组合。当执行call codestart 时,实际上是把call codestart 的下一条指令(也就是pop ebx )的地址压入堆栈,然后jmp 到codestart 。由于之前已经把pop ebx 的地址压入了堆栈,所以当真正执行到pop ebx 这条指令的时候,实际上就是把pop ebx 这条指令的地址放到了ebx 中。通过sub ebx ,offset codestart ,就得到了当前插入代码的在PE 的偏移地址和编译时的偏移地址之间差值。如果插入代码中有一个变量var1,该变量实际在内存中的地址应该是ebx +var1的偏移量与codestart 偏移量的差,即变量在内存中的真正地址=代码开始处codestart 在内存中的地址+变量与代码开始处之间的距离差。有时候也采用ebx -codestart 的偏移量+var1的偏移量的形式进行变量var1的重定位。old 2base 变量中保存有宿主PE 文件的基地址,old 2entrypoint 变量中保存着宿主PE 文件的入口地址。通过这两个变量可以使插入代码执行完后返回到宿主PE 文件的入口地址。

312 动态获取API 入口地址

一个PE 文件在编译和连接成功后,会有一个Import table 。当需要执行API 的时候,会先在Im 2port table 中得到API 的地址,然后调用它。这在一般的情况下是足以应付要求的了。可是,当往PE 文件中插入可执行代码时,情况不同了———可执行代码在PE 文件编译好之后才插入,它本身没有Import 图2 Win32可执行文件的执行过程Fig.2 Executing process of Win32EXE files

table 。那么,要在插入的代码中调用

Windows API 时,必须获取API 函数的线

性地址。要得到API 的地址,必须用到

LoadLibrary 和G et ProcAddress 函数;而要

使用以上两个函数,必须得到K ernel32.dll

的地址。如图2所示,在每个程序执行的

时候系统都会调用K ernel32.dll 中的Cre 2

ateProcess 函数来创建一个进程;而Cre 2

ateProcess 函数在完成装载应用程序后,会?121?第2期 胡 珊:向PE 文件中插入可执行代码的研究

将一个返回地址压入堆栈并转而执行应用程序。如果应用程序使用ret指令的话,程序就会返回Cre2 ateProcess函数设定的地址。从这个过程可以得到一个很重要的数据,就是堆栈中的返回地址。这个地址只要在程序入口的地方用mov命令把[esp]读出来就可以(例如mov edi,[esp])。因为这个地址在K ernel32.dll模块中,而PE文件被装入内存时是按内存页对齐的,只要从返回地址按照页对齐的边界一页页地往低地址搜索,就必然可以找到K ernel32.dll文件头地址;即K ernel32模块的基地址。另外, LoadLibrary和G et ProcAddress函数就处于K ernel32.dll中,可利用K ernel32的基地址来定位PE文件的导出表,并从导出表的信息中查找到LoadLibrary和G et ProcAddress函数的地址。

(1)定位到K ernel32.dll的PE文件头(流程图如图3所示)。(2)从K ernel32.dll的PE文件头中的可选文件头中取出数据目录表的第一个数据目录,得到导出表的地址。(3)从导出表的NumberOf2 Names字段得到以命名函数的总数,并以这个数字做微循环的次数来构造一个循环。(4)从Addres2 sOfNames字段指向的函数名称地址表的第一项开始,在循环中将每一项定义的函数名与要查找的函数名比较;如果没有任何一个函数名符合,说明文件中没有指定名称的函数。(5)如果某一项定义的函数名与要查找的函数名符合,那么记住这个函数名在字符串地址表中的索引值(如x),然后在AddressOf2 NameOrdinals指向的数组中以同样的索引值x去找数组项中的值,假如该值为m。(6)以m值作为索引值,在AddressOf Functions字段指向的函数入口地址表中获取的RVA就是函数的入口地址,当函数被装入内存后,这个RVA值加上模块实际装入的基址(ImageBase),就得到了函数真正的入口地址。

图3 定位kernel32.dll基地址的流程图

Fig.3 Flowchart of finding the imagebase of kernel32.dll

在得到了LoadLibrary和G et ProcAddress的地址值之后就可以利用这两个函数的地址来获取其他Windows API函数的地址,从而实现在插入代码中调用API函数。

4 结 语

Windows PE型病毒的实现方法就是把病毒代码插入PE文件中。研究向PE文件中插入可执行代码不仅可以了解PE型病毒的工作原理,而且可以利用此原理实现一些其他功能。如给文件加密、压缩可执行文件等。向PE文件中插入可执行代码的实现需要对操作系统内核尤其Windows PE可执行文件加载机制有深入了解。限于篇幅本文没有详细描述全部的设计过程,仅从数据结构和实现的机理上阐述了实现这一技术的基本原理、方法和应注意的一些问题。

参考文献:

[1]PIETREK M.Windows95系统程式设计[M].侯俊杰译.北京:旗标出版有限公司,1997.

[2]罗云彬.Windows环境下32位汇编语言程序设计[M].北京:电子工业出版社,2002.

[3]飞思科技产品研发中心.Delphi下深入Windows核心编程[M].北京:电子工业出版社,2003.

[4]熊前兴.Windos N T可执行文件块结构剖析[J].小型微型计算机系统,2000,21(5):508-511.

[5]孙瑞,朱桂林.Windows PE可执行文件压缩机制分析[J].计算机工程,2003,29(21):188-191.

[6]童学红,唐红.对Windows2000可执行文件格式的分析及其应用[J].计算机应用研究,2003,20(3):15-19.

[下转第126页]

予了证明,并给出了该算法实现的思想。

参考文献:

[1]谢志鹏,刘宗田.基于概念格的关联规则发现[J].小型微型计算机系统,2000,10:1029-1031.

[2]石磊.探索式数据挖掘模型的讨论[J].河南科学,2000,1:45-48.

[3]陆丽娜,陈亚萍,魏恒义.挖掘关联规则中Apriori算法的研究[J].小型微型计算机系统,2000,9:940-943.

[4]陈富赞,寇继松,王以真.数据挖掘方法的研究[J].系统工程与电子技术,2000,8:79-81.

[5]汤宇松,刘相峰,黄亚楼.数据挖掘系统设计[J].系统工程理论与实验践,2000,9:57-63.

[6]刘同明.数据挖掘技术及其应用[M].北京:国防工业出版社,2001:1.

[7]HAN Jiawei,K AMBER Micheline.数据挖掘———概念与技术[M].北京:高等教育出版社,2001:1.

R esearch and demonstration on association rules based bata mining

W A N G Jie,ZHA N G Ji ng,ZHA N G Ji2sheng,ZEN G Zi2wei

(School of Computer Science and Engineering,Anshan University of Science and Technology,Anshan114044,China) Abstract:Data mining is motivated by the decision support problem faced by most large retail organizations. The concept and function of association rules were discussed.A application program to seek the large item set in association rules was presented.And the efficiency improving of linked2self operation was analyzed. The largest item set to be a item set was illuatrated as a exaple.

K ey w ords:data mining;association rules;minsupprot

(R eceived May8,2004) [上接第122页]

Study on inserting executable codes into PE f iles

HU S han

(School of Computer Science and Engineering,Anshan University of Science and Technology,Anshan114044,China) Abstract:According to the structure character of win32PE file,two ways were realized to insert code into PE file:(1)insert code at the space available of the node,(2)add new node.And some problems such as variable address reorientation when code was inserted and acquirement of API entrance address of changing dynamic when code returned were presented in the process of writing the added code.

K ey w ords:portable and executable file;virtual address;relative virtual address;raw address

(R eceived June20,2004)

操作系统课程设计-模拟文件系统

目录 第1章需求分析 (1) 第2章概要设计 (1) 系统的主要功能 (1) 系统模块功能结构 (1) 运行环境要求 (2) 数据结构设计 (2) 第3章详细设计 (3) 模块设计 (3) 算法流程图 (3) 第4章系统源代码 (4) 第5章系统测试及调试 (4) 运行结果及分析 (4) 系统测试结论 (5) 第6章总结与体会 (6) 第7章参考文献 (6) 附录 (7)

第1章需求分析 通过模拟文件系统的实现,深入理解操作系统中文件系统的理论知识, 加深对教材中的重要算法的理解。同时通过编程实现这些算法,更好地掌握操作系统的原理及实现方法,提高综合运用各专业课知识的能力;掌握操作系统结构、实现机理和各种典型算法,系统地了解操作系统的设计和实现思路,并了解操作系统的发展动向和趋势。 模拟二级文件管理系统的课程设计目的是通过研究Linux的文件系统结构,模拟设计一个简单的二级文件系统,第一级为主目录文件,第二级为用户文件。 第2章概要设计 系统的主要功能 1) 系统运行时根据输入的用户数目创建主目录 2) 能够实现下列命令: Login 用户登录 Create 建立文件 Read 读取文件 Write 写入文件 Delete 删除文件 Mkdir 建立目录 Cd 切换目录 Logout 退出登录 系统模块功能结构

运行环境要求 操作系统windows xp ,开发工具vc++ 数据结构设计 用户结构:账号与密码结构 typedef struct users { char name[8]; char pwd[10]; }users; 本系统有8个默认的用户名,前面是用户名,后面为密码,用户登陆时只要输入正确便可进入系统,否则提示失败要求重新输入。 users usrarray[8] = { "usr1","usr1", "usr2","usr2", "usr3","usr3", "usr4","usr4",

pe文件格式

PE文件格式详解(一)――基础知识 什么是PE文件格式: 我们知道所有文件都是一些连续(当然实际存储在磁盘上的时候不一定是连续的)的数据组织起来的,不同类型的文件肯定组织形式也各不相同;PE文件格式便是一种文件组织形式,它是32位Wind ow系统中的可执行文件EXE以及动态连接库文件DLL的组织形式。为什么我们双击一个EXE文件之后它就会被Window运行,而我们双击一个DOC文件就会被Word打开并显示其中的内容;这说明文件中肯定除了存在那些文件的主体内容(比如EXE文件中的代码,数据等,DOC文件中的文件内容等)之外还存在其他一些重要的信息。这些信息是给文件的使用者看的,比如说EXE文件的使用者就是Window,而DOC文件的使用者就是Word。Window可以根据这些信息知道把文件加载到地址空间的那个位置,知道从哪个地址开始执行;加载到内存后如何修正一些指令中的地址等等。那么PE文件中的这些重要信息都是由谁加入的呢?是由编译器和连接器完成的,针对不同的编译器和连接器通常会提供不同的选项让我们在编译和 联结生成PE文件的时候对其中的那些Window需要的信息进行设定;当然也可以按照默认的方式编译连接生成Window中默认的信息。例如:WindowNT默认的程序加载基址是0x40000;你可以在用VC连接生成EXE文件的时候使用选项更改这个地址值。在不同的操作系统中可执行文件的格式是不同的,比如在Linux上就有一种流行的ELF格式;当然它是由在Linux上的编译器和连接器生成的,

所以编译器、连接器是针对不同的CPU架构和不同的操作系统而涉及出来的。在嵌入式领域中我们经常提到交叉编译器一词,它的作用就是在一种平台下编译出能在另一个平台下运行的程序;例如,我们可以使用交叉编译器在跑Linux的X86机器上编译出能在Arm上运行的程序。 程序是如何运行起来的: 一个程序从编写出来到运行一共需要那些工具,他们都对程序作了些什么呢?里面都涉及哪些知识需要学习呢?先说工具:编辑器-》编译器-》连接器-》加载器;首先我们使用编辑器编辑源文件;然后使用编译器编译程目标文件OBJ,这里面涉及到编译原理的知识;连接器把OBJ文件和其他一些库文件和资源文件连接起来生成EXE文件,这里面涉及到不同的连接器的知识,连接器根据OS的需要生成EXE文件保存着磁盘上;当我们运行EXE文件的时候有W indow的加载器负责把EXE文件加载到线性地址空间,加载的时候便是根据上一节中说到的PE文件格式中的哪些重要信息。然后生成一个进程,如果进程中涉及到多个线程还要生成一个主线程;此后进程便开始运行;这里面涉及的东西很多,包括:PE文件格式的内容;内存管理(CPU内存管理的硬件环境以及在此基础上的OS内存管理方式);模块,进程,线程的知识;只有把这些都弄清楚之后才能比较清楚的了解这整个过程。下面就让我们先来学习PE文件格式吧。

linux下编译C语言

GCC 支持了许多不同的语言,包括C、C++、Ada、Fortran、Objective C,Perl、Python 和Ruby,甚至还有Java。 Linux 内核和许多其他自由软件以及开放源码应用程序都是用 C 语言编写并使用GCC 编译的。 编译C++程序: -c 只编译不连接 g++ file1 -c -o file1.o g++ file2 -c -o file2.o g++ file1.o file.o -o exec g++ -c a.cpp 编译 g++ -o a a.o 生成可执行文件 也可以g++ -o a a.cpp直接生成可执行文件。 1. 编译单个源文件 为了进行测试,你可以创建“Hello World”程序: #include #include int main(int argc, char **argv) { printf(“Hello world!n”); exit(0); } 使用如下命令编译并测试这个代码: # gcc -o hello hello.c

# ./hello Hello wordl! 在默认情况下产生的可执行程序名为a.out,但你通常可以通过gcc 的“-o”选项来指定自己的可执行程序名称。 2. 编译多个源文件 源文件message.c包含一个简单的消息打印函数: #include void goodbye_world(void) { printf(“Goodbye, world!n”); } 使用gcc的“-c”标记来编译支持库代码: # gcc -c message.c 这一过程的输出结果是一个名为message.o的文件,它包含适合连接到一个较大程序的已编译目标代码。 创建一个简单的示例程序,它包含一个调用goodbye_world的main函数 #include void goodbye_world(void): int main(int argc, char **argv) { goodbye_world(); exit(0); }

Linux 0.1.1文件系统的源码阅读

Linux 0.11文件系统的源码阅读总结 1.minix文件系统 对于linux 0.11内核的文件系统的开发,Linus主要参考了Andrew S.Tanenbaum 所写的《MINIX操作系统设计与实现》,使用的是其中的1.0版本的MINIX文件系统。而高速缓冲区的工作原理参见M.J.Bach的《UNIX操作系统设计》第三章内容。 通过对源代码的分析,我们可以将minix文件系统分为四个部分,如下如1-1。 ●高速缓冲区的管理程序。主要实现了对硬盘等块设备进行数据高速存取的函数。 ●文件系统的底层通用函数。包括文件索引节点的管理、磁盘数据块的分配和释放 以及文件名与i节点的转换算法。 ●有关对文件中的数据进行读写操作的函数。包括字符设备、块设备、管道、常规 文件的读写操作,由read_write.c函数进行总调度。 ●涉及到文件的系统调用接口的实现,这里主要涉及文件的打开、关闭、创建以及 文件目录等系统调用,分布在namei和inode等文件中。 图1-1 文件系统四部分之间关系图

1.1超级块 首先我们了解一下MINIX文件系统的组成,主要包括六部分。对于一个360K软盘,其各部分的分布如下图1-2所示: 图 1-2 建有MINIX文件系统的一个360K软盘中文件系统各部分的布局示意图 注释1:硬盘的一个扇区是512B,而文件系统的数据块正好是两个扇区。 注释2:引导块是计算机自动加电启动时可由ROM BIOS自动读入得执行代码和数据。 注释3:逻辑块一般是数据块的2幂次方倍数。MINIX文件系统的逻辑块和数据块同等大小 对于硬盘块设备,通常会划分几个分区,每个分区所存放的不同的文件系统。硬盘的第一个扇区是主引导扇区,其中存放着硬盘引导程序和分区表信息。分区表中得信息指明了硬盘上每个分区的类型、在硬盘中其实位置参数和结束位置参数以及占用的扇区总数。其结构如下图1-3所示。 图1-3 硬盘设备上的分区和文件系统 对于可以建立不同的多个文件系统的硬盘设备来说,minix文件系统引入超级块进行管理硬盘的文件系统结构信息。其结构如下图1-4所示。其中,s_ninodes表示设备上得i节点总数,s_nzones表示设备上的逻辑块为单位的总逻辑块数。s_imap_blocks 和s_zmap_blocks分别表示i节点位图和逻辑块位图所占用的磁盘块数。 s_firstdatazone表示设备上数据区开始处占用的第一个逻辑块块号。s_log_zone_size 是使用2为底的对数表示的每个逻辑块包含的磁盘块数。对于MINIX1.0文件系统该值为0,因此其逻辑块的大小就等于磁盘块大小。s_magic是文件系统魔幻数,用以指明文件系统的类型。对于MINIX1.0文件系统,它的魔幻数是0x137f。

PE格式基础及程序的装入

DOS MZ header部分是DOS时代遗留的产物,是PE文件的一个遗传基因,一个Win32程序如果在DOS下也是可以执行,只是提示:“This program cannot be run in DOS mode.”然后就结束执行,提示执行者,这个程序要在Win32系统下执行。 DOS stub 部分是DOS插桩代码,是DOS下的16位程序代码,只是为了显示上面的提示数据。这段代码是编译器在程序编译过程中自动添加的。 PE header 是真正的Win32程序的格式头部,其中包括了PE格式的各种信息,指导系统如何装载和执行此程序代码。 Section table部分是PE代码和数据的结构数据,指示装载系统代码段在哪里,数据段在哪里等。对于不同的PE文件,设计者可能要求该文件包括不同的数据的Section。所以有一个Section Table 作为索引。Section多少可以根据实际情况而不同。但至少要有一个Section。如果一个程序连代码都没有,那么他也不能称为可执行代码。在Section Table后,Section数目的多少是不定的。 二、程序的装入 当我们在explorer.exe(资源管理器)中双击某文件,执行一个可执行程序,系统会根据文件扩展名启动一个程序装载器,称之为Loader。Loader会首先检查DOS MZ Header,如果存在,就继续寻找PE header,如果这两项都不存在,就认为是DOS 16位代码,如果只存在DOS MZ Header,而其中又指示了而其中又指示了PE Header 的位置,那么Loader 就判定此文件不一个有效的PE文件,拒绝执行。 如果DOS Header 和PE Header都正常有效,那么Loader就会根据PE Header 及Section Table的指示,将相应的代码和数据映射到内存中,然后根据不同的Section进行数据的初始化,最后开始执行程序段代码。 三、PE格式高级分析 下面我们以一个真实的程序为例详细分析PE格式,分析PE格式最好有PE分析器,常用的软件是Lord PE,也有其它的分析工具和软件如PE Editor 、Stud PE等。 先分析一下磁盘文件的内容,这里我们使用UltraEdit32(UE)工具,这是一个实用的文件编辑器,可以编辑文本和二进制文件。

Linux下Makefile简单教程

目录 一:Makefile基本规则 1.1示例 1.2 隐式规则 1.3 伪目标 1.4 搜索源文件 二:变量 2.1使用变量定义变量值 2.2追加变量 三:条件判断 四:函数

Linux下Makefile总结 ——一步 MakeFile可以看做是一种简单的编程语言,其诞生的本质目的是实现自动化编译。 以Linux下gcc-c编译器为例,编译一个c语言程序需要经过以下几个步骤: 1.将c语言源程序预处理,生成.i文件; 2.预处理后的.i语言编译成汇编语言,生成.s文件; 3.汇编语言经过汇编,生成目标文件.o文件; 4.将各个模块的.o文件链接起来,生成一个可执行程序文件。 我们知道,在Visual C++6.0中,可以新建一个工程,在一个工程当中能够包含若干个c语言文件,则编译的时候直接编译整个工程便可。Linux下无法为多个c语言文件新建工程,但可以通过MakeFile实现它们的整合编译。 如上gcc-c编译步骤,如果使用Makefile则过程为: .C文件——>.o文件——>可执行文件 当然,Makefile中也加入了自己的设置变量方法与集成了一些函数,能够更有效地方便用户使用。 /**************************分隔符********************************/

一:Makefile基本规则 1.1示例 target ... : prerequisites ... command ... ... target也就是一个目标文件,可以是Object File,也可以是执行文件。prerequisites就是,要生成那个target所需要的文件或是目标。command也就是make需要执行的命令。(任意的Shell命令) 为了方便理解,我们来看一个示例: /*Makefile示例*/ edit : main.o kbd.o command.o display.o / insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o / insert.o search.o files.o utils.o main.o : main.c defs.h #生成main.o gcc -c main.c

PE文件头解析大全

PE可选头部 PE可执行文件中接下来的224个字节组成了PE可选头部。虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。OPTHDROFFSET宏可以获得指向可选头部的指针: PEFILE.H #define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \ ((PIMAGE_DOS_HEADER)a)->e_lfanew + \ SIZE_OF_NT_SIGNATURE + \ sizeof(IMAGE_FILE_HEADER))) 可选头部包含了很多关于可执行映像的重要信息,例如初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等等。IMAGE_OPTIONAL_HEADER结构如下: WINNT.H typedef struct _IMAGE_OPTIONAL_HEADER { // // 标准域 // USHORT Magic; UCHAR MajorLinkerVersion; UCHAR MinorLinkerVersion; ULONG SizeOfCode; ULONG SizeOfInitializedData; ULONG SizeOfUninitializedData; ULONG AddressOfEntryPoint; ULONG BaseOfCode; ULONG BaseOfData; // // NT附加域 // ULONG ImageBase; ULONG SectionAlignment;

ULONG FileAlignment; USHORT MajorOperatingSystemVersion; USHORT MinorOperatingSystemVersion; USHORT MajorImageVersion; USHORT MinorImageVersion; USHORT MajorSubsystemVersion; USHORT MinorSubsystemVersion; ULONG Reserved1; ULONG SizeOfImage; ULONG SizeOfHeaders; ULONG CheckSum; USHORT Subsystem; USHORT DllCharacteristics; ULONG SizeOfStackReserve; ULONG SizeOfStackCommit; ULONG SizeOfHeapReserve; ULONG SizeOfHeapCommit; ULONG LoaderFlags; ULONG NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; 如你所见,这个结构中所列出的域实在是冗长得过分。为了不让你对所有这些域感到厌烦,我会仅仅讨论有用的——就是说,对于探究PE文件格式而言有用的。 标准域 首先,请注意这个结构被划分为“标准域”和“NT附加域”。所谓标准域,就是和UNIX可执行文件的COFF 格式所公共的部分。虽然标准域保留了COFF中定义的名字,但是Windows NT仍然将它们用作了不同的目的——尽管换个名字更好一些。 ·Magic。我不知道这个域是干什么的,对于示例程序EXEVIEW.EXE示例程序而言,这个值是0x010B

怎样执行在Linux上运行应用程序

如何执行在Linux上运行的应用程序 关键字:Linux 先决条件 要充分理解本文,必须具备Windows 环境下桌面应用程序的工作经验,我认为读者对如何使用Linux 桌面有一个基本的了解。使用一个运行的Linux 计算来机探讨本文的概念和示例是很有帮助的。 概述 有时候第一次在Linux 上运行一个应用程序需要一点额外工作。有些应用程序,比如服务器服务,可能无法安装为服务,因此您需要从命令行启动这些应用程序。对于启动这些应用程序的用户帐户而言,需要在应用程序文件中设置执行许可标志(x)。 运行用户空间应用程序 Linux 在内核空间或用户空间运行进程。用户空间是操作系统的区域,应用程序通常在此运行。简单地说,每个用户帐户有其自己的用户空间,应用程序在这个领域内运行。 默认情况下,只有root 用户有权访问内核空间。root 用户是Linux 中的超级用户,相当于Windows 中的管理员帐户。在root 用户帐户下运行应用程序可能会引起安全风险,是不可取的。 很多服务器服务需要root 权限启动服务。然而,服务启动后,root 帐户通常会将其移至服务帐户。严格地说,Linux 中的服务帐户才是标准的用户帐户。主要区别是服务帐户仅用于运行一个服务,而不是为任何实际登录的用户准备的。 设置权限 您可以使用chmod 命令在一个文件中设置执行权限。在Linux 中,umask 设置通常用来防止下载的文件被执行,也有充分的理由相信,因为它有助于维护Linux 计算机的安全性。 大多数Linux 发行版具有一个值为022 的umask 设置,这意味着,默认情况下一个新文件权限设置为644.权限的数字表示形式采用读(4)、写(2)、执行(1) 的格式。因此,默认权限为644 的应用程序下载意味着文件所有者有读写权限,而组用户和其他用户只有读权限。 例如,为每个人赋予一个文件的执行权限,使用chmod a+x 命令。a 表示所有人,加号(+) 表示添加,而x 表示执行。同样地,如果应用程序是一个服务器服务,您应该确保只有授权帐户才有权执行此服务。 如果一个应用程序能够在标准用户帐户权限下运行,但只有特定组中的用户才需要使用它,您可以将该组所有者权限设置为可执行,然后将这些用户添加到该组中。 更具体地说,您可以在一个可执行文件中设置访问控制列表(ACL) 权限,赋予特定用户或组权限来运行该应用程序。使用setfacl 实用工具设置ACL 权限。 对于这些需要以root 用户启动进程的应用程序,比如服务器服务,您有几个选择。总结了允许用户执行需要root 权限的服务器服务的各种选项。 选项描述 作为root 用户不推荐用于服务器服务。当用户已经知道root 密码而且应用程序泄露不是首要关注问题时,可用于应用程序。 SetUID 由于安全问题,不推荐使用。SetUID 允许标准用户以另一个用户方式,比如root 用户,执行一个文件。 sudo 很常用,并且被认为是一个很好的实践。sudo 授予一个用户或组成员权限以执行可能额外需要root 权限的文件。该用户不需要知道root 密码。 带有文件权限的标准用户帐户在一个文件上为用户所有者、组所有者或其他人(所有人)

stm32sdiofatfs文件系统源码分析

、概述 1、目的 在移植之前,先将源代码大概的阅读一遍,主要是了解文件系统的结构、 各个函数的功能和接口、与移植相关的代码等等。 2、准备工作 在官方网站下载了0.07c 版本的源代码,利用记事本进行阅读。 二、源代码的结构 1、源代码组成 源代码压缩包解压后,共两个文件夹,doc是说明,src里就是代码。src文件夹里共五个文件和一个文件夹。文件夹是option,还有OOreadme.txt、 diskio.c、diskio.h、ff.c、ff.h、integer.h。对比网上的文章,版本已经不同了,已经没有所谓的tff.c 和tff.h 了,估计现在都采用条件编译解决这个问题了,当然文件更少,可能编译选项可能越复杂。 2、00readme.txt 的说明 Low level disk I/O module is not included in this archive because the FatFs module is only a generic file system layer and not depend on any specific storage device. You have to provide a low level disk I/O module that written to control your storage device .主要是说不包含底层10代码,这是个通用文 件系统可以在各种介质上使用。我们移植时针对具体存储设备提供底层代码。 接下来做了版权声明-可以自由使用和传播。 然后对版本的变迁做了说明。 3、源代码阅读次序

先读integer.h,了解所用的数据类型,然后是ff.h, 了解文件系统所用的数据结构和各种函数声明,然后是diskio.h,了解与介质相关的数据结构和操作函数。再把ff.c和diskio.c两个文件所实现的函数大致扫描一遍。最后根据用户应用层程序调用函数的次序仔细阅读相关代码。 三、源代码阅读 1、integer.h 头文件 这个文件主要是类型声明。以下是部分代码。 typedef intINT; typedef unsigned int UINT; typedef signed charCHAR;/* These types must be 8-bit integer */ 都是用typedef 做类型定义。移植时可以修改这部分代码,特别是某些定义与你所在工程的类型定义有冲突的时候。 2、ff.h 头文件 以下是部分代码的分析 #include “ intege使用i n teger.h 的类型定义 #ifndef _FATFS #define _FATFS 0x007版本号007c, 0.07c #define _WORD_ACCESS 0如//果定义为1,则可以使用word 访问。 中间有一些看着说明很容易弄清楚意思。这里就不例举了。 #define _CODE_PAGE 936 /* The _CODE_PAGE specifies the OEM code page to be used on the target system. /936 -Simplified Chinese GBK (DBCS, OEM, WindoW跟据这个中国应该是936.

文件系统结构分析

文件系统结构分析 1嵌入式文件系统 1.1嵌入式文件系统体系结构 在嵌入式系统中,文件系统是嵌入式系统的一个组成模块,它是作为系统的一个 可加载选项提供给用户,由用户决定是否需要加载它。同时,它还需要满足结构紧 凑、代码量小、支持多种存储设备、可伸缩、可剪裁、可移植等特点。基于上面的要 求,嵌入式文件系统在设计和实现时就要把它作为一个独立的模块来整体考虑。特别 是对文件系统内部资源的管理要做到独立性。 由于嵌入式文件系统是作为嵌入式系统的一个可选加载项提供给用户的,当 用户针对其应用的特殊要求对嵌入式系统进行配置时没有选择加载文件系统,但 是用户还是需要使用到系统I/O。由于这种情况的出现就决定了嵌入式系统中的文件 系统不再具有I/O设备的管理功能。系统I/O的管理和使用接口的提供将由 I/O管理 模块完成,文件系统作为一个独立的自包含模块存在。 基于以上考虑,嵌入式文件系统的体系结构如图1所示。 1卩 硬件 图1嵌入式文件系统体系结构 在嵌入式文件系统的最上层是文件系统 API。文件系统的一切功能都是通过这一层提供给用户的。同时,在整个文件系统中也只有这一层对用户是可见的。 在这一层中所提供的所有功能接口都将严格的遵循 POSIX标准。 文件系统核心层是实现文件系统主要功能的模块。在这一层中,文件系统要把

用户的功能操作转化成对文件系统的抽象对象的操作。这些操作将通过下面的功能模块最终落实到物理介质上面。如果文件系统需要支持多种具体的文件系统格式的话,这一层还可以进一步细分成虚拟文件系统和逻辑文件系统。 块高速缓存的存在是为了提高文件系统的性能。在这一层中缓存着以前访问过的块设备数据。文件系统通过一定的算法来高效的管理这些数据,以提高缓冲的性能。同时,它的存在使下层的数据操作对上层的文件操作透明,提高了文件系统的模块性。 1.2 嵌入式文件系统体系的功能与特点 文件系统是操作系统的重要组成部分,用于控制对存储设备的存取。它提供对文件和目录的分层组织形式、数据缓冲(对于实时系统,允许绕过缓冲)以及对文件存取权限的控制。 嵌入式系统所使用的文件系统除了要提供通用文件系统的功能外,还由于嵌入式操作系统的特殊性而具有其自身的一些特点。嵌入式文件系统的设计应该满足如下目标: 1.实现按名存取。和桌面操作系统类似,用户对文件的操作是通过其“文件名”来完成的。因此,用户只需知道待操作文件的文件名,就可以方便的访问数据,而不必关心文件在物理设备上是如何存放的,以及如何对文件的打开、关闭操作进行处理等细节。所有与文件相关的管理工作都由文件系统组件隐式完成。 2.与实时系统相适应。嵌入式应用大多数都具有实时性需求。实时系统不仅 要求计算结果地准确无误,而且要求特定的指令要在限定的时间内完成,这就对文件系统提出了很高的要求。在通用操作系统中,往往采取分页和虚拟存储器管理的机制来满足规定的指令时间。然而嵌入式实时操作系统一般都不具有虚拟存储器管理机制,且各种外部设备的性能差异较大,控制文件系统的实时性变得非常困难。为了尽可能提高文件系统的实时性,除了选取高速存储介质作为嵌入式系统的外设外,还应该根据设备的特点设置一定大小的高速缓冲,以提高数据存取的相应速度。 3.支持多任务环境。面对日益复杂的计算环境,应用常常采取“分而治之” 的方法,将解决方案划分为多个任务,每个任务完成相对单一的功能。实时操作系统的设计目标之一就是对多任务的支持。从应用的层面上看,多任务可以对文件进行并发读操作,在实时内核进程间同步与通信机制支持下进行写操作。此外,文件系统内部实现也应该具备较好的可重入性,即利用同步机制对全局数据结构 进行必要的保护。 4.支持多种逻辑文件系统标准。随着操作系统技术的发展,出现了多种成熟的桌面文件系统标准,如 Windows下的FAT系列,Linux中的ext系列等。将这些成熟标

PE文件结构

检验PE文件的有效性 <1>首先检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE,是则表示DOS MZ header有效 <2>一旦证明文件的Dos header 有效后,就可用e_lfanew来定位PE header <3>比较PE header 的第一个字的值是否等于IMAGE_NT_HEADER,如果前后两个值都匹配. PS.WinHex使用方法 1.Alt+G跳到指定位置 2.Ctrl+Shift+N放入新文件 3.大文件扩容,新建一个扩容大小+1的文件,把这个文件的数据复制后写入整个文件的尾地址. 4.文本搜索ctrl+F 5.十六进制搜索ctrl+alt+x 6.文本显示F7 7.打开内存alt+F9 8.进制转换器F8 9.分析选块F2 10.计算HASH ctrl+F2 11.收集文本信息ctrl+F10 12.编辑模式F6 一.IMAGE_DOS_HEADER <1>位置00H,WORD(2个字节)的e_magic为4D5A,即MZ <2>位置3CH,60,LONG(4个节节)的e_lfanew为64+112=176即B0H, 二.IMAGE_NT_HEADERS <1>位置B0H,DWORD(4个字节),PE开始标记,写入50450000,即PE <2>位置B4H,WORD,PE所要求的CPU,对于Intel平台,为4C01 <2>位置B6,WORD,PE中段总数,计划有3个段,.text代码段,.rdata只读数据段,.data全局变量数据段,所以值为0300, <3>位置C4,WORD,表示后面的PE文件可选头的占空间大小,即224字节(E0),值为E000 <4>位置C6,WORD,表示文件是EXE还是DLL,如果是可执行文件写0200,如果是dll,写0020, <5>位置C8,WORD,表示文件格式,如果是0B01表示.exe,如果是0701表示ROM映像

怎么在linux操作系统上安装可执行的软件

仅以RedHat Linux 为参照,包括但不限于其他版本的Linux都要遵循此方法,以下是说明: 先来看看Linux软件扩展名。软件后缀为.rpm最初是Red Hat Linux提供的一种包封装格式,现在许多Linux发行版本都使用;后缀为.deb是Debain Linux 提供的一种包封装格式;后缀为.tar.gz、tar.Z、tar.bz2或.tgz是使用Unix 系统打包工具tar打包的;后缀为.bin的一般是一些商业软件。通过扩展名可以了解软件格式,进而了解软件安装。 RPM格式软件包的安装 1.简介 几乎所有的Linux发行版本都使用某种形式的软件包管理安装、更新和卸载软件。与直接从源代码安装相比,软件包管理易于安装和卸载;易于更新已安装的软件包;易于保护配置文件;易于跟踪已安装文件。 RPM全称是Red Hat Package Manager(Red Hat包管理器)。RPM本质上就是一个包,包含可以立即在特定机器体系结构上安装和运行的Linux软件。RPM 示意图见图1。 大多数Linux RPM软件包的命名有一定的规律,它遵循名称-版本-修正版-类型-MYsoftware-1.2 -1.i386.rpm 。 2.安装RPM包软件 #rpm -ivh MYsoftware-1.2 -1.i386.rpm RPM命令主要参数: -i 安装软件。 -t 测试安装,不是真的安装。 -p 显示安装进度。 -f 忽略任何错误。 -U 升级安装。 -v 检测套件是否正确安装。 3.卸载软件 #rpm -e 软件名 需要说明的是,上面代码中使用的是软件名,而不是软件包名。例如,要卸载software-1.2.-1.i386.rpm这个包时,应执行: #rpm -e software 4.强行卸载RPM包

如何在linux中自由执行python程序

可执行的Python程序 这部分内容只对Linux/Unix用户适用,不过Windows用户可能也对程序的第一行比较好奇。首先我们需要通过chmod命令,给程序可执行的许可,然后运行程序。 chmod命令用来改变文件的模式,给系统中所有用户这个源文件的执行许可。然后我们可以直接通过指定源文件的位置来执行程序。我们使用./来指示程序位于当前目录。 为了更加有趣一些,你可以把你的文件名改成仅仅helloworld,然后运行./helloworld。这样,这个程序仍然可以工作,因为系统知道它必须用源文件第一行指定的那个解释器来运行程序。 只要知道程序的确切位置,你现在就可以运行程序了——但是如果你希望你的程序能够从各个位置运行呢?那样的话,你可以把你的程序保存在PATH环境变量中的目录之一。每当你运行任何程序,系统会查找列在PATH环境变量中的各个目录。然后运行那个程序。你只要简单地把这个源文件复制到PATH所列目录之一就可以使你的程序在任何位置都可用了。

我们能够用echo命令来显示PATH变量,用$给变量名加前缀以向shell 表示我们需要这个变量的值。我们看到/home/swaroop/bin 是PATH变量中的目录之一。swaroop是我的系统中使用的用户名。通常,在你的系统中也会有一个相似的目录。你也可以把你选择的目录添加到PATH变量中去——这可以通过运行PATH=$PATH:/home/swaroop/mydir完成,其中“/home/swaroop/mydir”是我想要添加到PATH变量中的目录。 当你想要在任何时间、任何地方运行你的程序的时候,这个方法十分有用。它就好像创造你自己的指令,如同cd或其他Linux终端或DOS提示符命令那样。

linux下编写c源程序并编译运行

姓名:雨田河南大学rjxy 班级:XXXX 实验二Linux基本操作 实验二Linux基本操作 编写c源程序并用编译运行 【需求】 ◆在当前目录下创建新文件t.c,用vi编辑器一段简单代码,代码要求在屏幕上输出 文字“Hello Linux!”; ◆用gcc编译t.c文件,并运行,查看输出结果,若结果错误,请根据提示修改;【系统及软件环境】 操作系统:Virtualbox,Fedora 13 【实验配置文件及命令】 1.配置文件: 2.命令:touch、rpm、gcc、./等

进入Linux操作系统,应用程序-> 系统工具-> 终端,输入命令:su 输入密码切换到root超级用户。 1.在当前目录建立一个新的目录test:$ mkdir test 在test目录下建立文件t.c :$touch t.c 3编辑程序源代码:vi t.c 首先按下键盘的“i”键,字符界面下方出现“insert”提示字符,此时输入以下代码: #include "stdio.h" int main() { printf("Hello Linux!\n"); return 0; } 4 保存退出:先按下“Esc”键,然后按下“shift”和“:”键,界面上出现冒号,然后输入“xq!”或者“x”对代码保存退出。 5 由于系统默认没有安装C语言编译程序,下面进行安装gcc 程序; 此处不再赘述,以下引用实验指导书: 1.gcc的安装 (1)查看gcc是否安装 rpm –q gcc (2)指定安装源 在“系统-分配光驱”里选择“Fedora-13-i386-DVD.iso” (3)查看安装源挂载位置 df命令,可查看到虚拟光驱挂载点 返回结果为:/media/Fedora 13 i386 DVD (4)使用安装源 安装的文件为RPM安装包,所在位置为安装光盘中的“Packages”目录下,可用“cd”命令进入此目录 cd /media/ Fedora 13 i386 DVD/Packages ★由于“Fedora 13 i386 DVD”名字中有空格,若直接输入,则会提示找不到此目录,可用“tab”键自动补全 【方法】cd /media/F)/P() 则可返回如下结果: cd /media/Fedora\ 13\ i386 \DVD\ /Packages (5)查看当前目录下是否有gcc安装包

基于c语言的文件系统FAT16操作源代码

文件: FAT.H //微控设计网原创 https://www.sodocs.net/doc/4418199469.html, 作者: debug版主typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long uint32; #pragma pack(1) typedef struct { uint8 BS_jmpBoot[3]; uint8 BS_OEMName[8]; uint16 BPB_BytesPerSec; uint8 BPB_SecPerClus; uint16 BPB_RsvdSecCnt; uint8 BPB_NumFATs; uint16 BPB_RootEntCnt; uint16 BPB_TotSec16; uint8 BPB_Media; uint16 BPB_FATSz16; uint16 BPB_SecPerTrk; uint16 BPB_NumHeads; uint32 BPB_HiddSec; uint32 BPB_TotSec32; uint8 BS_DrvNum; uint8 BS_Reservedl; uint8 BS_BootSig; uint32 BS_VolID; uint8 BS_VolLab[11]; uint8 BS_FilSysType[8]; uint8 ExecutableCode[448]; uint8 ExecutableMarker[2]; } FAT_BPB; typedef struct { uint8 NAME[8]; uint8 TYPE[3]; } FILE_NAME; typedef struct { uint16 Start; uint32 Size; } FILE_POSIT; typedef struct {

Linux文件系统与案例详解及源码导读

[文件系统]Linux文件系统—源代码导读 众所周知,文件系统是Unix系统最基本的资源。最初的Unix系统一般都只支持一种单一类型的文件系统,在这种情况下,文件系统的结构深入到整个系统内核中。而现在的系统大多都在系统内核和文件系统之间提供一个标准的接口,这样不同文件结构之间的数据可以十分方便地交换。Linux也在系统内核和文件系统之间提供了一种叫做VFS(virtual file system)的标准接口。 这样,文件系统的代码就分成了两部分:上层用于处理系统内核的各种表格和数据结构;而下层用来实现文件系统本身的函数,并通过VFS来调用。这些函数主要包括: * 管理缓冲区(buffer. c)。 * 响应系统调用fcntl() 和ioctl()(fcntl.c and ioctl.c)。 * 将管道和文件输入/输出映射到索引节点和缓冲区(fifo.c, pipe.c)。 * 锁定和不锁定文件和记录(locks.c)。 * 映射名字到索引节点(namei.c, open.c)。 * 实现select( )函数(select . c)。 * 提供各种信息(stat.c)。 * 挂接和卸载文件系统(super.c)。 * 调用可执行代码和转存核心(exec.c)。 * 装入各种二进制格式(bin_fmt*.c)。

VFS接口则由一系列相对高级的操作组成,这些操作由和文件系统无关的代码调用,并且由不同的文件系统执行。其中最主要的结构有inode_operations 和file_operations。file_system_type是系统内核中指向真正文件系统的结构。每挂接一次文件系统,都将使用file_system_type组成的数组。file_system_type组成的数组嵌入到了fs/filesystems.c中。相关文件系统的read_super函数负责填充super_block结构。 -------------------------------------------------------------------------------- 源码导读 *Linux 如何维护它支持的文件系统中的文件 *描述了虚拟文件系统(Virtual File System VFS ) *解释了Linux 核心中真实的文件系统如何被支持 Linux 的一个最重要的特点之一使它可以支持许多不同的文件系统。这让它非常灵活,可以和许多其他操作系统共存。在写作本章的时候,Linux 可一直支持15 种文件系统:ext 、ext2 、xia 、minix 、umsdos 、msdos 、vfat 、proc 、smb 、ncp 、

pe文件结构 入门 教程

三年前,我曾经写了一个手工打造可执行程序的文章,可是因为时间关系,我的那篇文章还是有很多模糊的地方,我一直惦记着什么时候再写一篇完美的,没想到一等就等了三年。因为各种原因直到三年后的今天我终于完成了它。现在把它分享给大家,希望大家批评指正。 我们这里将不依赖任何编译器,仅仅使用一个十六进制编辑器逐个字节的手工编写一个可执行程序。以这种方式讲解PE结构,通过这个过程读者可以学习PE结构中的PE头、节表以及导入表相关方面的知识。为了简单而又令所有学习程序开发的人感到亲切,我们将完成一个Hello World! 程序。功能仅仅是运行后弹出一个消息框,消息框的内容是Hello World!。 首先了解一下Win32可执行程序的大体结构,就是通常所说的PE结构。 如图1所示PE结构示意图: 图1 标准PE结构图 由图中可以看出PE结构分为几个部分: MS-DOS MZ 头部:所有PE文件必须以一个简单的DOS MZ 头开始。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ header 之后的DOS程序。以此达到对Dos系统的兼容。(通常情况DOS MZ header总共占用64byte)。 MS-DOS 实模式残余程序:实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显

示一个错误提示,大多数情况下它是由汇编编译器自动生成。通常,它简单调用中断21h,服务9来显示字符串"This program cannot run in DOS mode"。(在我们写的程序中,他不是必须的,可以不予以实现,但是要保留其大小,大小为112byte,为了简洁,可以使用00来填充。) PE文件标志:是PE文件结构的起始标志。(长度4byte, Windows程序此值必须为0x50450000) PE文件头:是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从DOS MZ header中找到PE header的起始偏移量,跳过了MS-DOS 实模式残余程序,直接定位到真正的文件头PE header,长度20byte。 PE文件可选头:虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。(长度 224byte )。 各段头部:又称节头部,一个Windows NT的应用程序典型地拥有9个预定义段(节),它们是“.text”、“.bss”、“.rdata”、“.data”、“.rsrc”、“.edata”、“.idata”、“.pdata”和“.debug”。一些应用程序不需要所有的这些段,同样还有些应用程序为了自己特殊的需要而定义了更多的段。(每个段头部占40byte,我们这里也不需要所有的段,仅需3个段。) 通常我们是将PE整个结构分成四个部分,把MS-DOS MZ 头部和MS-DOS 实模式残余程序作为第一部分,可以称他为DOS部分,而PE文件标志、PE文件头、PE文件可选头三个部分作为第二部分,称之为PE头部分,因为这部分才是Windows下真正需要的部分,所以从PE文件标志开始才是真正的PE部分。各段头部是第三部分,称之为节表。它详细描述了PE文件中各个节的详细信息。最后就是各个节的实体部分了,称为节数据。 以上仅仅是对PE结构各部分的大体讲解。接下来再手写这个Hello World!程序过程中,我将详细介绍每个部分的含义。 首先准备一下工具,一个十六进制编辑器足以。我们这里使用VC++ 6.0所携带的十六进制编辑器,您也可以使用如WinHex等十六进制编辑工具。 打开VC,选择文件,新建菜单项,然后选择一个二进制文件,单击确定。一切就绪了,下面就开始手写可执行程序,如图2所示:

相关主题