电子科技大学逆向工程实验报告作业.docx
《电子科技大学逆向工程实验报告作业.docx》由会员分享,可在线阅读,更多相关《电子科技大学逆向工程实验报告作业.docx(11页珍藏版)》请在冰点文库上搜索。
电子科技大学逆向工程实验报告作业
电子科技大学
学生姓名:
马侬
学号:
20152*03**0*
指导教师:
何兴高
日期:
2016.7.15
一.题目名称:
简易记事本软件逆向分析
二.题目内容
由于记事本功能简单,稍有经验的程序员都可以开发出与记事本功能近似的小软件,所以在一些编程语言工具书上也会出现仿照记事本功能作为参考的示例。
为了便于分析因此选取了一个简易的记事本,因此本实验将着重研究从源程序到机器码的详细过程而不注重程序本身的功能。
另一方面简易源程序代码约130多行。
本实验目的是了解源程序是怎么一步步变成机器码的又是怎么在计算机上运行起来的。
三.知识点及介绍
利用逆向工程技术,从可运行的程序系统出发,运用解密、反汇编、系统分析、程序理解等多种计算机技术,对软件的结构、流程、算法、代码等进行逆向拆解和分析,推导出软件产品的源代码、设计原理、结构、算法、处理过程、运行方法及相关文档等。
随着用户需求的复杂度越来越高软件开发的难度也在不断地上升快速高效的软件开发已成为项目成败的关键之一。
为了提高程序员的产品率开发工具的选择尤为重要因为开发工具的自动化程度可以大大减少程序员繁琐重复的工作使其集中关注他所面临的特定领域的问题。
为此当前的IDE不可避地要向用户隐藏着大量的操作细节而这些细节包含了大量的有价值的技术。
四.工具及介绍:
在对软件进行逆向工程时,不可避免地需要用到多种工具,工具的合理使用,可以加快调试速度,提高逆向工程的效率。
对于逆向工程的调试环节来说,没有动态调试器将使用的调试工作很难进行。
可以看出,各种有效的工具在逆向工程中占据着相当重要的地位,有必要对它们的用法做一探讨。
PEExplorer简介:
PEExplorer是功能超强的可视化Delphi、C++、VB程序解析器,能快速对32位可执行程序进行反编译,并修改其中资源。
功能极为强大的可视化汉化集成工具,可直接浏览、修改软件资源,包括菜单、对话框、字符串表等;另外,还具备有W32DASM软件的反编译能力和PEditor软件的PE文件头编辑功能,可以更容易的分析源代码,修复损坏了的资源,可以处理PE格式的文件如:
EXE、DLL、DRV、BPL、DPL、SYS、CPL、OCX、SCR等32位可执行程序。
该软件支持插件,你可以通过增加插件加强该软件的功能,原公司在该工具中捆绑了UPX的脱壳插件、扫描器和反汇编器.,出口, 进口和延迟导入表的功能,使您可以查看所有的可执行文件使用的外部功能,和其中包含的DLL或库的基础上进行分类的结果。
这里一个非常有用的功能是语法的Viewer,它显示功能的调用语法,它知道和可以让你扩大自己的定义的语法数据库。
非常好用。
Dumpbin是VC自带的二进制转储工具可以将PE/COFF文件以文字可读的方式显示出来。
MicrosoftCOFF二进制文件转储器(DUMPBIN.EXE)显示有关通用对象文件格式(COFF)二进制文件的信息。
可以使用DUMPBIN检查COFF对象文件、标准COFF对象库、可执行文件和动态链接库(DLL)。
具有提供此DLL中所输出的符号的清单的功能。
LINK.exe将通用对象文件格式(COFF)对象文件和库链接起来,以创建可执行(.exe)文件或动态链接库(DLL)。
五.源程序
notepad.cpp:
notepad:
:
notepad(QWidget*parent):
QMainWindow(parent),
ui(newUi:
:
notepad)
{
ui->setupUi(this);
this->setWindowTitle("newfile");
QObject:
:
connect(ui->NewFileaction,SIGNAL(triggered()),
this,SLOT(NewFile()));
QObject:
:
connect(ui->OpenFileaction,SIGNAL(triggered()),
this,SLOT(OpenFile()));
QObject:
:
connect(ui->SaveFileaction,SIGNAL(triggered()),
this,SLOT(SaveFile()));
QObject:
:
connect(ui->SaveAsFileaction,SIGNAL(triggered()),
this,SLOT(SaveAsFile()));
QObject:
:
connect(ui->Coloraction,SIGNAL(triggered()),
this,SLOT(SetColor()));
QObject:
:
connect(ui->Fontaction,SIGNAL(triggered()),
this,SLOT(SetFont()));
QObject:
:
connect(ui->Aboutaction,SIGNAL(triggered()),
this,SLOT(About()));
QObject:
:
connect(ui->Helpaction,SIGNAL(triggered()),
this,SLOT(Help()));
}
notepad:
:
~notepad()
{
deleteui;
}
voidnotepad:
:
changeEvent(QEvent*e)
{
QMainWindow:
:
changeEvent(e);
switch(e->type()){
caseQEvent:
:
LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
voidnotepad:
:
NewFile(){
this->setWindowTitle("newfile");
ui->Text->clear();
}
voidnotepad:
:
OpenFile()
{
QStringfilename=QFileDialog:
:
getOpenFileName
(this,"getfile",QDir:
:
currentPath(),"(*.*)");
if(!
filename.isEmpty())
{
QFile*file=newQFile;
file->setFileName(filename);
if(file->open(QIODevice:
:
ReadOnly)==true)
{
QTextStreamin(file);
ui->Text->setText(in.readAll());
this->setWindowTitle(filename);
}
else
{
QMessageBox:
:
information(this,"ERROROccurs","filenotexist");
}
file->close();
deletefile;
}
}
voidnotepad:
:
SaveFile()
{
QStringfilename=this->windowTitle();
// if(pare("newfile")!
=0)
// {
QFile*file=newQFile;
file->setFileName(filename);
if(file->open(QIODevice:
:
WriteOnly)==true)
{
QTextStreamout(file);
outtoPlainText();
file->close();
deletefile;
}
else
{
QMessageBox:
:
information(this,"ERROROccurs","fileopenerror");
}
// }
}
voidnotepad:
:
SaveAsFile()
{
QStringfilename=QFileDialog:
:
getSaveFileName
(this,"savefile",QDir:
:
currentPath());
QFile*file=newQFile;
file->setFileName(filename);
if(file->open(QIODevice:
:
WriteOnly)==true)
{
QTextStreamout(file);
outtoPlainText();
file->close();
deletefile;
}
else
{
QMessageBox:
:
information(this,"ERROROccurs","fileopenerror");
}
}
voidnotepad:
:
SetColor()
{
QColorcolor=QColorDialog:
:
getColor(Qt:
:
white,this);
if(color.isValid()==true)
{
ui->Text->setTextColor(color);
}
else
{
QMessageBox:
:
information(this,"ERROROccurs","setcolorerror");
}
}
voidnotepad:
:
SetFont()
{
boolok;
QFontfont=QFontDialog:
:
getFont(&ok,QFont("Arial",18),this,"setfont");
if(ok)
{
ui->Text->setFont(font);
}
else
{
QMessageBox:
:
information(this,"ERROROccurs","setfonterror");
}
}
voidnotepad:
:
About()
{
Dialogmychild;
mychild.exec();
}
voidnotepad:
:
Help()
{
QDesktopServices:
:
openUrl(QUrl(""));
}
六.过程及分析
1)平台介绍
操作系统:
WindowsXPProfessionalwithSP3
开发工具:
VisualStudio2005ProfessionalEdition
开发语言:
VC++
源文件:
notepad.cpp约130行
2)程序的编译与链接
目标程序运行。
整个程序包含9个函数,
源程序被编译成机器码,在这个过程中除了词法分析、语法分析、语义分析、机器码生成外,最需要程序员关注的是程序的链接过程。
每个C/C++源文件是一个独立的编译模块,也就是说每个文件会首先被编译成目标文件,如这里的*.obj文件,这个过程是编译器的工作。
在目标文件中源程序的函数已被翻译成了机器码。
此外目标文件还包含最重要的一个信息就是重定位信息,这里的重定位信息一般是指静态重定位信息。
静态重定位信息包含了怎样修改引用数据和子程序的指令以及数据的重定位信息。
为什么要包含重定位信息呢?
前面已提到,每个源文件是一个独立的编译模块,那么如果在这个源文件中的函数调用了另外一个源文件中的函数或引用了它的变量时,那么在编译本源文件时是无法知道那个函数的地址的。
因些在生成这些指令时,只能放占位符这样的信息。
当进入链接过程的时假,链接器除了要进行空间分配外,就是要进行符号的解析和符号的重定位。
在汇编级或机器指令级,实质上已经没有了函数的概念了。
因为函数本身是作为高级语言的一种抽象,现在目标文件中只是一堆机器码。
为了表示一个指令序列或数据空间,用使用了符号这一术语。
链接器的空间分配是指根据PE文件格式规范生成可执行文件,在这个过程中如果安排指令和数据以及动态重定位等等的过程。
简单地讲,符号解析是指将找到各模块间相互引用的函数符号,符号重定位就是将前面提到的指令占位符号修改成正常的指令。
当然还包括数据的重定位,相象一下程序引用了一个动态链接库里的变量。
象这些同样要生成重定位信息。
为了减少干扰,将源程序进行Release编译。
在工程的Release目录可以看到notepad.obj文件。
在"开始">"MicrosoftVisualStudio2005"->"VisualStudioTools"->"Visual
Studio2005CommandPrompt",启动命令提示符。
然后执行dumpbin命令,导出符号信息。
由于导出信息很多,只列出如下几个符号:
0D500000000SECT30notype()External|
_WinMain@1601D00000000SECT5notype()External|?
GetFileName@@YAXXZ(void__cdeclGetFileName(void))07D00000000SECT21notype()External|?
WndProc@@YGJPAUHWND__@@IIJ@Z(long__stdcallWndProc(structHWND__*,unsignedint,unsignedint,long))02500000000SECT8notype()External|?
FileToEditBox@@YA_NPAUHWND__@@PAD@Z(bool__cdeclFileToEditBox(structHWND__*,char*))
这里源程序中自定义的函数,它的名称已经是面目全非了。
这象处理的原因在于,C++的函数重载导致的。
函数重载使得,相同函数名称却有不同的函数签明。
所以不经过处理,在下层就无法知道确切的函数。
因此,为了使每个函数的标识唯一,就要对函数名称进行易容处理(mangle),相反的过程叫作复容处理(demangle)。
3)PE/COFF格式
VC/C++链接器,生成的可执行文件是PE格式,PE格式一类文件的规范,这个规范明确指定了在Windows平台可执行程序文件的内部结构,主机针对x86保护模式的程序。
COFF就是目标文件*.obj的格式规范。
PE文件另一个名称就是映象文件,说它是映象文件是因为操作系统的加载器把它加载到内存后,会形成一个它的映象。
但内存映象与文件并不一定一致辞。
如调试符号信息一般不会被加载到内存,它主要由调试器使用。
常见文件就是*.dll和*.exe类型的文件。
而*.com并不是PE文件,但它也是可执行文件,它运行的环境是虚拟8086模式,并非保护模式。
由PE格式的布局图。
PE文件使用的是一个平面地址空间,所有代码和数据都合并在一起,组成一个很大的结构。
主要有:
.text是在编译或汇编结束时产生的一种块,它的内容全是指令代码;.rdata是运行期只读数据;.data是初始化的数据块;.bss是未初始化的数据节;.idata包含其它外来DLL的函数及数据信息,即输入表;.rsrc
包含模块的全部资源:
如图标、菜单、位图等。
现在使用PEExplorer对编译的notepad.exe程序进行逆向。
如图5所示。
由图可以知道程序入口点是0x000028DFh。
当程序被加载到内存执行时,第一条指令将从这里取得。
注意,这个地址是相对虚拟地址(RVM),程序的入口点地址还要道基地址才能得出。
PEExplorer逆向notepad.exe和数据目录
节区头数据,分别是读到了数据目录和区段头信息。
4)调用协定
调用协定规定了函数调用的参数传递方式及返回值的传递方式。
它是应用程序二进制兼容的必要面规范。
常见的调用协定有如下方式:
1__stdcall
用于调用Win32API函数。
采用__stdcall约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。
由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条retn指令直接清理传递参数的堆栈。
2_cdecl:
是C调用约定,按从右至左的顺序压参数入栈,由调用者把参数弹出栈。
对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。
另外,在函数名修饰约定方面也有所不同。
fastcall
快速调用方式。
它的主要特点就是快,因为它是通过寄存器来传送参数的。
实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。
4参数传递分析在目标程序中有这样一个函数声明如下:
BOOLShowFileInfo(HWNDhwnd,HDChDC,HDROPhDropInfo)
发生调用地方为:
ShowFileInfo(hwnd,hDC,hDropInfo);
可以看到最后一条指令是堆栈平衡用的,传递了三个参数,每个参数的大小都为4个字节,所以大小刚好是0x0Ch。
还可以看到第一个压栈的参数是hDropInfo,另外两参数都是用ebp来做基址寻址取到的,说明前两个参数不是局部变量。
参数传递方向从右到左依次压栈。
5)堆栈平衡
参数传递后由调用者或被调用者负责平衡堆栈,但函数使用了局部变量,那堆栈又是如何保持平衡的呢?
这里引入了一个叫栈帧(StackFrame)的概念。
栈帧实质就一个函数栈所用的堆栈空间。
每个函数都平衡了,那么整个程序栈也就平衡了。
如图8所示,函数体的第一条指令就是保存ebp寄存器,它存的就是上一个函数的栈帧边界。
第二条指令就是制定当前函数的栈帧的起始位置。
第三条
指令就是为函数分配局部变量的堆栈空间了。
函数栈的平衡
根椐VC/C++的调用协定,寄存器EAX、ECX、EDX是易变寄存器,也就是说调用函数不能假定被调用函数不改变它们的值。
因此,调用函数想保留它们的值,在调用一个函数之前应自已先把它们保存起来了。
另外的5个通用寄存器(EBX、ESP、EBP、ESI、EDI),则是非易变的。
被调用函数在使用它们之前必须先保存。
所以上图的汇编指令就不难理解了。
函数执行完毕后,只需把先前保存在栈中的EBP弹到ESP就保持了栈的平衡了。
情况确实如此。
如图9所示,最后一条指令是popebp,然后返回。
根据返回指令,还可行知此函数使用的是cdecl调用协定。
因为它没有参数的堆栈平衡。
函数返回平衡堆栈
七.心得体会
逆向工程是一个实践性很强的课程,通过上机实验使我在本次课程的学习中收获很多,通过对程序的逆向分析,本人对计算机技术有了更深的认识。
感谢何兴高老师的谆谆教诲和精彩地讲课,何老师为人随和热情,治学严谨细心。
同时希望能进一步学习更多和逆向工程相关的知识。