2023/11/3 点击就送 得到一个.s文件,是gcc汇编后得到的文件。在kali
1 2 gcc -c 1.s #生成.o gcc 1.o # 生成a.out
再把a.out拖到ida,简单难度的逆向
幸运数字
爆破0-999找前四位符合SYC{的
砍树 先用jadx看看主逻辑,大概就是砍树,然后验证flag。但是我砍了777+它给我退出了(怒
问题就出在这个哭脸的返回值上。要使它为1!
用ida打开相应的so层一探究竟
Alt+F搜索
进入之后反汇编看看
大概就是text经过处理后和dest相等
简单的异或
1 2 3 4 5 6 7 8 9 10 dest = [0 , 32 , 32 , 23 , 27 , 54 , 14 , 54 , 38 , 23 , 4 , 42 , 41 , 7 , 38 , 21 , 82 , 51 , 45 , 15 , 58 , 39 , 17 , 6 , 51 , 7 , 70 , 23 , 61 , 10 , 60 , 56 , 46 , 34 , 24 ] S = "Sycloverforerver" S = list (S) text = [] for i in range (34 ): text.append(chr (dest[i] ^ ord (S[i % 7 ]))) print ('' .join(text))
这里一开始想复杂了,看到char给人改成小端了,然后就没出来flag。理解不够到位
doublegame(未)
2023//11/5 wordy 打开ida发现只反汇编了一小段程序,很多灰色数据部分。然后考虑花指令。这里学习了一下使用自动patch
1 2 3 4 5 6 7 8 startaddr=0x1135 endaddr=0x3100 for i in range (startaddr,endaddr): if get_wide_byte(i)==0xEB : if get_wide_byte(i+1 )==0xFF : patch_byte(i,0x90 ) print ("[+] Addr {} is patched" .format (hex (i)))
然后P一下就都蓝了。翻找一下可以找到flag
help 迷宫题
有个CreateMap()函数,在它后面的跳转点打个断点,动态调试
进入CreateMap()找map
在Export data 中提取出来,编辑成16个一行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1111111111111111 1000001111110111 1011101111110111 1011101100010111 1011101101010111 1011100001010111 1011111101010111 1011111100010111 1011111110110111 1011111110110111 1000011000010001 1111011110110101 1111011110110101 1000011110110100 1011111110000111 1011111111111111
在check()函数可以发现步法,以及初始位置(1,15)
那么接下来就只需要找路了,如果能搞个dfs搜索就好了,,
1 2 3 4 import hashlibmd=hashlib.md5() md.update('wwdddwwwaaawwwwwwwwwddddssssdddssdsssssssdddwwwwddsssd' .encode('utf-8' )) print (md.hexdigest())
2023/11/6 teaaaa x找调用函数
比较特别的是修改了key的值,然后是一个简单改动的xtea,复制然后把+改-就差不多了
2023/11/7 小黄鸭 先是查了下壳发现里面有很多文件
然后扔到binwalk看看是真有很多
尝试binwalk -e解开发现全是zlib,,
然后递归解开 binwalk -eW,发现里面有一些pyc,所以应该是py打包成的exe
用工具拆开:
反编译里面的1.py
比较简单。逻辑是输入一个flag,如果里面的内容是字母就rot13,然后ord+2
如果不是字母就直接ord+1
所以逆过程就是先ord-1,把符号数字找出来,剩下字母的再-1,然后解开rot13
最后把他们拼在一起
安全编程 是鹏城杯2023的题,当时看到是rust以及这个题目感觉很难orz
之前看过一点rust的内容,给我的感觉就是很安全()没学完
ida打开之后非常地复杂。从字符串摸到了main函数,很复杂
标了一下字符串。然后在kali运行了一下,是个猜数游戏
逻辑应该是程序自动生成一个1-10的数字,用户要输入一个1-10的数字,猜对100次修复encflag输出flag。
这里学习了一下ida的远程linux调试,感觉这样不用gdb了捏
步骤是把要运行的elf放到kali的ida文件夹里,然后启动对应的server,然后在ida打开调试
这道题的话,因为是rust所以代码比较难读,但是程序逻辑比较简单。所以可以在程序的每个跳转点处打断点,然后观察执行顺序,找出来需要的变量和常量
这里主要是修改了guess_number,改成99,再猜1次就好了。程序把生成的猜数答案放在了eax里(返回值)所以照着猜就好了
终于学会了远程调试,开熏
2023/11/12 啊啊啊好几天没做题了。。猛肝了一堆实验。。
z3test(未)
好像解出了一个错的矩阵。。。
Let’s Go(未 看不懂
ez_chal 打开之后发现函数名什么的都没加载出来,看wp得知是被混淆了(很好奇是怎样混淆的,混淆函数应该也在这里面吧)
然后依照动作标一下名字。
看到加密的形式是一个魔改的xtea
密文已经给出了:
这里的encode没有被混淆,是64位的整数(动态调试里发现的)
下面验证flag的时候把encode转换为了_DWORD *
双字型,32位无符号整数,所以把这个切割一下。
这个题目里面这个encode是以小端序的方式存储的
所以顺序应该是C1……DC…………
在动调里找到的key:
里面的常量,函数名,delta都被混淆了,都要在动态调试里把真实的值找出来
delta=0x9E3778B9
然后就写脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> void decipher (unsigned int num_rounds, uint32_t v[2 ], uint32_t const key[4 ]) { unsigned int i; uint32_t v0 = v[0 ], v1 = v[1 ], delta = 0x9E3778B9 , sum = delta * num_rounds; for (int i = 0 ; i < num_rounds; i++) { v1 -= v0 ^ ((key[(sum >> 11 ) & 3 ]) + sum) ^ (v0 + ((v0 >> 5 ) ^ (16 * v0))); sum -= delta; v0 -= v1 ^ ((key[sum & 3 ]) + sum) ^ (v1 + ((v1 >> 5 ) ^ (16 * v1))); } v[0 ] = v0; v[1 ] = v1; } int main () { uint32_t key[4 ] = {0 }; uint32_t encode[] = { 0xC19EA29C , 0xDC091F87 , 0x91F6E33B , 0xF69A5C7A , 0x93529F20 , 0x8A5B94E1 , 0xF91D069B , 0x23B0E340 }; unsigned int rounds = 64 ; char firstkey[40 ] = {0 }; memcpy (firstkey, "NewStar!NewStar!" , strlen ("NewStar!NewStar!" )); for (int i = 0 ; i < strlen (firstkey); i += 4 ) { key[i / 4 ] = *(uint32_t *)(firstkey + i); printf ("%u\n" , key[i / 4 ]); } for (int i = 0 ; i < 8 ; i += 2 ) { decipher(rounds, &encode[i], key); } printf ("Decrypted data is: %s \n" , (char *)encode); return 0 ; }
真的,写脚本的时候得看清楚了,这少一点那少一点的。
STL STL是C++的一个标准库
阅读逻辑比较简单
但是没搞明白这里的逆过程
官方是这样写的,但是没看懂
于是看了别的办法发现可以爆破。爆破也是个好办法呢!
然后学到了不止char存在小端序,int64也有。就是这些大于16位的就会存在小端序。
八个位是一个字节
十六个位,两个字节,一个字
A9
A是高字节,9是低字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from Crypto.Util.number import long_to_bytesencode=[0x2882D802120E ,0x28529A05954 ,0x486088C03 ,0xC0FB3B55754 ,0xC2B9B7F8651 , 0xAE83FB054C ,0x29ABF6DDCB15 ,0x10E261FC807 ,0x2A82FE86D707 ,0xE0CB79A5706 , 0x330560890D06 ] enc_max=max (encode) enc_min=min (encode) hex_list=[0 ] flag='' for v in encode: for v7 in range (0x90000 ,0x70000000 ): if ((v7<<15 )^v7)==v: print (long_to_bytes(v7),hex (v),hex (v7)[2 :].zfill(8 )) tmp=hex (v7)[2 :].zfill(8 ) for i in range (len (tmp)-1 ,0 ,-2 ): hex_list.append(int (tmp[i-1 :i+1 ],16 )) break print (hex_list)for i in range (len (hex_list)-2 ,0 ,-1 ): hex_list[i]^=hex_list[i+1 ] for i in hex_list[::-1 ]: flag+=chr (i) print (flag)
这里有些注意点
long_to_byte
是把长整型转换为字节码,字节码在密码学算法中用得比较多,这里是用来看足不足一个字节,因为后面要调整顺序。
zfill
是补0
hex(v7)
会返回一个十六进制字符串 ,所以需要[2:]
去掉0x
2023/11/13 浪漫至死不渝 做的时候和做完都感觉自己是一条狗被创飞了
主要是阅读js代码,找到第一组加密函数,是栅栏,把一个明文加密成了一个类似key的字符串在第二组加密中使用
第二组就是简单的加减异或
1 2 3 4 5 6 7 8 9 10 11 12 13 encode=[125 , 130 , 131 , 122 , 117 , 110 , 123 , 125 , 130 , 131 , 122 , 117 , 110 , 123 , 99 , 99 , 99 , 99 ] Text1="5201314WXHN" Text1=list (Text1) for j in range (18 ): if j<14 : encode[j]-=10 encode[j]^=ord (Text1[j%7 ]) else : encode[j] -= 99 encode[j] ^= ord (Text1[j-7 ]) print ('' .join(chr (code) for code in encode))
听说Cpp很难 感觉有点稀里糊涂做的
可以直接找到密文
这是主要的加密逻辑
在动态调试里可以发现v18的值为0xA,在text_67
中其实key是多少都无所谓
写一下
1 2 3 4 5 6 7 8 9 10 11 12 v19 = [77 , 95 , 61 , 123 , 55 , 104 , 115 , 87 , 39 , 104 , 81 , 89 , 127 , 38 , 107 , 89 , 115 , 87 , 85 , 91 , 89 , 111 , 106 , 89 , 39 ,87 , 114 , 87 , 79 , 87 , 120 , 120 , 125 ] print (len (v19))for i in range (len (v19)): v19[i]=(v19[i]+10 )^10 v19[i]-=10 print ('' .join(chr (code) for code in v19))
这里把v19的数组小小改动了一下,不知道那两个负数是什么情况
2023/11/14 flower or tea 去花指令+xtea
全是E8,nop
注意如果有特别多的栈指针不平衡,可以大批量还原成数据重新生成函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> uint32_t enc[]={ 0x9AF9464B ,0xC417B89E ,0xB217A713 ,0xC93BA9E8 ,0x94F3E44E ,0xB5CC2AB5 ,0x4451E42C ,0x7A8A289A , 0x53C8D008 ,0x6E117B49 ,0x9BFFD794 ,0x5EFF2DF9 ,0x17E72531 , 0xDFBD9979 ,0x8F871B3A ,0x73E8C5AC ,0xB28670A6 ,0x5AF6A369 ,0x2CF7DA24 ,0x347B66AF ,0xB9C84D60 ,0x911E912F ,0xBD5A2F9B ,0xCB96733A ,0xC59968BE ,0xA00013E9 ,0xC12F4EA4 ,0xDE863A10 ,0xA0C4D594 , 0x4380983C ,0x7E2F7648 , 0xE54DDC89 ,0x3F27A690 ,0xB58D3199 ,0x604AE517 ,0x9C903984 ,0xF4E04481 ,0x3CF4EDFF ,0 ,0 }; void decrypt (unsigned int num_rounds, uint32_t v[2 ], uint32_t const key[4 ]) { unsigned int i; uint32_t v0 = v[0 ], v1 = v[1 ], delta = 0x31415927 , sum = delta * num_rounds; for (i = 0 ; i < num_rounds; i++) { sum -= delta; v0 -= (key[sum & 3 ] + sum) ^ (v1 + ((v1 >> 5 ) ^ (16 * v1))); v1 -= sum ^ (key[(sum >> 11 ) & 3 ] + sum) ^ (v0 + ((v0 >> 5 ) ^ (16 * v0))); } v[0 ] = v0; v[1 ] = v1; } int main () { uint32_t key[4 ]={32 ,27 ,39 ,44 }; unsigned int r=54 ; char flag[40 ]={0 }; for (int i=0 ;i<40 ;i+=2 ){ decrypt(r,&enc[i],key); } for (int i=0 ;i<20 ;i++){ flag[i]=(char )enc[2 *i]; flag[39 -i]=(char )enc[2 *i+1 ]; } for (int i=0 ;i<40 ;i++){ printf ("%c" ,(char )enc[i]); } printf ("\n" ); for (int i=0 ;i<40 ;i++){ printf ("%c" ,flag[i]); } return 0 ; }
tea的数据类型基本就是模板,不要尝试用ida中反汇编出来的。。。
2023/11/15 myself 一个smc,但没找着修改代码的函数在哪
在x32dbg里面把加密函数拉出来了,是个xtea,解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> void decrypt (unsigned int num_rounds, uint32_t v[2 ]) { unsigned int i; uint32_t v0 = v[0 ], v1 = v[1 ], delta = 0x61C88647 , sum = -delta * num_rounds; for (i = 0 ; i < num_rounds; i++) { v1 -= ((v0 >> 5 ) + 4 ) ^ (16 * v0 + 3 ) ^ (sum + v0); v0 -= ((v1 >> 5 ) + 2 ) ^ (16 * v1 + 2 ) ^ (sum + v1); sum += delta; } v[0 ] = v0; v[1 ] = v1; } int main () { unsigned int r=32 ; uint32_t enc[] = { 0xBDBDF9F0 , 0xE26194C4 , 0x80799125 , 0x1F0FC219 , 0xEB6A1815 , 0x84F572C5 , 0x40CC3A85 , 0xD2A32ABB }; for (int i=0 ;i<8 ;i+=2 ){ decrypt(r,&enc[i]); } printf ("%s" ,(char *)enc); return 0 ; }
然后密文是以char存储的,八个一组加密。存储形式要是搞不清楚可以动调的时候看看
2023/11/18 鹏城杯BabyRe 好久之前的比赛题,当时没做出来,看了大佬的wp复习了好多以前的知识
加密流程很清晰
flag分成4组,12个一组丢进去32轮加密
比赛的时候被这个移位迷惑了,当时不知道这只是ida反汇编出来的一些函数,实际上加密函数里没有。被困在这弄了很久。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <windows.h> #include <stdio.h> #include <stdlib.h> void decrypt (unsigned char * data) { unsigned int * _data = (unsigned int *) data; unsigned int rand_num[32 ][6 ]; srand(0xDEADC0DE ); for (int _ = 0 ; _ < 32 ; _++) { for (int i = 0 ; i < 6 ; i++) { rand_num[_][i] = rand(); } } for (int _ = 31 ; _ >= 0 ; _--) { unsigned int a = _data[0 ] >> 7 ; unsigned int b = rand_num[_][4 ] + a; unsigned int c = (_data[0 ] >> 15 ) ^ (_data[0 ] << 10 ) | 3 ; _data[2 ] -= b + (rand_num[_][5 ] ^ c); a = _data[2 ] >> 7 ; b = rand_num[_][2 ] + a; c = (_data[2 ] >> 15 ) ^ (_data[2 ] << 10 ) | 3 ; _data[1 ] -= b + (rand_num[_][3 ] ^ c); a = _data[1 ] >> 7 ; b = rand_num[_][0 ] + a; c = (_data[1 ] >> 15 ) ^ (_data[1 ] << 10 ) | 3 ; _data[0 ] -= b + (rand_num[_][1 ] ^ c); for (int i = 0 ; i < 12 ; i++) { data[i] = 167 * (data[i] - 66 & 0xFF ); } } } int main () { unsigned char enc[] = { 0x48 , 0x4D , 0x3B , 0xA0 , 0x27 , 0x31 , 0x28 , 0x54 , 0x6D , 0xF1 , 0x21 , 0x35 , 0x18 , 0x73 , 0x6A , 0x4C , 0x71 , 0x3B , 0xBD , 0x98 , 0xB6 , 0x5A , 0x77 , 0x2D , 0x0B , 0x2B , 0xCB , 0x9B , 0xE4 , 0x8A , 0x4C , 0xA9 , 0x5C , 0x4F , 0x1B , 0xF1 , 0x98 , 0x3D , 0x30 , 0x59 , 0x3F , 0x14 , 0xFC , 0x7A , 0xF4 , 0x64 , 0x02 , 0x2B , 0x00 }; decrypt((unsigned char *)enc); decrypt((unsigned char *)enc + 12 ); decrypt((unsigned char *)enc + 24 ); decrypt((unsigned char *)enc + 36 ); for (int i = 0 ; i < 48 ; i++) { printf ("%c" , enc[i]); } return 0 ; }
这里面有个要注意的地方:
对于data[i]=23*(data[i]+66)
逆过程为data[i]167*(data[i]-66&0xff)
因为加密中运算的时候把类型强制转换成了unsigned _int8 所以是0-255内的,要&0xff。这个167
是怎么回事呢?是23*x(mod 255)的乘法逆元。这样子就可以不打表算了。捡起来一点密码学知识…
回顾起来发现题目真的不难,就是做题的时候被一些细节问题绊住了,还是不够熟练。