-
-
[原创]新春战疫网络安全公益赛-2020-Reverse-42(挺难的一个题)
-
发表于: 2020-10-12 14:16 4015
-
这个题目是一个exe文件,并没有壳。但是但不调试的时候会出现异常无法直接继续调试。
访问内存问题,而且是程序设定问题,是一个肯定要触发的异常,继续f8程序就会跳进exit(),无法定位到seh链。执行程序的时候,输入字符串后会弹出一个窗口,然后程序就会崩溃。目前尚不知道程序是怎样执行的。
然后进入idapro观察,发现函数调用关系比较复杂,但是通过字符串可以看出一些信息。
可以看到可能有格式检查和长度检查。进入字符串部分查看到主要逻辑。
在这段代码中可以看出经历了字符串长度检查(调用了strlen),而且再检查长度之间,发现调用了一个格式检查函数sub_411348。进入之后发现可以F5。整个函数就是检查字符串格式。
从代码中可以看出flag第一位是f,且要有 { 和 } 。然后把{}中间的内容截取存储进数组中。结合上面的外层函数,可以判断上面的34可能是{xxxx}中的字符串长度。然后再返回最外层代码,跟着判断完字符串长度及格式可以继续运行的流程走下去,可以发现又调用了这样的函数
进入函数之后发现可以F5。发现这个也是程序的主要逻辑函数。函数有三个参数。经过观察可以猜测三个参数的作用。共有a1、a2、a3三个参数,猜测a1是存放字符串的某一段内存区域,a2是用户输入字符串的头指针,a3是用户输如字符串{xxx}内部分长度。
这是这个函数的主要逻辑。开始没有看出这段话函数的作用是什么。后来在大致了解三个参数的作用之后大概判断出了关于字符串格式的所有信息。这面是一整个循环循环中嵌套了多个选择。在循环的第一个选择中包含了一个else,主要思路就是按位遍历输入字符串,判断是不是‘-’,如果是的话v7自增,且在a1+ v7++*40 + v8++
的地方置零。所以这里v7可能是‘-’的个数,v4可能是一个方阵,宽度为40。在不是‘-’的情况下也有两种情况,v7值是1 2 3 4或者0(或者是比4大的数字)。当v7不是1 2 3 4的时候,直接把a1的对应位赋成a2对应位置的字符,如果1 2 3 4的时候稍微复杂一点。如果a1是一个方阵的话,会把v7对应的值的行的v8位向左偏移一定位置。因为v7时‘-’的个数,并且会根据‘-’的个数把输入字符存储在a1字符串的不同部分,所以猜测flag格式应该时flag{xxxx-xxxxx-xxxx}。并且在v7中case一共有四个可能性,所以猜测‘-’可能有四个,也即是说flag可能会被分成五段。所以a1可能是一个40*5
的方阵。如果排除每一种情况的偏移,在内存中应该时以这种形式存储。
但是加上了偏移位之后,纵向上就没有固定顺序了。但是如果是每一块分行存储的话,加上的偏移可能是当前字符串的长度。所以加上偏移后的内存可能会变成这样,即每一块都单独的存储在每一行中(但是置零的位置不变),所以可以从偏移中猜测出每一块的长度,即第一块是7(0包含一位),第二块、第三块和第四块都是4,第五块长度未知。这时第一段判断的所有信息都分析出来了。在第二段判断中会判断当前位的下一位是不是-,但是就算判断是v7的值有不会加1,则会在对应的位置0。genuine之前判断出的字符串长度为34,则可以算出第五段长度为11。所以这是内存的状态应该是这样的
这时每一行存放的就是每一块包含-在内的完整字符串,一共有五段,在内存区域中还有一个-用来标记本来应该所在的位置。所以flag的格式应该是flag{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}
。最后一行代码没有实际作用,因为在下一次循环时值会被覆盖。
动态调试(跳过第一次异常点直接运行下面处理输入字符串格式的部分即可)后查看内存,证实了猜测是正确的。
然后跟着执行下去,发现程序把分成五段存储的flag整合成一个字符串存进内存中。(这里是输入正确的flag进行跟踪的)
继续执行,程序来到了这样一段代码,其主要是把整合过后的字符串每两位以16进制的形式(例如输入字符串ab将会以16进制数字ab进行运算)和先前给定的字符串进行按位运算,并且每个四位存入内存中。执行过程以及运算过后的数据存储形式如下。
在运算完之后,接着执行会经历一个循环,其作用就是把上述运算的五位组合起来组合成放入内存中。
其中,这里的324区域是作为控制器使用的,存放循环五次的计数。294存放运算之后的结果,即把五个数字拼在一起的结果。
跳出循环之后,会经历两个异或运算
因为在内存中组成了五位,但是一次性只能运算四位,所以294和290分别存放的是前四位结果和最后一位的结果。分别和76BDF08F
和FEE1DEAF
进行异或运算,并把结果存进2A4和2A0中。
下面有两个相似的循环运算,过程与上面相同,只是运算数不同。总结这三段循环就是把拼成一起的字符串分成三段,然后分别进行异或运算。最后的运算结果体现在内存中如图。
下面的代码引用了这三段运算结果。
这里调用了上面可以使结果前移两位的函数,这个函数本质上其实是乘法。,上面前移两位是通过*100
实现的。所以这里就是把三组运算结果分别和自己相乘,且相乘两次,其实就是进行了立方运算。但是乘法与上面不同的是这里调用的时候还会进行一次求和运算。通过or语句来改变标志位来判断跳转。
然后把这三个立方运算加和,把运算结果调用API进行输出。然后程序会再一次执行立方和运算,然后与42进行对比,如果正确,则输出正确。
所以只需要找出三个数的立方和是42即可逆出算法。通过查资料发现-80538738812075974^3+80435758145817515^3+12602123297335631^3=42
所以把这三个数字先分别变成16进制,然后分成两个部分,每部分四字节,与上面程序中给定的六个字符串进行异或,然后把结果拼在一起,分别两位与3nder5tandf10@t
进行异或,然后变成字符串,按照上面分析出的格式写成flag,即可解出题目。
最后经过计算,得出flag为flag{ed82ab5-5c7a-da78-b7a8-d2f5fbef453}
。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!