杰言杰语

以中有足乐者,不知口体之奉不若人也.


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

二.HTML基础(2)

发表于 2017-03-28 | 分类于 WebDesign | 阅读次数

1. 复习与回顾

#1.1 单标签

1
2
3
4
5
<!-- 文本内容--> 注释标签 Ctrl+/
<br> 换行标签
<hr> 横线标签

1.2 双标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<p></p> 段落标签
<hn></hn> n 的取值 1-6 标题标签
<font size=”10” color=”red”></font>
<strong></strong> <b></b> 文本加粗
<em></em> i 文本斜体
<ins></ins> u 文本下横线
<del></del> s 删除线

1.3 img标签与属性

src 属性:

  • alt 属性 : 图片无法显示,对图片的描述
  • title 属性: 鼠标放到图片上显示的文字
  • width 属性 : 宽度
  • height 属性 : 高度

1.4 锚链接

1
2
3
<p id="cat">猫</p>
<a href="#cat">抓猫</a>

1.5 列表

  • 无序列表
1
2
3
4
5
<ul>
<li></li>
<li></li>
</ul>
  • 有序列表
1
2
3
4
5
<ol>
<li></li>
<li></li>
</ol>
  • 自定义列表
1
2
3
4
5
6
<dl>
<dt></dt>
<dd></dd>
<dd></dd>
</dl>

2. 标签介绍

2.1 Meta标签介绍 (作用在head标签)

    • Charset :字符集
    • ASCII
    • ANSI
    • Unicode
    • Gbk
    • Gb2312
    • Big5

  • 作用 : 对网站进行优化的作用SEO与网址的描述.


  • 作用 : 网址重定向

2.2 Link标签介绍 (作用在head标签)


  • 作用 : 给网页 titile 中放置小图标


  • 作用 : 引入外部样式表

2.3 表格 (table)

作用 : 显示数据

  • 组成 :
    • table 定义表格
    • tr 行
    • td 列 (单元格)
  • 属性介绍 :

    • border : 设置表格边框
    • width : 设置表格宽度
    • Height : 设置高度
    • cellspacing : 设置单元格之间的距离
    • cellpadding : 文字距离单元格边框的距离
    • bgcolor=”red” : 设置背景颜色
    • align=center left| right : 给 td 或者 tr 设置让文字居中
    • table 设置表格居中
  • 设置表格标题,用法和 td 一样

  • 设置表格表头
    <caption> <h3>课程表</h3></caption>

  • 单元格的合并

    • 横向和合并
1
2
3
4
5
<tr>
<td></td>
<td colspan="2"></td>
<td></td>
</tr>
  • 纵向合并
1
2
3
4
5
<tr>
<td></td>
<td rowspan="2"></td>
<td></td>
</tr>

3. 表单 (from)

3.1 作用

表单的作用用来收集信息

3.2 组成

1
2
3
<form action="">
</from>
  • 属性 :
    • action | 处理用户数据信息
    • Method : get | post
      • Get 数据提交 : 通过地址栏的方式进行数据提交,将用户输入的信息显出来.
      • Post 提交 : 数据通过后台进行提交,不会将用户信息显示出来,安全性比较好.

3.3 表单控件

表单文字输入

1
2
用户名 : <input type="text" maxlength="6">
密码 : <input type="password">

  • 属性 :
    • maxlength : 设置文本输入框最多能输多少字符
    • readonly=”readonly” 设置文本输入框为只读(只能读不能编辑)
    • disabled=”disabled” 控件属于非激活的状态
    • name=”username” 给输入框设置名称
    • Value=”” 设置或者显示输入的值

单选按钮

1
2
<input type="radio" name="xb" checked="checked" value="nan">男
<input type="radio" name="xb" value="nv" >女

  • 属性 :
    • checked=”checked” 设置默认选中项

下拉按钮

1
2
3
4
5
6
7
<select multiple="multiple">
<option>山东</option>
<option>山西</option>
<option>河南</option>
<option>河北</option>
<option selected="selected">北京</option>
</select>
  • 属性 :
    selected=”selected” 设置默认选中项
    multiple=”multiple” 可以实现多选效果

下拉列表分组显示

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="name">
<optgroup label="北京市">
<option> 西城区 </option>
<option>东城区</option>
</optgroup>
<optgroup label="山东省">
<option> 西城区 </option>
<option>东城区</option>
</optgroup>
</select>

多选控件

1
2
3
<input type="checkbox" checked="checked">java
<input type="checkbox">android
<input type="checkbox">ios

多行文本输入框

1
2
3
<textarea>
</textarea>

图片上传控件

1
<input type="file">

按钮系列

1
2
3
4
<input type="submit" value="登录">
<input type="button" value="普通按钮" >
<input type="reset" value="重置按钮">
<input type="image" src="图片按钮.jpg">`

表单分组控件

1
2
3
<fieldset>
<legend>用户注册</legend>
</fieldset>

表单补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 网址判断 -->
<input type="url">
<!-- 邮箱判断 -->
<input type="email">
<!-- 日期控件 -->
<input type="date">
<!-- 时间控件 -->
<input type="time">
<!-- 只能输入数字的控件 -->
<input type="number" step="5">
<!-- 进度条 -->
<input type="range" max="100" step="5">

一.HTML基础(1)

发表于 2017-03-28 | 分类于 WebDesign | 阅读次数

1. 认识windows快捷键

快捷键的认识

2. 认识网页

2.1 网页的组成

文字,图片.按钮,输入框,视频等元素组成.

2.2 Web标准

  • 结构标准 : HTML
  • 表现标准 : CSS
  • 行为标准 : JS

2.3 浏览器与服务器的关系

  • Http : 浏览器与服务器之间传输信息的一种规范
  • Https : 加密处理
  • Url地址 : scheme://host.domain:port/filename

3. 认识HTML

3.1 概述

html ( Hyper Text Markup Language ) 中文译为“超文本标记语言”,主要是通过html标记对网页中的文本,图片,声音等内容进行描述.

3.2 HTML结构

1
2
3
4
5
6
7
<! Doctype html>
<html> 根标签
<head>
<title></title>
</head>
<body></body>
</html>

<! Doctype html> 文档类型

html 文档头部分

网页的标题

html 结构的主体部分

3.3 标签的分类

  • 单标签:只有开始标签没有结束标签
    例如:<! Doctype html>
  • 双标签:有开始标签和有结束标签
    `
    例如:

3.4 标签关系分类

  • 并列关系(兄弟)
    <head></head> | <body></body>
  • 嵌套关系(包含)
    <head> <title></title> </head>

4. sublime的常用快捷键

快捷键 说明书
Ctrl+shift+D 快速复制
Ctrl+L 快速选中
Ctrl+鼠标左键 集体输入
Ctrl+h 查找替换
Ctrl+f 标签查找
Ctrl+shift+↑(↓) 整体移动

5. Html 标签介绍

5.1 单标签

  • 文本注释标签
    <!--hello, world!-->
  • 文本换行标签
    <br>
  • 横线标签
    <hr>

5.2 双标签

  • 段落标签
    <p>hello,world!</p>
  • 标题标签(数字只能取1~6)
    <h1></h1>
  • 文本标签
    <font></font>
  • 文本格式化标签
    • 文字加粗显示
      <strong><strong>
      <b></b>
    • 文字斜体
      <em></em>
      <i></i>
    • 文字删除线
      <del></del>
      <s></s>
    • 文字下划线标签
      <ins></ins>
      <u></u>

5.3 图片标签

<img>

属性 :

Src: 设置显示图片(图片名称或者图片路径)
Title:用来设置鼠标方到图片上显示的文字
Alt:当图盘无法正常显示的时候,对图片的描述
Width: 用来设置图片宽度
Height: 用来设置图片高度

5.4 相对路径

  • 当图片和文件(html)在同一文件夹中
    <img src="1.jpg">
  • 当图片在文件(html)的下一级目录中
    `<img src="1/1.jpg">
  • 当图片在文件(html)的上一级目录中
    <img src="../图片所在文件夹名/1.jpg">

5.5 绝对路径

凡是带有磁盘路径的都是绝对路径或者带有网站地址的也是绝对路径

5.6 超链接

<a href="http://www.itceo.net">我是程序员</a>
<a href="itceo.html">我是程序员</a>
属性 :
Title: 当鼠标放到超链接上显示的文字
target=”_self” 默认值网页在当前页面中打开
target=”_blank” 网页在新窗口中打开

5.7 锚链接介绍

  • 任何一个标签设置 id 属性,并取值
    <p id="cat">猫</p>
  • 给 a 标签设置 href 属性 “#id 名称”
    <a href="#cat">抓猫</a>

5.8 简单的下载功能

a 标签中通过给 href 属性设置一个压缩文件,即可实现下载功能.

5.9 超链接不跳转

<a href="#">NULL</a>

5.10 特殊字符

特殊字符

6. 列表

6.1 无序列表(ul )

1
2
3
4
5
<ul>
<li></li> 列表项
<li></li>
......
</ul>

属性介绍 :
type=”square” 小方块显示
type=”circle” 小圆圈

6.2 有序列表(ol )

1
2
3
4
<ol>
<li></li>
<li></li>
</ol>

6.3 自定义列表(dl )

1
2
3
4
5
6
<dl>
<dt></dt> 小标题
<dd></dd> 列表项
<dd></dd>
<dd></dd>
</dl>

7. 总结

<embed src="1.mp3" hidden="true">放置背景音乐
<marquee>页面自动滚动效果</marquee>

第13章 运行时压缩

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 数据压缩

经过压缩的文件若能100%恢复,就称该压缩为”无损压缩”(Lossless Data Compression) ;若不能恢复原状,称之为”有损压缩”(Loss Data Compression).

1.1 无损压缩

它们最根本的压缩理念也是Run-Lenght, Lempel-Ziv, Huffman, 然后应有了一些各自特有的技术(压缩率,压缩/解压时间).

1.2 有损压缩

有损压缩允许压缩文件(数据)时损失一定信息,以此换取高压缩率.

2. 运行时压缩器

项目 普通压缩 运行时压缩
对象文件 所有文件 PE文件(exe,dll,sys)
压缩结果 压缩文件(zip,rar) PE文件(exe,dll,sys)
解压缩方式 使用专门解压缩程序 内部含有解码程序
文件是否可执行 本身不可执行 本身可执行
优点 可以对所有文件以高压缩率压缩 无须专门解压缩程序便可直接运行
缺点 若无专门解压缩软件则无法使用压缩文件 每次运行均需要解码程序导致运行时间过长

把普通PE文件创建后运行时压缩文件的实用程序称为”压缩器(Packer)”.经反逆向(Anti-Reversing)技术特别处理的压缩器称为保护器(Protector).

2.1 压缩器

PE压缩器是指可执行文件的压缩器,称为”运行时压缩器”,它是PE文件的专用压缩器.

  • 实用目的
    压缩PE文件的大小,便于网络传输保存
    隐藏PE文件内部代码与资源
    压缩后的数据以难以辨认的二进制保存,从文件本身来看,还能有效隐藏内部代码与资源.

  • 使用现状

现状的实用程序,”打补丁”文件,普通程序都广泛应用运行时压缩.

  • 压缩器种类
    PE压缩器大致分两类 : 一类是单纯用于压缩普通PE文件的压缩器;另一类是对源文件进行较大变形,严重破坏PE头的压缩器.

2.2 保护器

不但像普通压缩器一样仅对PE文件进行运行时压缩,而应用了多种防止代码逆向分析的技术.

  • 使用目的
    防止破解,保护代码与资源.

  • 使用现状
    安全保护程序为了防止恶意破解而使用各种保护器来保护自己,常见的恶性代码中也大量使用保护器来防止(或减少)杀毒软件的检测.有些保护器还提供”多变的代码”,每次都会生成不同形态(但功能相同)的代码,这给病毒诊断带来很大的困难.

  • 保护器种类
    保护器种类多样,有公用程序,商业程序,还有专门提供而已代码使用的保护器.

3. 运行时压缩测试

用压缩器UPX压缩notepad.exe,比较了下文件状态.

比较notepad.exe与notepad_upx.exe

比较notepad.exe与notepad_upx.exe

  • PE头的大小一样(0~400h)
  • 节区名称改变(“.text->”upx0”,”.data”->”upx1”).
  • 第一个节区的RawDataSize=0(文件中的大小为0).
  • EP位于第二个节区(原notepad.exe的EP在第一个节区).
  • 资源节区(.rsrc)大小几乎无变化.

我们用PEView查看第一个节区头,如图.

PEView

从VirtualSize值可以发现问题,第一个节区的VirtualSize值竟被设置为10000(而SizeOFRawData值为0).也就是说,经过UPX压缩后的PE文件在运行瞬间将(文件中)压缩的代码解压到(内存中的)第一个节区.

第12章 PE文件格式

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 介绍

PE文件是Window操作系统下使用的可执行文件格式.PE文件是指32位的可执行文件,也称为PE32.64位的可执行文件称为PE+或PE32+,是PE(pe32)文件的一种扩展形式.

2. PE文件格式

种类 主扩展名
可执行系列 EXE / SCR
库系列 DLL / OCX / CPL / DRV
驱动程序系列 SYS / VXD
对象文件序列 OBJ

OBJ(对象)文件之外的所有文件都是可执行文件,DLL / SYS 文件等虽然不能直接在Shell(Explorer.exe)中运行,但可以使用其它方法执行.

下面以打开notepad.exe为例,notepad.exe文件运行需要的所有信息都存储在PE头中,如果加载到内存,从何处开始运行,运行中需要什么DLL,有哪些堆栈内存等,大量信息以结构体形式存储在PE头中, 学习PE文件格式就是学习PE头中的结构体.
notepad.exe文件

2.1 基本结构

从 DOS 头(Dos header)到节区头(Section header)是PE头部分,其下的节区合称PE体.文件中使用偏移(offset),内存中使用VA(Virtual Address ,虚拟地址)来表示位置.文件加载到内存时,情况就会发生变化(节区的大小,位置等).文件的内容一般可分为代码(.text) / 数据(.data) / 资源(.rsrc)节,分别保存.

各节区头定义了各节区在文件或内存中的大小,位置,属性等.

PE头与各节区的尾部存在一个区域,称为NULL填充(NULL padding).在计算机中,为了提高处理文件,内存,网络包的效率.文件 / 内存中各节区的起始位置应该在各个文件 / 内存 最小单位的倍数位置上,空白区域将用NULL填充.

notepad.exe加载到内存中的情形

2.2 VA & RVA

VA指的是进程虚拟内存的绝对地址,RVA(Relative Virtual Address,相对虚拟地址)指从某个基准位置(ImageBase)开始的相对地址.VA 与 RVA 满足下面的换算关系.
RVA + ImageBase = VA
PE头内部信息大多以RVA形式存在.原因在于,PE文件(主要是DLL)加载到进程虚拟内存的特定位置时,该位置可能已经加载了其他PE文件(DLL).此时必须通过重定位(Relocation)将其加载到其他空白的位置,若PE头信息使用的是VA,则无法正常访问,因此使用RVA来定位信息,即是发生了重定位,只要相对于基准位置的相对地址没有变化,就能正常访问到指定信息,不会出现任何问题.

3. PE头

3.1 DOS 头

在PE头的最前面添加了一个IMAGE_DOS_HEADER结构体,用来扩展已经有的DOS EXE头.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAE_DOS_HEADER { //DOS .EXE header 位置
WORD e_magic; //Magic number; 0x00
WORD e_cblp; //Bytes on last page of file 0x02
WORD e_cp; //Pages in file 0x04
WORD e_crlc; //Relocations 0x06
WORD e_cparhdr; //Size of header in paragraphs 0x08
WORD e_minalloc; //Minimum extra paragraphs needed 0x0A
WORD e_maxalloc; //Maximum extra paragraphs needed 0x0C
WORD e_ss; //Initial (relative) SS value 0x0E
WORD e_sp; //Initial SP value 0x10
WORD e_csum; //Checksum 0x12
WORD e_ip; //Initial IP value 0x14
WORD e_cs; //Initial (relative) CS value 0x16
WORD e_lfarlc; //File address of relocation table 0x18
WORD e_ovno; //Overlay number 0x1A
WORD e_res[4]; //Reserved words 0x1C
WORD e_oemid; //OEM identifier (for e_oeminfo) 0x24
WORD e_oeminfo; //OEM information; e_oemid specific 0x26
WORD e_res2[10]; //Reserved words 0x28
LONG e_lfanew; //File address of new exe header 0x3C
} IMAGE_DOS-HEADER, *PIMAGE_DOS_HEADER;

IMAGE_DOS_HEADER 结构体的大小为40个字节,从该结构体必须知道两个重要成员,第一个字段e_magic与最后一个字段e_lfanew.

e_magic : DOS签名(signature, 4D5A=>ASCII值”MZ”)
e_lfanew : 指示NT头的偏移(根据不同文件拥有可变值).

所有PE文件在开始部分(e_magic)都有DOS签名(“MZ”).e_lfanew值指向NT头所在位置(NT头的名称在IMAGE_DOS_HEADERS).

使用 Hex Editor 打开 notepad.exe ,查看IMAGE_DOS_HEADERS结构体.

IMAGE_DOS_HEADERS

根据 PE 规范,文件开头的2个字节为4D5A,e_lfanew值为000000E0,假若修改了这些值,可以发现程序无法正常运行,因为根据PE的规范它已经不是PE文件了.

3.2 DOS 存根

DOS存根(stub)在DOS头下方,是个可选项,且大小不固定(即使没有DOS存根,文件也能正常运行).DOS存根由代码与数据混合而成.

DOS存根

文件偏移40~4D区域为16位的汇编指令.

打开命令窗口,输入debug c:\windows\notepad.exe

3.3 NT 头

1
2
3
4
5
6
typedef struct _IMAGE_DOS_HEADER
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_ OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER32;

IMAGE_DOS_HEADERS结构体由三个成员组成, 第一个成员的签名为(Signature)结构体,其值为50450000h(“PE”00).另外两个成员分别为文件头(File Header)与可选头(Optional Header)结构体.
IMAGE_DOS_HEADERS

3.4 NT头 : 文件头

1
2
3
4
5
6
7
8
9
10
typedef struct _IMAGE_DOS_HEADER
{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER32;

IMAGE_DOS_HEADERS结构体中有以下四种重要成员(若设置不对,不能正常运行.)

  • Machine
    每个CPU都拥有唯一的Machine码,兼容32位 Intel x86 芯片的Machine码为14C.
  • NumberOfSections
    NumberOfSections用来指出文件中存在的节区数量.该值一定要大于0,且当定义的节区数量与实际节区不同时,将发生运行错误.

  • SizeOfOptionalHeader
    IMAGE_DOS_HEADERS结构体的最后一个成员为IMAGE_DOS_HEADER32结构体.SizeOfOptionalHeader成员用来指定出IMAGE_DOS_HEADER32结构体的长度.IMAGE_DOS_HEADER32结构体由C语言编写而成,故其大小已确认.但是Windows的PE装载器需要查看IMAGE_DOS_HEADER的SizeOfOptionalHeader值,从而识别出IMAGE_DOS_HEADER32结构体的大小.

  • Characteristics

该字段用来标识文件的属性,文件是否可以运行状态,是否为DLL文件等信息.以 bit OR 形式组合起来.

3.5 NT头 : 可选头

在IMAGE_DOS_HEADER32结构体中需要关注以下成员,这些值文件是必须运行的,设置错误将导致文件无法正常运行.

  • Magic
    为IMAGE_DOS_HEADER32结构体时,Magic码为10B;为IMAGE_DOS_HEADER64结构体时,Magic码为20B;

  • AddressOfEntryPoint
    AddressOfEntryPoint持有EP的RVA值,该值指出程序最先执行的代码起始地址,相当重要.

  • ImageBase
    进程虚拟内存的范围是 0~FFFFFFFF(32位系统).PE文件被加载到如此大的内存中时,ImageBase指出文件的优先载入地址.

EXE,DLL文件被装载到用户内存的0~7FFFFFFF中,SYS文件被载入内核内存的80000000~FFFFFFFF中.

  • SectionAlignment,FileAlignment
    PE文件的Body部分划分为若干节区,这些节存储着不同类别的数据.FileAlignment指定了节区在磁盘文件中的最小单位,而SectionAlignment则指定了节区在内存中最小单位.磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment值的整数倍.

  • SizeOfImage
    加载到PE文件到内存时,SizeOfImage指定了PE Image在虚拟内存中所占空间的大小.

  • SizeOfHeader
    SizeOfHeader用来指定整个PE头的大小,该值也必须是FileAlignment的整数倍.

  • SubSystem

该SubSystem值用来区分系统驱动文件(.sys)与普通的可执行文件(.exe,*.dll).Subsystem成员可拥有的值如表.

值 含义 备注
1 Driver文件 系统驱动(如:ntfs.sys)
2 GUI文件 窗口应用程序(如:notepad.exe)
3 CUI文件 控制台应用程序(如:cmd.exe)
  • NumberOfRvaAndSize
    NumberOfRvaAndSize用来指定DataDirectory(IMAGE_OPTIONAL_HEADER32结构体的最后一个成员)数组的个数.

  • DataDirectory
    DataDirectory是由IMAGE_DATA_DIRECTORY结构体组成的数组,数组的每项都要被定义的值.

IMAGE_OPTIONAL_HEADER
notepad.exe的IMAGE_OPTIONAL_HEADER

HEX Editor 描述的是notepad.exe的IMAGE_OPTIONAL_HEADER结构体区域.

3.6 节区头

把具有相拟属性的数据统一保存在一个被称为”节区”的地方,然后需要把各节区属性记录在节区头中.

需要为每个code/data/resource分别设置不用的特性,访问权限等.
类别| 访问权限
—|—
code| 执行,读取权限
data | 非执行,读取权限
resource | 非执行,读取权限

IMAGE_SECTION_HEADER
节区头是由IMAGE_SECTION_HEADER结构体组成的数组,每个结构体对应一个节区.

4. RVA TO RAW

PE文件加载到内存时,每个节区都要能准确完成内存地址与文件偏移间的映射.这种映射一般称为 RVA to RAW.

  • 查找RVA所在节区.
  • 换算公式
    RAW - PointtoRawData = RVA - VirtualAddress
    RAW = RVA - VirtualAddress + PointtoRawData

5. IAT

IAT (Import Address Table,导入地址表).IAT保存的内容与Windows操作系统的核心进程,内存,DLL结构等有关.简单理解 : IAT是一种表格,用来记录程序正在使用那些库中的那些函数.

5.1 DLL

  • 不要把库包含到程序中,单独组成DLL文件,需要时调用即可.
  • 内存映射技术使加载后的DLL代码,资源在多个进程中实现共享.
  • 更新库时只要替换相关DLL文件即可.

加载DLL文件时有两种方式 : 一种是”显式链接”(Explicit Linking),程序使用DLL时加载,使用完毕后释放内存;另一种是”隐式链接(Implicit Linking)”,程序开始时即一同加载DLL,程序终止时再释放占用的内存.

5.2 IMAGE_IMPORT_DESCRIPTOR

IMAGE_IMPORT_DESCRIPTOR结构体中记录着PE文件要导入那些库文件.

提示
Import : 导入,向库提供服务(函数)
Export : 导出,从库向其他PE文件提供服务(函数)

第11章 Tut.ReverseMe1

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 运行

弹出消息对话框,显示2条信息.

  • 删除所有烦人的Nags
  • 查找registration code
    选择"确认"按钮,显示主窗口

2. 分析

2.1 目标(1) : 去除消息框

使用OllyDbg打开文件,从00401162地址处看到主函数ThunRTMain.
EP代码

要去除消息框,只要操作调用消息框的函数部分.Visual Basic中调用消息框的函数为MSVBVM50.rtcMsgBox.

在OllyDbg中使用鼠标右键菜单的Search for-All intermodular calls命令,将会列出程序中调用的API目录.
All  intermodular  calls

根据函数名称排序,共有四处调用要找到的函数rtcMsgBox.选择鼠标右键菜单中的Set breakpoint on every call to rtcMsgBox菜单,在所有调用rtcMsgBox的代码处设置断点,如图.
在所有调用rtcMsgBox的代码处设置断点

然后在调试器中按F9键运行程序,程序运行到设有断点的地方停下来.
运行到402CFE地址处停止

向上拉一下滚动条,可以看到消息框显示的字符串,该部分就是程序开始运行时用来显示消息框的代码部分.

2.2 打补丁(1) : 去除消息框

第一次尝试
先修改402CFE地址处的CALL命令,如下图示.
402CFE地址处

修改后,402CFE地址处的ADD,ESP,14命令的含义是,按照传递给rtcMsgBox()参数的大小(14)清理栈.并用NOP填充其余两个字节,以保证代码不会乱(原来CALL命令的大小为5个字节,ADD命令用3个字节,还余下2个字节).

但这样修改是错误的,原因是没有正确处理rtcMsgBox()函数的返回值(EAX寄存器).

第二次尝试

可以看到402C17地址处表示函数开始的栈帧prologue.
402C17地址

402CFE的rtcMsgBox函数调用代码也是属于其他函数内部的代码.所以如果上层函数无法调用,或直接返回.最终将不会调用rtcMsgBox函数.像下面这样修改401C17处的指令

原来

修改后

注意
要根据传递给函数的参数大小调整栈(RETN XX).

此时就成功去除消息框.

2.3 目标(2) : 查找注册码

随便输入一个abcd1234,会弹出错误消息框.
错误消息框

在OllyDbg中检索消息字符串,查看402A69地址处的代码,双击鼠标得到.
402A69地址处的代码

看图中代码,402A2A地址处有`I'mlena151字符串,其下方的402A2F地址处是vbaStrCmp()函数调用代码.vbaStrCmp()API是VB中比较字符串的函数,在本例中用于比较用户输入的字符串与I'mlena151字符串作为比较,我们再向上拖动滚动条.

在4028BD处也存在I'mlena151字符串,可以肯定该字符串就是正确的注册码.

成功消息框

第10章 函数调用约定

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 函数调用约定

Calling Convention ,它是对函数调用时如何传递参数的一种约定.调用函数前先要把参数压入栈然后再传递给函数.栈就是定义在进程中的一段内存空间,向下(低地址方向)扩展,且其大小被记录在PE头中.也就是说,进程运行时确定栈内存的大小(与malloc/new动态分配内存不同).

  • 函数执行完毕后,栈中的参数如何处理?
    由于只是临时使用存储在栈中的值,即使不再使用,清除工作也会浪费CPU资源.下一次再向栈存入其他值时,原有值会被自然覆盖掉,并且栈内存是固定的,所以既不能也没必要释放内存.

  • 函数执行完毕后,ESP值如何变化?
    ESP值要恢复到函数调用之前,这样可引用的栈大小才不会减缩.栈内存是固定的,ESP用来指定栈的当前位置,若ESP指向栈底,则无法再使用该栈.函数调用后如何处理ESP,这就是函数用来约定要解决的问题.主要的函数调用约定如下.

    • cdecl
    • stdcall
    • fastcall

术语说明
调用者 == 调用函数的一方.
被调用者 == 被调用的函数.
比如在 main()函数中调用printf()函数时,调用者为main(),被调用者为printf().

1.1 cdecl

cdecl是主要在C语言中使用的方式,调用者负责处理栈.

1
2
3
4
5
6
7
8
9
10
11
#include "stdio.h"
int add(int a, int b)
{
return (a + b);
}
int main(int argc, char* argv[])
{
return add(1, 2);
}

用OllyDbg调试cdecl.exe文件,从图中可以看到401013~40101C地址间的代码可以发现,add()函数的参数1,2与逆序方式压入栈,调用add()函数(401000)后,使用 ADD ESP,8命令整理栈.调用者main()函数直接清理其压入栈的函数参数,这样的方式就是cdecl.
cdecl.exe文件

提示
cdecl方式的好处在于,它可以像C语言中的printf()函数一样,向被调用函数传递长度可变的参数.这种长度可变的参数在其他调用约定中很难实现.

1.2 stdcall

stdcall方式常用语Win 32 API,该方式由被调用者清理栈.因C语言中默认的函数调用方式为cdecl.若想使用stdcall方式编译源码,只能使用_stdcall关键字即可.

1
2
3
4
5
6
7
8
9
10
11
#include "stdio.h"
int _stdcall add(int a, int b)
{
return (a + b);
}
int main(int argc, char* argv[])
{
return add(1, 2);
}

用OllyDbg调试stdcall.exe文件,从图中可以看到,在main()函数中调用add()函数后,省略了清理栈的代码(ADD ESP,8).

栈的清理工作由add()函数中最后(40100A)的 RETN 8 命令来执行. RETN 8 命令的含义是 RETN + POP8字节,即返回后使用ESP增加指定空间.

stdcall.exe
stdcall方式的好处在于,被调用者函数内部存在着栈清理代码,与每次调用函数时都要用ADD ESP,XXX命令的cdecl命令相比,代码尺寸更小.虽然 Win32 API 是使用C语言编写的,但其采用的方式是stdcall方式.

1.3 fastcall

fastcall方式与stdcall方式基本类似,但该方式通常会使用寄存器(而非栈内存)去传递那些需要传递给函数的部分参数.若某函数有4个参数,则前两个参数分别使用ECX,EDX寄存器传递.

fasgcall方式的优势在于可以实现对函数的快速调用(因为从CPU的立场看,访问寄存器的速度要远比内存快得多.)单从函数调用本身来看,fastcall方式非常快,但是有时需要额外的系统分销来管理ECX,EDX寄存器.

第9章 Process Explorer - 最优秀的进程管理工具

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. Process Explorer

Process Explorer 是 Windows 操作系统下最优秀的进程管理工具.

Process Explorer的运行界面

2. 具体有哪些优点呢?

  • Process / Child 进程树结构.
  • 以不同颜色显示进程运行/终止.
  • 进程终止 (kill) 功能 (支持Kill Process Tree) 功能.
  • 检测 DLL/Handle (检索加载到进程中的DLL或进程占有的句柄).

第8章 abex'crackme#2

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 运行abex’crackme#2

运行界面

输入合适的Name与Serial,按Check按钮,会弹出错误的消息框!
错误的消息框

2. Visual Basic文件的特征

2.1 VB专用引擎

举个VB引擎的例子,显示消息框时,VB代码中要调用MsgBox()函数.其实,VB编辑器真正调用到的MSVBVM60.dll里的rtcMsgBox()函数,在该函数内部通过调用user32.dll里的MessageBoxW()函数(Win32 API)来工作(也可以在VB代码中直接调用user32.dll里的MessageBoxM()).

2.2 本地代码和伪代码

VB文件可以编译为本地代码 (N code) 与伪代码 (P code) .本地代码一般使用易于调试器解析的IA-32指令;而伪代码是一种解析器 (Interpreter) 语言,它使用由VB引擎实现虚拟机并自解析的指令 (字节码) .因此,若想准确解析VB的伪代码,就需要分析VB引擎并实现模拟器.

2.3 事件处理程序

VB主要用来编写GUI程序,由于VB程序采用windows操作系统的事件驱动工作,所以在main()或winmain()中并不吃存在用户代码(希望调试的代码),用户代码存在于各个事件处理程序(event handler)之中.

就上述abex’crackme#2而言,用户代码在点击check按钮时触发的事件处理程序内.

2.4 未文档化的结构体

VB中使用的各种信息 (diglog,control,form,module,function等) 以结构体形式保存在文件内部.由于微软未作公开这种结构体信息,所以调试VB文件会难一些.

3. 开始调试

运行OllyDbg,查看abex’crackme#2文件的反汇编代码
EP

执行程序后,在EP代码中首先要做的是调用VB引擎的主函数 (ThunRTMain()).
ThunRTMain()

EP的地址为401238.401238地址处的PUSH 401E14命令用来把RT_MainStruct结构体的地址(401E14)压入栈.然后40123D地址处的CALL 00401232命令调用401232地址处的的JMP DOWRD PTR DS:[4010A0]指令.该JMP指令会跳转至VB引擎的主函数ThunRTMain()(前面压入栈的401E14的值作为ThunRTMain()的参数.)

3.1 间接调用

40123地址处的CALL 401232命令用于调用ThunRTMain()函数,这里使用了较为特别的技法.不是直接转到MSVBVM60.dll里的ThunRTMain()函数,而是通过中间401232地址处的JMP指令跳转.
间接调用

这就是VC++,VB编译器中常用的间接调用法 (Indirect Call).

4010A0地址是IAT (Import Address Table,导入地址表)区域,包含着MSVBVM60.ThunRTMain()函数的实际地址.

3.2 RT_MainStruct结构体

要注意的是ThunRTMain()函数的参数RT_MainStruct结构体.这里,RT_MainStruct结构体存在于401E14地址处.VB引擎通过参数传递过来的RT_MainStruct结构体获取到程序运行需要的所有信息.

3.3 ThunRTMain()函数

ThunRTMain()代码开始

4. 分析crackme

目前找RT_MainSturct结构体非容易之事,我们借助思路查找错误消息框和字符串.

4.1 检索字符串

All  referenced  text  strings

双击相应字符串,转到其他地址处.
403458地址处

消息框的标题(“Wrong serial!”),内容(“Nope,this serial is wrong!”),还有实际调用信息框函数的代码(4034A6)都显示出来了.

从编程的观点来看,使用某种算法生成序列号,通过比较用户输入的序列号与字符串,代码分为TRUE(序列号相同)与FALSE(序列号不同)两大部分.通俗点说,就是前后存在的字符串比较代码.且序列号正确时程序代码会调用消息框输出成功信息.

往上拖动滚动条,在前面看到了条件转移语句的代码.
条件转移指令

调用403329地址的_vbaVarTstEq()函数,比较(Test命令)返回值(AX)后,由403332地址的条件转移指令(JE指令)决定执行”真”代码还是”假”代码.

上述代码使用的汇编指令说明

  • Test : 逻辑比较 (Logical Compare)
    与bit-wise logical”AND”一样(仅改变EFLAGS寄存器而不改变操作数的值)若两个操作数中一个为0,则AND运算结果被置为0->ZF=1.
  • JE : 条件跳转指令 (Jump if equal), 若ZF=1,则跳转.

4.2 查找字符串地址

403329地址处的_vbaVarTstEq()函数为字符串比较函数,起上方的2个PUSH指令为比较函数的参数,即比较字符串.

调试至403329

403329地址处
00403321地址处的SS:[EBP-44]表达的是 : SS是栈段,EBP是基址指针寄存器.换言之,SS:[EBP-44]指的是栈内地址,它恰好又是函数中声明的局部对象的地址(局部对象存储在栈内).在此状态下查看栈.(栈地址会随调试的环境不同而改变)

跟随堆栈地址

EDX表示实际的地址,EAX表示用户输入的地址.

我们选择数据窗口,长型查看实际的字符串.
Serial字符串

我们重新运行程序,输入Name=”kevin”,Serial=”CFC9DACD”并提示破解成功!
成功

但是Name和Serial之间什么关系呢?为了测试我们输入Name的另外一个值,Serial保存不变.程序显示错误!这就证明了最初的判断 : “以name字符串为基础随时生成Serial”的算法.

4.3 生成Serial的算法

查找函数开始部分

很显然条件转移代码属于某个函数,该函数可能就是Check按钮的事件来处理程序.原因在于选择Check按钮后,该函数会被调用执行.且含有用户代码来弹出成功/失败消息框.

我们向上查找函数开始部分,仔细查看00402ED0地址处的命令.

00402ED0地址处

上述代码是栈帧代码,开始执行函数就会形成栈帧.由此得知该位置就是函数开始部分,即是Check按钮事件的处理程序.

汇编指令
VB文件的函数之间存在着NOP指令.NOP指令表示不执行任何动作的指令.

为了准确分析代码,在402ED0位置处下断点后调试.

按钮事件的处理程序

4.4 预测代码

  • 若是Win32 API程序,则有如下特点.
    • 读取Name字符串 (使用GetWindowText / GetDlgItemText 等API).
    • 启动循环,对字符加密 (XOR / ADD / SUB等).

上述代码使用VB引擎函数编写而成,若预测正确,在事件处理程序开始代码开始调试.查找读取Name字符串的部分后,接着就是出现加密循环.

4.5 读取Name字符串的代码

读取Name字符串的代码

查看00402F8E地址处的代码可以看到,函数的局部对象ss:[ebp-0x88]地址传递给(PUSH)函数的参数.查看该地址.先在00402FA0处下一个断点然后选中跟随数据
ss:[ebp-0x88]

Name字符串(以字符串对象形式)存储到[EBP-88]地址处.

4.6 加密循环

继续调试,遇到如下循环,即一系列循环语句.

循环语句

简单的动作原理为 : 链表中使用next指针引用下一个元素,并且设置EBX,4 使其按指定的次数运转循环.

实际仅使用了接收的Name字符串中的4个字符.在代码内检查字符串的长度,若少于4个字符,就会弹出错误消息框.

4.7 加密方法

  • 从该给定的Name字符串前端逐一读取字符(共4次).
  • 将字符转换为数字(ASCII代码).
  • 向变换后的数字加64.
  • 再次将数字转换为字符.
  • 连接变换后的字符.

5. 总结

通过”Wrong serial”字符串作为目标,然后查找该字符串的代码.从查找的代码开始,向上拖动滑动条.找到生成栈帧(函数开始)的部分.

第7章 栈帧

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

栈帧 (Stack Frame) ,在程序中用于声明局部变量/调用函数.

1. 栈帧

栈帧就是利用EBP (栈帧指针) 寄存器访问栈内局部变量,参数,函数返回地址等的手段.ESP寄存器承担着栈顶指针的作用,而EBP寄存器则负责栈帧指针的职能.程序运行时,ESP寄存器的值随时变化,访问栈中函数的局部变量,参数时,若以ESP值为基准编写程序会十分困难,并且以很难使CPU引用到准确的地址.所以,调用某函数时,先要把用作基准点 (函数起始地址) 的ESP值保存到EBP,并维持在函数内部.这样,无论ESP的值如何变量,以EBP的为基准 (base) 能够安全访问到相关的函数的局部变量,参数,返回地址,这就是EBP寄存器作为栈帧指针的作用.

栈帧结构

2. 调试示例: StackFrame.exe

2.1 StackFrame.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "stdio.h"
long add(long a, long b)
{
long x = a, y = b;
return (x + y);
}
int main(int argc, char* argv[])
{
long a = 1, b = 2;
printf("%d\n", add(a, b));
return 0;
}

使用OllyDbg调试工具打开StackFrame.exe文件,按CTRL+G快捷键 (Go to命令) 转到401000地址处.
调试器界面

2.2 开始执行 main() 函数&生成栈帧

首先在StackFrame.cpp源程序的主函数分析,代码如下.
int main(int argc, char* argv[])
{

函数main()是程序开始执行的地方,在main()函数的起始地址(401020)处,按F2键设置一个断点,然后按F9运行程序,程序运行到main()函数的断点处暂停.

栈初始

当前ESP的初始值为0019FF3C,EBP的值为0019FF80.切记地址401250保存在ESP(0019FF3C)中,它是main()函数执行完毕后返回的地址.

main()函数一开始运行就生成与其对应的函数栈帧.

00401020 push ebp ; # main()

PUSH是一条压栈指令,上面这条PUSH语句的含义是”把EBP值压入栈”.main()函数中,EBP为栈帧指针,用来把EBP之前的值备份到栈中 (main()函数执行完毕,返回之前,该值会再次恢复).

00401021 mov ebp,esp

MOV是一条数据传送命令,上面这条MOV语句的命令是”把esp值传送到ebp”.换原之,就是两值相等,并且直到main()函数执行完毕,EBP的值始终保持不变.也就是说,我们通过EBP可以安全访问到存储在栈中的函数参数与局部变量.执行完401020与401021地址处后的两条命令后,函数main()的栈帧就生成了.

进入OllyDbg的栈窗口,单击鼠标右键,在弹出的菜单中依次选择Address-Relative to EBP,把地址转换为相对于EBP的偏移后,能够直接观察到栈内情况.

备份到栈中的EBP初始值

如图,当前EBP值为0019FF38,与ESP的值一致,19FF80地址处保存着19FF80,它是main()函数开始执行时EBP持有的初始值.

2.3 设置局部变量

下面开始分析源文件StackFrame.cpp中的变量声明与赋值语句.
long a = 1, b = 2;

main()函数中,上述语句用于在栈中为局部变量(a,b)分配空间,并赋初始值.

00401023 sub esp,0x8

SUB是汇编语言中的一条减法指令,上面这条语句用来将ESP的值减去8个字节.执行该指令前ESP的值为:0019FF38,执行后变为:0019FF30.为什么要减去8个字节,其实质为函数的局部变量 (a与b)开辟空间,以便将它们保存在栈中.由于局部变量a与b都是long型,它们分别占据4个字节大小,所以需要在栈中开辟8个字节的空间来保存这2个变量.

使用SUB指令从ESP中减去8个字节,为2个函数变量开辟栈空间后,在main()内部,无论ESP的值如何变化,变量a与b的栈空间都不会受到损坏.由于EBP的值在main()函数内部是固定不变的,我们就能以它作为基准来访问函数的局部变量了.

1
2
00401026 mov [EBP-4],1 ; [EBP-4] = local 'a'
0040102D mov [EBP-8],2 ; [EBP-8] = local 'b'

分析上面的2条MOV命令,它们的含义是”把数据1与2分别保存到[local.1]与[local2]中”.

变量a与变量b

2.4 add()函数参数传递与调用

StackFrame.cpp源代码中使用如下语句调用add()函数,执行加法运算并输出函数返回值.
printf(“%d\n”, add(a, b));

5行汇编代码

请看上面5行汇编代码,它描述了调用add()函数的整个过程.地址40103C处为”CALL 401000”命令,该命令用于调用401000处的函数,而401000处的函数即为add()函数.函数add()接收a,b这2个长整型参数,所以调用add()之前先把2个参数压入栈, 地址401034~40103B之间的代码即用于此.参数入栈的顺序与C语言源码中的参数顺序刚好相反.换言之,变量b首先入栈,接着变量a再入栈.执行完地址401034~40103B之间的代码后,栈内变化情况所示.

传递add()函数的参数

接下来进入add()函数(401000)内部,分析整个函数调用过程.

返回地址
执行CALL命令进入被调用的函数之前,CPU会先把函数的返回地址压入栈,用作函数执行完毕后的返回地址.在地址处调用了40103C处调用了add()函数,它的下 一条命令的地址为401041.函数add()执行完毕后,程序执行完返回到401041地址处,该地址即被称为add()函数的返回地址.执行完40103C地址处的CALL命令后进入函数,栈内情况如图:
函数add()返回地址

2.5 开始执行add()函数&生成栈帧

StackFrame.cpp源代码中,函数add()的前2行代码如下:
long add(long a, long b)
{

函数开始执行时,栈中会单独生成与其对应的栈帧.

1
2
0040100 push ebp
00401001 mov ebp,esp

上述2行代码与开始执行main()函数时的代码完全相同,先把EBP值保存到栈中,再把当前的ESP存储到EBP,这样函数add()的栈帧就生成了.

函数add()的栈帧

可以看到,main()函数使用的EBP值(19FF38)被备份到栈中,然后EBP的值被设置为一个新值19FF20.

2.6 设置add()函数的局部变量(X,Y)

StackFrame.cpp源代码中.
long x = a, y = b;

上面一行语句声明了2个长整型的局部变量(X,Y),并使用2个形式参数(a,b)分别为它们进行赋值初始.

00401003 sub esp,0x8

在栈内存中为局部变量X,Y开辟8个字节的空间.

1
2
3
4
5
6
7
00401006 mov eax,[arg.1] //a
00401009 mov [local.2],eax //x
0040100C mov ecx,[arg.2] //b
0040100F mov [local.1],ecx //y

函数add()的局部变量X/Y

2.7 ADD 运算

StackFrame.cpp源代码中,下面这条语句用于返回2个局部变量之和.
return (x + y);

1
2
3
00401012 mov eax,[local.2] //语句执行完毕,变量X的值会被传送到EAX
00401015 add eax,[local.1] //变量Y

上述这条语句中,变量Y与EAX的原值(X)相加,且运算结果被存储到EAX中,运算完后EAX的值为3.

2.8 删除函数add()的栈帧&函数执行完毕(返回)

“删除函数栈帧与函数执行完毕返回”对应于StackFrame.app文件中的如下代码.
return (x + y);
}

执行完加法运算后,要返回函数add(),在此之前要先删除函数add()的栈帧.

00401018 mov esp,ebp

上面这条命令将当前的EBP赋值给ESP,等价于将EBP中的值恢复到ESP中.

提示
执行完上面的命令后,地址401003处的SUB ESP,8命令失效,即是函数里面的局部变量不再有效.

0040101A pop ebp

上面这条命令用于恢复函数add()开始执行时备份到栈中的EBP值,它与401000地址处的PUSH EBP 命令对应.EBP值恢复为0019FF38,它是main()函数的EBP值.到此,add()函数的栈帧就被删除了.

删除函数add()的栈帧

可以看到ESP的值为:0019FF24,该地址值位:401041,它是执行CALL 401000命令时CPU存储到栈中的返回地址.

0040101B retn

执行上述语句,存储在栈中的返回地址被返回.

2.9 从栈中删除函数add()的参数 (整理栈)

00401041 add esp,0x8

函数add()执行完毕后,就不需要参数a与b了,所以要把esp加上8,将它们从栈中清理掉.

2.10 调用 printf() 函数

StackFrame.cpp源代码中用于打印运算结果的语句如下所示.
printf(“%d\n”, add(a, b));

调用printf()函数的汇编代码如下.

1
2
3
4
5
6
7
00401044 push eax
00401045 push StackFra.0040B384 ; ASCII "%d\n"
0040104A call StackFra.00401067
0040104F add esp,0x8

地址401044处的EAX寄存器中存储函数add()的返回值,它是执行加法运算后的结果3.地址40104A处的CALL 401067命令中调用的是401067地址处的函数.它是一个C标准函数printf(),由于上面的printf()函数有两个参数,大小为8个字节,所以在40104F地址处使用ADD命令,将ESP加上8个字节,把函数的参数从栈中删除,函数printf()执行完毕后通过ADD命令删除参数.

2.11 设置返回值

StackFrame.cpp中设置返回值的语句如下:
return 0;

main()函数使用该语句设置返回值 (0).

00401052 xor eax,eax

XOR命令用来进行异或运算,起特点为 “2个相同的值进行XOR运算,结果为0”.XOR命令比MOV EAX ,0命令执行速度快,常用语寄存器的初始化操作.

2.12 删除栈帧%main()函数终止

StackFrame.cpp中对于代码如下:
return 0;
}
最终主函数终止,同add()函数一样,其返回值要先从栈中删除与其对应的栈帧.

1
2
3
00401054 mov esp,ebp
00401056 pop ebp

执行完上面2条命令后,main()函数的栈帧即将被删除,其局部变量a,b也不再有效.执行完毕.

00401057 RETN

执行完上面命令后,主函数执行完毕之后,程序执行跳转到 (401250),该地址指向Visual C++的启动函数区域,随后执行进程终止代码.

3. 设置 OllyDbg的选项

3.1 打开OllyDbg的Debugging options对话框 (快捷键Alt+O).

OllyDbg的Debugging  options对话框

可以设置一些适合自己的选项.

4. 总结

栈帧技术使用EBP寄存器(而非ESP寄存器)管理局部变量/参数/返回地址等.

第6章 分析abex'crackme#1

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

Abex’crackme是一个简单的绝著名小程序,国内外都有许多网站都对它进行了详细的讲解与说明.将自己的破解方法与其他人作为比较,能够提供自己的计算水平.

1. abex’crackme #1

调试前先运行程序,显示”Make me think your HD is a CD-ROM”消息.
运行程序

消息的最后部分出现了”CD-Rom”这个词,由于我们没有太多的选择,只能继续点击消息窗口的”确定”按钮.

程序弹出ERROR消息窗口后就结束了运行.
弹出消息窗口

1.1 开始调试

首先运行OllyDbg软件载入小程序,代码窗口可以看到程序的汇编代码.
EP代码

EP代码非常短,是因为采用了汇编语言写出来的可执行文件,如果是使用了VC++/VC/Delphi等开发工具编写程序时,除了自己编写的代码外,还有一部分启动函数是由编译器添加的,经过反编译后,代码看上去就变得非常复杂.但是如果直接使用了汇编语言编写程序,汇编代码会直接变为反汇编代码.main()直接出现在EP中,简单直观.

1.2 分析代码

重点看部分关于 Win32 API 调用的内容.
Win32  API  调用的内容

在消息窗口按”确定”后,程序会调用GetDriveTyppe() API,获取C驱动器的类型(大部分返回的是HDD类型),然后操作它,使之被识别为CD-ROM类型,再在消息窗口输出”OK,I really think that your HD is a CD-ROM !”.下面逐行分析crackme的代码.
比较两值

若两值相等,则跳转到40103D,否则从401028继续执行!

提示

指令 说明
PUSH 入栈指令
CALL 调用指定的函数
INC 值加1
DEC 值减1
JMP 跳转到制定地址
CMP 比较给定的两个操作数与SUB命令类似,但操作数的值不会改变,仅改变EFLAGS寄存器. (若2个操作数的值一致,SUB结果为0,ZF设置为1)
JE 条件跳转指令 (若ZF为1,则跳转)

2. 破解

首先移动光标到401026地址处,按空格键,在打开的汇编窗口中将汇编指令JE SHORT 0040130D更改为JMP 0040103D,如图所示:

修改汇编指令

意思就是将条件分支语句(JE)替换为无条件跳转语句(JMP).

3. 将参数压入栈

首先,请看地址00401000~0040100E之间的命令,可以发现调用MessageBoxA()函数之前使用了4个PUSH命令,把函数需要的参数逆序压入栈.
4个PUSH命令

将上述汇编代码转换为C语言函数调用代码,如下所示.

1
MessageBox(NULL,"Make me think your HD is a CD-Rom." ,"abex' 1st crackme",MB_OK|MB_APPLMODAL);

比较C语言代码与汇编代码可以看到,函数调用时的参数顺序(正序)与参数入栈时的顺序(逆序)相反.那么参数入栈时,为什么采用逆序的方式?我们可以从内存结构(FILO,Firts In Last Out)即可.

栈的结构是FILO (先进后出),所以把参数压入栈时,只有按照逆序的方式压入,MessageBoxA()函数才能以正确的顺序接收到该参数!

利用调试器执行到EIP=0040100E地址处,观察右下角的栈窗口.
栈

MessageBoxA()函数从栈中获取需要的参数时,存储在栈中的参数会按照先进后出的规则依次弹出.

1…456…13
王裕杰

王裕杰

以中有足乐者,不知口体之奉不若人也.

130 日志
13 分类
24 标签
GitHub 微博 豆瓣 知乎
© 2016 - 2017 王裕杰
由 Hexo 强力驱动
主题 - NexT.Muse