假期集训逆向
假期留校集训,关在实验室自生自灭,做了几道简单的逆向题,各大平台的都有。
实验吧
whatamitoyou
给了提示“你会唱歌吗?”,ELF文件,先用IDA静态分析,main函数开始先进行了很长很长的声明很多变量,变量赋值,再把变量的地址赋值给另个变量,赋值的部分把int值按R转化成字符串会发现是倒序的一些英文句子,倒序是因为数字转化成字符串存在内存中的小端存储问题,在x86结构的内存中数字是高位存在低地址,把转化成字符串就会调转顺序:
根据提示猜测这些句子可能是歌词,于是Google搜到了这首歌:
发现题中的歌词是乱序的,猜测解题和顺序有关。
“准备工作”结束后进行了一系列看不大懂的运算:
Linux下edb动态调试,开始的声明,赋值直接跳过后在漫长的“准备工作”最后,结合ida可以发现把第一句歌词的地址赋给了[rbp-8]:
这里开始操作输入的字符串,把字符放入[rbp-0x11]:
跟进跳转到这里用字符对歌词地址进行操作“旧地址+(字符-0x41+0x20)*8=新地址”
:
结合IDA找到第二句歌词"I should have just told you"
的地址,有两个,都进行逆运算得出输入的第一个字符应该是67='C'
:
一直重复就能找出全部字符串,其实也有规律,在栈里存每一段歌词的地址下面不远处有连续四个地方存的是其他歌词的地址,字符ABCD运算下来分别就对应着这四句歌词的位置,这样很快就能得出输入的字符串:CBDABCADBCCABBABBABACBCCABDADBABABB
。
逆向观察
ELF文件,IDA查看:
把src
拷贝给dest
,然后遍历dict
里的字符串,如果有字符串同时与dest
和输入的字符串相等则输入正确,很简单,只有一个小端问题,上一题有介绍。
FLAG
给的是一个网址,当时很懵逼,这TM不是逆向么…查看网页源码可以看见一段JS代码:
如果输入的字符串满足这串超级长的判断则correct……
这串判断是由很多判断用&&连接起来的,分开之后按长度排序是这样的:
这就可以看出端倪了,按照这个顺序一个个判断的话每一次判断可以确定一个字符,上脚本吧:
1 | src='超长判断'.split('&&') |
证明自己吧
主函数是这样的:
输入V4
,然后跟进判断函数:
输入的数据异或0x20
,v5
减5
,之后比较,直接把v5
的数据加5
再异或0x20
就可以了。
BUUCTF
刮开有奖
IDA查看主函数,跟进找到判断函数:
输入长度为8的字符串给String
之后进行判断:
跟进sub4010F0
分析得知是一个排序算法,将v7
后连续10个数据进行排序,sub401000
是base64编码,再根据后面的if判断,可以知道String
第一位是排序后的v7
,第二位是排序后的v11
,后六位分别是V1Ax
和ak1w
的base64解码,拼在一起得到flag。
CG-CTF
WxyVM1
ELF文件,IDA查看:
结构很简单,输入,,执行sub4005B6
,判断长度,比较,要比较的字符串是给了的,只需要分析sub4005B6
这个加密函数,跟进:
从0x6010C0
开始取15000个数据来对输入进行加密,每三个一组,三个中第一个决定操作方式,第二个决定操作输入字符串中的第几位的字符,第三位决定进行操作的数据,IDC逆向脚本跑一下就能跑出输入的字符串
1 | auto i; |
也可以dump下来用python脚本跑。
maze
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
地图是在判断是否越界的函数中可以看出是8*8,判断是否撞墙的函数中可以找到地图数据,人脑遍历地图路线得到答案,地图和答案如下:
1 | ****** |
杭电CTF
Beat our dice game and get the flag
代码看起来比较畸形,用ollydbg动态调试看字符串可以大体猜出程序,一个扔骰子的游戏,要求多次按enter
随机扔骰子,必须都扔到程序规定的数,甚至还要求扔到七:
跟进找到每一个判断跳转,改掉没有扔到指定数的跳转,让程序一直运行下去到最后flag就会自己冒出来了:
这种没有输入的题,一般会比较简单,因为不用输入就表示flag就藏在文件中,只要能运行到某些命令就能拿到flag,不需要逆向算法什么的,这种应该只能叫破解,不能称为逆向才对。
从文件中找flag
这个题好像实验吧也有,文件是一个叫keylead的文件,WinHex查看文件头发现是一个tar.xz的压缩包,Linux下解压后是一个ELF文件,IDA查看C语言伪代码,还是一个扔骰子…一样的方法,只是要在Linux环境下调试,用edb把关键跳转改掉,就可以得到flag
flag我就不放了,懒得再去复盘截图,跟上一题一样的操作。
Bugku
Mountain climbing
有一个UPX的壳,脱壳细节我在这里不细说,图简单的话吾爱破解有UPX脱壳工具,脱壳后先上IDA:
先用随机数生成一系列数据,然后根据输入的字符串在数据中找出一条从数据第一行到最后一行的路线使得路线上的值加起来最大,计算机是没有真正的随机数的,程序中给定了随机数种子,生成的随机数就是确定的,先照着程序自己写一个一样的随机数跑出数据:
然后写脚本遍历所有路线求最大值吧:
1 |
|
交上去却是错的,回去慢慢看发现还有一个加密,是将输入的偶数位异或0x4
,IDA看比较丑,可以在ollydbg里面观察输入数据的变化很明显就能看出来(此处我输入的是'0'*19
):
把之前得到的字符串再异或一下就可以得到flag了。
Take the maze
迷宫,IDA查看:
先让输入key,然后有个isdebuggerpresent反调试,之后判断key长度,第17位异或1,之后有个奇怪的函数,之后判断必须全是十六进制字符,不然goto16就结束了,然后进入关键判断,跟进,代码如下:
1 | BOOL __cdecl sub_463480(int a1) |
上下左右的函数长得都很像,大概像这样:
其他三个结构都是一样的,不再赘述,result
的赋值都没有意义,因为函数并没有返回有用的值到上层,从这里走到下一行需要加26还有到第11行(下标为10)再继续走就会越界可知地图是11*26+25的规模,关键在于判断是否能够这样走的那个判断,IDC脚本把0到311的异或结果给跑出来看看:
1 | auto i; |
结果如下:
这是地图中每一个点能否往下走的dump结果,其他三个方向一样的方法,最后把四个方向的dump结果统一在一张地图上,脚本如下:
1 | d=[1,1,0,1,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,] |
地图如下:
其中u,d,l,r分别代表在此坐标往上,下,左,右可行,地图很简单就可以找到一条路:
照着路线逆向出字符串"06360836063b0839173e0639"
交上去不对,回头看之前那个奇怪的加密函数:
这个函数里面看起来很丑,不好分析,用ollydbg动态调试发现就是个遍历字符串每个字符异或下标i
,此处我的输入样例为'0'*24
第17位异或1:
异或下标:
脚本:
1 | a = list("06360836063b0839073e0639") |
得到flag。
file
IDA先看main函数:
有个flllag
像flag但是交上去不对,在这里只是一个字符串,用于判断,程序先读取文件内容(此处文件缺失),然后与现有的数据进行异或比较判断文件正确与否,文件md5就是flag,flllag
和sttr_home
都能跟进找到,只需再分析sub_400EB9
函数对sttr_home
的加密,跟进:
用同一个函数处理两个参数再加起来,再跟进这个函数:
就是一个十六进制转数字的函数,上层函数中第一个参数的返回值乘16再加第二个参数的返回值,合在一起就是把两个十=十六进制字符转化成一个数字,至此就已分析完毕,上脚本:
1 | str1='664e06226625425d562e766e042d422c072c45692d125c7e6552606954646643' |
在线计算所创建文件的md5为 914a7b9df69eab5b74b9edb7070e53e8
,加上flag{}就ok了。
not only smc
有个UPX的壳,shellcode里对upx进行了检测,脱壳后无法运行,就用ollydbg带壳调试,还有smc(自改变代码),下VirtualAlloc
断点,找smc解密,断点会断好几次,在程序结束前最后一次断下的时候Ctrl+F9
运行到返回,找到到smc解密,是取输入字符串第九位开始后面的五位反复与给定字符串异或:
因为自解密出来是一个函数,而VC下stdcall的函数头基本是固定的,像是这样:
由此可以猜测五位解密字符,反复尝试后确定是jUnk_
,之后步过解密出来的函数,输入的数据会发生变化,说明函数中对输入数据进行了加密,步入分析,有很多junk code(垃圾代码),用来迷惑我们的,在输入的数据处下内存断点找到对数据进行加密的关键代码,是将输入的字符串与给定字符串一一异或:
这个函数结束后继续步过,到后面又有一个函数再次改变了输入的数据,步入分析,还是有junk code,还是靠断点找到关键位置,分析得出这是一个三重嵌套循环异或,输入字符串的不同位之间异或是两层,外层还循环了65次
总的解密脚本如下:
1 | ans = [0xE8, 0x90, 0x24, 0xE6, 0x0A, 0xE3, 0xF7, 0xA8, 0x09, 0xC0, 0x35, 0x74, 0x26, 0x4D, 0xA0, 0x2D, |
RE_Cirno
给了一张图片,下载下来WinHex打开发现有PK头,于是改后缀名为zip打开得到exe,IDA看main函数,很简单的程序,给定一个字符串,遍历字符串全部加9但是却没有保存,然后有个提示:
“9层栅栏”指栅栏密码,“反向”指逆序,先把字符串加9的结果算出来为"of}e8lhz9n~r:9J{t8p{jg|"
,这个东西栅栏解码后也不可能是flag,再看看汇编代码:
发现加9之后还异或了9,此处是IDA7.0伪代码处理的bug,换IDA6.8就会正常显示异或9,重新计算结果:
1 | arr1=[115,94,97,114,103,47,107,114,65,48,49,105,117,118,101,48,113,95,99,47,92,116,93,102] |
这个看起来就顺眼多了,有”flag”,有花括号,也有”Cirno”,接下来就是解栅栏密码,根据开头"flag{"
的格式还有提示的"9层"
,确定应该是3*9的栅栏,但是字符串只有24位,应该要加3位,边做边猜最后确定字符串修改为"fotl1eas0gvw{30Cr}1y_rc_nu_"
解码后得到""flag{C1rno1sv3rycute0w0}___
,将加上的下划线去掉得flag。
babyLoginPlus
有VM加密,IDA先看看,主函数长这样:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
跟进第一个函数,里面对byte_408034
进行赋值,长这样:
1 | int sub_401510() |
值我们不用算,动态调试的时候可以看,之后第二个函数是申请内存之后进行4个拷贝,长这样:
ollydbg里面看这四个拷贝的地方长这样(最上面16个字符是输入的字符串):
在输入的字符串处下内存访问断点,一步一步找能找到程序把输入的字符串一个一个的转移到另一个地方:
跟过去下内存断点就能找到转移后对字符串进行的一系列操作,最终解密脚本如下:
1 | src = [0x32, 0x26, 0x18, 0x21, 0x41, 0x23, 0x2A, 0x57, 0x44, 0x29, 0x35, 0x12, 0x20, 0x17, 0x45, 0x1C, |