0x00前言
这回上海大学生网络安全邀请赛就看了这题,实在是没空。不过这次我作为跑龙套的re手思路最清晰的一回了,值得记录下来。
0x01思路 作为汇编基础不扎实的跑龙套re手,拿到题目当然是仍经IDA来F5一波。
main函数的逻辑很简单,if判断为真就在sub_401BA0输出flag。通过OD动调得知v11就是通过输入的v9作为种子什么的,经过sub_401BA0函数获得uuid。
if里第一个函数简单的判断输入长度是否为16位,然后是一个while循环,我一开始navie的认为只能输入a-f的字符,后来才发现输入1-9的时候类型转无符号型,结果也会比&大,以后遇到类型转换一点要注意了。
第二个函数没看懂是什么操作,但谷歌了一下%02X
格式化输出位两位16的意思,动调的结果也确实如此。值得注意的是sub_401B60的两参数是传引用进去的,在执行完这个函数之后第一个参数的值发生了改变,所以一个函数除了看返回值外还要看它的参数是否为指针或者引用。太菜了现在才领悟到这个道理。
第三个函数IDA的伪代码也没看懂是什么操作,好在这个函数在这之前与输入的字符串关。第一个参数为取指针,直接动调一下看结果,出来了一串好像是int型的东西,后一个函数会用到先dump下来。
第四个函数这回看懂了,就是将转换后的16进制与上一个函数的结果的第i位和前i位的和进行异或,先把要异或的数dump下来备用。返回值恒为真,先放一边。
第五个函数尤为关键,跟第一个函数一样决定了能不能夺得flag。传入8个字节异或后的结果,每字节不能打大于9且要满足7个判断语句。这里按照它的逻辑把八位数爆出来就行了,但是一开始用python写的脚本一堆问题,整得我在比赛结束前没做出来。最后用c++写下就搞出来了。
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 #include <iostream> #include <string> #include <ios> using namespace std ;long long int dword_40F020 = 0x8A ;long long int dword_40F024 = 0x01A1 ;long long int dword_40F028 = 0x012A ;long long int dword_40F02C = 0x0269 ;long long int dword_40F030 = 0x0209 ;long long int dword_40F034 = 0x68 ;long long int dword_40F038 = 0x039F ;long long int dword_40F03C = 0x02C8 ;long long int dword_40F040 = 0x00FF ;int test (int v) ;void flag (int v) ;int main () { int v = 0 ; int i; for (i = 10000000 ; i <= 99999999 ; ++i) { int j = i; while (j != 0 ) { if (test(j % 10 ) == 1 ) { cout << i << endl ; flag(i); return 0 ; } j /= 10 ; } dword_40F020 = 0x8A ; dword_40F024 = 0x01A1 ; dword_40F028 = 0x012A ; dword_40F02C = 0x0269 ; dword_40F030 = 0x0209 ; dword_40F034 = 0x68 ; dword_40F038 = 0x039F ; dword_40F03C = 0x02C8 ; dword_40F040 = 0x00FF ; } return 0 ; } int test (int v) { int result = 0 ; switch (v) { case 0 : dword_40F028 &= dword_40F038; dword_40F02C *= dword_40F028; goto LABEL_4; case 1 : if (!dword_40F02C) goto LABEL_6; dword_40F028 /= dword_40F02C; dword_40F024 += dword_40F034; goto LABEL_4; case 2 : dword_40F030 ^= dword_40F034; dword_40F03C += dword_40F020; goto LABEL_4; case 3 : dword_40F03C -= dword_40F030; dword_40F030 &= dword_40F024; goto LABEL_4; case 4 : dword_40F034 *= dword_40F020; dword_40F02C -= dword_40F038; goto LABEL_4; case 5 : dword_40F020 ^= dword_40F02C; dword_40F038 -= dword_40F03C; goto LABEL_4; case 6 : if (!dword_40F03C) goto LABEL_6; dword_40F034 |= dword_40F024 / dword_40F03C; dword_40F024 /= dword_40F03C; goto LABEL_4; case 7 : dword_40F038 += dword_40F028; dword_40F034 |= dword_40F024; goto LABEL_4; case 8 : dword_40F020 *= dword_40F02C; dword_40F030 -= dword_40F03C; goto LABEL_4; case 9 : dword_40F028 += dword_40F034; dword_40F02C ^= dword_40F030; LABEL_4: result = ((dword_40F038 == 231 ) + (dword_40F034 == 14456 )+ (dword_40F030 == 14961 )+ (dword_40F02C == -13264 )+ (dword_40F028 == 16 )+ (dword_40F024 == 104 )+ (dword_40F020 == -951 ) == 7 ); if (dword_40F03C != -239 ) goto LABEL_6; break ; default : LABEL_6: return 0 ; break ; } return result; } void flag (int v) { int a[] = { 0x7C ,0xAB ,0x2D ,0x91 ,0x2F ,0x98 ,0xED ,0xA9 }; int j = 0 ; while (v) { for (int i = 0 ; i <= 256 ; ++i) { if (v % 10 == (i ^ a[j])) { printf ("%02x" , i); break ; } } v /= 10 ; j += 1 ; } }
运行结果:
27059416 7aaa29982a98eaab
输入到题目文件中:
Plz solve the puzzle: 7aaa29982a98eaab Congrats! flag{5cb92582-66a8-e5b7-d3bf-3b99df8ac7f0}
0X02总结
应当注意类型装换,以及转换后的字节大小
除了函数的返回值,还可以从参数的引用或指针获取结果
可以通过动态调试函数的结果得知函数的功能