鱼无论次 发表于 2017-4-14 19:13

逆向基础之二

本帖最后由 鱼无论次 于 2017-4-14 21:42 编辑

把自己最近的读书笔记与学习心得分享总结,希望各位看官能有所收获吧。
写的有点乱,如果有错误请指正。
不知道为什么我感觉看代码是歪的,可能是我从oneNote复制到有道笔记再复制过来

参考书籍:
C++反汇编与逆向技术
黑客免杀攻防
天书夜谈

需要用到的工具:
IDA
OD
编译环境:VS2013 Debug版

首先附上跳转表:



主要内容(不分先后顺序)
1.if-else反汇编
2.for反汇编
3.do-while反汇编与while反汇编(阉割版for循环)
4.总结for、do-while、while最容易记的特征
5.switch-case的三种不同表现方式


1.先来个最简单if语言看下再汇编层是如何体现出来的
渣渣代码:
int nNumberA = 2,nNumberB = 3;
002813DEmov         dword ptr ,2
002813E5mov         dword ptr ,3
if (nNumberA > nNumberB)
002813ECmov         eax,dword ptr
002813EFcmp         eax,dword ptr             //cmp比较两个操作数做减法,不送回结果,只影响标志位
002813F2jle            wmain+4Bh (028140Bh)            //我们发现汇编指令满足条件是小于等于则跳转,绕过代码块,好像跟我们写的条件相反。
{
printf("A>B\r\n");
002813F4mov         esi,esp
002813F6push      285858h
002813FBcall          dword ptr ds:
00281401add         esp,4                                          //cdecl调用方式在函数内没有任何平衡参数操作,而在退出函数后对esp执行加8操作
00281404cmp         esi,esp                                        //VS自带的检查堆栈平衡
00281406call      __RTC_CheckEsp (0281140h)          //这个是VS自带的检查堆栈平衡
}            

我们来做个测试
if (nNumberA == nNumberB)
000313ECmov         eax,dword ptr
000313EFcmp         eax,dword ptr
000313F2jne         wmain+4Bh (03140Bh)                   //jnz(不等于)汇编翻译的跳转是跳出去的条件,好像与我们处处作对一样
总结:
       汇编翻译的跳转指令是跳出代码的条件,与你写的条件完全相反。希望各位以后看到汇编翻译的是 jne ,那么你写的条件就是等于

1.2继续if-else if-else代码的反汇编,这里我把VS的一些检查堆栈代码给删掉了
上渣渣代码:
int nNumberA = 5;
00B41A1Emov         dword ptr ,5
if (nNumberA == 2)
00B41A25cmp         dword ptr ,2            
00B41A29jne         wmain+44h (0B41A44h)               //不成立跳到下一个if判断
{
printf("nNumberA=2\r\n");
00B41A2Bmov         esi,esp
00B41A2Dpush      0B458B0h
00B41A32call      dword ptr ds:
00B41A38add         esp,4                                          //cdecl调用方式在函数内没有任何平衡参数操作,而在退出函数后对esp执行加8操作
00B41A42jmp         wmain+7Ah (0B41A7Ah)            //如果代码成立执行到这里直接jmp跳出全部判断语句外面
}
else if(nNumberA == 3)
00B41A44cmp         dword ptr ,3
00B41A48jne         wmain+63h (0B41A63h)               //不成立跳到下一个if判断
{
printf("nNumberA=3\r\n");
00B41A4Amov         esi,esp
00B41A4Cpush      0B45920h
00B41A51call      dword ptr ds:
00B41A57add         esp,4                                          //cdecl调用方式在函数内没有任何平衡参数操作,而在退出函数后对esp执行加8操作
00B41A61jmp         wmain+7Ah (0B41A7Ah)             //如果代码成立执行到这里直接jmp跳出全部判断语句的外面
}
Else                                                                         //不用判断了什么都不是就直接执行了
{
printf("都不是\r\n");
00B41A63mov         esi,esp
00B41A65push      0B459CCh
00B41A6Acall      dword ptr ds:
00B41A70add         esp,4
}


2.最强的for循环反汇编
int Sum = 0;
0085139Emov         dword ptr ,0                     
for (int i = 0; i < 5; i++)
/*************    int i=0   ************/
008513A5mov         dword ptr ,0                            //这句就是int i=0,只初始化一次,初始化完就没有利用价值了
008513ACjmp         wmain+37h (08513B7h)            //直接跳到判断语句也就是i<5的地方
/**************int i = 0************/

/**************i++**************/
008513AEmov         eax,dword ptr
008513B1add         eax,1
008513B4mov         dword ptr ,eax   
/**************i++**************/

/**************i<5***************/
008513B7cmp         dword ptr ,5
008513BBjge         wmain+48h (08513C8h)             //还记得我们的汇编翻译特征码?唱反调,不大于等于则跳出去循环外

/**************i<5***************/
008513B7cmp         dword ptr ,5
008513BBjge         wmain+48h (08513C8h)             //还记得我们的汇编翻译特征码?唱反调,不大于等于则跳出去循环外

{
Sum = Sum + i;
008513BDmov         eax,dword ptr
008513C0add         eax,dword ptr
008513C3mov         dword ptr ,eax   
008513C6jmp         wmain+2Eh (08513AEh)            //执行完代码跳回去执行i++操作         
}
return 0;
008513C8xor         eax,eax

我带画张图帮助大家理解,图画的很渣:


for循环硬生生把i++插到中间去,不是按照我们的思维放在i<5的后面


3.do-while循环与while循环(阉割版for循环)
while (Sum < 5)
008C13A5cmp         dword ptr ,5         
008C13A9jge         wmain+36h (08C13B6h)   //一开始就判断
{
Sum++;
008C13ABmov         eax,dword ptr
008C13AEadd         eax,1
008C13B1mov         dword ptr ,eax
}
008C13B4jmp         wmain+25h (08C13A5h)    //无条件跳转指令回到循环开始的地方

do-while语句的特征,不管怎么样先执行一遍再说
do
{
Sum = Sum + 1;
00B013A5mov         eax,dword ptr
00B013A8add          eax,1                                    
00B013ABmov         dword ptr ,eax         //先执行一遍循环体代码再说
} while (Sum<5);
00B013AEcmp         dword ptr ,5
00B013B2jl          wmain+25h (0B013A5h)         //再判断
return 0;
00B013B4xor         eax,eax

4.总结特征
For有2个jmp
While循环有1个jmp
Do-while没有jmp


5.switch-case的三种不同表现方式
方式一:case<3
代码如下
int NnumA = 2;
00B339CEmov         dword ptr ,2
switch (NnumA)
00B339D5mov         eax,dword ptr
00B339D8mov         dword ptr ,eax
00B339DEcmp         dword ptr ,1       //我们发现switch语句的特点就是把判断全部放在前面
00B339E5je          wmain+42h (0B339F2h)            
00B339E7cmp         dword ptr ,2
00B339EEje          wmain+5Fh (0B33A0Fh)
00B339F0jmp         wmain+7Ah (0B33A2Ah)
{
case 1:
{
printf("%d", NnumA);
00B339F2mov         esi,esp
00B339F4mov         eax,dword ptr
00B339F7push      eax
00B339F8push      0B358A8h
00B339FDcall      dword ptr ds:
00B33A03add         esp,8
00B33A06cmp         esi,esp
00B33A08call      __RTC_CheckEsp (0B311D1h)
break;
00B33A0Djmp         wmain+7Ah (0B33A2Ah)         //跳出判断外面               
}
case 2:
{
printf("%d", NnumA);
00B33A0Fmov         esi,esp
00B33A11mov         eax,dword ptr
00B33A14push      eax
00B33A15push      0B358A8h
00B33A1Acall      dword ptr ds:
00B33A20add         esp,8
00B33A23cmp         esi,esp
00B33A25call      __RTC_CheckEsp (0B311D1h)
break;
}
}
return 0;
00B33A2Axor         eax,eax

问题:
       那么我们就滋生出了一个问题:假如我有10甚至100个判断,程序前面就写100个判断语句吗?那么我们来实验下
类似:
Cmp eax,1
Jxxx xxxx
Cmp eax,2
Jxxx xxxx
Cmp eax,3
Jxxx xxxx
Cmp eax,4
……….

这就引出了switch算法的优化问题
方式2:那么就出现我们第二种方式case > 3 并且case < 256
       计算机是很聪明的,会优化算法。索引地址是从0开始的,我写1-4是方便阅读。其实真正是0-3


这四个值刚好就是我们case的4个值得首地址
部分代码:
case 1:
{
printf("%d", NnumA);
010313BFmov         eax,dword ptr
}
case 2:
{
printf("%d", NnumA);
010313D3mov         eax,dword ptr
}
case 3:
{
printf("%d", NnumA);
010313E7mov         eax,dword ptr
}
case 4:
{
printf("%d", NnumA);
010313FBmov         eax,dword ptr
}

方式3:大于255就采用了平衡二叉树方式优化算法
假如我们case 1000、700、500、300、100平衡二叉树表现形式
然后我们再上代码:
switch (NnumA)
008913CCmov         eax,dword ptr
008913CFmov         dword ptr ,eax
008913D2cmp         dword ptr ,1F4h                   //来中间值500做中间值,判断是否大于500
008913D9jg          wmain+55h (08913F5h)                     //大于500就跳到8913F5H
008913DBcmp         dword ptr ,1F4h               //判断是否等于500
008913E2je          wmain+91h (0891431h)                        
008913E4cmp         dword ptr ,64h                   //判断是否等于100
008913E8je          wmain+0B9h (0891459h)
008913EAcmp         dword ptr ,12Ch                //判断是否等于300
008913F1je          wmain+0A5h (0891445h)                  
008913F3jmp         wmain+0CBh (089146Bh)                  //都不是就退出啦
008913F5cmp         dword ptr ,2BCh               //刚才大于500就跳到这里来了,判断是否等于700
008913FCje          wmain+7Dh (089141Dh)
008913FEcmp         dword ptr ,3E8h               //判断是否等于1000
00891405je          wmain+69h (0891409h)
00891407jmp         wmain+0CBh (089146Bh)                //都找不到就退出

自己画的大致图,虽然不是很严谨:

IDA反汇编图:


致谢
感谢15PB老师们的辛勤栽培!

鱼无论次 发表于 2017-4-14 22:13

慵懒丶L先森 发表于 2017-4-14 22:09
之前点开发现要250阅读权限吓尿了,感谢用心总结

我在整理,我网路不好没办法保存

鱼无论次 发表于 2018-12-13 09:23

Huanghousec 发表于 2017-4-18 16:09
下次请注明来源:kanxue,tks!

看雪的也是我另外一个ID发的:lol

aki阳子 发表于 2017-4-14 20:39

学习了。。。。

黑山走天涯 发表于 2017-4-14 20:51

学习了!!!!!!!

笑颜一如从前Q 发表于 2017-4-14 20:52

感谢分享

fwwzhen 发表于 2017-4-14 20:57

感谢分享

羊先生没胡子 发表于 2017-4-14 21:47


感谢分享

talent1190 发表于 2017-4-14 21:54

这是好东西,反正看不懂

慵懒丶L先森 发表于 2017-4-14 22:09

之前点开发现要250阅读权限{:301_1008:}吓尿了,感谢用心总结

yt20090291 发表于 2017-4-14 22:36

谢谢楼主分享
页: [1] 2 3
查看完整版本: 逆向基础之二