逆向基础之二
本帖最后由 鱼无论次 于 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老师们的辛勤栽培!
慵懒丶L先森 发表于 2017-4-14 22:09
之前点开发现要250阅读权限吓尿了,感谢用心总结
我在整理,我网路不好没办法保存 Huanghousec 发表于 2017-4-18 16:09
下次请注明来源:kanxue,tks!
看雪的也是我另外一个ID发的:lol 学习了。。。。 学习了!!!!!!! 感谢分享 感谢分享
感谢分享 这是好东西,反正看不懂 之前点开发现要250阅读权限{:301_1008:}吓尿了,感谢用心总结 谢谢楼主分享