【逆向】【Part 2】逆向分析基础

1.基本知识回顾

1.MessageBox函数

PS:这个函数出现的频率太高了,建议记下来(以防考试)。

函数原型:
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT UType);
参数:
 hWnd:标识将被创建的消息框的拥有窗口。如果此参数为NULL,则消息框没有拥有窗口。

 lpText:指向一个以NULL结尾的、含有将被显示的消息的字符串的指针。

 lpCaption:指向一个以NULL结尾的、用于对话框标题的字符串的指针。

 uType:指定一个决定对话框的内容和行为的位标志集。

2.测试指令TEST和CMP

  • AND

了解TEST之前先来看:逻辑与指令AND

逻辑与的运算规则是:进行逻辑与运算的两位都是逻辑1,则结果为1;否则,结果都是0。

相当于乘法,或 (or) 相当于加法

也就是说,逻辑0和逻辑0相与的结果为0,逻辑0和逻辑1相与的结果为0,逻辑1和逻辑0相与的结果为0,只有逻辑1和逻辑1相与的结果才为1。

and reg,imm/reg/mem   ;逻辑与:reg=reg^imm/reg/mem
and mem,imm/reg       ;逻辑与:mem=mem^imm/reg

and指令支持的目的操作数是寄存器和存储单元,源操作数是立即数、寄存器和存储单元,但不能都是存储器操作数。

它设置标志CF=OF=0,(进位和溢出为都是0),根据结果按定义影响SF,ZF,PF(符号、零标志、奇偶标志) 

  • TEST

测试指令TEST将两个操作数按位进行逻辑与运算。格式如下:

test reg,imm/reg/mem       ;逻辑与:reg^imm/reg/mem
test mem,imm/reg           ;逻辑与:mem^imm/reg

Test指令不返回逻辑与结果,只根据结果像AND指令一样来设置状态标志。TEST指令通常用于检测一些条件是否满足,但又不希望改变操作数的情况。

test指令和cmp指令类似。一般后面跟条件转移指令,目的是利用条件转向不同的分支。

  • SUB

了解cmp指令之前,先来看一下减法指令SUB。

减法指令SUB(Substract )使目的操作数减去源操作数,差的结果送到目的操作数。

sub reg,imm/reg/mem   ;减法:reg=reg-imm/reg/mem
sub mem,imm/reg       ;减法:mem=mem-imm/reg 

像ADD指令一样,SUB指令支持寄存器与立即数、寄存器、存储单元,以及存储单元与立即数、寄存器间的减法运算,按照定义影响6个状态标志位。

mov eax,0aaff7348h   ;eax=aaff7348h

sub al,27h           ;eax=aaff7321h,OF=0,SF=0,ZF=0,PF=1,CF=0 
  //没有溢出,最高位是0,结果不是0,最低字节中“1”的个数为0或偶数(PF=1),最高有效位没有出现借位

sub ax,3fffh         ;eax=aaff3322h,OF=0,SF=0,ZF=0,PF=1,CF=0 

sub eax,0bb000000h   ;eax=efff3322h,OF=0,SF=1,ZF=0,PF=1,CF=1
//和上面不同的是:最高位是1(负数),有借位出现
  • CMP

比较指令cmp(Compare)使目的操作数减去源操作数,差值不回送到目的操作数,但按照减法结果影响状态标志

cmp reg,imm/reg/mem       ;减法:reg=reg-imm/reg/mem
cmp mem,imm/reg           ;减法:mem=mem-imm/reg 

cmp指令通过减法运算影响状态标志,根据标志状态可以获知两个操作数的大小关系。它主要是为了给条件转移等指令使用其形成的状态标志。

  • 关于TEST和CMP的例子
lpString1=dword ptr 10h

push ebp
mov ebp,esp
mov eax,[ebp+lpString1]  
;寄存器相对寻址也可写成mov eax,lpString1[ebp],有效地址是寄存器内容与位移量之和

push offset String2 ;"2012"
push eax            ;lpString1
call ds:__imp__lstrcmpW@8  ;lstrcmpW(x,x)

push 0  ;uType
push offset Caption  ;"MESSAGE"  ,说明文字即标题

test eax,eax
jnz short loc_401035
;short是无条件转移指令 短转移:同一个代码段中,转移范围一个字节(增大127字节,向地址减小方向128)

push offset Text ;"Hello! 2012",被显示的消息

call ds:__imp__GetActiveWindow@0;GetActiveWindow()
push eax
call ds:__imp__MessageBox@16;MessageBoxW(x,x,x,x)

xor eax,eax
pop ebp
retn 10h    ;lpCaption

/////设置标志状态
test    eax, eax
if (eax==0)
   ZF=1            //(true)相与的结果为0
else
   ZF=0            //(false)相与的结果不为0


jnz  401035        //(fasle跳)如果ZF为0跳转
if (ZF==0)
   GOTO 401035
else
   继续执行下一条指令
 

 关于上面出现的 retn 10h :

  • retn

http://www.weizn.net/?post=201

retn操作:先eip=esp,然后esp=esp+4  (RETN等价于一条指令:POP   eip)
retn N操作:先eip=esp,然后esp=esp+4+N  (等价于pop eip ,add esp,n)

回顾

  • CALL指令:

    (1)段内转移的CALL指令等价于两条指令:
  push eip
  jmp   目的位置
  也就是说,执行段内转移的CALL指令时,相当于先后执行以上两条指令。
  (2)段间转移的CALL指令等价于三条指令:
  push CS
  push eip
  jmp   目的位置

  • ret指令

(相当于pop EIP)

CSDN论坛:https://bbs.csdn.net/topics/360239577

ret 是32位地址的返回或16位选择子加32位地址的返回(由编译器分辨是翻译成retn还是retf)

retn 是32位地址的返回(NEAR)
retf 是16位选择子加32位地址的返回(FAR)

下面找一堆:

*ret 用在c风格函数调用返回时,ret n 用在pascal风格和windows api的__stdcall规范的函数返回调用

ret:(return)默认retn.
retn:return near=pop eip
retf: return far=pop eip;pop cs

看雪:https://bbs.pediy.com/thread-46439.htm 

 

然后这个题:https://max.book118.com/html/2017/0110/82007884.shtm

有点奇怪哈?不是应该选A吗?

然后我不死心:

?希望考试没有这题吧

不钻牛角尖了,反正一句pop eip得了

然后我再来看看这个代码里面的retn 10h是啥意思:

关于这个代码:https://blog.csdn.net/guanshanyue96/article/details/89001669

这个博客写的更详细,过程。

本来以为自己可以很快画完的,结果在画的时候遇到了不少问题。

  • 问题1:对于lpString1=dword ptr 10h  没有理解到位,导致 mov eax,[ebp+lpString1]想当然。

那么我来看看ptr这个命令的意思:

  • 类型转换操作符PTR

类型转换操作符PTR用于更改变量名的类型

mov eax,dword ptr array

EAX是32位寄存器,属于双字量类型,array被定义为字量类型,二者类型不同,所以需要转换(mov不允许不同类型的数据进行传送数据)

那么对于这个

lpString1=dword ptr 10h 

实际上就是lpString1就是一个10h的立即数而已。

后面

mov eax,[ebp+lpString1]

其实就相当于

mov eax,[ebp+10h]

所以在我画的图看到一团乱麻的地方就是因为我没有理解清楚,想当然认为lpString是4字节什么玩意的。

  • 问题2:关于retn和被调用的函数里面会不会释放自己的参数,堆栈平衡之类的还不熟练。

但是根据上面的梳理,磕磕绊绊应该也可以了。

上面被调用的函数都会自己扫尾,而且跳过来。

后面retn 10h,因为理解清楚了上面lpString1的含义,也就清楚了。

  • 更多问题,后面遇到再挨个解决。

2.动态分析常用方法

软件逆向分析

主要手段:

  • 静态分析&动态分析 静态:不运行代码和程序,IDA
  • 动态:运行进程,分析寄存器和内存的变化;Ollydbg/Windbg
  •  设置断点

1)代码定位断点。把断点设置在某个代码段,当程序执行到下断的位置时会暂停运行,这时可以通过单步运行方式来跟踪和分析代码。

2)数据断点。在分析内存数据时,可以对数据下内存访问断点,当程序对下断数据进行访问时就会中断:

也可对数据下内存写入断点,当程序修改下断数据时就会中断在写指令的代码段。

3)条件断点

“BYTE PTR[esp+0x08]= =0x20”。

条件表达式的意思是:取出堆栈中第三个元素,当它的低位一个字节的值为0x20时 ,中断在设置断点的代码段。

  • 字符串追踪

用字符串进行追踪时,可以借助调试工具内存地址中查找字符串,找到后对它设置数据断点并单步追踪程序。

  • API函数跟踪

在跟踪API函数时,首先要借助调试工具找到API函数的代码段,再在函数入口地址处下断,反向查找调用API函数的代码直至确定最终的要找的代码段

 

===========

后记:

2020.1.08:考完了。估计以后不一定再会看这方面的东西了,除非工作需要了吧?

博客只整理了三个PART,后面进度赶不上了,就直接在书上画画写写整理整理自己的理解背诵背诵了。

关于考试:自己一开始太大意了,觉得时间很充足,以前考试考完还有很长时间,前面就做的很细致很慢,导致最后30分钟两道还没有写。

2020.1.9:今天出成绩了,意料之中,毕竟很多题没有很准确也差些没写完。班里很多同学考的都很好,请教了一个考了90+的女同学,她说考前的那个晚上她只睡了1个小时,相比较我只熬到了晚上1.30来说,自己真的还差了很多。

突然增长的一些自信又压了下去,感觉我的学习方法还需要继续改进。

我之前有一种想法,通过这些考试,我可以锻炼自己的学习方法,争取考研可以事半功倍,不要做无用之功。

现在稍微总结回顾一下我的学习方法:我总是试图找到很多可供参考的东西,然后阅读了解很多相关的东西,最后才看那些“重点”,缺少深加工 和 反复理解思考 以及 及时复习温故

那在以后的学习中,希望我可以做到至少下面这几点:

学习过程中时刻思考-多角度思考可能会出现的题目考点-思考是不是自己在“表面上努力自我感动”然后及时调整学习方法-最后,我真的佩服那个女生,自愧不如,相形见绌,(语言能力有限)总的来说就是革命尚未成功,同志仍需努力!加油!

发布了68 篇原创文章 · 获赞 20 · 访问量 6867

猜你喜欢

转载自blog.csdn.net/qq_43633973/article/details/103681303