外壳执行 执行董事是什么意思思

程序运行流程——链接、装载及执行
程序运行流程——链接、装载及执行
在阅读完《深入理解计算机系统》第一章(计算机系统漫游)、第七章(链接)以及第十章(虚拟存储器)和《程序员的自我修养——链接、装载与库》后,历时悠久的梦想终于要实现了。开篇之初,首先提出一个迷惑了很久的一个问题:什么是虚拟存储器?它跟进程的虚拟地址空间有什么关系?
虚拟存储器是建立在主存--辅存物理结构基础上,有附加的硬件装置及操作系统存储管理软件组成的一种存储体系。&
顾名思义,虚拟存储器是虚拟的存储器,它其实是不存在的,而仅仅是由一些硬件和软件管理的一种“系统”。他提供了三个重要的能力:1,它将主存看成一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据(这里存在“交换空间”以及“页面调度”等概念),通过这种方式,高效地利用主存;2,它为每个进程提供了统一的地址空间(以虚拟地址编址),从而简化了存储器管理;3,操作系统会为每个进程提供独立的地址空间,从而保护了每个进程的地址空间不被其他进程破坏。&
虚拟存储器与虚拟地址空间是两个不同的概念:虚拟存储器是假想的存储器,而虚拟存储空间是假想的内存。它们之间的关系应该与主存储器与内存空间之间的关系类似。
链接部分:
链接就是将不同部分的代码和数据收集和组合成一个单一文件的过程,也就是把不同目标文件合并成最终可执行文件的过程。当然,务必知道:这个过程不涉及内存。链接可以分为三种情形:1,编译时链接,也就是我们常说的静态链接;2,装载时链接;3,运行时链接。装载时链接和运行时链接合称为动态链接。在此,我们的链接部分将主要讲述静态链接,而装载时链接我们放在装载部分讲,运行时链接忽略。
很多时候,从示例入手比较简单。我们写两个小程序a.c和b.c&&
[cpp] /*&&&&&a.c&&&&*/&&extern&int&&&int&main()&&{&&int&a&=&100;&&swap(&a,&shared);&&}&&
[cpp] /*&&&&&b.c&&&&*/&&&&int&shared&=&1;&&&&void&swap(int&*a,int&*b)&&&&{&&&&&&&*a&^=&*b&^=&*a&^=&*b;&&&&}&&&&
编译这两个文件得到“a.o”和“b.o”两个目标文件
?gcc&-c&a.c&b.c
从代码中可以看到三个符号:share,swap和main。
静态链接的整个过程分为两步:
第一步:空间和地址分配。扫描所有的输入目标文件,获得他们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。这样,连接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。
这里可能会有一个问题:建立了什么样的映射关系。看了下面图,你可能就会有所了解。映射关系就是指可执行文件与进程虚拟地址空间之间的映射。那么,这里程序还没有执行,更不会出现进程,哪里来的进程地址空间呢?此时虚拟存储器便发挥了很大的作用:虽然此时没有进程,但是每个进程的虚拟地址空间的格式都是一致的。所以,为可执行文件的每个段甚至每个符号符号分配地址也就不会有什么错了。注意:在链接之前,目标文件中的所有段的虚拟地址都是0,因为虚拟空间还没有被分配,默认都为0.等到链接之后,可执行文件中的各个段已经都被分配到了相应的虚拟地址。仍然看下图。。。
综上所述。链接后可执行文件中的各个段的虚拟地址都已经确定了。那么,各个符号的地址呢?因为各个符号在段中相对位置是固定的,所以这个时候“main”,“share”及“swap”的地址也都确定了。
其中,“main”位于“text”段的最开始处,偏移量为0,所以“main”这个符号在最终的输出文件中的地址应该是
0x&+&0&即0x;同理,“swap”的偏移量为0x34,
“swap”这个符号在最终的输出文件中的地址应该是
0x&+&0x34&即0x;“shared”相对于“data”的偏移量为0,
在最终的输出文件中的地址应该是
0x。这里可能会有点小小的疑问:shared怎么在.data段中呢?刚开始不是未初始化吗?是的,但是我们这里现在是已经合并好的段,它已经是合并后的文件格式状况,shared已经知道它的值为1了,仔细看上图。
如下表所示
第二步:符号解析与重定位
首先,符号解析。解析符号就是将每个符号引用与它输入的可重定位目标文件中的符号表中的一个确定的符号定义联系起来。若找不到,则出现编译时错误。&&&&&&&
解释一下什么是符号定义和什么是符号引用吧:须知,这样的区分是源于某一特定的模块而言。如上所示,对a.o而言,它里面的shared即为符号定义而b.o里面的shared为符号引用;相对地,
对b.o而言,它里面的shared即为符号定义而a.o里面的shared为符号引用;
其次,重定位。
不同的处理器指令对于地址的格式和方式都不一样。我们这里采用的是32位的x86处理器,介绍两种寻址方式。
X86基本重定位类型
重定位修正方法
绝对寻址修正S&+&A
R_386_PC32
相对寻址修正S&+&A&-&P
A:保存在被修正位置的值,对于32位cpu的话,采用
R_386_PC32寻址的话
它应该为0xFFFFFFFC即-4,它是代表地址的四个字节;而采用
R_386_32寻址,它应该为0.
P:被修正的位置。考虑以下程序
102b:&11&11&11
上述蓝色fc标记处即是被修正的位置,即0x1027.
S:符号的实际地址。也就是第一步中空间和地址分配时得到的符号虚拟地址。
举例来说吧!链接成的可执行文件中,假设main函数的虚拟地址为0x1000,swap函数的虚拟地址为0x2000;shared变量的虚拟地址为0x3000;
绝对地址修正:对shared变量的地址修正。
S:shared的实际地址为0x3000;
A:被修正位置的值,即0.
所以最后这个重定位修正地址为:0x3000,不变!
相对寻址修正:对符号“swap”进行修正。
S:符号swap的实际地址,即0x2000;
A:被修正位置的值,即0xFFFFFFFC(-4);
P:被修正位置,及0x1027
最后的重定位修正地址为:S&+&A&-P&=&0x2000&+(-4)-&0x1027&=&0xFD5.即修正后的程序为:
d5&0f&00&00
102b:&11&11&11
发现熟悉的规则了吗?下一条指令(PC)的地址为0x102b,加上这个修正值正好等于0x2000,
0x102b&+&0xFD5&=&0x2000,刚好是swap函数的地址。
以上内容没有涉及到c标准库,仅仅是自己实现的两个c语言程序之间的链接状况,也就是“程序里面的printf怎么处理”没有说明。这里,我们就要提及“静态库”的概念。其实一个静态库可以简单地看成一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。与静态库链接的过程是这样的:ld链接器自动查找全局符号表,找到那些为决议的符号,然后查出它们所在的目标文件,将这些目标文件从静态库中“解压”出来,最终将它们链接在一起成为一个可执行文件。也就是说只有少数几个库和目标文件被链接入了最终的可执行文件,而非所有的库一股脑地被链接进了可执行文件。
装载部分:
首先,小议一下动态链接。动态链接其实有分为装载时链接和运行时链接,在这里,我们只考虑装载时链接而不考虑运行时链接。
为什么要动态链接呢?
主要原因有两个:第一,考虑内存和磁盘空间。静态链接极大地浪费内存空间。因为在静态链接的情况下,假设有两个程序共享一个模块,那么在静态链接后输出的两个可执行文件中各有一个共享模块的副本。如果同时运行这两个可执行文件,那么这个共享模块将在磁盘和内存中都有两个副本,对磁盘和内存造成极大地浪费;第二,程序的更新。一旦程序中的一个模块被修改,那么整个程序都要重新链接、发布给用户。如果这个程序相当的大,那么后果就会更加严重!
动态链接做了什么?
务必知道,动态链接是相对于共享对象而言的。动态链接器将程序所需要的所有共享库装载到进程的地址空间,并且将程序汇总所有为决议的符号绑定到相应的动态链接库(共享库)中,并进行重定位工作。
下面开始说说装载。装载的方式主要有两种:覆盖装入和页映射。因为虚拟存储器的出现,覆盖装入已经被淘汰了。而页映射是虚拟存储机制的一部分,伴随着虚拟存储器的发明而诞生。具体的页映射可以参考《深入理解计算机系统》的第十章“虚拟存储器”。
以Linux内核装载ELF为例简述一下装载过程。当我们在Linux系统的bash下输入一个命令执行某个ELF程序时,在用户层面,bash进程会调用fork()系统调用创建一个新的进程,然后新的进程调用execve()来执行指定的ELF文件,原先的bash进程继续返回等待刚才启动时新进程结束,然后继续等待用户输入命令。这里需注意,随着一个新进程的出现,操作系统会为它创建一个独立的虚拟地址空间。
【创建虚拟地址空间】我们知道一个虚拟空间由一组映射函数将虚拟空间的各个页映射到相应的物理空间,那么创建一个虚拟空间实际上并不是创建空间而是创建映射函数所需要的数据结构。举例来说,在x86的Linux下创建虚拟地址空间实际上只是分配一个页目录(页表)就可以了,甚至不设置页映射关系,这些映射关系等到后面程序发生“缺页”时在进行设置。
在进入execve()系统调用之后,Linux内核就开始进行真正的装载工作。在内核中,execve()系统调用相应的入口是sys_execve(),作用:参数的检查复制;调用do_execve(),流程:查找被执行的文件,读取文件的前128个字节以判断文件的格式是elf还是其它;调用search_binary_handle(),流程:通过判断文件头部的魔数确定文件的格式,并且调用相应的装载处理程序。ELF可执行文件的装载处理过程叫load_elf_binary(),它的主要步骤如下:
1,检查ELF可执行文件格式的有效性,比如魔数、程序头表中段的数量。
2,寻找动态链接的“.interp”段,找到动态链接器的路径,以便于后面动态链接时会用上。
3,读取可执行文件的程序头,并且创建虚拟空间与可执行文件的映射关系。
【读取可执行文件的程序头,并且创建虚拟空间与可执行文件的映射关系】创建虚拟空间时的页映射关系函数是虚拟空间到物理内存的映射关系,而这一步所做的事虚拟空间与可执行文件的映射关系。我们知道,当程序发生缺页是,操作系统会为物理内存分配一个物理页,然后将该缺页从磁盘中读取到内存,在设置缺页的虚拟页与物理页之间的映射关系,这样程序才可以得以正常运行。但是明显的一点是,当操作系统捕获到缺页错误时,他应当知道程序当前需要的页在可执行文件中的哪一个位置。而这就是虚拟存储与可执行文件之间的映射关系。实际上,这种映射关系仅仅是保存在操作系统内部的一个数据结构。当发生缺页错误是,CPU将控制权交给操作系统,操作系统利用专门的缺页处理例程来查询这个数据结构(映射关系),然后找到所需页所在的虚拟内存区域,以及在可执行文件的偏移,然后把该页加载进物理内存,同时将该虚拟页与物理页之间建立映射关系,最后把控制权还给进程,进程从刚才缺页位置重新开始执行。
4,初始化ELF进程环境。
5,将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,对于静态链接的ELF可执行文件,它就是ELF文件的文件头中e_entry所指的地址;对于动态链接的ELF可执行文件,程序入口点就是动态链接器。
【将CPU指令寄存器设置成可执行文件的入口,启动运行】对动态链接来讲,此时就启动了动态链接器。
当load_elf_binary()执行完毕,返回至do_execve()在返回至sys_execve()时,系统调用的返回地址已经被改写成了被装载的ELF程序的入口地址了。所以,当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到ELF程序的入口地址。此时,ELF可执行文件装载完成。接下来就是动态链接器对程序进行动态链接了。
动态链接基本分为三步:先是启动动态链接器本身,然后装载所有需要的共享对象,最后重定位和初始化。
1,动态链接器自举
就我们所知道的,对普通的共享对象文件来说,它的重定位工作是由动态链接器来完成;它也可以依赖于其他共享对象,其中被依赖的共享对象由动态链接器负责链接和装载。那么,对于动态链接器本身呢,它也是一个共享对象,它的重定位工作由谁完成?它是否可以依赖于其他的共享对象文件?
动态链接器有其自身的特殊性:首先,动态链接器本身不可以依赖其他任何共享对象(人为控制);其次动态链接器本身所需要的全局和静态变量的重定位工作由它自身完成(自举代码)。
我们知道,在Linux下,动态链接器ld.so实际上也是一个共享对象,操作系统同样通过映射的方式将它加载到进程的地址空间中。操作系统在加载完动态链接器之后,就将控制权交给动态链接器。动态链接器入口地址即是自举代码的入口。动态链接器启动后,它的自举代码即开始执行。自举代码首先会找到它自己的GOT(全局偏移表,记录每个段的偏移位置)。而GOT的第一个入口保存的就是“.dynamic”段的偏移地址,由此找到动态链接器本身的“.dynamic”段。通过“.dynamic”段中的信息,自举代码便可以获得动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,然后将它们重定位。完成自举后,就可以自由地调用各种函数和全局变量。
2,装载共享对象
完成自举后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表当中,称之为“全局符号表”。然后链接器开始寻找可执行文件所依赖的共享对象:从“.dynamic”段中找到DT_NEEDED类型,它所指出的就是可执行文件所依赖的共享对象。由此,动态链接器可以列出可执行文件所依赖的所有共享对象,并将这些共享对象的名字放入到一个装载集合中。然后链接器开始从集合中取出一个所需要的共享对象的名字,找到相应的文件后打开该文件,读取相应的ELF文件头和“.dynamic”,然后将它相应的代码段和数据段映射到进程空间中。如果这个ELF共享对象还依赖于其他共享对象,那么将依赖的共享对象的名字放到装载集合中。如此循环,直到所有依赖的共享对象都被装载完成为止。
当一个新的共享对象被装载进来的时候,它的符号表会被合并到全局符号表中。所以当所有的共享对象都被装载进来的时候,全局符号表里面将包含动态链接器所需要的所有符号。
3,重定位和初始化
当上述两步完成以后,动态链接器开始重新遍历可执行文件和每个共享对象的重定位表,将表中每个需要重定位的位置进行修正,原理同前。
重定位完成以后,如果某个共享对象有“.init”段,那么动态链接器会执行“.init”段中的代码,用以实现共享对象特有的初始化过程。
此时,所有的共享对象都已经装载并链接完成了,动态链接器的任务也到此结束。同时装载链接部分也将告一段落!接下来便是程序的执行了。。。
对于写过c程序的人来说,一个公认的事实是:程序是从main函数开始的。然而,真的是
这样吗?其实不然,在程序执行到main函数之前,很多事情已经由入口函数(入口点)完成了。接下来将通过一个Linux下的可执行文件p来说明它的执行过程。
因为p不是一个内置的shell命令,所以shell会认为p是一个可执行文件,通过调用某个驻留在存储器中称为“加载器”的操作系统代码来为我们运行之。装载部分已经在上述部分详细描述了。装载完成后,控制权跳转到程序的入口点,也就是符号_start的地址,在_start地址处的启动代码如下
[cpp] 0x&&&_start&&&Call&_libc_init_first&&Call&_init&&Call&main&&Call&atexit&&Call&_exit&&/*&&&一下不会执行&&&*/&& &
首先从.init和.text节中调用初始化例程后,启动代码调用应用程序main程序,执行我们的c程序主体。在应用程序返回后,启动代码调用atexit注册的函数,然后调用_exit结束进程,将控制返回给操作系统。
一个典型的程序的运行步骤大致如下:
1,操作系统创建进程(装载了),将控制权交给程序的入口,即运行库的入口函数。
2,入口函数对运行库和程序运行环境进行初始化,包括堆、I/O、线程、全局变量构造等等。
3,入口函数在完成这些初始化之后,调用main函数,正式开始执行程序的主体部分。
4,main函数执行完成后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用来结束进程。
Hello程序的执行
&&& 初始时,外壳程序执行它的指令,等待我们输入一个命令。当我们在键盘上输入字符串“./hello”后,外壳程序将字符逐一读入寄存器,再把它存放到存储器中,如图1-5所示。
附录&Windows下面可执行文件a.exe的运行过程
发表评论:
TA的最新馆藏[转]&[转]&[转]&[转]&[转]&文档分类:
下载后只包含 1 个 DOC 格式的文档,没有任何的图纸或源代码,
下载前请先预览,预览内容跟原文是一样的,在线预览图片经过高度压缩,下载原文更清晰。
您的浏览器不支持进度条
淘豆网网友近日为您收集整理了关于外壳编程的文档,希望对您的工作和学习有所帮助。以下是文档介绍:ting变量中。如果没有双引号,bash和pdksh将产生语法错,而tcsh则将hello赋给变量greeting。.单引号的功能则最强。当你把字符串用单引号括起来时,外壳将忽视所有单引号中的特殊字符。例如,如果你想把登录时的用户名也包括在greeting变量中,应该使用下面的命令:greeting=&hellothere$LOGNAME&(在bash和pdksh环境下)setgreeting=&hellothere$LOGNAME&(在tcsh环境下)这将会把hellothereroot存储在变量greeting中,如果你是以root身份登录的话。但如果你在上面使用单引号,则单引号将会忽略$符号的真正作用,而把字符串hellothere$LOGNAME存储在greeting变量中。.使用反斜杠是第三种使特殊字符发生转义的方法。反斜杠的功能和单引号一样,只是反斜杠每次只能使一个字符发生转义,而不是使整个字符串发生转义。请看下面的例子:greeting=hello\there(在bash和pdksh环境下)setgreeting=hello\there(在tcsh环境下)在命令中,反斜杠使外壳忽略空格,从而将hellothere作为一个单词赋予变量greeting。当你想要将一个特殊的字符包含在一个字符串中时,反斜杠就会特别地有用。例如,你想把一盒磁盘的价格$5.00赋予变量disk_price,则使用如下的命令:disk_price=\$5.00(在bash和pdksh环境下)setdisk_price=\$5.00(在tcsh环境下)如果没有反斜杠,外壳就会试图寻找变量5,并把变量5的值赋给disk_price。5.3数值运算命令如果需要处理数值运算,我们可以使用expr命令,下面列出可以使用的数值运算符及其用法:exprexpression说明:expression是由字符串以及运算符所组成的,每个字符串或是运算符之间必须用空格隔开。下面列出了运算符的种类及功能,运算符的优先顺序以先后次序排列,可以利用小括号来改变运算的优先次序。其运算结果输出到标准输出设备上。:字符串比较。比较的方式是以两字符串的第一个字母开始,以第二个字符串的最后一个字母结束。如果相同,则输出第二个字串的字母个数,如果不同则返回0。*乘法/除法%取余数+加法-减法&小于38计计第二篇Linux高级语言及管理编程下载&=小于等于=等于!=不等于&=大于等于&大于&AND运算|OR运算注意当expression中含有*、(、)等符号时,必须在其前面加上\,以免被外壳解释成其他意义。例如:expr2\*\(3+4\)输出结果为14。test命令在bash和pdksh环境中,test命令用来测试条件表达式。其用法如下:testexpression或者[expression]test命令可以和多种系统运算符一起使用。这些运算符可以分为四类:整数运算符、字符串运算符、文件运算符和逻辑运算符。1)整数运算符int1-eqint2如果int1和int2相等,则返回真。int1-geint2如果int1大于等于int2,则返回真。int1-gtint2如果int1大于int2,则返回真。int1-leint2如果int1小于等于int2,则返回真。int1-ltint2如果int1小于int2,则返回真。int1-neint2如果int1不等于int2,则返回真。2)字符串运算符str1=str2如果str1和str2相同,则返回真。str1!=str2如果str1和str2不相同,则返回真。str如果str不为空,则返回真。-nstr如果str的长度大于零,则返回真。-zstr如果str的长度等于零,则返回真。3)文件运算符-dfilename如果filename为目录,则返回真。-ffilename如果filename为普通的文件,则返回真。-rfilename如果filename可读,则返回真。-sfilename如果filename的长度大于零,则返回真。-wfilename如果filename可写,则返回真。-xfilename如果filename可执行,则返回真。4)逻辑运算符!expr如果expr为假,则返回真。第5章计外壳编程计计39下载expr1-aexpr2如果expr1和expr2同时为真,则返回真。expr1-oexpr2如果expr1或expr2有一个为真,则返回真。tcsh中没有test命令,但它同样支持表达式。tcsh支持的表达式形式基本上和C语言一样。这些表达式大多数用在if和while命令中。tcsh表达式的运算符也分为整数运算符、字符串运算符、文件运算符和逻辑运算符四种。1)整数运算符int1&=int2如果int1小于等于int2,则返回真。int1&=int2如果int1大于等于int2,则返回真。int1&int2如果int1小于等于int2,则返回真。int1&int2如果int1大于int2,则返回真。2)字符串运算符str1==str2如果str1和str2相同,则返回真。str1!=str2如果str1和str2不相同,则返回真。3)文件运算符-rfile如果file可读,则返回真。-wfile如果file可写,则返回真。-xfile如果file可执行,则返回真。-efile如果file存在,则返回真。-ofile如果当前用户拥有file,则返回真。-zfile如果file长度为零,则返回真。-ffile如果file为普通文件,则返回真。-dfile如果file为目录,则返回真。4)逻辑运算符exp1||exp2如果exp1为真或exp2为真,则返回真。exp1&&exp2如果exp1和exp2同时为真,则返回真。!exp如果exp为假,则返回真。5.4条件表达式bash、pdksh和tcsh都有两种条件表达方法,即if表达式和case表达式。5.4.1if表达式bash、pdksh和tcsh都支持嵌套的if...then...else表达式。bash和pdksh的if表达式如下:if[expression]mandselif[expression2]mandsfielif和else在if表达式中均为可选部分。elif是elseif的缩写。只有在if表达式和任何在它之40计计第二篇Linux高级语言及管理编程下载前的elif表达式都为假时,才执行elif。fi关键字表示if表达式的结束。在tcsh中,if表达式有两种形式。第一种形式为:if(expression1)mandselseif(expression2)mandsendiftcsh的第二种形式是第一种形式的简写。它只执行一个命令,如果表达式为真,则执行,如果表达式为假,则不做任何事。其用法如下:if(mand下面是一个bash或pdksh环境下if表达式的例子。它用来查看在当前目录下是否存在一个叫.profile的文件:if[-f.profile]thenecho&Thereisa.prof2播放器加载中,请稍候...
该用户其他文档
下载所得到的文件列表外壳编程.doc
文档介绍:
第5章外壳编程在DOS中,你可能会从事一些例行的重复性命令,此时你会将这些重复性的命令写成批处理命令,只要执行这个批处理命令就等于执行这些命令。在Linux系统中也有类似的批处理命令,它的功能比起DOS的批处理命令更为强大,相对也较为复杂,已经和一般的高级语言不相上下。这些批处理命令在Linux中叫做外壳脚本(外壳Script)。外壳脚本是以文本方式储存的,而非二进制文件。所以外壳脚本必须在Linux系统的外壳下解释执行。不同外壳的脚本大多会有一些差异,所以我们不能将写给A外壳的脚本用B外壳执行。而在Linux系统中大家最常使用是Bourne外壳以及C外壳,所以本章结合这两个外壳的相同点和不同点来介绍外壳编程。5.1创建和运行外壳程序5.1.1创建外壳程序你可以用任何的编辑器编辑外壳程序,只需将要执行的外壳或Linux命令写入外壳程序即可。例如,假设你的系统在启动时挂接有一台CD-ROM驱动器,而你想更换驱动器中的CD,并读取其中的内容。一种方法是:首先把想要读取的CD放入CD-ROM驱动器中,然后使用unmount命令卸载CD-ROM驱动器,最后再使用mount命令挂接CD-ROM驱动器。命令如下:umount/dev/cdrommount-tiso9660/dev/cdrom/cdrom你可以创建一个包含这两个命令的文件名为remount的外壳程...
内容来自淘豆网转载请标明出处.如何使用外壳工具完成加密_百度知道
如何使用外壳工具完成加密
我有更好的答案
如何使用外 具完成加密 使用外 密工具,开发商可以在没有程序源 或不对程序源 做任何改动的情况下快捷地完成 加密工作,并保证有着较高的加密强度。若没有相应的硬件狗存在,加密后的程序将无法启动。 外 密工具的特点 1、 使用三种方式加密:外壳、嵌入、内外结合。 外 所谓外壳,是加密工具加到被加密程序的一部分程序。加密程序启动时外 先被执行。外壳 要检查硬件狗的存在性和一致性。如果对应的硬件狗存在,则进入原程序的入 否则终止程序。外壳只 在加密程序启动时执行一次。 嵌入 所谓嵌入,是加密工具在被加密程序中嵌入一个公共程序,加密程序在整个运行期内以一定的 频率执行这个公共程序,该程序检查硬件狗的存在性和一致性。如果对应的硬件狗存在,则继续执行原程 序,否则终止程序。本加密方式可选...
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁  记录外壳活动有很多好处,比如当需要监控用户的行为,回溯系统崩溃前的过程。实现这一功能的关键工具相当简单,它就是COM接口IShellExecuteHook。编写一个实现了这一接口的COM对象后,再在系统中注册,就可以容易地控制并影响Windows外壳的运行。Windows 98和Windows 2000都支持IShellExecuteHook外壳扩展,而在Windows 95和Windows NT 4.0上则必须安装活动桌面扩展后才支持(也就是说必须安装IE 4.01)。  一个实现了IShellExecuteHook接口的COM对象可以截获所有对ShellExecute和ShellExecuteEx函数的调用。ShellExecute和ShellExecuteEx函数主要用于执行应用程序,它们可以接收一个文件名并能自动获得同文件名相关的可执行文件名。此外,它们还支持系统安全认证。如果在NT上设定了用户的可执行权限,ShellExecute和ShellExecuteEx函数将会在创建新的进程前检查权限(CreateProcess和WinExec函数则没有这项功能)。函数调用的流程如下:  (1)获得将要运行的可执行文件名。  (2)根据程序名检查用户执行权限。  (3)激活全部已注册的IshellExecuteHook扩展。  (4)当所有扩展和权限都同意执行,创建新的进程并返回。  Windows外壳大量调用ShellExecute和ShellExecuteEx函数来执行几乎是所有的资源管理器的操作,比如双击目录、浏览文件夹内容、打印编辑文档、查看文件属性、选择文档的上下文相关菜单等等。此外,开始菜单的运行对话框和DOS方式下的Start.exe也使用ShellExecuteEx函数来执行程序。简单地说几乎用户的所有外壳操作都可以被扩展截获,包括其他应用程序对ShellExecute和ShellExecteEx的调用。   编写外壳活动记录器  首先需要创建一个进程内COM对象,选菜单命令New | ActiveX Library,然后点击菜单New|Com Object,创建COM对象框架,按图2.14填充对话框的内容,然后点击OK按钮。Delphi就会自动生成框架文件,并保存生成的文件。  IShellExecuteHook的接口定义在shlobj.pas单元中,添加shlobj到单元uses部分,然后添加IShellExecuteHooko方法原型到COM对象声明部分,声明部分代码如下: unit ShellExecuteHookOinterfaceusesWindows, ActiveX, ComObj, ShlObj, ShellAPI;type TTShellExecuteHook = class (TComObject, IShellExecuteHook)protected function Execute(var ShellExecuteInfo: TShellExecuteInfo): HRconst Class_TShellExecuteHook: TGUID = '{935FA400-243D-11D3-B06E-857B2AE2BE64}';  下面就是用来截获并记录外壳操作的实现部分,一旦外壳扩展被注册后,每次ShellExecute 和ShellExecuteEx函数运行时都会调用COM对象的Execute函数。我们的核心代码就是通过Execute方法实现的。方法定义如下:function TTShellExecuteHook.Execute(var ShellExecuteInfo: TShellExecuteInfo): HR  Execute方法会从外壳获得一个类型为TshellExecuteInfo的参数,参数定义如下: _SHELLEXECUTEINFOA = recordcbSize: DWORD;fMask: ULONG;Wnd: HWND;lpVerb: PAnsiClpFile: PAnsiClpParameters: PAnsiClpDirectory: PAnsiCnShow: IhInstApp: HINST;{ Optional fields }lpIDList: PlpClass: PAnsiChkeyClass: HKEY;dwHotKey: DWORD;hIcon: THhProcess: TH  这个记录结构中的lpFile包含了要运行的文件名,而lpVerb则表明执行的动作,动作由一些标准的字符串代表,比如,open(打开)、print(打印)、edit(编辑)、explore(浏览)、properties(属性)、find(查找)和其他上下文菜单的命令名。 有时,lpFile并不包含可执行文件名,这是因为ShellExecute接到的运行参数是一个文档名。比如当我们在资源管理器中双击文本文件时,Windows用文本文件名作为参数调用ShellExecute函数,而ShellExecute函数则获得同文本文件相关联的可执行文件名,然后执行。   TShellExecuteInfo结构中还记录了要运行程序的很多信息,然而这里我们只能在Execute方法中修改nCmdShow参数,nCmdShow参数定义了窗口在运行后的显示状态,包括最大化、最小化、正常等选项,对于其他参数的修改都会被外壳忽略。除此之外,在Execute方法中可以根据情况允许外壳继续缺省的任务或通知外壳取消执行,这可以通过Execute函数的返回值来实现。  如果Execute的返回值为S_FALSE,外壳就继续缺省的任务,如果返回S_OK,则外壳认为扩展已经成功,就不再继续执行了。另外如果返回一个错误代码或系统无法识别的值,则外壳会弹出错误信息。这给了我们一个控制程序运行的机会,比如可以限制任何对记事本的调用,代码如下:function TTShellExecuteHook.Execute(var ShellExecuteInfo: TShellExecuteInfo): HRvar FileName: Sbegin Result := S_FALSE; with ShellExecuteInfo do begin  FileName := UpperCase(ExtractFileName(lpFile));  if Pos('NOTEPAD', FileName) = 1 then  begin   Result := S_OK;   hInstApp := 32;   MessageBox(Wnd, '不允许记事本运行!', '错误', MB_OK or MB_ICONERROR);     进一步,我们甚至可以利用这点实现一个自定义的安全认证机制,根据用户要求限制运行的程序。有兴趣的朋友可以试验一下,一定很有意思。  有一点要注意的是,在Execute方法下不能调用ShellExecute和ShellExecuteEx函数外部程序,如果是这样的话,我们的Execute方法又会被新的ShellExecute调用,这样系统就会进入死循环。如果我们确实想在Execute方法中调用外部程序的话,可以使用CreateProcess或WinExec函数来替代。这两个函数不会被ShellExecuteHook截获。  对于外壳动作记录器来说,只要在Execute方法中记录程序信息到日志文件中就可以了,代码非常简单,因为所有需要的信息都在TShellExecuteInfo记录中包含了,这里只记录运行的动作、文件名和时间,需要记录其他信息的话,大家可自行修改,代码示意如下: function TTShellExecuteHook.Execute(var ShellExecuteInfo: TShellExecuteInfo): HRvar FileStream: TFileS a:TStringL S:begin Result := S_FALSE; with ShellExecuteInfo do begin  FileStream:=TFileStream.Create('c:/shellexecutehook.txt',fmopenwrite);  S:=string(lpVerb)+':'+string(lpFile)+DateTimeToStr(Now)+#13#10;  FileStream.Seek(FileStream.Size,soFromBeginning);  FileStream.Write(PChar(S)^,Length(S));  FileStream.F &< &
上一篇:& 下一篇:
51自学网 我要自学网视频教程,视频下载 自学网 自学编程网 CAD自学网 PS自学网 www.51zixue.net

我要回帖

更多关于 执行董事是什么意思 的文章

 

随机推荐