第007篇:腾讯管家反病毒工程师-电话二面

最后更新于:2022-04-01 07:39:42

我在1月8号的下午三点,接到了腾讯电脑管家反病毒工程师的电话二面。腾讯这次又是突然袭击,我丝毫没有准备。不过我上网看到有人竟然在晚上七点多还会接到腾讯的面试电话,这也就说明大家以后如果向腾讯投递了简历,那么就一定要时刻准备着。 这次的面试问题要比上一次更加的深入且全面,整个电话面试持续了22分钟。如果拿腾讯的面试问题与金山相比,最显著的区别在于,腾讯的这次面试是完全围绕着我的简历进行的,也就是说,我的简历上有什么,面试官就问什么。所以我们一定要把自己的优势完全体现在简历上,并且要对简历内容非常熟悉才可以。而金山主要是针对于基础知识的考察,几乎涵盖了安全领域的所有可问的问题。当然也有可能是因为我在投金山时,简历内容实在是乏善可陈,面试官觉得从我的简历中实在是找不出什么可问的。这里我贴出我向腾讯投的简历(部分),大家可以结合我的简历,看看面试官是如何提问题的。 **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a955cf9.jpg)** 图1 我的简历(部分) 由于我的简历基本是依据我的博客内容创建的,所以面试官向我提的问题,当时我都会回忆出博文内容,然后再做解答。只有个别的问题我的博文才没有涉及。接下来我会给出面试官提出的问题,之后我会加上我当时的解答,如果我的博文有涉及,我还会贴出博客地址,供想要完整答案的朋友方便进行查询。 **1、你的简历中提到你曾经逆向分析过游戏存档文件,请说说你是如何分析的。并讲讲你的注册码生成器的算法是如何得出的**。 其实这个问题与上一位面试我的面试官的第一个问题很是类似。我就回答说首先需要找到切入点,查找游戏中的金钱或者游戏点数,利用十六进制代码查看工具对存档文件进行搜索比对,包括人物属性也可以这样查找并修改。某些复杂的部分,比如法术,我还需要使用十六进制对比软件才可以。而注册机的编写,我需要利用IDA Pro来静态分析游戏的验证流程,同时也需要使用OD进行动态跟踪,查看数据的变化。 有兴趣的读者可参考: 《[逆向工程第001篇:解锁FIFA07传奇模式](http://blog.csdn.net/ioio_jy/article/details/26754257)》 《[逆向工程第002篇:打造自己的仙剑奇侠](http://blog.csdn.net/ioio_jy/article/details/28142093)》 《[逆向工程第005篇:跨越CM4验证机制的鸿沟(上)](http://blog.csdn.net/ioio_jy/article/details/40021179)》 《[逆向工程第006篇:跨越CM4验证机制的鸿沟(中)](http://blog.csdn.net/ioio_jy/article/details/41991005)》 《[逆向工程第007篇:跨越CM4验证机制的鸿沟(下)](http://blog.csdn.net/ioio_jy/article/details/41994047)》 **那么你所分析的存档文件是否经过加密?你分析过加密的存档文件吗?** 我答,我所分析的《FIFA》游戏和《仙剑奇侠传》游戏的存档文件都是没有加密的,我也没分析过加密的存档文件,不过我正打算分析一个。(我其实最开始分析的是PSP的战神游戏的存档,但是由于它可能是进行了加密处理,我水平有限,使得我的分析就搁浅了,以后应该会重启分析,并将分析过程写入“[逆向工程研究](http://blog.csdn.net/column/details/reversing.html)”系列中)。 **2、你学习这些知识有多长时间了?最初为什么学习这些?** 我答道,从我对这个领域什么都不知道到现在为止,大概是学了一年,而真正接触反病毒的知识,也就是这半年的时间。我最初念研究生的时候,对自己未来的方向并没有一个明确的认识,直到一个偶然的机会,看了“小甲鱼”的利用OD进行破解与PE文件结构的课程,觉得这个领域很有趣,就开始认真学习这方面的知识。 **3、请你说说免杀都有几种方式。**       面试官向我提这个问题,我心里一惊。虽说关于免杀的知识,我或多或少懂一些,但是毕竟久未接触,很多东西已经记不清了。而且我的博文中还未涉及到关于免杀的知识。于是我说,首先,加壳可以算是一种免杀,然后比如在代码中加入花指令也可以免杀,或者我们可以提取出特征码,进行修改并隐藏,也能够达到免杀效果。 **花指令都有哪些?** 我答,比如pushad与popad就算一种花指令,其它的一时想不起来。 (后来查书,其实我还是把要点都答出来了。对免杀有兴趣的朋友,我强烈推荐任晓辉的《黑客免杀攻防》,这本书可以说是免杀类书籍的独苗。由于免杀这个领域的知识很多,在此我不再列出标准答案。) **4、你对漏洞了解多少?** 我答,对于漏洞,我目前仅仅懂一些基本的原理,比如缓冲区溢出漏洞。我还没达到独立发现漏洞的水平。 **那你说说什么是缓冲区溢出漏洞。** 我说,比如有一条CALL语句,那么系统在执行这个CALL语句之前,会将下一条语句的地址压栈。那么如果我们能够找到栈中该地址的位置,就能够利用溢出的办法,将该地址修改为我们想要执行的语句的地址,别有用心的人就可以根据这个,让程序去执行恶意代码了。 有兴趣的读者可参考: 《[反病毒攻防研究第003篇:简单程序漏洞的利用](http://blog.csdn.net/ioio_jy/article/details/39012563)》 **5、你对内核了解多少?** 我说,关于内核,我目前仅仅是初学阶段,包括WinDbg,也是刚刚开始学习使用,目前只会利用WinDbg进行结构的解析。 **既然你是初学,那么我问你一个简单的内核问题,请你解释一下什么是“进程环境块”,也就是PEB。** 我答道,我曾经学过如何隐藏DLL,它就是利用了PEB的知识。我们首先需要根据PEB找到TEB,里面有三条链表,想要隐藏DLL,只需把DLL在这三条链表中脱链即可。 **那么这三条链表叫什么名字?** 我答,记不清了。DLL脱链后,就能够躲过一些安全类工具的监测,但是“冰刃”依旧可以检测到。 **“冰刃”采用的是什么原理来检测的?** 我答,这个我曾经看过一篇关于这个的文章,但是没看懂,也就不清楚。(通过查资料得知,“冰刃”在枚举进程模块时使用的是ZwQueryVirtualMemory,有兴趣的读者可参考看雪论坛jokersky的文章《[【原创】【屠夫科普】R3枚举目标进程加载模块【C++标准版】【Part.1】【Part.2】](http://bbs.pediy.com/showthread.php?t=125080)》) 有兴趣的读者可参考: 《[反病毒攻防研究第013篇:恶意程序隐藏初探——DLL隐藏](http://blog.csdn.net/ioio_jy/article/details/40264291)》 《[Windows内核学习笔记第002篇:本地调试TEB](http://blog.csdn.net/ioio_jy/article/details/41879149)》 **6、你的U盘防火墙是什么原理?** 我说,当我们的U盘插入计算机后,我们的系统就能够检测到U盘的存在,它就会发送一个消息,这样我们就可以检测中U盘中是否存在autorun.inf这个文件,如果存在,那么就对其内容进行解析。比如其中有open命令,那么就查看open后面的文件名是什么,之后删除U盘中以该文件名命名的文件。因为这个文件很可能就是病毒。这也就实现了防火墙的功能。 有兴趣的读者可参考: 《[安全类工具制作第002篇:U盘防火墙的制作](http://blog.csdn.net/ioio_jy/article/details/39509995)》 **7、你的系统行为监控是什么原理?** 我回答说,这个可以从两个方面来实现监控,一个是通过对敏感API函数的钩取进行监控,另外一个是有些API函数自身就是能够达到监控的效果,比如有一个函数(名字一时没想起来)就能够检测文件及文件夹的变动,可以通过创建线程来实现这个功能。 有兴趣的读者可参考: 《[安全类工具制作第007篇:行为监控工具的开发](http://blog.csdn.net/ioio_jy/article/details/40428785)》 《[反病毒攻防研究第014篇:监视恶意程序的复制与自删除](http://blog.csdn.net/ioio_jy/article/details/40376487)》 **8、你都分析过那些病毒?** 我说我分析过熊猫烧香、QQ盗号木马以及敲竹杠病毒,大概是三四个的样子。 **你说说熊猫烧香是怎么实现感染的。** 我说病毒的感染一般是有两种方式,一是将自身代码植入文件的缝隙,一是新建节区来添加恶意代码。熊猫烧香一方面是将代码复制到PE文件中,并修改资源实现图标的更改,一方面是将一段网址植入网页文件中,使得当我们执行那个网页文件,就能够跳到病毒作者想让我们访问的网站中。从而也就实现了感染。 **你说说你所分析过的“敲竹杠”病毒** “敲竹杠”病毒的原理其实就是通过篡改系统的密码,让受害用户去联系病毒作者,并花钱去买密码。像我最近分析了一个基于.NET的“敲竹杠”病毒,虽说很少见地采用了.NET框架,但是其实也是这个原理。这种.NET的程序,只要使用Reflector软件,就能够直接看到它的源代码,分析起来很简单。其实“敲竹杠”病毒分析起来基本都很简单。 有兴趣的读者可参考: 《[病毒木马查杀实战](http://blog.csdn.net/column/details/killvirus.html)》 面试官围绕我的简历的提问基本上就是这些,令我没有想到的是,他还向我提问了几个关于算法和数据结构的问题。尽管我也看过一些网友面试腾讯的经历,他们都说会提问这方面的知识,但是他们所面试的是软件开发类的职位,我没想到反病毒工程师的职位也要问这些。加上我这方面的基础很一般,尽管研究生入学考试,我们的专业课考的就是数据结构,但是毕竟许久未接触,很多都忘了,所以回答得不好,因此不再给出我当时的解答。这里我只列出问题,大家可以自行查找答案: **1、什么是链表?** **2、什么是二叉树?它是怎么实现的?** **3、查找都有哪几种方式?** **4、什么是二分法查找?** **5、什么是冒泡排序?** **6、各种排序算法的效率是怎样的?** 由于第一个关于链表的问题我就没回答好,所以面试官问我,不是考过了程序员和软件设计师吗,为什么不清楚链表呢?(又是依据我的简历来提问的)你编程不需要用链表吗?我说我主要编这几种程序,一是序列号生成器这样的程序,二是针对于病毒的某一特定行为来编程实现应对,三是某一特定病毒木马的专杀程序,这些程序基本是不需要使用链表的。 我的腾讯二面基本就是这样的一个流程,希望大家能够从中汲取自己所需的部分,多多讨论,多多交流。平时就多注重知识的积累,以及动手能力的培养。
';

第006篇:腾讯管家反病毒工程师-电话面试

最后更新于:2022-04-01 07:39:40

##一、前言 我在2015年的1月5号的中午,很荣幸地参加了腾讯管家反病毒工程师的电话面试。其实能够接到这次的面试让我有些惊讶。首先,能够给腾讯投简历,纯属机缘巧合。其次,简历是1月4号下午投的,竟然还没到24小时就接到了电话面试。最后,突然面试我,我也是吓了一跳,毕竟什么准备都没有,不像之前的金山,还给了我五个小时的准备时间。不过突然袭击也好,能够直接反映出一个人的真实水平。 接下来我想论述两个方面的内容,一个是我能够参加腾讯电话面试的背景,另一个是面试官问的技术问题。如果对背景不感兴趣的朋友,可以直接跳到第三部分进行查看。 ## 二、背景 我之所以说这是机缘巧合,是因为我已经在家闲了半年了,我自己内心很是着急,想找单位实习。毕竟我的同学要么也是去了各个不同的单位上班,要么是跟着导师做项目,就我自己什么也没干。也正是因为这样,我才能够有时间和精力来好好经营我的CSDN博客(**此为接下来事情发展的铺垫**)。其实CSDN是一个在计算机界中极为著名的网站,只不过像我这种只专注于病毒木马研究的人来说,并不那么受欢迎。虽说个人认为我的博文的访问量还不错,可是和大家的互动极少,我不知道我的文章究竟写得怎样,特别是在真正的反病毒工程师的眼中到底如何。于是,在去年12月30号的时候,无意之间,我发现我的浏览器的“九宫格”界面中保存着“[吾爱破解](http://www.52pojie.cn/)”论坛的网址,见下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a774ea3.jpg) 图1 我心想,不如注册一个账号吧,打算把我的一些博文搬过去。但是,想成为“[吾爱破解](http://www.52pojie.cn/)”的会员,要么需要花钱买,要么需要申请,申请的条件是将自己的原创文章发给论坛管理员审阅,如果管理员认为该文章确实是原创,并且有足够的技术含量,那么就会直接批准申请人成为正式会员(**这里也是接下来事情发展的铺垫**)。而审核者是论坛的创始人——Hmily。查看了一下之前一些网友的申请记录,我发现Hmily总能够将那些抄袭者揪出来,这一技能被很多网友所叹服,大家都认为能过H大这关很不容易。于是,我也就斗胆发了会员申请,并且附上了我的CSDN博客中,[逆向工程](http://blog.csdn.net/column/details/reversing.html)系列与[病毒木马查杀实战](http://blog.csdn.net/column/details/killvirus.html)系列博文的网址,静待审阅。 很快我就通过审核了,见下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a79ef27.jpg) 图2 Hmily还加了一句评语,这在以前的审核通过的通知中很是罕见。我顿时就有了一种被认同的感觉,此时我才第一次觉得,我的博文还是不错的,毕竟专业人士也是这么认为的,看来我的努力并没有白费。之后我就去论坛的“招聘求职”板块发了我的第一篇帖子——《[请大家帮我看看我是否能够胜任病毒分析或逆向分析的职位](http://www.52pojie.cn/thread-316423-1-1.html)》,并附上了我的个人简历: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a7efd2e.jpg) 图3 我主要是想听听大家的意见,看看我究竟能不能够胜任相关的工作。大家的跟帖也是五花八门,在此就不细说。而在这个版块,Hmily也曾发帖说,会为能力达标,有求职意向的网友投递简历,而我在此也是希望能够把握到这个机会。 之后,我在接下来的五天时间里面,慢慢地将我CSDN博客中[逆向工程](http://blog.csdn.net/column/details/reversing.html)系列与[病毒木马查杀实战](http://blog.csdn.net/column/details/killvirus.html)系列的博文,搬到了论坛中的相应版块(**接下来很重要**)。出乎我意料的是,大家对我的文章的热情空前高涨。一共13篇文章,截止到现在,在CSDN中的访问量为4357,但是在“[吾爱破解](http://www.52pojie.cn/)”中却疯长到了13843,而且这也仅仅是用了五天时间。其中[逆向工程](http://blog.csdn.net/column/details/reversing.html)系列的五篇文章全部置顶,[病毒木马查杀实战](http://blog.csdn.net/column/details/killvirus.html)系列的八篇文章全部成为“精华”,使得“[吾爱破解](http://www.52pojie.cn/)”论坛首页出现了这么一种“前无古人”的场面: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a865447.jpg) 图4 “精华采撷”板块中的十篇文章,有八篇都是我写的,如同我的个人专栏。然后,Hmily也知道我求职心切,决定帮我向腾讯与金山投递简历,这里我十分感激Hmily。接下来还应当感谢的是“病毒分析区”的版主willJ,向腾讯推荐了我。当天下午就收到了腾讯的邮件,说是正在对我的简历进行评估。 willJ推荐的职位是“MIG07-反病毒工程师(深圳)”,在腾讯的招聘页面,对这个职位的说明是: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a8e508f.jpg) 图5 至此,铺垫内容已经讲述完毕。总结来看,正是因为我的CSDN的博文,加上“[吾爱破解](http://www.52pojie.cn/)”论坛上的热心网友的帮忙,才促成了我参与腾讯电话面试的机会。 ## 三、电话面试内容 腾讯的面试官也是认真看过我的简历以及我在“[吾爱破解](http://www.52pojie.cn/)”论坛上发表的帖子的。因此他的第一个问题就是: **1、你曾经逆向分析过游戏的存档文件,你说说你是如何进行分析的。** 这个问题我再熟悉不过了,毕竟能够成功分析出[FIFA07](http://blog.csdn.net/ioio_jy/article/details/26754257)与[仙剑奇侠传](http://blog.csdn.net/ioio_jy/article/details/28142093)的存档文件,可以说是我这一切学习过程的开始。我回答的是,我的习惯是先找到一个切入点,比如游戏中的金钱与游戏点数,通过十六进制文件查看器进行寻找,进而修改,包括人物属性等也可以这样修改。想修改存档文件,我需要使用十六进制代码查看器与十六进制代码比较软件。 **2、你分析出这些后,编写过修改器吗?** 我答道,这个还真没做过。不过这个很简单,它就是基本的文件操作,调用几个API函数就可以了。 (其实我曾经在《[逆向工程第007篇:跨越CM4验证机制的鸿沟(下)](http://blog.csdn.net/ioio_jy/article/details/41994047)》中编写过一个读取“密码本”中相应内容的程序,其原理与修改器差不多。只不过修改器不单单需要读取文件内容,还需要修改文件内容。主要使用的无非是CreateFile、SetFilePointer、WriteFile以及ReadFile等API函数。当然,如果真要编写存档文件的修改器,我觉得先根据存档文件建立一个数据结构会比较好。) **3、你只分析过熊猫烧香病毒吗?** 我说,不是,我还分析过QQ盗号木马以及几个“敲竹杠”病毒,一共是三四个的样子吧。我分析病毒主要也是为了写博客,督促自己学习,也可以分享给大家。 **4、我看你的帖子发布得很频繁,为什么写得那么快呢?** 我说这是因为这些文章我最早是发布在我的CSDN博客上的,因为在CSDN上和大家的交流很少,我也不知道自己的文章究竟如何,所以我就把那些文章搬到“[吾爱破解](http://www.52pojie.cn/)”论坛了。其实正常来说,我需要一两天的时间才能写好一篇文章。 **5、病毒的知识你学了多久?** 其实这些知识我也就是在我上次电话面试金山失败后,才系统性的学习的,在那之前,都是漫无目的地看书,长进很小。所以我回答说大概半年。 **6、以后想研究哪些领域?** 我说想学学反病毒引擎的原理,手机病毒还有网络攻防等。 **7、想具体从事哪方面的工作?** 我说是病毒分析以及安全类软件的开发。 面试官基本上就是问了我这些问题,都很简短,毕竟整个面试也才持续大概八分钟,这与上次的三四十分钟形成了鲜明的对比。主要也是因为面试官通过我的简历以及我的博文,对我的水平也有了较为全面的认识,所以觉得也没有太多的问题想问的吧。接下来他允许我向他提问,我问的是: **1、请问您觉得我的文章怎么样?** 他说,你写的文章,我觉得,怎么说呢,很详细,让人很愿意读下去。 (腾讯面试官是第二位对我的文章持肯定态度的专业人士) **2、您觉得我能够胜任病毒分析的职位吗?** 他答当然可以。 **3、如果我能够入职,那么我实习期的待遇是怎样的?** 他说这个他并不清楚。 整个面试的过程大概也就是这样,他说接下来需要上报他的Leader再进行下一阶段。 ## 四、后记 可能读者会觉得我的背景介绍的篇幅要远多于面试的内容。其实这个我就是想说,坚持写技术博客是一件很重要的事情,特别对于像我这种没有参与过什么项目的闲人来说,更显重要。另外,机遇也是不能忽视的一个重要因素。希望大家能从中获得启发。
';

第005篇:猎豹移动反病毒工程师part5

最后更新于:2022-04-01 07:39:37

这是我当初接受面试的最后两个问题,当时,那位面试官问我对漏洞了解多少时,我说一点都不懂,问我懂不懂系统内核时,我同样只能说不知道。后来他跟我说,面试的考查重点不在于我所掌握的知识的广度,而是深度。这也是给后来人的经验。而在此,我非常感激那位面试官,也非常感激金山能够给予我那次宝贵的机会。 总结到了第五篇,也给我的首次金山面试的总结系列,画上了一个句号。虽说这是一次失败的经历,但由此我却懂得了很多,知道了未来努力的方向,否则我还会像一只没头的苍蝇一样到处乱撞。五篇的总结,令我获益匪浅,我觉得这十几个问题也是涵盖了类似职位的所有方面,即便是其他公司的安全类职位,只要不是涉及到网络的,应该就是这样了。这里我坚持从书中找答案,而不是从网上找,其原因就是书中更系统,往往也更权威。而网上网友的水平参差不齐,对于较深入的问题,我也很难判断孰对孰错,尽管网上的知识是比书中的要新的,但也是打下了扎实的功底之后,再去研究网上的最新技术,才能很快掌握。所以我在这里也呼吁大家支持正版书籍,买几本正版纸质书籍,真的不贵。 我现在的首要工作依旧是不断地提升自己的技术水平,多看多想勤动手,一定要打下坚实的基础。因为只要不出太大的意外,我依旧要再给金山投简历,最终目标就是要加入金山,成为一名出色的反病毒工程师。个人觉得对于反病毒工程师这个职位来说,除了弄清楚我所罗列的这十多个问题之外,最好再学学手动查杀病毒木马的技术,以及免杀技术。前者能够帮助自己对于病毒木马加深理解,有助于安全类软件的编写,后者有助于了解现今杀软的不足,从而多些思路进行改进。毕竟,攻与防的对立统一,是永恒的话题。 说到这最后的两个问题,由于过于复杂,知识点过多,我在此不打算像之前那样详细分析,而是仅仅列出参考书目,让大家自己去学习。只有把参考书目的内容弄懂了,才能说自己刚刚入门。而最后的这两个问题,可以说是建立在前十几个问题的基础之上的。当我刚开始接触漏洞知识的时候,突然觉得自己进入了一个崭新的世界,突然将之前的很多知识都串联起来了,我也希望大家也能有这样的感觉。而内核这块,需要学会使用WinDbg调试器,而这也是金山招聘的隐性要求。 那么下面就是最后的两个问题: **13、请简述你对于漏洞的理解。** 答:参考**《暗战亮剑——软件漏洞发掘与安全防范实战》**、**《0day安全:软件漏洞分析技术》、《黑客防线2009 缓冲区溢出攻击与防范专辑》**等。 **14、请简述你对系统内核的理解。** 答:参考**《寒江独钓——Windows内核安全编程》**、**《天书夜读——从汇编语言到Windows内核编程》**、**《Windows内核安全编程从入门到实践》**等。
';

第004篇:猎豹移动反病毒工程师part4

最后更新于:2022-04-01 07:39:35

这次所讨论的三个问题,比如DLL以及HOOK,很容易被病毒木马所利用,因此必须要比较全面地进行了解。而异常处理机制,则往往与漏洞相关联。它们自身的概念并不难理解,只是由之引申而来的问题,在计算机安全领域则是比较高级的技术,也是需要掌握的。尽管面试的时候,往往是理论性比较强,但是实际上最重要的还是动手能力。作为一名反病毒工程师(尽管我目前还不是),不单单要掌握恶意程序的编写技术,更需要清楚知道如何对付这些病毒木马。从这里也能够看出来,反病毒工程师的技术要求是比较高的,毕竟不单单要知道如何以正常手段使用计算机技术,更要知道如何以非正常手段编写出恶意程序,知己知彼,才能更好地与病毒木马作斗争。 问题10——问题12如下: **10、请简述你对动态链接库(DLL)的理解。** 答:(以下内容选自**《VC++深入详解》**第19.1节——动态链接库概述) 自从微软推出第一个版本的Windows操作系统以来,动态链接库(DLL)一直就是Windows操作系统的基础。动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他DLL调用来完成某项工作的函数。只有在其他模块调用动态链接库中的函数时,它才发挥作用。在实际编程时,我们可以把完成某种功能的函数放在一个动态链接库中,然后提供给其他程序调用。 WindowsAPI中所有的函数都包含在DLL,其中有3个最重要的DLL。 ▪Kernel32.dll 它包含那些用于管理内存、进程和线程的函数,例如CreateThread函数; ▪User32.dll 它包含那些用于执行用户界面任务(如窗口创建和消息的传送)的函数,例如CreateWindow函数; ▪GDI32.dll 它包含那些用于画图和显示文本的函数。 **知识扩展:** (以下内容选自**《C++黑客编程揭秘与防范》**第3.7.1节——DLL注入) 木马或者病毒编写质量的好坏取决于其隐藏程度,而不在于其功能多少。无论是病毒或是木马,它们都是可执行程序,如果它们是EXE文件的话,那么就必将会产生一个进程,产生进程就很容易被发现。为了不被发现,可以选择为DLL文件,DLL文件加载到进程的地址空间中,不会有进程名,因此其隐蔽性相对较好。DLL文件如果不被进程加载又如何在进程中呢?方法是强制让某进程加载DLL文件,这个强制的手段就是下面要介绍的通过创建远程线程将DLL注入到某个指定的进程中。 (以下内容选自**《Windows核心编程第5版》**第22.4节——使用远程线程来注入DLL) …… 从根本上说,DLL注入技术要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL。由于我们不能轻易地控制别人进程中的线程,因此这种方法要求我们在目标进程中创建一个新的线程。由于这个线程是我们自己创建的,因此我们可以对它执行的代码加以控制。幸运的是,Windows提供了如下所示的CreateRemoteThread函数,它使得在另一个进程中创建线程变得非常容易: …… 现在我们已经理解了我们要做什么,让我们来总结一下必须采取的步骤。 (1)用VirtualAllocEx函数在远程进程的地址空间中分配一块内存。 (2)用WriteProcessMemory函数把DLL的路径名复制到第1步分配的内存中。 (3)用GetProcAddress函数来得到LoadLibraryW或LoadLibraryA函数(在Kernel32.dll中)的实际地址。 (4)用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并在参数中传入第1步分配的内存地址。这时,DLL已经被注入到远程进程的地址空间中,DLL的DllMain函数会收到DLL_PROCESS_ATTACH通知并且可以执行我们想要执行的代码。当DllMain返回的时候,远程线程会从LoadLibraryW/A调用返回到BaseThreadStart函数(在第6章中介绍)。BaseThreadStart然后调用ExitThread,使远程线程终止。 现在远程进程中有一块内存,它是我们在第1步分配的,DLL也还在远程进程的地址空间中。为了对它们进行清理,我们需要在远程线程退出之后执行后续步骤。 (5)用VirtualFreeEx来释放第1步分配的内存。 (6)用GetProcAddress来得到FreeLibrary函数(在Kernel32.dll中)的实际地址。 (7)用CreateRemoteThread函数在远程进程中创建一个线程,让该线程调用FreeLibrary函数并在参数中传入远程DLL的HMODULE。 **11、请简述你对HOOK的理解。** 答:(以下内容选自**《VC++深入详解》**第20.1.1节) 首先回顾一下第一章中讲述的Windows消息传递机制。当在应用程序窗口中按下鼠标左键时,操作系统会感知到这一事件,然后产生鼠标左键按键消息,接着把此消息放到应用程序的消息队列中,应用程序通过调用GetMessage函数取出消息,然后调用DispatchMessage函数将这条消息调度给操作系统,操作系统会调用在设计窗口类时指定的应用程序窗口过程对这一消息进行处理。这一过程就是所有运行在Windows平台下的窗口应用程序的消息传递过程,如图20.1所示。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a7561e8.jpg) 图20.1 Windows应用程序的消息传递机制 在实际应用中,有时可能需要对某个特殊消息进行屏蔽,例如,我们开发了一个应用程序,不想让它对键盘上的回车键和空格键做出响应,就需要截获所有消息,然后进行判断,如果是回车或空格按键消息,就将这两种消息屏蔽掉,也就是说,不让这样的消息继续传递下去。另一种情况,例如,我们开发了一个安装程序,在安装过程中希望安装程序不能响应用户的鼠标和键盘的按键消息,以免影响软件的安装过程,那么也需要截获这两类消息,然后让它们不再继续向下传递。 为了实现这一功能,可以安装一个HOOK过程,称为钩子过程。操作系统在传递消息时,将我们感兴趣的消息先传递给HOOK过程,在此函数中进行检查,然后再决定是否放行该消息。这就好像逃犯在逃亡时可能会经过许多路段,为了抓住他,警察要在某些地方设置检查站,以便检查过往的车辆和行人。我们可以把车辆和行人看做是消息,检查站就是HOOK过程。如果在某个检查站发现了这个逃犯,就会把他抓起来。这样就相当于阻止了逃犯的逃亡过程,让他无法再继续逃亡下去了。这个道理和钩子过程是一样的,操作系统将我们感兴趣的消息都先交给钩子过程,后者实际上就是一个函数,在此函数中进行判断,如果是我们希望屏蔽掉的消息,那么就直接处理掉,不让它再继续向下传递。如果是其他我们不感兴趣的消息,就直接放弃对它们的处理。这就好像对于那些不是逃犯的行人和车辆一样,警察将会让他们继续前进。 (以下内容选自**《C++黑客编程揭秘与防范》**第5.1节——HOOK知识前奏) 在DOS时代进行编程,那时操作系统提供的编程接口不称为API函数,而称为中断服务向量。也就是说,当时的操作系统提供的编程接口只有中断,要进行写文件要调用系统中断,要进行读文件也要调用系统中断(当然,也不调用操作系统的中断直接调用更底层的中断)……中断服务向量类似于Windows下的API函数,中断服务向量在操作系统的某个地址保存着,它是以数组形式保存着的,我们也称其为中断向量表。DOS时代的HOOK技术也就是修改中断向量表中的中断地址。比如,要捕获写操作,那么就修改中断向量表中关于写文件的地址,将写文件的中断地址保存好,然后替换为我们的地址,这样当程序调用写文件中断时,我们的函数就被执行了;当程序执行完可以继续调用原来的中断地址,从而完成写文件的操作。 在Windows下HOOK技术的方法比较多,使用比较灵活,常见的应用层的HOOK方法有Inline Hook、IAT HOOK、Windows钩子……HOOK技术涉及到了DLL相关的知识。因为HOOK其他进程的时候需要访问其他进程的地址空间,使用DLL是必然的。HOOK技术也涉及到了注入的知识,想要把完成HOOK功能的DLL加载到目标进程空间中就要使用注入的知识了。让我们来学习常用的HOOK技术吧。 **知识扩展:** **(以下内容选自**《**C++黑客编程揭秘与防范》**第5.2节——内联钩子:InlineHook) API函数都保存在操作系统提供的DLL文件中,当在程序中使用某个API函数时,在运行程序后,程序会隐式地将API所在的DLL加载入进程中。这样,程序就会像调用自己的函数一样调用API …… 假设要对某进程的kernel32.dll的CreateFile()函数进行HOOK,首先需要在指定进程中的内存中找到CreateFile()函数的地址,然后修改CreateFile()函数的首地址的代码为jmp MyProc的指令。这样,当指定的进程调用CreateFile()函数时,就会首先跳转到我们的函数当中去执行,这样就完成了我们的HOOK了。 …… 下面来梳理一下InlineHook的流程吧。流程如下。 (1)构造跳转指令。 (2)在内存中找到欲HOOK函数地址,并保存欲HOOK位置处的前5个字节。 (3)将构造的跳转指令写入需HOOK的位置处。 (4)当被HOOK位置被执行时会转到我们的流程执行。 (5)如果要执行原来的流程,那么取消HOOK,也就是还原被修改的字节。 (6)执行原来的流程。 (7)继续HOOK住原来的位置。 这就是Inline Hook的大概的流程。 (以下内容选自**《C++黑客编程揭秘与防范》**第5.3.5节——IAT HOOK介绍) 在前面的内容中提到这样一个问题,在IMAGE_IMPORT_DESCRIPTOR中,有两个IMAGE_THUNK_DATA结构体,第一个为导入名字表,第二个为导入地址表(IAT)。两个结构体在文件当中是没有差别的,但是当PE文件被装载内存后,第二个IMAGE_THUNK_DATA的值会被修正,该值为一个RVA,该RVA加上映像基址后,虚拟地址就保存了真正的导入函数的入口地址。 在这个描述当中我们知道,要对IAT进行HOOK大概分为3个步骤,首先是获得要HOOK函数的地址,第二步是找到该函数所保存的IAT中的地址,最后一步是把IAT中的地址修改为HOOK函数的地址。这样就完成了IAT HOOK。也许这样的描述不是很清楚,那么下面就来举例说明一下。 比如要在IAT中HOOK系统模块kernel32.dll中的ReadFile()函数,那么首先是获得ReadFile()函数的地址,第二步是找到ReadFile()所保存的IAT地址,最后一步是把IAT中的ReadFile()函数的地址修改为HOOK函数的地址。 (以下内容选自**《C++黑客编程揭秘与防范》**第5.4.1节——钩子原理) Windows下的应用程序大部分是基于消息模式机制的,一些CUI的程序不是基于消息的。Windows下的应用程序都有一个消息函数,根据不同的消息来完成不同的功能。Windows操作系统提供的钩子机制的作用是用来截获、监视系统中的消息的。Windows操作系统提供了很多不同种类的钩子,不同的钩子可以处理不同的消息。 钩子分为局部钩子和全局钩子。局部钩子是针对一个线程的,而全局钩子则是针对整个操作系统内基于消息机制的应用程序的。全局钩子需要使用DLL文件,DLL文件里存放了钩子函数的代码。 在操作系统中安装了全局钩子以后,只要进程接收到可以发出钩子的消息后,全局钩子的DLL文件会被操作系统自动或强行地加载到该进程中。由此可见,设置消息钩子也是一种可以进行DLL注入的方法。 **12、请简述你对异常处理机制的理解。** 答:(以下内容选自**《Windows环境下32位汇编语言程序设计 典藏版》**第14.1节——异常处理的用途) Windows操作系统对异常的处理流程相对比较复杂,与DOS操作系统相比,最大的区别在于DOS的异常处理是被动的,一般仅用来处理操作系统内部的异常,对于其他层次的异常是无法处理的,比如,使用INT 21h去读盘的时候发生错误会激发INT 24h中断,但在BIOS服务程序级别用INT 13h去读盘时发生错误就不会激发INT 24h中断,对应用程序胡作非为引发的异常更是束手无策;而Windows的异常处理机制是依靠80x86处理器的保护机制来主动捕获异常,所以Win32下异常处理程序的用途不仅仅局限于防止程序被Windows野蛮地终止,合理利用它们可以让有些功能的实现方式变得更加简单,一般来说,可以在下面这些情况下使用异常处理程序。 ●用来处理非致命的错误 程序执行中发生某些异常时只需要终止发生异常的模块(或子程序),并没有必要终止整个程序的运行,这时可以在异常处理程序中指定让程序转移到一个“安全”的地方去执行,并在这里完成资源释放、删除临时文件、显示错误提示等扫尾工作后从出错模块返回。 ●处理“计划内”的异常 程序中的有些功能本来就是设计在异常处理模块中实现的。Windows系统中虚拟内存的实现就是一个绝好的例子,第10章中介绍的内存映射文件也是以同样的方法实现的。在这些情况下,“异常”是作为一个触发条件使用的。 另外,在WindowsAPI中使用异常处理程序进行参数的合法性检测也是很常见的。一般来说,大部分子程序都需要对输入的参数进行合法性检测,特别是对于指针类型的参数,但是当参数涉及的数据结构太复杂的时候,合法性检测会大大降低程序的效率,这时可以假定参数全部合法并尝试直接使用这些参数,如果异常处理程序没有捕获到错误,那么表示参数是合法的,这样要比在每个步骤中检测参数(或操作结果)的合法性要简洁得多。 ●处理致命错误 虽然捕获到致命错误的时候终止程序是最好的选择,但是程序在退出之前,可以在异常处理程序中进行释放资源、删除临时文件等操作,甚至可以详细记录产生异常的指令位置和环境,以便用来分析产生异常的原因。 (以下内容选自**《加密与解密 第三版》**第11.1节——基本概念) 所谓异常就是在应用程序的正常执行过程中发生的不正常事件。CPU引发的异常称为硬件异常,例如访问一个无效的内存地址。操作系统或应用程序引发的异常称为软件异常。 …… 除了CPU捕获一个事件并引发一个硬件异常外,在代码中也可以强制引发一个软件异常。只需调用RaiseException函数: ~~~ VOID RaiseException( DWORD dwExceptionCode, //标识所引发异常的代码 DWORD dwExceptionFlags, //异常继续是否执行的标识 DWORD nNumberOfArguments, //附加信息 CONST DWORD *lpArguments //附加信息 ); ~~~ 程序捕获软件异常的方法与捕获硬件异常的完全相同。 …… 首先来看看一个应用程序发生错误后,Windows是如何结合SEH机制进行处理的。 (1)因为有多种异常,系统首先判断异常是否应发送给目标程序,如果应该发送,并且目标程序正处于被调试状态,则系统挂起程序,填写如下结构: ~~~ typedef _EXCEPTION_DEBUG_INFO{ EXCEPTION_RECORD ExceptionRecord; DWORD dwFirstchance; }EXCEPTION_DEBUG_INFO; ~~~ 将成员dwFirstchance置为1,并向调试器发送EXCEPTION_DEBUG_EVENT消息。剩下的事情就由调试器全权负责了,调试器可能处理这个异常,也可能无法处理。 (2)如果调试器未能处理异常或程序根本没有被调试,系统就会查找是否存在与线程相关的异常处理过程,如果目标程序中存在与线程相关的异常处理程序,系统就调用程序的线程相关的SEH异常处理例程,交由其处理。 (3)与线程相关的异常处理过程可以有一个或多个,每个可以选择处理或者不处理异常,如果它不处理并且存在多个线程相关的异常处理过程,可交由链起来的其他异常处理过程进行处理,依此类推。 (4)如果程序线程的异常处理均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知调试器,这次EXCEPTION_DEBUG_INFO结构的dwFirstchance成员置为0。 (5)如果程序未处于被调试状态或者调试器仍然未能够处理,并且程序调用了API函数SetUnhandledExceptionFilter设置了与进程相关的异常处理过程的话,系统转向对它的调用。 (6)如果程序没有设置相关的异常处理过程或者进程相关的异常处理过程也未能处理这个异常,系统会调用默认的系统异常处理程序,通常显示一个对话框,可以选择“确定”或者最后将其附加到调试器上的“取消”按钮。如果没有调试器能被附加于其上或调试器还是处理不了异常,系统就调用ExitProcess终结程序。 (7)不过在终结之前,系统再次调用发生异常的线程中所有的异常处理过程,这是线程异常处理过程获得的最后清理未释放资源的机会,其后程序就终结了。 学习SEH要树立一个最基本的概念:SEH是系统发现异常或错误时,在终结应用程序之前给应用程序的一个最后改正错误的机会,从程序设计的角度来说,就是系统在终结程序之前给程序的一个执行其预设定的回调函数的机会。 …… 一般地,按作用域(即其监视范围)分,SHE可分为两类:一类是监视某线程中某段代码是否发生异常的异常处理过程,一般称为线程相关的异常处理过程,或称为Per_Thread类型的异常处理过程,有时也简称为线程异常处理。另一类是监视整个进程中所有线程是否发生异常的异常处理过程,称为进程相关的异常处理过程,或称为“Final”型异常处理过程。有人也称之为筛选器,源于其对应于设置其的API函数SetUnhandledExceptionFilter中的Filter一词,在Win32 API文档中,称之为顶层(top-level)异常处理。 **本篇文章参考资料:** 1、孙鑫、余安萍,**《VC++深入详解》**,电子工业出版社。 2、冀云,**《C++黑客编程揭秘与防范》**,人民邮电出版社。 3、[美]Jeffrey Richter、[法]Christophe Nasarre(著),葛子昂、周靖、廖敏(译),**《Windows核心编程(第5版)》**,清华大学出版社。 4、段钢(主编),Blowfish、沈晓斌、丁益青、单海波、王勇、赵勇、唐植明、softworm、afanty、李江涛、林子深、印豪、冯典、罗翼、林小华、郭春杨(编委),**《加密与解密(第三版)》**,电子工业出版社。
';

第003篇:猎豹移动反病毒工程师part3

最后更新于:2022-04-01 07:39:33

从现在开始,面试的问题渐渐深入。这次的三个问题,都是对PE格式的不断深入的提问。从最初的概念,到病毒对PE格式的利用,再到最后的壳的问题。这里需要说明的是,由于壳是一个比较复杂的概念,面试中也仅仅只能对原理性的东西进行提问,但是实际上,是要求面试者能够熟练掌握脱壳乃至写壳的技术的。我这里并没有对此进行特别详细的阐述,所以大家如果想深入理解,还是参考相应的资料,并加强实际的动手能力比较好。 以下是问题7——问题9: **7、请说明PE文件由几部分构成,并解释什么是导入表、导出表以及附加数据。** 答:(以下内容选自**《Windows PE权威指南》**第3.3.3节——程序员眼中的PE结构) 在程序员眼中,PE文件格式是由许多数据结构组成的,数据结构是一系列有组织的数据的集合,如图3-9所示。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a6d639f.jpg) 图3-9 程序员眼中的PE结构 如图所示,一个标准的PE文件一般由四大部分组成: □DOS头 □PE头(IMAGE_NT_HEADERS) □节表(多个IMAGE_SECTION_HEADER结构) □节内容 其中,PE头的数据结构最为复杂。简单来说,PE头包含: □4个字节的标识符号(Signature) □20个字节的基本头信息(IMAGE_FILE_HEADER) □216个字节的扩展头信息(IMAGE_OPTIONAL_HEADER32) 说明:如果按照“头部+身体”的信息组织方式来看: PE文件头部 = DOS头 + PE头 + 节表 PE文件身体 = 节内容 节内容中会出现各种不同的数据结构,如导入表、导出表、资源表、重定位表等,关于这些数据的组织方式会在后面的章节中陆续接触到。 (以下内容选自**《Windows环境下32位汇编语言程序设计 典藏版》**第17.2.1节——导入表简介) 在Win32编程中常常用到“导入函数”(Import functions),导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中,在调用者程序中只保留一些函数信息,包括函数名及其驻留的DLL名等。 对于存储在磁盘上的PE文件来说,它无法得知这些导入函数会在内存的哪个地方出现,只有当PE文件被装入内存,Windows装载器才将DLL装入,并将调用导入函数的指令和函数实际所处的地址联系起来,这就是“动态链接”的概念。动态链接是通过PE文件中定义的“导入表”(Import Table)来完成的,导入表中保存的正是函数名和其驻留的DLL名等动态链接所必需的信息。 (以下内容选自**《Windows环境下32位汇编语言程序设计 典藏版》**第17.3节——导出表) 当PE文件被执行的时候,Windows装载器将文件装入内存并将导入表中登记的DLL文件一并装入,再根据DLL文件中的函数导出信息对被执行文件的IAT表进行修正。在这些包含导出函数的DLL文件中,导出信息被保存在导出表中,通过导出表,DLL文件向系统提供导出函数的名称、序号和入口地址等信息,以便Windows装载器通过这些信息来完成动态链接的过程。 扩展名为.exe的PE文件中一般不存在导出表,而大部分的.dll文件中都包含导出表,但是这并不是必然的,比如,用作纯资源的.dll文件就不提供导出函数,文件中也就不存在导出表;另外,偶尔也可以见到包含导出函数和导出表的.exe文件。 (以下内容选自**《加密与解密 第三版》**第13.6节——附加数据) 某些特殊的PE文件在各个区块的正式数据之后还有一些数据,这些数据不属于任何区块。由于PE文件被映射到内存是按区块映射的,因此这些数据是不能被映射到内存中的,这些额外的数据称为附加数据(overlay)。 附加数据的起点可以认为是最后一个区块的末尾,终点是文件末尾。用LordPE查看实例overlay.exe的区块,如图13.54所示。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a6f2e89.jpg) 图13.54 查看区块信息 从图13.54可以计算出最后一个区块末尾的文件偏移值为3200h+600h=3800h。用十六进制工具打开目标文件,跳到3800h,会发现后面还有一段数据,这就是附加数据,如图13.55所示。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a714a50.jpg) 图13.55 附加数据 用PEiD分析实例overlay.exe,会给出结果“Nothing found [Overlay]*”,其中Overlay就表明有附加数据的存在。带有附加数据的文件脱壳时,必须将附加数据粘贴回去,如果文件有访问附加数据的指针,也要修正。 本节实例overlay.exe实际是用UPX加壳了,由于附加数据的存在,干扰了PEiD分析。用OllyDbg打开实例,来到OEP处。 ~~~ 00401436 55 push ebp 00401437 8BEC mov ebp,esp 00401439 6AFF push -1 ~~~ 此时,抓取内存映像保存到磁盘中,然后用ImportREC重建输入表,最终文件为dumped_.exe。 运行实例原文件,然后单击菜单“File/Open”,程序将会读取附加数据并在编辑框中显示出来,如图13.56所示。而运行脱壳后的文件dumped_.exe,不能将原来的文字显示出来,如图13.57所示。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a72d4b9.jpg) 图13.56 读取附加数据 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c428a7457d6.jpg) 图13.57 脱壳后读取附加数据 由于附加数据没有被映射到内存里,因此抓取的映像文件里也没有附加数据。现在将原文件的附加数据移到脱壳后的文件里。用十六进制工具打开overlay.exe,将3800h后的附加数据追加到dumped_.exe文件末尾E000h处。 运行已有附加数据的dumped_.exe,但执行“File/Open”仍不能正确读取数据。用OllyDbg分析一下实例是如何读取自身附加数据的。用CreateFileA设断,执行“File/Open”功能后,会中断到这段代码处。 ~~~ 00401040 push ecx 00401041 push 0 00401043 call dword ptr [<GetModuleFileNameA>] ;取自身文件名 00401049 push 0 …… 00401062 call dword ptr [<&CreateFileA>] ;打开自身 00401068 mov dword ptr [ebp-11C],eax …… 004010DC push 0 004010DE push 0 004010E0 push 3800 ;注意这个值 004010E5 mov edx,dword ptr [ebp-11C] 004010EB push edx 004010EC call dword ptr [<SetFilePointer>] ;移动读写指针 …… 0040110E call dword ptr [<&ReadFile>] 00401127 mov ecx,dword ptr [ebp-108] 0040112D push ecx 0040112E mov edx,dword ptr [ebp-10C] 00401134 push edx 00401135 call dword ptr [<SetWindowTextA>] ;将附加数据显示到文本框里 ~~~ 用CreateFileA打开一个文件后,文件指针默认是指向文件的第一个字节的。程序用SetFilePointer设置指针,指向附加数据,然后用ReadFile将附加数据读取出来。这里SetFilePointer函数比较关键,其原型如下: ~~~ DWORD SetFilePointer( HANDLE hFile, //文件句柄 LONG lDistanceToMove, //移动的距离,这个是低32位 PLONG lpDistanceToMoveHigh, //移动的距离,这个是高32位 DWORD dwMoveMethod //移动方式 ); ~~~ 由于脱壳后,文件大小发生变化,追加后的附加数据地址已改变,此处变为E000h,因此需要修正SetFilePointer的参数,将其指向附加数据。 ~~~ 004010DC push 0 004010DE push 0 004010E0 push 0E000 ;将此处指向附加数据E000h 004010E5 mov edx,dword ptr [ebp-11C] 004010EB push edx 004010EC call dword ptr [<SetFilePointer>] ~~~ 也就是说,对于带有附加数据的程序,抓取内存映像后,必须将附加数据追加到脱壳文件的最后,同时修正读取附加数据的相应指针。 **知识扩展:** (以下内容选自**《加密与解密 第三版》**第10.15.1节——文件格式检查) 文件格式可以通过PEheader开始的标志Signature来检测。也许读者会说,检测DOS Header的Magic Mark不是也可以检测此PE文件是否合法吗?这个想法没有错,但是检测Magic Mark不一定能确定就是PE文件,如果某文本文件正好在开始就是“MZ”字符串,就会误判断。 (1)判断文件开始的第一个字段是否为IMAGE_DOS_SIGNATURE,即5A4Dh。 (2)再通过e_lfanew找到IMAGE_NT_HEADERS,判断Signature字段的值是否为IMAGE_NT_SIGNATURE,即00004550h,如果是IMAGE_NT_SIGNATURE,就可以认为该文件是PE格式。 具体实现的代码如下: ~~~ BOOL IsPEFile(LPVOID ImageBase) { PIMAGE_DOS_HEADER pDH=NULL; PIMAGE_NT_HEADERS pNtH=NULL; if(!ImageBase) //判断映像基址 return FALSE; pDH=(PIMAGE_DOS_HEADER)ImageBase; if(pDH->e_magic!=IMAGE_DOS_SIGNATURE) //判断是否为MZ return FALSE; pNtH=(PIMAGE_NT_HEADERS32)((DWORD)pDH+pDH->e_lfanew); if(pNtH->Signature!=IMAGE_NT_SIGNATURE) //判断是否为PE格式 return FALSE return TRUE; } ~~~ (以下内容选自**《C++黑客编程揭秘与防范》**第4.2.7节——3种地址的转换) 某数据的文件偏移=该数据所在节的起始文件偏移+(某数据的RVA-该数据所在节的起始RVA)。 除了上面的计算方法以外,还有一种计算方法,把节的起始RVA的值减去节的起始文件偏移值,得到一个差值。然后再用RVA减去这个得到的差值就可以得到其所对应的FileOffset了。 **8、请解释一下病毒一般如何感染PE文件。** 答:(以下内容选自**《C++黑客编程揭秘与防范》**第4.6节——添加节区) 添加节区在很多场合都会用到,比如在加壳中,在免杀中都会经常使用到对PE文件添加一个节区。添加一个节区的方法有4个步骤,第1个步骤是在节表的最后面添加一个IMAGE_SECTION_HEADER,第2个步骤是更新IMAGE_FILE_HEADER中的NumberOfSections字段,第3步是更新IMAGE_OPTIONAL_HEADER中的SizeOfImage字段,最后一步是添加文件的数据。当然了,前3个步骤是没有先后顺序的,但是最后一个步骤一定要明确如何改变。 (以下内容选自**《C++黑客编程揭秘与防范》**第6.2节——简单病毒剖析) 大部分病毒都有感染的功能,病毒会把自身当中的或者需要其他程序来完成的指定功能的代码感染给其他的正常文件。就像人类的流行感冒,办公室中只要有一个人携带感冒病毒,就有可能所有人都会被传染。如果没被传染就说明已经预防过了,因此在机器上安装杀毒软件还是非常有必要的。 前面说了,病毒要感染其他文件也就是把病毒本身的攻击代码或者病毒期望其他程序要完成的功能代码写入到其他程序当中,而想要对其他程序写入代码就必须要有写入代码的空间。除了把代码写入到其他程序中以外,还必须让这些代码有机会被执行到。就上面两个问题而言都是比较容易解决的,下面分别来讨论一下。 病毒要对其他程序写入代码,必须确定目标程序有足够的空间让他把代码写入。通常情况下有两种比较容易实现的方法,第一种在前面的章节介绍过,就是添加一个节区,添加一个节区后就有足够的空间让病毒来写入了。第二种方法是缝隙查找,然后写入代码。何为缝隙?在每个节与节之间,必然有没有使用到的空间,这个空间就叫缝隙。只要确定要写入代码的长度,然后根据这个长度来查找是否有满足该长度的缝隙就可以了。 **9、请说说你对于壳的理解。** 答:(以下内容选自**《加密与解密 第三版》**第12.1.1节——壳的概念) 在自然界中,植物用壳来保护种子,动物用壳来保护身体等。同样,在一些计算机软件里面也有一段专门负责保护软件不被非法修改或反编译的程序。它们附加在原程序上通过Windows加载器载入内存后,先于原程序执行,得到控制权,执行过程中对原始程序进行解密、还原,还原完成后再把控制权交还给原始程序,执行原来的代码的部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原,这样就可以比较有效地防止破解者对程序文件的非法修改,同时也可防止程序被静态反编译。由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,就把这样的程序称为“壳”了。 …… 加壳软件一般都有良好的操作界面,使用也比较简单。除了一些商业壳,还有一些个人开发的壳,种类较多。壳对软件提供了良好保护的同时,也带来了兼容性问题,选择一款壳保护软件后,要在不同硬件和系统上多测试。由于壳能保护自身代码,因此许多木马或病毒都喜欢用壳来保护和隐藏自己。对于一些流行的壳,杀毒引擎能对目标软件脱壳,再进行病毒检查。而大多数私人壳,杀毒软件不会专门开发解压引擎,而是直接把壳当成木马或病毒处理。 有加壳就一定会有脱壳。一般的脱壳软件多是专门针对某加壳软件而编的,虽然针对性强、效果好,但收集麻烦。因此掌握手动脱壳技术十分必要。 **知识扩展:** (以下内容选自**《加密与解密 第三版》**第13.1.1节——壳的加载过程) 壳和病毒在某些方面比较类似,都需要比原程序代码更早地获得控制权。壳修改了原程序的执行文件的组织结构,从而能够比原程序的代码提前获得控制权,并且不会影响原程序的正常运行。这里简单说说壳的常见加载过程。 (1)保存入口参数 加壳程序初始化时保存各寄存器的值,外壳执行完毕,再恢复各寄存器内容,最后再跳到原程序执行。通常用pushad/popad、pushfd/popfd指令对来保存与恢复现场环境。 (2)获取壳自己所需要使用的API地址 一般外壳的输入表中只有GetProcAddress、GetModuleHandle和LoadLibrary这几个API函数,甚至只有Kernel32.dll以及GetProcAddress。如果需要其他的API函数,则通过LoadLibraryA(W)或LoadLibraryExA(W)将DLL文件映像映射到调用进程的地址空间中,函数返回的HINSTANCE值用于标识文件映像映射到的虚拟内存地址。 LoadLibrary函数的原型如下: ~~~ HINSTANCE LoadLibrary( LPCTSTR lpLibFileName //DLL文件名的地址 ); //返回值:成功返回模块的句柄,失败返回NULL。 ~~~ 如果DLL文件已被映射到调用进程的地址空间里,可以调用GetModuleHandleA(W)函数获得DLL模块句柄。函数的原型如下: ~~~ HMODULE GetModuleHandle( LPCTSTR lpModuleName //DLL文件名地址 ); ~~~ 一旦DLL模块被加载,线程就可以调用GetProcAddress函数获取输入函数的地址。函数原型如下: ~~~ FARPROC GetProcAddress( HMODULE hModule, //DLL模块句柄 LPCSTR lpProcName //函数名 ); ~~~ 参数hModule是调用LoadLibrary(Ex)或GetModuleHandle函数的返回值。参数lpProcName可以采用两种形式:第一种是以0结尾的字符串地址;第二种形式是调用地址的符号的序号(微软公司非常反对使用序号)。 读者必须熟练掌握这三个函数的用法,外壳中用到其他函数就是用这三个函数来调用的。现在有些壳,为了提高强度,甚至连系统提供的GetProcAddress函数都不用,而是自己写个相同功能的函数代替GetProcAddress,以提高函数调用的隐蔽性。 (3)解密原程序的各个区块数据 壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个区块。在程序执行时外壳将会对这些区块数据解密,以让程序能正常运行。壳一般是按区块加密的,那么在解密时也按区块解密,并且把解密的区块数据按照区块的定义放在合适的内存位置。 (4)IAT的初始化 IAT的填写,本来应该由PE装载器实现。但由于加壳时,自己构建了一个输入表,并让PE头中的输入表指针指向了自建的输入表。所以,PE装载器就将对自建的输入表进行了填写。那么原来PE的输入表的填写,只好由外壳程序实现了。外壳要做的就是将这个新输入表结构从头到尾扫描一遍,对每一个DLL引入的所有函数重新获取地址,并填写在IAT表中。 (5)重定位项的处理 文件执行时将被映射到指定的内存地址中,这个初始内存地址称为基址。当然这只是程序文件中声明的,程序运行时能够保证系统一定满足其要求吗? 对于EXE的程序文件来说,Windows系统会尽量满足。例如某EXE文件的基地址为400000h,而运行时Windows系统提供给程序的基地址也同样是400000h。在这种情况下就不需要进行地址“重定位”了。由于不需要对EXE文件进行“重定位”,所以加壳软件把原程序文件中用于保存重定位信息的区块干脆也删除了,这样使得加壳后的文件更加小巧。有些工具提供“Wipe Reloc”的功能,其实就是这个作用。 不过对于DLL的动态链接库文件来说,Windows系统没有办法保证每一次DLL运行时提供相同的基地址。这样“重定位”就很重要了,此时壳中也需要提供进行“重定位”的代码,否则原程序中的代码是无法正常运行起来的。从这点来说,加壳的DLL比加壳的EXE修正时多了一个重定位表。 (6)HOOK-API 程序文件中的输入表的作用是让Windows系统在程序运行时提供API的实际地址给程序使用。在程序的第一行代码执行之前,Windows系统就完成了这个工作 壳一般都修改了原程序文件的输入表,然后自己模仿Windows系统的工作来填充输入表中相关的数据。在填充过程中,外壳就可填充HOOK-API的代码的地址,这样就间接地获得程序的控制权。 (7)跳转到程序原入口点(OEP) 从这个时候起壳就把控制权交还给原程序了,一般的壳在这里会有明显的一个“分界线”。当然现在越来越多的加密壳将OEP一段代码搬到外壳的地址空间里,然后将这段代码清除掉。这种技术称为Stolen Bytes。这样,OEP与外壳间就没那条明显的分界线了,增加了脱壳的难度。 (以下内容选自**《加密与解密 第三版》**第16.3.1节——外壳的加载过程) Windows的PE加载器加载可执行程序时,首先根据输入表获取所有API调用的地址,并填写到IAT中,再重定位所有的重定位项,最后调用WinMain(HINSTANCE hInstance,HINSTANCEhPrevInstance,PSTR szCmdLine,int iCmdShow)执行;如果是DLL,则调用DllMain(HINSTANCE hInstance,DWORDdwReason,LPVOID lpReserved)。加壳后,这个加载过程就由外壳来模拟了。 **本篇文章参考资料:** 1、戚利,**《Windows PE权威指南》**,机械工业出版社。 2、罗云彬,**《Windows环境下32位汇编语言程序设计(典藏版)》**,电子工业出版社。 3、段钢(主编),Blowfish、沈晓斌、丁益青、单海波、王勇、赵勇、唐植明、softworm、afanty、李江涛、林子深、印豪、冯典、罗翼、林小华、郭春杨(编委),**《加密与解密(第三版)》**,电子工业出版社。 4、冀云,**《C++黑客编程揭秘与防范》**,人民邮电出版社。
';

第002篇:猎豹移动反病毒工程师part2

最后更新于:2022-04-01 07:39:30

这次拿三个问题来讨论,是关于调试器的。因为对于反病毒工程师而言,类似于OllyDbg和IDA的使用方法是必须掌握的,但是在面试中又不太方便考察,所以只能对其快捷键或者调试器实现原理之类的问题进行提问。这里我还是选取比较权威的书籍上的答案,免得仅仅提供我自己的答案会误导大家,也难有说服力。如果说大家以后在面试的时候遇到这些问题,只要回答出关键点就可以,无需一字不落地背下来。因为我摘录的答案的篇幅往往比较长,为的就是让大家更为全面地看待这个问题。 问题4——6如下: **4、在IDA中,快捷键X的作用是什么?什么是交叉引用?** 答:(以下内容选自**《加密与解密 第三版》**第3.2.5节) 通过交叉参考(XREF)可以知道指令代码相互调用关系。如下面代码所示,这句“CODE XREF:sub_401120+B↑j”,表示该调用地址是401120,“j”表示跳转(jump)。其他一些符号:“o”表示偏移值(offset),“p”表示子程序(procedure)。双击这里或按回车键可以跳到调用该处的地方。 ~~~ .text:00401165 loc_401165: ;CODE XREF:sub_401120+B↑j .text:00401065 push 0 ;nExitCode .text:00401067 FF 15 B0 40 40 00 call ds:PostQuitMessage</span> ~~~ 在loc_401165字符上按X键,将打开交叉参考窗口。 (关于交叉引用更为详细的说明,可参考**《IDA Pro权威指南 第2版》**第9章——交叉引用与绘图功能) **** **5、请解释一下什么是DR寄存器。** 答:(以下内容选自**《IDA Pro代码破解揭秘》**第6.2节) 要真正理解调试,了解各种中断及调试寄存器是很有必要的,特别是有关进程状态变化的。Intel Software Developers Manual在这方面依然是最好的参考资料,我知道的与它相比,不过是小巫见大巫。不过,IA-32平台处理调试基本上是通过二者(中断或调试寄存器)之一实现的。 首先介绍一下调试寄存器。IA-32平台有8个调试寄存器,最多可以监视4个地址。访问这些寄存器是通过mov指令(调试寄存器暗中充当目的操作数或源操作数)的变体实现的。要知道,访问这些寄存器是需要有RING0特权的,这当然是一个限制,但考虑其影响极大,这样做也是有道理的。对每一个断点,有必要具体指定涉及的地址、位置的长度(范围在一个字节与双字之间)、处理程序(当生成一个调试异常时)以及这个断点是否被启用。前4个调试寄存器,DR0到DR3可以包含4个32位地址(定义了断点应该出现的地址)。接下来的2个调试寄存器DR4和DR5取决于操作模式可以交替使用。当DE(Debugging Extension,调试扩展)标志被设在CR4(Control Register4,控制寄存器4)里时,DR4和DR5保留,试图引用它们时,将引发一个无效操作码异常。如果这个标志没有被设置,DR4和DR5则作为别名替代调试寄存器6和7。 DR6(DebugRegister 6,调试寄存器6),作为调试状态寄存器指示对最后发生的调试异常进行条件检查的结果。用位模式访问DR6,位0到位3与前4个调试寄存器相关。这些位指示哪个断点条件被满足而生成了一个调试异常。DR6的位13在置位时表示下一条指令引用一个调试寄存器,连同DR7(我们随后就会介绍)的一部分被使用。位14对我们来说或许是最有诱惑力的,它被置位时指示处理器处于单步模式(single-step mode),我们稍后介绍。最后使用的是位15,它指示当调试陷阱标志被置位时,作为任何切换的结果都会抛出调试异常。最后,我们介绍调试寄存器7,也就是DR7。所有的黑客都对这个寄存器垂涎欲滴,它也被称为调试控制寄存器,像DR6一样被理解为位字段。这个寄存器的第一个字节对应断点是否是活动的,如果是活动的则对应它的作用域。位0、2、4和6确定调试寄存器是否被启用,位1、3、5和7对应同样的断点,但基于全局的作用域。在这个例子里,作用域被定义为在任务切换时断点是否保持,是否针对全局启用的断点对所有的任务都是可用的。根据Intel的手册,现在的处理器不支持位8和9。不过在以前,可以用它们确定引起断点事件的精确的指令。接下来是位13,这是一个比较有意思的位,它允许在访问调试寄存器本身之前中断。最后是位16到31,这些位确定什么类型的访问会引起断点,以及所在地址的数据长度。当CR4中的DE标志被置位时,位16到17、20到21、24到25、28到29被解释成下面的意思: 00 – Break onexecution 01 – Break onwrite 10 – Break onI/O read or writes 11 – Break onread and writes but not instruction fetches 当DE标志没有置位时,除了10的值没有定义以外,其他的解释同上面一样。位18到19、22到23、26到27、30到31对应各种断点的长度,值00表示长度为1,值01表示2字节长度。在32位平台上10未被定义,在64位平台上它表示8字节长度。你可能已经猜到了,11表示长度为4字节。 现在,把所有这些位与DR0至DR3联系起来似乎比较困难,但实际并不是这样的。每个2位组合对应DR0至DR3范围里特定的连续的寄存器。这些长度必须对齐某些边界(取决于它们的大小)——例如,16位需要以字为界,32位以双字为界。这由处理器通过掩码地址的低位相关地址来强制执行,因此,一个未对齐的地址将不会产生预期的结果。如果访问起始地址加上它的长度范围内的任意地址,将产生异常,通过使用2个断点有效地允许未对齐的断点;每个断点被适当地对齐,在它们两个之间包含了讨论的长度。这里还有一点值得一提。当断点访问类型只是执行时,应当把长度设为00,其它的值导致未定义的行为。 注意:有趣的是,调试寄存器本身并没有受到很多关注。不过,私下里还是有很多利用它们的rootkits和后门。比如说,可以在指向进程结构的指针上设置一个全局访问断点,在进程的链表里隐藏进程。当访问这个地址时,将出现调试异常,并重定向到它们的处理程序并执行任意多的任务,包括返回列表里下一个进程的地址。 更糟的是,它们可以启用DR7里的GD标志,并导致访问调试寄存器本身并引发异常,阻挠审查寄存器来检查它们当前的配置。 **6、请说明单步执行分为哪几种,区别是什么,其实现原理是什么。** **答:(以下内容选自**《IDA Pro代码破解揭秘》第5.2.2节) 单步是指每次执行一条指令,然后把控制权交给调试器的过程。IA-32系列处理器在硬件上直接支持单步。通过一次执行一条指令,我们可以仔细监视代码的特定部分。不过不太可能用这个方法调试整个程序。单步通常用于理解特定部分的代码。 从CPU的角度来看,调试器设置EFLAGS寄存器里的TF(Trap Flag,陷阱标志)位。一条指令执行完后就会生成调试异常,调试器会把这个调试异常当做中断(INT 0x01)捕获。 注意:大多数调试器都提供step(单步)命令。通常把他们称为步入(step into)和步过(step over)。从用户角度来看,它们之间唯一的不同是出现在某些指令上,其实就是call和rep指令上。当碰到call时,步入命令将跟随call,而步过命令将在call之后的指令上中断。这通常由设置断点来完成,而不是单步执行到返回。 **知识扩展:** (以下内容选自**《加密与解密 第三版》**第2.1.5节) 当执行一个INT 3断点时,该地址处的内容被调试器用INT 3指令替换了,此时OllyDbg将INT 3隐藏了,显示出来仍是下断前的指令,而实际上这个指令已经被替换成了CC。 这个INT 3指令,其机器码是CCh,也常称为CC指令。当被调试进程执行INT3指令导致一个异常时,调试器就会捕捉这个异常从而停在断点处,然后将断点处的指令恢复成原来的指令。当然,如果自己写调试器,也可用其他一些指令代替INT 3来触发异常。 用INT 3断点的好处是可以设置无数个断点,缺点是改变了原程序指令,容易被软件检测到。例如为了防范API被下断,一些软件会检测API的首地址是否为CCh,以此来判断是否被下了断点。在这用C语言来实现这个检测,方法是取得检测函数的地址,然后读取它的第一个字节,判断它是否等于“CCh”。下面这段代码就是对MessageBoxA函数进行的断点检测: ~~~ FARPROC Uaddr; BYTE Mark = 0; (FARPROC&)Uaddr = GetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA"); Mark = *((BYTE*)Uaddr); //取MessageBoxA函数第一字节 if(Mark = = 0xCC) //如该字节为CC,则认为MessageBoxA函数被下断 return TRUE; //发现断点 ~~~ 程序编译后,对MessageBoxA设断,程序将会发现自己被设断跟踪。当然躲过检测的方法是将断点下在函数内部或末尾,例如可以将断点下在函数入口的下一行,就可以躲过检测了。 (以下内容选自**《加密与解密 第三版》**第15.3.7节——通过TrapFlag检测) CPU符号位EFLAGS中一位叫做TF,即TRAP FLAG。当这个TF=1的时候CPU执行完EIP的指令就会触发一个单步异常。例如这段代码: ~~~ pushfd push eflags or dword ptr [esp],100h ;TF=1 popfd ;这条指令之后TF=1了 nop ;这一条执行后会触发异常,应该提前安装SHE,跳到其他地方执行 jmp die ;如果顺序执行下来了,说明被跟踪 ~~~ **本篇文章参考资料:** 1、段钢(主编),Blowfish、沈晓斌、丁益青、单海波、王勇、赵勇、唐植明、softworm、afanty、李江涛、林子深、印豪、冯典、罗翼、林小华、郭春杨(编委),**《加密与解密(第三版)》**,电子工业出版社。 2、[美]Chris Eagle(著),石华耀、段桂菊(译),**《IDA Pro权威指南(第2版)》**,人民邮电出版社。 3、[美]Dan Kaminsky、Justin Ferguson、Jason Larsen、Luis Miras、Walter Pearce(著),看雪论坛翻译小组(译),**《IDAPro代码破解揭秘》**,人民邮电出版社。
';

第001篇:猎豹移动反病毒工程师part1

最后更新于:2022-04-01 07:39:28

我在2014年7月1日参加了猎豹移动(原金山网络)反病毒工程师的电话面试,但是很遗憾,由于我当时准备不足,加上自身水平不够,面试官向我提出的很多技术问题我都没能答出来(这里面既有基础类的问题,也有比较高深的问题),结果没能通过那次的面试。痛定思痛,我认认真真总结出了当时向我提出的所有问题,一共是十多条,我会分为几篇文章来进行剖析。并且根据问题的难易程度,由浅入深进行排序。参考了大量的相关资料,给出了业界较为权威的书籍上的答案,可能对于某些问题,会同时摘录几本书的内容,并且给出知识扩展,以更为全面地看待这个问题。 猎豹移动的反病毒工程师(珠海)职位分为社会招聘和校园招聘,其中社会招聘要求如下: **工作职责:** 1、样本鉴定;      2、分析病毒样本并提供解决方案;    3、信息安全技术研究。    **任职要求:** 1、热爱底层工作,对反病毒、逆向工程、系统漏洞等有强烈兴趣; 2、熟悉x86系列汇编语言,能熟练读懂汇编代码; 3、熟练使用IDA、OD等工具进行反汇编; 4、有一定C/C++程序功底; 5、有良好的团队合作意识、善于沟通、有耐心、责任心强; 6、工作细心、积极主动、推动力强、有较快、较好的学习新技术能力。 校园招聘的要求如下: **工作职责:** 1、样本鉴定;      2、分析病毒样本并提供解决方案;    3、信息安全技术研究。    **任职要求:**   1、热爱底层开发工作,对反病毒、逆向工程、系统漏洞等有强烈兴趣; 2、熟悉x86系列汇编语言,能熟练读懂汇编代码; 3、具有C/C++程序开发经验者优先。 这次所讨论的是三个基础问题,主要关于汇编一些指令的用法。那么接下来就是我所总结的技术面试问题:**1、请解释一下汇编中的ADC和REP指令。** 答:(以下内容均选自软件“**汇编指令助手 V1.1**”) 带进位加法指令ADC(Addition Carry) 格式:ADC OPRD1,OPRD2 功能:OPRD1←OPRD1+OPRD2+CF 说明: (1)OPRD1为任一通用寄存器或存储器操作数,可以是任意一个通用寄存器,而且还可以是任意一个存储器操作数。OPRD2为立即数,也可以是任意一个通用寄存器操作数。立即数只能用于源操作数。 (2)OPRD1和OPRD2均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的,但不允许两个都是存储器操作数。 (3)加法指令运算的结果对CF、SF、OF、PF、ZF、AF都会有影响。以上标志也称为结果标志。 (4)该指令对标志位的影响同ADD指令。 重复前缀的说明 格式:REP            ; CX<>0重复执行字符串指令         REPZ/REPE      ; CX<>0且ZF=1重复执行字符串指令         REPNZ/REPNE    ; CX<>0且ZF=0重复执行字符串指令 功能:在串操作指令前加上重复前缀,可以对字符串进行重复处理。由于加上重复前缀后,对应的指令代码是不同的,所以指令的功能便具有重复处理的功能,重复的次数存放在CX寄存器中。 说明: (1)REP与MOVS或STOS串操作指令相结合使用,完成一组字符的传送或建立一组相同数据的字符串。 (2)REPZ/REPE常用与CMPS串操作指令结合使用,可以完成两组字符串的比较。 (3)REPZ/REPE常与SCAS指令结合使用,可以完成在一个字符串中搜索一个关键字。 (4)REPNZ/REPNE与CMPS指令结合使用,表示当串未结束(CX=1)且当对应串元素不相同(ZF=0)时,继续重复执行串比较指令。 **知识扩展:** 带借位减去指令SBB(SuBtraction with Borrow) 格式:SBB OPRD1,OPRD2 功能:是进行两个操作数的相减再减去CF进位标志位,即从OPRD1←OPRD1-OPRD2-CF,其结果放在OPRD1中。 循环控制指令LOOP 格式:LOOP 标号 功能:(CX)←(CX)−1,(CX)<>0,则转移至标号处循环执行,直至(CX)=0,继续执行后继指令。 说明: (1)本指令是用CX寄存器作为计数器,来控制程序的循环。 (2)它属于段内SHORT短类型转移,目的地址必须距本指令在-128到+127个字节的范围内。 (以下内容选自**《IDA Pro代码破解揭秘》**第2.4节) EFLAGS寄存器是32位寄存器,包含一组状态、系统标志及控制标志。每个标志由寄存器里一位代表,从0位到31位我们有下面这些标志。 CF:进位标志,指示在算术运算中是否带有进位或借位。用于无符号算术运算。 PF:奇偶标志,为机器中传送信息时可能出错提供校验。当目的操作数中1的个数为偶数时置1(PE),否则置0(PO)。 AF:辅助进位标志,记录运算时低4位(半个字节)产生的进位值。有进位时置1(AC),否则置0(NA)。 ZF:零标志。运算结果为0时置1(ZR),否则置0(NZ)。 SF:符号标志,记录运算结果的符号。结果为负时置1(NG),否则置0(PL)。 TF:陷阱标志,用于单步方式操作。当TF为1时,每条指令执行完后产生陷阱,由系统控制计算机;当TF为0时,CPU正常工作,不产生陷阱。 IF:允许中断标志。当IF为1(EI)时,允许中断;IF为0(DI)时关闭中断。 DF:方向标志,在串处理指令中控制处理信息的方向。当DF置1(DN)时每次操作后,变址寄存器SI和DI减量,这样就使串处理从高地址向低地址方向处理;当DF置0(UP)时,则反之。 OF:溢出标志。在运算过程中,若操作数超出了机器能表示的范围则称为溢出,此时OF标志位为1(OV);否则置0(NV)。 IOPL(12位到13位):I/O特权级标志。指出当前运行任务的I/O端口的特权级。 NF:嵌套任务标志。只在当前任务是前一任务的子任务时设置。 RF:回复标志。控制处理器对调试异常的响应。 VM:虚拟8086标志。控制是否启用虚拟8086模式。 AC:对齐检查标志。设置为启用存储器的对齐检查的参考。 VIF:虚拟中断标志。IF的虚拟映像,与VIP标志联合使用。 VIP:虚拟中断标志。确定是否有中断被挂起。 ID:标识标志,确定CPU是否支持CPUID指令。 第22到31位当前被保留。 (以下内容选自**《黑客免杀攻防》**第9.3节) ~~~ 013A13CC lea edi,dword ptr ss:[ebp-0xC0] 013A13D2 mov ecx,0x30 013A13D7 mov eax,0xCCCCCCCC 013A13DC rep stos dword ptr es:[edi] ;向EDI指向的地址处依次填入EAX中的内容,循环ECX次(也就是填0xCC操作)。这是因为汇编指令 ;“int 3”的OpCode就是0xCC,很显然这样做会大大提高程序的排错能力,如果由于某些不可知的原 ;因导致代码跑到了不该去的地方,那么这些成排的“int 3”指令会马上将其断下来。 ~~~ **2、请说明寄存器EBP与ESP的功能,并解释在大多数函数入口点处的反汇编代码中,这两个寄存器的作用。** 答:(以下内容选自**《0day安全:软件漏洞分析技术 第2版》**第2.1.4节) 每一个函数都独占自己的栈帧空间。当前正在运行的函数的栈帧总是在栈顶。Win32系统提供两个特殊的寄存器用于标识位于系统顶端的栈帧。 (1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 (2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。 注意:EBP指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念,本书在叙述中将坚持使用“栈帧底部”这一提法以示区别;ESP所指的栈帧顶部和系统栈的顶部是同一个位置,所以后面的叙述中并不严格区分“栈帧顶部”和“栈顶”的概念。请您注意这里的差异,不要产生概念混淆。 函数栈帧:ESP和EBP之间的内存空间为当前栈帧,EBP标识了当前栈帧的底部,ESP标识了当前栈帧的顶部。 (以下内容选自**《0day安全:软件漏洞分析技术 第2版》**第2.1.5节) 函数调用大致包括以下几个步骤 …… (4)栈帧调整:具体包括。 保存当前栈帧状态值,以备后面恢复本栈帧时使用(EBP入栈); 将当前栈帧切换到新栈(将ESP值装入EBP,更新栈帧底部); 给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈帧); …… ~~~ push ebp ;保存旧栈帧的底部 mov ebp,esp ;设置新栈帧的底部(栈帧切换) sub esp,xxx ;设置新栈帧的顶部(抬高栈帧,为新栈帧开辟空间) ~~~ (以下内容选自**《黑客免杀攻防》**第9.3节) ~~~ 013A13C0 >push ebp ;EBP入栈保存(一般情况下将某个寄存器入栈保存的目的只有两个,一个 013A13C0 ;是需要将其通过栈传递给某个函数或代码使用,另外一种情况就是后面的 013A13C0 ;代码要使用到这个寄存器,因此要将其原始的值保存起来, 013A13C0 ;以备恢复) 013A13C1 mov ebp,esp ;然后将堆栈指针ESP的值传递给EBP,如此一来在这个函数内只需要使用 013A13C1 ;EBP就可以对栈进行操作了。这样做的好处是不需要对ESP做过多的操 013A13C1 ;作,从而更好地保证了程序的健壮性(也增加了易读性) 013A13C3 sub esp,0xC0 ;将ESP减0xC0,也就是将栈顶抬高0xC0。这里有一个专业名词叫做 013A13C3 ;“打开栈帧”。但是通过源代码我们知道根本用不了这么大的空间,这 013A13C3 ;是编译器在编译Debug版本时为了增强程序的健壮性与可调试性而做 013A13C3 ;的一件事 ~~~ **3、请说明CALL与RET指令的实现原理。** 答:(以下内容选自**《汇编语言 第二版》**王爽著,第10章,仅针对16位系统,而32位及64位系统也可参考) call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。它们经常被共同用来实现子程序的设计。 ret指令用栈中数据,修改IP的内容,从而实现近转移; retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。 CPU执行ret指令时,进行下面两步操作: (1)(IP)=((ss)*16+(sp)) (2)(sp)=(sp)+2 CPU执行retf指令时,进行下面4步操作: (1)(IP)= ((ss)*16+(sp)) (2)(sp)=(sp)+2 (3)(CS)= ((ss)*16+(sp)) (4)(sp)=(sp)+2 可以看出,如果我们用汇编语法来解释ret和retf指令,则: CPU执行ret指令时,相当于进行: ~~~ pop IP ~~~ CPU执行retf指令时,相当于进行: ~~~ pop IP pop CS ~~~ CPU执行call指令时,进行两步操作: (1)将当前的IP或CS和IP压入栈中; (2)转移。 call指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理相同。 (以下内容选自**《0day安全:软件漏洞分析技术第2版》**第2.1.5节) 函数调用大致包括以下几个步骤。 (1)参数入栈:将参数从右向左依次压入系统栈中。 (2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行。 (3)代码区跳转:处理器从当前代码区跳转到被调用函数的入口处。 …… ~~~ call函数地址 ;call指令将同时完成两项工作:a)向栈中压入当前指令在内存中的位置即保存返回 ;地址。b)跳转到所调用函数的入口地址函数入口处 ~~~ …… 类似地,函数返回的步骤如下。 (1)保存返回值:通常将函数的返回值保存在寄存器EAX中。 (2)弹出当前栈帧,恢复上一个栈帧。 具体包括: ● 在堆栈平衡的基础上,给ESP加上栈帧的大小,降低栈帧,回收当前栈帧的空间。 ● 将当前栈帧底部保存的前栈帧EBP值弹入EBP寄存器,恢复出上一个栈帧。 ● 将函数返回地址弹给EIP寄存器。 (3)跳转:按照函数返回地址跳回母函数中继续执行。 还是以C语言和Win32平台为例,函数返回时的相关的指令序列如下。 ~~~ add esp,xxx ;降低栈帧,回收当前的栈帧 pop ebp ;将上一个栈帧底部恢复到ebp retn ;这条指令有两个功能:a)弹出当前栈顶元素,即弹出栈帧中的返回地址。 ;至此,栈帧恢复工作完成。b)让处理器跳转到弹出的返回地址,恢复调用前 ;的代码区 ~~~ **本篇文章参考资料:** 1、林文龙,“**汇编指令助手 V1.1**”,小龙软件工作室。 2、[美]DanKaminsky、Justin Ferguson、Jason Larsen、Luis Miras、Walter Pearce(著),看雪论坛翻译小组(译),**《IDA Pro代码破解揭秘》**,人民邮电出版社。 3、任晓珲,**《黑客免杀攻防》**,机械工业出版社。 4、王清(主编),张东辉、周浩、王继刚、赵双(编著),**《0day安全:软件漏洞分析技术(第2版)》**,电子工业出版社。 5、王爽,**《汇编语言(第2版)》**,清华大学出版社。
';

前言

最后更新于:2022-04-01 07:39:26

> 原文出处:[安全工程师面经](http://blog.csdn.net/column/details/securityengineer.html) 作者:[ioio_jy](http://blog.csdn.net/ioio_jy) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # 安全工程师面经 > 讨论计算机安全工程师(如反病毒工程师)的面试经验,分析面试中经常出现的问题并给出相应的解答。
';