zoukankan      html  css  js  c++  java
  • 逆向新手踩坑指南之爬爬山能锻炼身体

    首先坐下,打开电脑,平复一下心情,开始逆向分析。

    对了,开始之前,首先推荐一下本站大神的逆向工程系统教程:【传送门

    好了,正文开始。软件运行一下是这样的:

    逆向新手踩坑指南之爬爬山能锻炼身体

    直接IDA打开,字符串窗口(shift+F12):

    逆向新手踩坑指南之爬爬山能锻炼身体 

    what a f********ck???!!!这是啥?说好的字符串呢?说好的各种函数呢?

    想了想忘了一件事情,先查查有没有壳:

    逆向新手踩坑指南之爬爬山能锻炼身体

    看到了刚才的小坑,顺便在看看有没有什么加密方式:

    逆向新手踩坑指南之爬爬山能锻炼身体

    没有,很好,直接upx脱壳,然后再次进入IDA【其实根据刚才IDA的提示也能看出来是upx的壳】: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    这次看到了字符串,然后根据关键字符串交叉引用后来到函数: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    直接F5: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    圈一里面是什么暂时不想管,直接从18行开始基本比较明显了,一个是屏幕输出函数printf,一个是从屏幕获得输入,圈二IDA本身分析得也很明显,就是判断长度的,字符串限制为19。22行跟进去以后是这样的 :

    逆向新手踩坑指南之爬爬山能锻炼身体

    这TM是啥?!?!?!不过结合26行,31行,37行能看到类似于a=a+b一样的东西,是从dword_41A138[]里面取值,然后直接加到dword_423D78这个变量里面。

    从29行和35行能判断这个输入的字符串一定是L或者R【根据ASCII码】,既然22行不懂,就直接先动态走一走:

    输入19个字符串后:

    逆向新手踩坑指南之爬爬山能锻炼身体

    在堆栈段看到了自己的输入,然后跟进411C4F这个函数: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    跟进去看得有点头晕….但是看到函数的返回值eax里面是13,换成十进制就是19,也就是这个函数是用来判断输入长度的【从IDA的整体流程也能推测出来】,继续跟: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    进入411C88这个函数: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    这里是一个重点,函数在这里一直在41195C和411992之间循环,并且,在411987这一步直接和4做了异或,直接导致将我们输入的字符串所有偶数位做了异或。从这一点判断,这个函数应该是IDA伪代码的第22行,对字符串进行了处理 :

    逆向新手踩坑指南之爬爬山能锻炼身体

    处理方法就是偶数位和4做异或,根据IDA的判断,异或后的值只能是L或者R。于是再次输入的时候就知道该输入些什么了。经过反推,奇数位只能是L或者R,偶数位只能是V(代表R)或者H(代表L)。字符串输入什么变得清晰了。

    直接让程序执行完,然后手动改栈里面的值,再返回:

    逆向新手踩坑指南之爬爬山能锻炼身体

    从这里开始的一大长串我没有仔细分析,太多了,看得心烦,跟了几次以后发现总是在这里[411D97]跳转,方便起见直接这里下了断点,也在[411D9C]这里下了断点,之所以这样,是想随时关注着各个寄存器的变化和堆栈的变化 :

    逆向新手踩坑指南之爬爬山能锻炼身体

    然后结合IDA的整个流程: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    能大概推测出这里应该就是根据输入的字符串判断是在圈一还是圈二。

    处理完毕以后就能直接出结果了:

    逆向新手踩坑指南之爬爬山能锻炼身体

    还是我太天真,将输入提交以后发现并不正确。然后思路基本就卡到这里了,连续一两天没有进展,心里憋屈得不得了。后来再次看题目的时候发现题目是mountain climbing。英文翻译是爬山,爬山,爬山,山呢?!!!于是我的思路就有变成了找山…

    继续回到IDA来看吧,因为已经没什么可以看的了。这次就变成了分析前面所有没分析过的函数,首先就是dword_41A138[]里面是什么?

    逆向新手踩坑指南之爬爬山能锻炼身体

    从字面上理解rand()就是随机数函数,每次都产生不一样的随机数,然后根据这个去进行加数字的运算,这TM能算出来啥?!

    然而事实告诉我还是我太天真了(原谅我大学不是计算机专业,没好好学过C….)。

    c语言里面有一个产生随机数的函数,rand()。但是在学习过程中发现这是伪机的,虽然叫随机,但是每一次结果都一样(下面图片来自随便一个搜索引擎,随便一搜一大把的讲解):

    逆向新手踩坑指南之爬爬山能锻炼身体

    于是思路一下子清晰了,srand()是一个固定的值(IDA里面对没有定义的数字后面会有一个u,undefined),那么rand()产生的数字也是固定的!!!根据代码描述,基本可以断定那个dword_41A138[]就是山了,数组下标是这样的101,201,202,301,302,303…写成这样也许更直观: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    从IDA里的27行到41行看: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    76(十进制ASCII码)是L,82(十进制ASCII码)是R,对应上面那张图就是从101到201是L,从101到202是R。L和R就代表了如何下山。没错,你没有看错,就是下山,不是爬山。

    问题来了,根据等差数列进行算数,一共由210个数字堆成山,一共20层,走法多了去了,这TM谁知道哪条路是对的…正在一筹莫展的时候想起了这一句话:

    逆向新手踩坑指南之爬爬山能锻炼身体

    要找最大的数,那么也简单,从下山开始,只选择最大的那个数。后来发现也不行,比如201是比202大,但是303比所有101,201,202加起来都要大,如果选了201,那么永远走不到303。这怎么办?

    只能遍历了。

    问题又来了,怎么遍历?!!我想到了二叉树遍历,虽然C语言忘得差不多了,但是还依稀记得一点数据结构。当我自信满满得去操作一番搜索引擎的时候又进到了一个坑里,二叉树遍历的代码是这样的(来自用搜索引擎搜索二叉树遍历后返回的一大把博客中随便一个网络博客):

    捕获.PNG一共316行,相信我,这不是我想要的,首先以我近乎于只穿着内裤的C语言功底读懂316行代码还需要很多时间,其次还要搞明白并熟练使用结构体变量。而且,经过耐心的学习以后发现二叉树遍历不能满足我的需求,因为二叉树遍历无法实现从E到D: 

     逆向新手踩坑指南之爬爬山能锻炼身体

    于是我的这道题目再次陷入深坑….

    这里卡了一天半左右,没有任何思路,后来无聊,想了想一共有多少条路?我能不能手工遍历(我是一个天真善良吃苦耐劳的八道杠优秀代表)。从上往下数第二层有2条,走到第三层有4层,第四层有8条…不会算了,在纸上画到第五层的时候我已经有点崩溃了,又耐心思考了一个小时左右,耐着性子画到了六层,终于让我这个晕乎乎的脑袋看到了规律,2的几次方。从山顶到第二层有2条路,到第三层有4条路,是2的平方。第四层是2的3次方。因为每一个节点对应下一层的节点只有两个选择。我打开windows键,在运行处输入calc以后用科学计算器算了算2的19次方:

    逆向新手踩坑指南之爬爬山能锻炼身体

    这是逼着我用程序来解这道题(此时心里有一万句脏话想说出口)。

    既然用程序,问题就又来了,我如何表现每一种走法呢?这里我又卡住了,盯着自己在纸上画的所谓的山看了好久也没有想出来用什么办法来表现每一条路径。看看作者的源程序:

    逆向新手踩坑指南之爬爬山能锻炼身体

    作者用了两个数字来写,i就是第几层,j就是在i层的第几个数。可是我表现不了从302到403啊,等等,302,403两个数层数和尾标都是差1,在看看整个山的分布: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    找到了一个规律,每条路都是只能差1,比如这条路:101,201,302,403,503,604…这条路里,层数差1是固定的,可以用循环实现,问题是如何实现1,1,2,3,3,4这个路数。要么加0,要么加1。要么加0,要么加1。要么加0,要么加1。默念无数遍以后得到了这样一个数列01101,我嘞个王母娘娘,臣妾终于做到了~!!!这TM不就是二进制么,0代表往左走一步,1代表往右走一步。(伟大的二进制~!伟大的十六进制~!)如果有6层,那么01101换算成十进制就是13,一共有32条路,00000也算一条路的话(十进制的0),11111就是最后一条路,十进制就是31,0到31一共32个数字,Nice,完美实现了所有路径的表示,此时,半天又过去了…于是代码实现就容易得多了,先定一个小目标,实现6层的山,用Python: 

     逆向新手踩坑指南之爬爬山能锻炼身体

     已然实现了所有路径的表示方法,Nice!!!

    下面面临着怎么把山里的数字给扒出来,从作者的定义方式来看,定义的数组的长度在2000多,否则下标放不下,简单,直接对照伪代码写出来C代码,然后自己跑一边就行,于是有了这段C代码:

    逆向新手踩坑指南之爬爬山能锻炼身体

     由于强迫症的限制,我就不想在windows上跑,没环境,不想装,伟大的linux干啥都行,为啥非要在windows上吊死,于是在我的kali上跑完以后是这样的(自己又给自己挖了一个坑):

     逆向新手踩坑指南之爬爬山能锻炼身体

    好了,数字找到了,也能按照自己的意愿去进行遍历了,剩下的就是融合C和Python在一起,用Python实现脚本计算(别问我为啥不用C)。

    问题TMD又来了,我要如何把210个数字移到Python里,这里,我选择了裸奔的方式:手工输入!(C语言如何实现文件读写不会!不想学!)210个数字写到Python的字典里,比如:

    逆向新手踩坑指南之爬爬山能锻炼身体

    实现遍历的方法在前面已经写出来,还剩下如何根据路径把对应的数字进行相加,给一段关键代码: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    原理就是根据路径寻找字典里对应的点的值,然后相加,每条路径都产生一个值,不断的比较,只取最大的那个就行。

    一阵开心地等待过后有了结果,然后根据最开始的对字符串进行处理的方式逆回去,提交答案以后又给了我当头一棒,我是有多天真!!!为什么不对?后来我把Python跑出来的结果用题目的exe跑了一遍,发现得到的数字不一样…..郁闷。为啥不一样?同样是rand函数,固定srand。痛定思痛过后反思到了也许是不同的平台就不一样,然而我还是不想在电脑上装VC。

    那怎么办?

    让程序自己吐出来这个数组!

    然后重新用OD运行这个exe,不过这次用F8走,为的是找到数组生成过程中产生的数字,于是我来到了这里:

    逆向新手踩坑指南之爬爬山能锻炼身体

    图里面这个不断的跳转就是在生成我们需要的这个数组,我们在411C29这个地方下断点,并且每次查看对应地址的内容: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    这就是我们需要的数组,总不能再跟210次,再写210个字典里的值吧?于是经过再次使用搜索引擎的技能,学会了条件断点记录到日志,这样,能把每次经过411C22这个点时的edx的值记录在案!!!!

    顺便一说,在Python里的文件读写,我还是会一点的…..

    记录的日志大概是这个样子的:

    逆向新手踩坑指南之爬爬山能锻炼身体

    经过这次的改变,我的Python脚本再次跑了起来,得到如下结果: 

    逆向新手踩坑指南之爬爬山能锻炼身体

    然后根据那一连串的1001去反推回L和R,再反推回V和H,于是这次用我的字符串跑exe以后,exe跑出来的结果和我的Python一样了。然后提交分数,FFFFF********KKKKKKKK!!!!!!还不对!!!为啥?我都找到最大的路了,得到最大的分数了!

    然后,我笑了,脑子高兴糊涂了,我提交了我的路径,正确!

    七天过去了,做逆向真是打发时间的好帮手~

    相信聪明的你已经看到了我所有犯过的错误,并且已经知道了我写的是哪里的writeup…..

  • 相关阅读:
    2014下半年软考总结
    Java学习之Filter
    java学习之PreparedStatement
    java学习之dom4j 对 xml的读取
    Windows查看端口占用及杀掉进程
    CentOS压力测试工具Tsung安装和图形报表生成Tsung安装配置
    Tsung压力测试:Openfire
    CentOS Mysql安装配置
    nodejs、webpack
    IOS操作系统上执行monkey测试
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/8064099.html
Copyright © 2011-2022 走看看