讲道理打了一段时间的ctf,尽是在抱队友的大腿和瞎玩啊QvQ,这里就记录一下自己打的一些东西好了,也会大家分享一下。
逆向破解的相关学习
破解思路:
一般破解的思路是:
检查是否加壳->(脱壳)->利用IDA分析函数分布(可以使用搜索string的方法)->利用ob动态调试查找
8.17
通过关键API交叉引用查找以及通过对关键API下断点来定位关键函数的代码
利用OllyDbg:
分析完程序之后我们发现需要输入数据,利用昨天“查找关键字的方法”查找不到(因为这里对字符串进行了ansi编码,而我们输入的是unicode模式的)所以要换一种方法。有过Win开发经验的话就会记得,弹框的函数为“MessageBox”,这个函数是需要确定。所以我们尝试利用MessageBox来查找该函数的位置(此时还需要知道一个事情,我们使用的MessageBox的函数在win里面其实是一个宏(记得函数颜色是紫色,和宏的颜色是一致的)win通过判断我们程序是否声明使用UNICODE的决定使用的函数种类(不同编码的函数处理方式不同)。所以这里应该调用的是MessageBoxA
做到后来发现,之所以找不到汉字,是以为此时编码方式为ANSI,汉字输入后被编码成了奇怪的东西。。。。。
使用IDA静态分析:
打开文件后,点击import
import 功能:查看此程序调用了哪些外部的函数(命令?)同样也是输入内容后自动匹配。
键入MessageBoxA后,找到对应函数,双击查看位置
此时利用交叉引用来查询在哪里调用了此函数。
交叉引用(xref):可以知道指令代码相互调用的关系。注意这个功能只能在使用系统定义的api才能生效,自定义函数是不能发挥作用的。
然后此时键入x,发现该函数被调用的次数后,点进去查看。
按下F5还原伪代码,仔细观察发现某个LoadString函数中读取的值与我们的输入值发生了比较。
最后使用Restorator进行了查找(话说restorator不是很会用啊。。。)
8.18
OD和IDA中都可以使用<CTRL + G>进行对api的跟踪,通过这个办法能够快速的找到我们需要的API的位置。
8.20
之前两天荒废了。。。今天继续
ollydbg可以搞得有一个叫做“调试”-“执行到用户代码”的功能,这个功能下可以把断点社会到用户当前的指令的下一个位置
PEiD的算法分析:
很多程序都喜欢使用成熟的标准算法来作为注册算法的一个部分,如MD5、Blowfish等。这些算法本身往往就十分复杂和难以你理解,如果从反汇编指令来阅读这些算法则更是难上加难。对于标准算法,实际上我们并不需要知道这些算法的详细计算过程,我们只需要知道是哪一个算法即可,因为标准算法网上都能找到成熟的库文件或者源码等。
PEiD有一个叫做Krypto ANALyzer的插件,使用这个插件可以对程序进行扫描,通过特征匹配来识别程序内部可能用到的一些标准算法。
Krypto ANALyzer的使用方法为:点击PEiD主界面右下角的“=>”按钮,选择“插件”菜单项,然后选择“Krypto ANALyzer”,就可以弹出Krypto ANALyzer插件了。Krypto ANALyzer插件会自动分析程序内部可能用到的标准算法,
8.26
今晚赶着学了一下android的smali文件的反编译。。。这里记录一下:
- 分析程序的主Activity
- 使用记事本打开HelloWorld\smali\com\example\helloworld\MainActivity.smali文件。每个smali文件都由若干条语句组成,所有语句都遵循着同一套语法规范。
如前三行代码:
1 | .class public Lcom/example/helloworld/MainActivity; |
表示一个私有字段info, 它的类型为“Ljava/lang/String;”
一些常见的Smali语法如下:
.field private isFlag:z 定义变量
.method 方法
.parameter 方法参数
.prologue 方法开始
.line 12 此方法位于第12行
invoke-super 调用父函数
const/high16 v0, 0x7fo3 把0x7fo3赋值给v0
invoke-direct 调用函数
return-void 函数返回void
.end method 函数结束
new-instance 创建实例
iput-object 对象赋值
iget-object 调用对象
invoke-static 调用静态函数
名词学习
加壳:
加壳一般是指保护程序资源的方法. 脱壳一般是指除掉程序的保护,用来修改程序资源.
在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。就像动植物的壳一般都是在身体外面一样理所当然(但后来也出现了所谓的“壳中带籽”的壳)。
加壳:其实是利用特殊的算法,对可执行文件里的资源进行压缩,只不过这个压缩之后的文件,可以独立运行,解压过程完全隐蔽,都在内存中完成。它们附加在原程序上通过加载器载入内存后,先于原始程序执行,得到控制权,执行过程中对原始程序进行解密、还原,还原完成后再把控制权交还给原始程序,执行原来的代码部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原,这样就可以比较有效地防止破解者对程序文件的非法修改,同时也可以防止程序被静态反编译。
输入表
输入表中保存的是在程序中调用的定义在其他DLL中的函数信息以及对应的DLL信息。在直接调用windows api的时候,这些api可以再程序的输入表中看到
API断点
OD中可以对特定的API设定断点,无论是直接调用的API还是动态调用的APi
int3
int3是留给调试工具使用的中断,调试工具运行后会替换int3的向量,使得中断方式后执行自己的代码。在单步(例如Debug中的命令p)调试程序时,调试工具会将要执行代码的下一条指令改成int 3,这样执行完当前这行代码后就会执行调试工具的代码,而不会继续执行,从而实现单步调试。一些软件为了阻碍被人破解其程序,会估计使用int3,这样一来,利用int3的调试工具就无法正常调试他们的程序了。
汇编中一般体现为如下指令:
1 | mov eax,0CCCCCCCCh |
rep指令的目的是重复其上面的指令.ECX的值是重复的次数.
STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址.
部分常用汇编段说明:
1 | push ebp |
这个一般是程序的开头,sub这一段当前函数分配局部变量的空间
1 | push ebx |
这三个是一定会有的,ebx是base regiter(基础寄存器,存储储存地址),esi和edi是寄存器压站,保存现场。
大端,小端
对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,这称为大端(big-endian)字节序。
记忆方法:大端——高尾端,小端——低尾端(本机子为小端)
dump内存
Dump的本意是"倾卸垃圾"、“把(垃圾桶)倒空”。在计算机技术中使用Dump的主要意思仍 然如此,即当电脑运行发现故障后,无法排除而死机,通常要重新启动。为了找出故障的原因 ,需要分析现场(即死机时整个内存的当前状况),在重新启动系统之前要把内存中的一片0、 1(这时它们尤如一堆垃圾)"卸出"保存起来,以便由专家去分析引起死机的原因。技术资料中 把这个"卸出"的过程叫dump;有时把卸出的"内容"也叫dump。国际标准化组织(ISO)把前者定 义为To record,at a particular instant,the contents of all or part of one stora geevice in another storage device.Dumping is usually for the purpose of debuggi n。"译文如下:"在某个特定时刻,把一个存储设备中的全部或部分的内容转录进另一个存储 设备之…
如何选择dump 的时机?
手动脱壳理想的最佳dump时机是指壳已经把程序代码包括资源等数据全部解密、输入表等数据还原但还未填充系统函数地址、DLL则还未重定位,此时dump出来的文件只需修正OEP、ImportTableRVA等信息即可正常运行完成脱壳。”
IAT
导入地址表(IAT):Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址
脱壳的基本步骤
- 脱壳的基本步骤
查壳 (PEID、FI、PE-SCAN)–>寻找OEP(OD)–>脱壳/Dump(LordPE、PeDumper、OD自带的脱壳插件、PETools)–>修复(Import REConstructor)