原创博客,转载请注出处!
未完待续~
The quieter you become , the more you are able to hear.
第一部分 代码逆向技术基础
Temp:(2018.05.13笔记梳理)
第一章 关于逆向工程
代码逆向工程(Reverse Code Engineering 简称RCE),分析可执行文件采用的方法大致分为:静态分析法和动态分析法。
学习逆向工程:
宜:有目标、有激情、会google(建议学下《google hacking 渗透性测试者的利剑》)
忌:贪心、急躁、急于求成
第二章 逆向分析Hello World!程序
0X01 OD 's shortcut key comment
f2 set/reset breakpoint
f4 execute till cursor
f7 step info
f8 step over
f9 run
ctrl + f9 execute till return
ctrl + g goto
ctrl + f2 restart
alt + b view breakpoint
alt + m view-memory
; comment
: label
* show the current EIP
- show the previous cursor
enter preview call/jmp address
0X02 How to set base camp by OD?
(1) command : goto
step1:ctrl + g //start find windows
step2:f4 //execute fill cursor
(2)set break point
setp1.f2 //set breakpoint
setp2:alt + b //view breakpoint
step3.select the breakpoint double-click to enter
(3)by comments
setp1.; /*set comments,if you want find it,reght click to select"Search for-user defined comment",the part shown in red is whree the cursor is.if he comment position coincides with the cursor positon,only red characters will be displayed*/
(4)set label
setp1.Move your cursor to somewhere
setp2.: //enput your label.It can be found by right click .
0X03 修改软件回显数据
实验代码如下:
#include "windows.h"
#include "tchar.h"
int _tmain(int argc, TCHAR *argv[])
{
MessageBox(NULL,
L"Hello World!",
L"www.reversecore.com",
MB_OK);
return 0;
}
使用VS2015生成Crackme,将Crackme载入OD
方法1:直接修改字符串缓冲区
通过先前设置的断点找到程序入口后,step info逐步找到MessageBoxW函数地址
在数据区找到对应地址的数据
直接在UNICODE更改数据,值得注意的是使用UNICODE修改字符串后必须以NULL结束,占据2个字节且必须在HEX中添加
在完成上述更改后又将HEX字段末尾处的60涂改为00,我对unicode占据2个字节不明白,姑且这样改,原因见下文。
开始还有疑问,最后的M是什么?后来发现是下个地址空间第一个数据,猜测是起提示作用,防止扩充的字符串溢出
运行结果如下
在保存程序副本和重复测试的时候发现结果不能重现,说明上述操作只是碰巧成功
多次运行查找原因为:忽略字符串所占地址区域4DBE80----4DBE90;2个HEX表示一个字节,而Unicode字符串必须以NULL结束且NULL占据2个字节,简而言之在4DBE90地址的最后要空出4个0;Unicode编码中采用2个字节表示1个字母;
重新设置Unicode字符串发现操作成功
程序保存运行后也成功修改
直接修改的缺陷显而易见,对重改后的字符串长度提出了要求,需要有一定的运气,crack后程序也不稳定
方法2.使用其它内存区域新建字符串传递消息
在程序未使用的内存区域选择一部分作为新的字符串缓冲区,我选择的是0x004fde60,在新地址中写入自定义字符串,其后运行到MessageBoxW函数,改变push的地址内容使其指向刚选择的内存地址
运行结果如下
第三章 小端标记法
例子:如果我们将 0x1234abcd 写入到以 0x0000 开始的内存中,则结果为
高位-------->低位
大端顺序 小端顺序
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
一般来讲(不同的操作系统可能会有不同),数据在计算机上存储时使用的时小端顺序,如果数据进行网络传输则使用大端顺序。
第四章 IA-32寄存器基本讲解
一、状态寄存器
PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成,如下所示:
条件码:
①OF(Overflow Flag)溢出标志。溢出时为1,否则置0。
②SF(Sign Flag)符号标志。结果为负时置1,否则置0.
③ZF(Zero Flag)零标志,运算结果为0时ZF位置1,否则置0.
④CF(Carry Flag)进位标志,进位时置1,否则置0.
⑤AF(Auxiliary carry Flag)辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0.
⑥PF(Parity Flag)奇偶标志。结果操作数中1的个数为偶数时置1,否则置0.
控制标志位:
⑦DF(Direction Flag)方向标志,在串处理指令中控制信息的方向。
⑧IF(Interrupt Flag)中断标志。
⑨TF(Trap Flag)陷井标志。
二、通用寄存器
通用寄存器可用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果。除此之外,它们还各自具有一些特殊功能。通用寄存器的长度取决于机器字长,汇编语言程序员必须熟悉每个寄存器的一般用途和特殊用途,只有这样,才能在程序中做到正确、合理地使用它们。
16位cpu通用寄存器共有8个:AX,BX,CX,DX,BP,SP,SI,DI. 八个寄存器都可以作为普通的数据寄存器使用。但有的有特殊的用途:AX为累加器,CX为计数器,BX,BP为基址寄存器,SI,DI为变址寄存器,BP还可以是基指针,SP为堆栈指针。
32位cpu通用寄存器共有8个: EAX,EBX,ECX,EDX,EBP,ESP,ESI,EDI功能和上面差不多
第五章 栈
栈是一种由高地址向低地址扩展的数据结构(如下图:是从下往上扩展的,即逆向扩展),栈的特征:先进后出(后进先出)
一般结构:
第六章 分析abex’crackme#1
运行后:
点击确定:
使用olldbg载入上面的程序:
将401026处跳转指令修改为jmp即可,即意为无条件跳转。
更改完毕后,先在401026处右键——>数据窗口跟随,找到此处对应数据,然后在该数据区右键——>复制到可执行文件,
右键,保存文件,重命名为1.exe
测试下:
证明破解成功。
第七章 栈帧
栈帧是利用寄存器访问栈内局部变量、参数、函数返回地址等的手段。
栈帧结构:
在栈中保存函数的返回地址是系统安全隐患之一,攻击者使用缓冲区溢出技术能够把保存在栈内存的返回地址更改为其它地址。
第八章 abex‘crackme#2
abexcm2-voiees.exe程序运行界面为:
VB程序采用事件驱动方式工作,所以在main()和WinMain()中并不存在用户代码(想要调试的代码),用户代码存在于各个事件处理程序中。
★:WinMain是一个函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。WinMain函数应初始化应用程序,显示主窗口,进入一个消息接收一发送循环,这个循环是应用程序执行的其余部分的顶级控制结构。
1.setup code
00401238处是EP的地址,在EP代码中首要要做得就是调用VB引擎的主函数(ThunRTMain())。401238地址处的命令用来把RT_MainStruct结构体的地址(401E14)压入栈。然后执行
0040123D . E8 F0FFFFFF call
进而执行指令:
00401232 $- FF25 A0104000 jmp dword ptr ds:[<&MSVBVM60.#ThunRTMain_100>] ; msvbvm60.ThunRTMain
该JMP指令会跳转至VB引擎的主函数ThunRTMain(),前面压入栈的401E14的值作为ThunRTMain()的参数
RT_MainStruct结构体数据如下图,此结构体的成员是其他结构体的地址,也就是说VB引擎通过参数传递来的RT_MainStruct结构体获取程序运行需要的所有信息。
2.Indirect call
如上图所示,40123d地址处的命令用于调用ThunRTMain()函数,这里使用特殊的方法,不是直接转到MSVBVM60.dll中的ThunRTMain()函数,而是通过中间的401232地址处的JMP命令跳转。
这是VC++/VB编译器中常用的间接调用法。
上图地址004010a0是IAT(Import Address Table,地址导入表)区域,包含着MSVBVM60.ThunRTMain()函数的实际地址。
3.进入ThunRTMain()函数中后,发现内存地址完全不同了。此处为MSVBVM60.dll模块的地址区域,即此处不是用户程序代码而是VB引擎代码。
4.字符串搜索
★:程序刚载入OD立即搜索和程序运行一段后再搜索结果不同。
5.VB消息框函数:
例如:004034A6 . FF15 34104000 call dword ptr ds:[<&MSVBVM60.#rtcMsgBox_595>] ; msvbvm60.rtcMsgBox
其中的rtcMsgBox函数即为消息框函数
6.判断输入和程序预设是否一致,来确定最后跳转到错误处理还是正确处理函数中。典型示例:
下图所示程序,当程序刚载入OD时进行字符串检索,发现提示作用的字符串,双击字符串找到对应的代码部分,以此为起点向前看看,发现403424处存在条件跳转,而403418处存在函数调用,推测出403424处的函数是输入正误的检测函数,推测其过程应该是:先调用403418处的检测函数检测用户键入数据,返回值存于ax中,再由403421处检测是否可以执行跳转(test:按比特与操作,其结果只改变标志位寄存器的值而并不改变操作数的值,若两个操作数中一个为0,则运算结果为0,则ZF=1)(je:条件跳转指令,若ZF=1则跳转)(此处test ax,ax;的操作就是检测执行0040341B处的调用后其返回值ax是否为0)。再往上分析,40341B检测函数上面的两个push操作则应该是该检测函数的参数。
7.VB使用的是unicode类型的字符串
8.分析abexcm2-voiees.exe生成Serial的加密算法:
VB文件的函数之间存在着NOP指令 NOP指令:不执行任何动作的指令,只消耗CPU时钟
API编程和windows编程的区别:API是控制台应用程序,就是运行时出现一个就像dos黑色窗口,而widows程序设计是窗口应用程序,就像WORD,记事本等这样的有窗口框架的程序设计。
编程思想不同,API语言是面向过程的,可以理解为顺序的执行;而Widows是面向过程的,事件触发,模块化的编程思想。
在VB中字符串使用字符串对象(这与C语言中使用char数组不同),OD中数据视图Hex模式很难辨认出实际的字符串,因此将数据视图模式改为Long-Address with ASCⅡ dump视图模式。
函数部分的代码查找:执行新函数一定会有栈帧的操作,且在VB中与其他函数代码会以NOP指令隔开。
分析程序加密算法:
加密循环语句:
1
2
3
4
5
6
7
8
9
10
|
00403102
. BB 04000000 mov ebx, 0x4
... ...
0040318B . FF15
30104000
call dword ptr ds:[<&MSVBVM60.__vbaVarForInit>] ; \__vbaVarForInit
00403191
. 8B1D 4C104000 mov ebx,dword ptr ds:[<&MSVBVM60.#rtcMidCharVar_632>] ; msvbvm60.rtcMidCharVar
00403197
> 85C0 test eax,eax
00403199
. 0F84 06010000 je abexcm2-.004032A5
... ...
0040329A . FF15 C0104000 call dword ptr ds:[<&MSVBVM60.__vbaVarForNext>] ; \__vbaVarForNext
004032A0 .^ E9 F2FEFFFF jmp abexcm2-.
00403197
004032A5 > 8B45
08
mov eax,dword ptr ss:[ebp+ 0x8 ]
|
参看VB函数手册(参看笨笨D幸福 的博客)可知函数__vbaVarForInit()和__vbaVarForNext()作用分别为:
bpx vbaVarForInit ->重复执行初始化 bpx vbaVarForNext ->重复执行循环结构, For… Next… (Loop)
加密算法代码如下图:
重要节点用断点标识,此段结合调试中数据、堆栈、寄存器的值可推出其加密过程为:
1、从给定的Name字符串前端逐一读取字符(共4次)
2、将字符转换为数字(ASCⅡ码)
3、将变换后的数字加64
4、将变换后的数字转化为字符
5、连接转换好的字符
加密算法推理分析过程:
第十章 函数调用约定
函数调用方式:
1.cdecl方式,该方式主要是C语言中使用的方式,调用者直接清理其压入栈的函数参数。其好处是可以像c语言中printf函数一样向被调用函数传递长度可变的参数。示例如下:
1
2
3
4
5
6
7
8
9
10
|
#include
int
add( int a, int b)
{
return
a+b;
}
int
main( int argc, char * argv[])
{
return
add(1,2);
}
|
反汇编后代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
00401000
push ebp
00401001
mov ebp,esp
00401003
mov eax,dword ptr ss:[ebp+ 8 ]
00401005
add eax,dword ptr ss:[ebp+c]
00401009
pop ebp
0040100a retn
0040100b int3
0040100c int3
0040100d int3
0040100e int3
0040100f int3
00401010
push ebp #main()
00401011
mov ebp,esp
00401013
push 2
00401015
push 1
00401017
call 00401000
0040101c add esp,
8
!!!!!!!!!!!!!!!!!
0040101f pop ebp
00401020
retn
|
上图中add()函数参数逆序压入栈,调用add()函数后使用add esp,8命令整理栈,调用者main()函数直接清理其压入栈的函数参数。
2.stdcall方式,该方式常用于Win32API,该方式由被调用者清理栈,c语言中默认是cdecl方式调用,如果想用stdcall方式编译源码则需要使用_stdcall关键字。示例如下:
源码为:
1
2
3
4
5
6
7
8
9
10
|
#include
int
_stdcall add( int a, int b)
{
return
a+b:
}
int
main ( int argc, char * argv[])
{
return
add(1,2);
}
|
反汇编后代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
00401000
push ebp
00401001
mov ebp,esp
00401003
mov eax,dword ptr ss:[ebp+ 8 ]
00401005
add eax,dword ptr ss:[ebp+c]
00401009
pop ebp
0040100a retn
8 !!!!!!!!!!!!!
0040100d int3
0040100e int3
0040100f int3
00401010
push ebp
00401011
mov ebp,esp
00401013
push 2
00401015
push 1
00401017
call 00401000
0040101c add esp,
8
0040101f pop ebp
00401020
retn
|
在main()函数中调用add()后省略了清理栈的代码add esp,8 。栈的清理工作由add()函数中最后的retn 8命令来执行。retn 8命令的含义为retn pop8字节,即返回后使esp增加指定的大小。stdcall方式的好处是,被调用者函数内部存在着栈清理代码,与每次调用函数时都要用add esp,xxx命令的方式相比代码尺寸要小。虽然win32api使用c语言编写,但它使用stdcall方式调用,这是为了获得更好的兼容性,使c语言之外的其他语言也能直接调用api。
3.fastcall方式
该方式通常会使用寄存器而非栈内存来传递哪些需要传递给函数部分参数(前两个),若函数有四个参数,则前两个参数使用ecx,edx寄存器来传递。此方式的优势是单从函数调用本身来看,此方式非常快,但有时需要额外的系统开销来管理ecx,edx寄存器,若调用前ecx,edx中有重要数据,则必须先备份。如果函数本身很复杂,需要把ecx,edx用作他途时也需要将他们的参数值存储到另外的某个地方。
第九章 Process Explorer ——最优秀的进程管理工具
建议到微软官网上下载:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
process explorer拥有windows任务管理器无法比拟的优秀界面组织结构。以parent/chirld树结构显示当前运行的进程,以不同颜色显示进程运行/终止情况,还具有任务管理器相同的杀死进程/树的能力。
打开后界面如下:
第十一章 视频讲座
Lena在网站:https://tuts4you.com/ 上贴出了很多chackme讲座,来帮助逆向初学者学习代码逆向分析技术。
(另:相似的国内学习论坛有:吾爱破解论坛、学破解论坛、飘云阁论坛、看雪论坛)
目标软件运行测试:
此处点击确定,若点击取消则程序退出运行。
点击register me,若点击Nag则程序退出运行。
使用olldbg载入程序
第十二章 究竟应当如何学习代码逆向分析
1.有学习目标:确定一个目标,如:想要成为一名逆向工程专家、为了就业、感兴趣、成为一名黑客、、、、、、就我而言,对我来讲逆向工程只是一个踏板,我规划的技术路线是:逆向工程——>病毒分析——>漏洞挖掘,我们都知道0day才是当今网络世界的终极武器,没有0day的攻击只是小大小闹,没有意义,在当下乃至未来的网络战场上掌握一个0day库是非常帮的一件事(脑补你开挂打游戏),到不是鼓励各位从事什么破坏活动,只是,在网络世界和现实世界联系日益紧密的今天,网络世界的能力也一定程度上相当于现实生活中的,而且随着现实和虚拟两个世界联系的越来越紧密,这种能力只会被放大而不会缩小。掌握信息战场的主动权,一定程度上也就相当于掌握了世界法则。
2.拥有积极的心态。这个我不懂,那个我不懂,,,其他人都会就我不会,,,这种情绪在开始的时候是难免的,每个人成长经历不同,天赋不同,所以在某些事情上起跑线不一样,没关系,这很正常,如果你都懂了那还学这个干嘛?要有一种平和随性的心态,干自己的事,别人怎么看并不重要。
3.享受其中的乐趣。记得电影里的一句话——“不要把乐趣局限在网络世界”。具体体会方式各位自行Get吧。
4.具体学习方式方法。这个因人而异。我提供的一点思路是——反复。学到后面的知识,关联前面的知识,不断反复,来回翻看。