0Day技术分析-1-基础知识

1 基础知识

本章介绍一些与0Day相关的基本概念及基础知识。

1.1. Bug与漏洞

有一个比较有趣的事件,话说某个软件中存在99个Bug,某一天研发人员心血来潮,修复了其中的一个Bug,于是,这个软件中有了117个Bug。

这个事件说明两个问题:每个软件之中肯定会存在Bug;而且随着不断的修复,Bug的数量有着十分微妙地变化。

一般情况下我们把Bug分别两类:

  • 功能性Bug

这类Bug影响到软件的正常功能,比如计算错误、统计错误、显示错误,这类Bug应该被测试人员发现

  • 安全性Bug

这类Bug超出了设计的范围,一般情况下不会影响软件的正常功能,却能够被攻击者利用,这类Bug即为漏洞

我们日常接触到的大多是功能性的Bug,一些功能性的Bug有可能是由客户提交过来的,而一般的客户都是正常的使用者,不太可能发现软件中的漏洞。

1.2. 0Day漏洞

0Day漏洞是指已经被发现,但官方还没有相关补丁的漏洞,换句话说,0Day漏洞是系统商还没有发布相关补丁前就被掌握(有可能系统商还没发现此漏洞)或公开的漏洞信息。

一般比较权威的漏洞发布平台有:

  • CVE,http://cve.mitre.org,这是目前最具权威的漏洞发布平台
  • CERT,计算机应急组,最大的特点就是即时
  • CNNVD,中国国家信息安全漏洞库,隶属于测评中心
  • CNVD,国家信息安全漏洞库(平台),已经外包 1.3. 漏洞挖掘

因为安全性漏洞具有较高的利用价值,所以总有那么一帮技术精湛、精力旺盛的家伙去寻找软件中的问题。

这帮家伙一般都精通汇编语言、机器语言、操作系统底层的知识,他们不是一般的程序员,他们写的出代码让大部分的程序都感到无比汗颜。

当然寻找漏洞的人并不一定是攻击者,漏洞挖掘实际上是一种高技术含量的测试,学术界一般采用静态分析的方法,即在代码中寻找漏洞,而工业界一般都采用Fuzz测试,即灰盒测试的方法。

1.4. 相关技术

上面提到,作为一个0Day漏洞分析人员,所要求的必备知识是很多的:

  • 汇编语言,16位/32位,尤其是16位汇编,必须掌握,我会整理出一个文档让大家自学
  • 操作系统底层相关知识,如保护模式的寻址方式、内存管理、一些Undocumented的知识等等
  • 逆向工具,如IDA、OD、SoftIce,还有最重要的PE文件格式,我会整理出相关文档让大家自学
  • 编程语言,你至少会写简单的汇编代码,知道一些机器代码,至少会用一种高级编程语言,这种语言最好是C,Python也不错 1.5 虚拟机

虚拟机可以帮我们建立多个操作系统,这些操作系统可以让我们方便的建立起我们的攻防环境、分析环境。

虚拟机在一定程度上保证了我们的安全,因为我们经常会分析一些类似病毒或是本身就是病毒的东西,把这些东西扔在虚拟机里比较安全,虚拟机是一个很好的沙箱。

另外,我们使用的一些工具也可能会被某些杀毒软件当成病毒,所以把这些工具扔在虚拟机里是个相当不错的方法。

我们这里以VMware为例,介绍一下VMware与宿主机器之间的网络配置。

1.5.1. Bridge(桥接模式)

默认使用VMNet0,不提供DHCP服务。

在桥接模式bridged下,虚拟机和宿主计算机处于同等地位,虚拟机就像是一台真实主机一样存在于局域网中。因此在桥接模式下,我们就要像对待其他真实计算机一样为其配置IP、网关、子网掩码等等。当我们可以自由分配局域网IP时,使用桥接模式bridged就可以虚拟出一台真实存在的主机。

但是如果你的主机仅仅是一个单机(大多数朋友也许只是单机ADSL上网).很显然,不存在上图中的路由器,很显然该模式并不适用.如果你单机的”本地网卡”显示为”已连接”你倒是可以在虚似机里,把虚拟机的网卡和主机的”本地连接”强制给一个IP和子网掩码,倒是可以连接.不过并不能实现上网.如果你的”本地连接”显示为”已断开”该模式就更不能用了.

使用桥接模式bridged的虚拟系统和宿主机器的关系,就像连接在同一个Hub上的两台电脑。想让它们相互通讯,你就需要为虚拟系统配置IP地址和子网掩码,否则就无法通信。如果你想利用VMWare在局域网内新建一个虚拟服务器,为局域网用户提供网络服务,就应该选择桥接模式bridged。

桥接bridged成功的电脑不管是虚拟机还是物理机,都可以实现互访,并且可以访问局域网中其它的电脑用主机的”本地网卡”作通讯媒介。

1.5.2. Host-only(主机模式)

默认使用VMnet1,提供DHCP服务。

在Host-only(主机模式):模式下,相当于虚拟机通过双绞线和宿主计算机直连,而宿主计算机不提供任何路由服务。因此在Host-only(主机模式):模式下,虚拟机可以和宿主计算机互相访问,但是虚拟机无法访问外部网络(也就是不能上网)。

该Host-only(主机模式):模式下,物理主机仅仅相当于和虚拟机通过交叉线连接起来而已(如果停用虚拟机里的vmnet1里的DHCP就真是这样了.)所以此模式仅仅只能让虚拟机和物理机相互通性而已.

当你想组成一个与物理网络相隔离的虚拟网络时,无疑非常适合使用Host-only模式。

Host-only模式下,不管是虚拟机还是物理机,都可以实现互访,用主机的”VMware NetworkAdapter VMnet1”进行通讯.不能访问物理机局域网中其它的电脑

1.5.3. NAT(网络地址转换模式)

默认使用VMnet8,提供DHCP服务。

在NAT模式下,宿主计算机相当于一台开启了DHCP功能的路由器,而虚拟机则是内网中的一台真实主机,通过路由器(宿主计算机)DHCP动态获得网络参数。因此在NAT模式下,虚拟机可以访问外部网络(也就是上网),反之则不行,因为虚拟机属于内网(想想你从某个地方可以直接该问另一个可以上网的局域网的内部电脑吗?)。

使用NAT模式,就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网。也就是说,使用NAT模式可以实现在虚拟系统里访问互联网。NAT模式下的虚拟系统的TCP/IP配置信息是由VMnet8(NAT)虚拟网络的DHCP服务器提供的,因此虚拟系统也就无法和本局域网中的其他真实主机进行通讯。采用NAT模式最大的优势是虚拟系统接入互联网非常简单,你不需要进行任何其他的配置,只需要宿主机器能访问互联网即可。

使用NAT模式不管是虚拟机还是物理机,都可以实现互访,用主机的”VMware NetworkAdapter VMnet8”进行通讯.但不能访问物理局域网的电脑。

在NAT模式下,可以为多个虚拟机分配不同的网段,即宿主机器和虚拟机不在同一个网段内。

注意使用NAT方式上网的时候,网关的设置,vmnet8的网关默认为xxx.xxx.xxx.2:

这里设置子网为10.10.10.0/24,户口NAT设置:

注意,这里是以10.10.10.2为网关的,所以在虚拟机里要设置网关为10.10.10.2。

1.6. PE文件

我们必须将PE文件单独拿出来,因为我们这节课的实验要用到,但我们只看一张图:

相关说明:

  • PE文件在磁盘中以0x200h字节为单位进行组织,即按512Byte对齐
  • PE文件加载到内存中是以0x1000h字节为单位进行组织的,即以4096Byte(一页)对齐
  • 我们发现PE文件中的节和加载到内存中是有地址偏移的
  • PE文件在硬盘中以0x200h对齐,是为了节省空间
  • PE文件在内存中以0x1000h对齐,是为了速度(目前我们讨论32位CPU)
  • File Offset Address,文件偏移地址:即数据在PE文件中相对于文件开头的偏移地址
  • Image Base Address,装载地址:即PE文件加载到内存的基址,简称IBA
  • Virtual Address,虚拟地址,即线性地址,简称VA
  • Relative Virtual Address,相对虚拟地址,即VA相对于IBA的偏移地址,简称RVA
  • 我们得出:VA = RVA + IBA
  • 我们得出:文件偏移地址 = VA – IBA – 节偏移
  • 我们得出:文件偏移地址 = RVA – 节偏移

另外的一些地址:

  • 线性地址,由物理地址转换而成,对应分页前的地址
  • 虚拟地址,即线性地址VA
  • 物理地址,对应地址总线的地址
  • 逻辑地址,由段寄存器中的选择子中所指向的标识符及一个偏移地址组成
  • 一般地址转换是这样:逻辑地址=>线性地址(即虚拟地址)=>(经过分页处理后)物理地址 1.7. 寻址方式

无论是16位的寻址还是保护模式下的寻址,对我们来说都是极为重要的。

1.7.1. 实模式的寻址

16位的寻址很简单,即段地址*16+16位的偏移地址

1.7.2. 保护模式的寻址

  • 32位CPU下通用寄存器变成32位,但段寄存器还是16位
  • 保护模式下,一段地址空间有很多的属性(基址、限长、访问类型)
  • 一段地址空间的基址+属性形成了64位的段描述符
  • 把所有的段描述符放在一个连续的空间,形成段描述符表
  • 全局描述表GDT,是操作系统使用的,只有一个
  • 中断描述表IDT,只有一个
  • 局部描述表LDT,每个任务都有一个
  • 48位的全局描述符表寄存器GDTR,指向GDT,高32位为基址,后16位为限长
  • 16位的局部描述符表寄存器LDTR,指向当前的LDT,高13位为LDT在GDT中的索引
  • 段寄存器中的高13位为此段在LDT中的索引
  • 当段寄存器的TI位为0时表示此段在GDT中,当TI位为1时则在LDT中
  • 我们发现,随着任务的切换,只需改变LDTR,LDT也就切换,各任务之间实现数据隔离,但GDT并不切换,他属于操作系统

我们通过两张图片来看一下:

  • 当TI=0时:
  • 当TI=1时:

1.8. 内存相关

1.8.1. C++内存布局

在坐的各位应该都写过C或是C++的程序,相信对内存布局应该比较了解,我们先看一张图:

说明:

  • 全局变量、用static修饰的局部变量都存储在静态数据区
  • 程序指令和大部分字面常量都存储在代码区
  • 大部分函数的形参和局部变量都存储在栈区
  • 程序中动态分配的内存都存储在堆区
  • 一小部分函数形参和局部变量存储在CPU寄存器组中 1.9. 函数调用

函数的调用非常重要,我们一定要知道栈在函数调用中的作用。

1.9.1. 函数调用

说明:

  • 再次强调,全局变量在静态数据区,所以上面的g是可以直接寻址的
  • 栈是向下增长的,即从高地址向低地址增长的
  • 参数是保存在栈中的,在stdcall的情况下,参数是从右到左入栈的
  • 局部变量是保存在栈中的
  • 在调用函数时,会先将返回地址入栈
  • 在调用函数时,会保存上层函数的EBP,然后将EBP置为当前的ESP作为新栈底
  • 当新的栈底设置后,会将栈顶抬高,为局部变量分配空间
  • 函数的参数都在EBP所指示的内存地址的正偏移处
  • 函数内部的局部变量都在EBP所指示的内存地址的负偏移处

相关寄存器:

  • ESP,永远指向栈顶,永远指向系统栈最上面的栈桢的栈顶
  • EBP,永远指向栈底,永远指向系统栈最上面的栈桢的栈底
  • EIP,永远指向CPU下一条将要执行的指令的地址

相关汇编指令:

  • pop
  • push
  • call
  • ret 2.0. 栈桢

栈桢我们在后面还要讲,这里我们先了解一下:

2 一个实验

2.1. 背景

几乎所有的软件都有用户登录功能

  • 一般情况下,用户名和密码保存在数据库中
  • 用户名在数据库里是唯一的,密码是加密的
  • 登录过程中对密码的传输是加密的
  • 登录的代码是判断用户输入的用户名和密码是否一致

这里我们分析一个C/S的用户登录程序,而且是简化版本的:

  • 这个程序我们使用C编写,不用Java或是DotNet的原因在于他们可以反编译,对我们来说,太没挑战性
  • Java的和DotNet的实验我们以后再说,最大的困难在于脱壳

我们通过这个小实验来看我们的研发,以说明我们在写代码的时候需要注意的一些地方。

2.2. 实验说明

目的:

  • 通过观察PE文件的运行,分析/猜测程序的代码思路
  • 通过分析程序的代码思路,找到通过密码验证的方法
  • 使用OD和UE修改PE文件来验证我们的思路
  • 通过此实验反思我们的代码,思考如何来避免此类问题的出现

面向的对象:

  • 具备一定C开发能力的人,并且可以看得懂一点点的32位汇编

我们的环境

  • VC6.0
  • Windows XP sp3
  • PE版本:Release版本
  • IDA Pro
  • OD
  • LoadPE 2.3.源码

2.4. 源码说明

关于密码

  • 我们这里定义了一个宏来保存密码
  • 其实跟从数据库里取出密码没什么区别,只是方式不同而已

关键代码说明

  • 当输入的密码不是1234567时,会提示输入了一个错误的密码
  • 当输入1234567时,会恭喜你输入了一个正确的密码

注意strcmp函数

  • 当两个字符串完全匹配时,返回0,否则返回非0值 2.5. 程序运行

2.6. 运行分析

输入分析 :

  • 当输入的密码不是1234567时,提示输入了一个错误的密码
  • 当输入1234567时,会恭喜你输入了一个正确的密码
  • 其它的我们一无所知 2.7. 两个假如

假如我们没有源码(我们根本没有看上面的源码):

  • 我们不知道密码是多少
  • 我们不知道密码保存在哪里(数据库?配置文件?是否加密?)
  • 那么,我们怎样来通过验证呢?
  • 也许我们可以猜一下,也许密码很好猜(这是另一个话题,我们以后再说)

假如你是程序员,你会如何实现这个功能?

  • 我们可能会先到某个地方(数据库)里去找到对的密码
  • 然后我们会将输入的密码跟找到的密码进行比较
  • 如果这两个密码一致,则提示恭喜用户输入成功
  • 如果这两个密码不一致,则提示用户输入了错误的密码
  • 我们可能会用到if/else 2.8. 两个想法

如果我们想要通过密码验证,我们需要找到那个关键的跳转,于是我们分析:

  • 没有源码,那么是不是可以在PE文件里找到这个if/else呢
  • 答案是可以的,只不过在我们只能看到汇编代码而已
  • if/else在汇编里是怎样的呢?不知道没关系,但是可以肯定的是if/else会用到跳转指令
  • 在汇编里好像有类似JMP/JNZ/JZ这样的跳转指令
  • 如果我们能够在PE文件找到判断密码的这个跳转指令就好办了
  • OK,我们如何找到这个判断密码的指令呢?(方法很多)

如果我们找到了这个跳转,我们肯定会有两个想法:

  • 如果找到了这个跳转,就可以试着去修改,如将JNZ修改成JZ,或者将JZ修改成JNZ,不管怎么样,我们先看看结果再说
  • 如果我们把跳转的地址直接修改到密码验证成功的地址就好了,这样我们就不用理会这个密码是保存在哪里、如何去判断密码的正确、是否加密的问题了 2.9. 用IDA看一下

说明:

  • 嗯,运气不错,果真有这样的跳转
  • 红色的跳转代表条件为假,绿色的跳转代表条件为真
  • 试试在jz short loc_40105F这一行按下空格键会发生什么
  • 显然,我们看到了这一行的汇编代码
  • 也看到了jz的跳转地址 ,0040104E,这是一个VA地址
  • 问题来了,我们如何按照我们之前的思路修改呢? 2.10. 使用OD修改

使用OD加载此PE文件,并按下Ctrl+G,输入上面的地址(0040104E),得到下图:

我们可以双击这一行进行修改:

我们先把JE改成JNE,点击Assemble按钮,然后右键->Copy to executable->selection,然后在弹出的窗口中按右键,选择save file。

说明:

  • 上面的修改是把这两个条件弄反,当输入正确的密码时提示失败,输入错误的密码时提示恭喜成功。
  • 如果我们把JE修改成JMP呢,即是无论输入什么,都会提示恭喜你输入了正确的密码
  • 我们必须验证一下 2.11. 使用UE修改

在使用IDA加载时,我们得到了一个VA,即0040106E,注意这个地址在不同的编译器、不同的版本、不同的操作系统上是不同的,比如上面我们得到的是一个0040104E,现在我们得到的是0040106E。

  • 我们使用UE修改的话,是直接修改的PE文件
  • 如果我们得到VA,还需要什么条件才能得到File Offset Address呢?
  • 还记得文件偏移地下怎么算吗?
  • 文件偏移地址 = RVA – 节偏移 = VA – IBA – 节偏移
  • 我们缺少IBA及节偏移
  • 没关系,我们使用LoadPE中的PEEditor看一下
  • 我们算一下(注意都是16进制):
  • 文件偏移地址 = 0040106E – 00400000 – (1000-1000)
  • 文件偏移地址 = 106E
  • 我们使用PEEditor中的工具算一下:

使用UE打开这个PE文件,Ctrl+G同样输入0x106E:

我们看到这个值是74,我们修改成75,另存,试一下。

原文发布于微信公众号 - zhisheng(zhisheng_blog)

原文发表时间:2016-04-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【Go 语言社区】Web 通信 之 长连接、长轮询(long polling)--转

基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。 一、什么是长连接...

1.1K3
来自专栏王磊的博客

Node出错导致运行崩溃的解决方案

许多人都有这样一种映像,NodeJS比较快; 但是因为其是单线程,所以它不稳定,有点不安全,不适合处理复杂业务; 它比较适合对并发要求比较高,而且简单的业务场景...

76114
来自专栏智能大石头

NewLife.Net——构建可靠的网络服务

1593
来自专栏FreeBuf

软件漏洞分析技巧分享

作者:riusksk【TSRC】 在日常分析软件漏洞时,经常需要耗费比较长的分析时间,少则几小时,多则数天,甚至更久。因此,经常总结一些分析技巧是非常有必要的,...

2719
来自专栏有趣的django

Django REST framework+Vue 打造生鲜超市(十二) 十三、首页、商品数量、缓存和限速功能开发

十三、首页、商品数量、缓存和限速功能开发  13.1.轮播图接口实现 首先把pycharm环境改成本地的,vue中local_host也改成本地  (1)goo...

7187
来自专栏源码之家

WDCP 504 MySQL server has gone away 解决方法

1402
来自专栏Kirito的技术分享

从Spring Session源码看Session机制的实现细节

去年我曾经写过几篇和 Spring Session 相关的文章,从一个未接触过 Spring Session 的初学者视角介绍了 Spring Session ...

64112
来自专栏Python中文社区

Python爬虫抓取收集考试大纲

專 欄 ❈ Garfield_Liang,Python中文社区专栏作者。 博客地址:http://www.jianshu.com/u/cac1d39abfa9 ...

23410
来自专栏Linyb极客之路

杂谈Java高并发

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题,但话又说回来了,既然逃避不掉,那...

5255
来自专栏大数据文摘

手把手 | 20行Python代码教你批量将PDF转为Word

3265

扫码关注云+社区

领取腾讯云代金券