通过调用栈对软件爆破,以后不用总是搜索字符串了
基础知识栈
栈是操作系统在运行时自动初始化的一块区域,是数据暂时存储的的动态内存区域。它的大小在Windows操作系统下由PE文件结构中PE文件头中IMAGEOPTIONALHEADER结构中SizeOfStackReserve字段所定义。在OD中,要想顺利分析软件的功能,在单步需要关注的便是寄存器和栈空间。下面,我们来看一下软件是怎么利用栈的。函数的调用过程
我们用一个充满函数的小软件来研究函数的调用,源代码如下:复制代码隐藏代码includeiostreamincludecstdioincludecstdlibincludewindows。husingnamespacestd;intfunction(inta,intb);这是第一个函数,揭示控制台应用程序函数调用过程intWINAPIwinfunction(inta,intb);winapi调用方式intcdeclcfunction(inta,intb);c函数方式intstdcallcppfunction(inta,intb);c函数方式intfastcallffunction(inta,intb);寄存器函数方式intstdcallstdfunction(inta,intb);其实就是WINAPI调用方式intmyfunction(inta,intb);递归调用演示intmain(){inta;intb;printf(定位汇编代码);scanf(d,a);scanf(d,b);scanf方便找汇编代码winfunction(a,b);cfunction(a,b);cppfunction(a,b);ffunction(a,b);stdfunction(a,b);myfunction(a,b);return0;}intfunction(inta,intb){intcab;加法运算最简单returnc;}intWINAPIwinfunction(inta,intb){intcab;加法运算最简单returnc;}intcdeclcfunction(inta,intb){intcab;加法运算最简单returnc;}intstdcallcppfunction(inta,intb){intcab;加法运算最简单returnc;}intfastcallffunction(inta,intb){intcab;加法运算最简单returnc;}intstdcallstdfunction(inta,intb){intcab;加法运算最简单returnc;}intmyfunction(inta,intb){a;b;myfunction(a,b);}
将编译出的软件用OD载入,通过API定位至调用语段:
简单分析一下,易得:复制代码隐藏代码0040159A8B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)0040159D8B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015A089542404movdwordptrss:〔esp0x4〕,edx;将EDX内容压入栈,就是我输入的b(2)004015A4890424movdwordptrss:〔esp〕,eax;将EAX内容压入栈,就是我输入的a(1)004015A7E881000000call未命名1。0040162D;WINAPI调用方式004015AC83EC08subesp,0x8;恢复堆栈004015AF8B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)004015B28B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015B589542404movdwordptrss:〔esp0x4〕,edx;将EDX内容压入栈,就是我输入的b(2)004015B9890424movdwordptrss:〔esp〕,eax;将EAX内容压入栈,就是我输入的a(1)004015BCE884000000call未命名1。00401645;c函数方式004015C18B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)004015C48B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015C789542404movdwordptrss:〔esp0x4〕,edx;将EDX内容压入栈,就是我输入的b(2)004015CB890424movdwordptrss:〔esp〕,eax;将EAX内容压入栈,就是我输入的a(1)004015CEE888000000call未命名1。0040165B;c函数方式004015D383EC08subesp,0x8;恢复堆栈004015D68B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)004015D98B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015DC89C1movecx,eax;将EAX寄存器的值放入ECX004015DEE890000000call未命名1。00401673;寄存器方式004015E38B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)004015E68B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015E989542404movdwordptrss:〔esp0x4〕,edx;将EDX内容压入栈,就是我输入的b(2)004015ED890424movdwordptrss:〔esp〕,eax;将EAX内容压入栈,就是我输入的a(1)004015F0E89A000000call未命名1。0040168F;stdcall调用方式004015F583EC08subesp,0x8;恢复堆栈004015F88B55F0movedx,dwordptrss:〔ebp0x10〕;将scanf的结果放入EDX(b)004015FB8B45F4moveax,dwordptrss:〔ebp0xC〕;将scanf的结果放入EAX(a)004015FE89542404movdwordptrss:〔esp0x4〕,edx;将EDX内容压入栈,就是我输入的b(2)00401602890424movdwordptrss:〔esp〕,eax;将EAX内容压入栈,就是我输入的a(1)00401605E89D000000call未命名1。004016A7;递归调用
然后我们随意跟进一个函数,如图(我选择的是C函数方式)
简单分析,易得:复制代码隐藏代码0040164555pushebp;ebp入栈保护现场0040164689E5movebp,esp;保存栈指针0040164883EC10subesp,0x10;设置栈指针0040164B8B5508movedx,dwordptrss:〔ebp0x8〕;从栈中读入edx(a)0040164E8B450Cmoveax,dwordptrss:〔ebp0xC〕;从栈中读入eax(b)0040165101D0addeax,edx;将eax和ebx相加,读入eax004016538945FCmovdwordptrss:〔ebp0x4〕,eax;保存eax的值入栈准备引用(c)004016568B45FCmoveax,dwordptrss:〔ebp0x4〕;将c作为返回值放入eax00401659C9leave;恢复栈0040165AC3retn;函数返回
综上所述,我们可以得到以下几点:
1。大多数调用协定都将栈作为参数传递的途径
2。在进入一个函数时执行call指令就是做了两步:
(1)将下一行指令地址压入栈
(2)跳转到call后的地址
3。将eax作为返回值
4。return指令实际做的事:
(1)跳转到栈顶的那个地址
(2)栈顶中的地址出栈
所以,函数为了返回必定要用栈,分析栈也就可以知道函数是从哪调用的。正式开始调用栈
栈不止可以用于函数调用,临时数据的存储都是用栈,但是,栈中为了函数调用的部分称为调用栈,用调用栈可以分析函数是从哪里调用的。OD对调用栈的支持
快捷键(AltK)
如图便是OD的调用栈窗口
第一行是Main函数里的函数
第二行是Main函数调用栈找地址法原理
通过调用栈,我们可以知道函数是从哪里调用的,便可以知道是哪里触发了函数,便可轻易找到是哪里判断了注册码是正确还是错误的。实战调用栈法
我又写了一个小程序,源码如下(验证部分和我上次发帖的一样):复制代码隐藏代码includeiostreamincludecstdioincludecstdlibincludestringincludewindows。husingnamespacestd;intcheck(stringusername,stringsn);检测注册码正确性intregiste();注册界面inthelp();帮助界面intmain(){cout1。注册endl2。帮助endl;intchoose;cinchoose;if(choose1){registe();}elseif(choose2){help();}else{cout没有这个选项!endl;system(pause);}return0;}inthelp(){cout这只是个帮助endl;system(pause);return0;}intregiste(){stringusername,sn;cout请输入用户名:endl;cinusername;cout请输入序列号:endl;cinsn;check(username,sn);system(pause);return0;}intcheck(stringuser,stringsn){for(inti0;iuser。length();i){for(intj0;jsn。length();j){if(user〔i〕1!sn〔i〕){MessageBox(NULL,序列号错误,失败,MBOK);return0;}}}MessageBox(NULL,序列号正确,成功,MBOK);return1;}
OD载入,运行
在弹出错误框后,暂停,打开调用栈
可以看出,从我的exe调用的而非系统dll调用的最上层的调用来自:
未命名1。004018B2,其中未命名1为我的程序名。
进入004018B2
看到上面将序列号错误压入了栈,所以我们判断这个call显示了这个弹窗,所以我们想要跳过这个call,而它上面就有一个je跳转,我们改为jmp看一下。
至此,软件爆破完成!感兴趣的同学可以尝试分析一下剩余的语句的功能,对自己的能力提升是有帮助的。结语
调用栈破解法很常用,学会了它,你就不用总是搜索字符串了。现在,自己找个CrackMe或编译我提供的源代码试一试吧,祝你好运!
能否打破魔咒?李轩豪若摘得春兰杯,将创造世界围棋的历史自80后棋手开始,世界围棋界从来没有哪一位高手的第一个世界冠军是28岁以后获取的。譬如中国的孔杰,出生于1982年11月25日,2009年12月在第14届三星杯上首次登上……
带来三条产品线!英特尔CES放大招移动U也有桌面性能一年一度的CES盛会再次在拉斯维加斯召开,而且今年的CES尤为难得:经过疫情的万般阻挠后终于回归线下,似乎往日科技行业的热闹又回来了。在CES首日,英特尔迫不及待地发布了三款全……
还在用平板上网课?收好这份投影攻略,赶紧解锁大屏网课新世界当居家办公碰上孩子网课,狭小的家庭空间,紧张的亲子关系,想想就令人窒息。但如果大人要上班,家里没人顾着孩子,那就更是苦恼网课效率低,学习跟不上,孩子很容易产生自我怀疑。……
不可一世的丫蛋,终于付出了代价大家还记得丫蛋这个名字吗?是不是很少出现在大众面前了,有人称她为不可一世的丫蛋,为什么这么说呢?丫蛋也是赵本山的得意弟子,2009年被赵本山无意之间选中,登上春晚的舞台,……
没有加入FIFA的国家或地区也有世界杯?答案是肯定的,无法参与国际足联世界杯的国家或地区,有着自己的世界杯,也就是CONIFA。这是由独立足球协会联合会创办的比赛,前身是VivaWorldCup。宣传海报……
提升人才与产业匹配度近日,在浙江宁波弘肽生物技术有限公司车间,戴着口罩的工人在生产线上进行提取、填充、分装作业,开足马力制作完成的抗原检测试剂从机器中吐出,奔向下一道工序。从开年起,弘肽生物各个条……
国庆长假人闲钱不闲,这类基金或可为投资者持续打工上证指数近日连跌,业界人士认为,对市场的正确解读或是天之将明,其黑尤烈。在对市场的持续期待中即将迎来国庆长假,人闲钱不闲应是投资者深植内心的投资理念,如何在假期让闲钱获得收益?……
冠心病患者必须得清淡饮食吗?很多人都认为血脂异常以及动脉粥样硬化等慢性疾病的发生就是因为肉吃多了,或者是油脂摄入过多造成的,所以必须吃得越清淡越素才越好,越有利于控制冠心病和其他各种慢性病,但事实并不是这……
德甲2斯图加特三连平北京时间9月10日21:30,德甲第6轮,拜仁慕尼黑主场迎战斯图加特。上半场,基米希主罚任意球稍稍打高,特尔门前包抄被扑错失良机,阿方索戴维斯助攻特尔打入德甲处子球。下半场,弗……
暴走故宫的一天跟着我的步伐走一遍故宫,看完我这篇文章不迷路哈哈,提前一天或者一周网上预约买好门票,大门票60,里面珍宝馆10,钟表馆10元,兴高采烈的赶到故宫正门,大家知道正门是指哪个门吗?……
电商行业进入存量竞争时代,如何通过在线客服系统提升服务品质?近年来,随着互联网的高速发展,电商行业可谓是竞争激烈,各类新兴平台、模式层出不穷,以往的电商平台已经显得力不从心,造成了流量的分化现象。至此电商行业进入存量竞争时代,产品价格、……
靠一个角色成就一生的8个演员,有人难以出戏,有老戏骨落魄去世文凯哥鉴史编辑凯哥鉴史一个角色吃一辈子都有哪些演员?前一段老前辈《三国》关羽的饰演者陆树铭:一辈子一个关羽就够了!有很多演员奋力演绎,只为了能让这个角色深入人……