2019 红帽杯 Re WP
目录
0x01 xx
测试文件:https://www.lanzous.com/i7dyqhc
1.准备
获取信息
- 64位文件
2.IDA打开
使用Findcrypt脚本可以看到
结合文件名是xx,因此猜测代码用到了xxtea加密方法
3.流程总结
因此,总的流程为:
- 判断输入的字符串的每个字符是否包含在"qwertyuiopasdfghjklzxcvbnm1234567890"中
- 取输入字符串的前4位字符,即"flag",扩展为16位,作为xxtea加密的秘钥key
- 将输入的字符串使用key加密,加密后的字符保存在字符数组v18,共24位字符
- 打乱v18数组,保存到v19数组中
- 将24位字符,每3位为一组,每一组异或值(具体看代码),得到新的加密字符串
- 将新的加密字符串与已经存在的字符串比较,相同即获得胜利
因此,只需要逆向变换,就能得到flag
使用动态调试,可以获取到已经存在的字符串
enc = 'CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA'
4.脚本解密
Python带了xxtea的包,不过我用的时候,一直提示我“ValueError: Need a 16-byte key.”,用rjust或者'\x00'*16补足了16位也不管用。
所以用了另外一种方法,借用了下面xxtea的文章:
参考文章:https://blog.****.net/weixin_41474364/article/details/84314674
# encoding: utf-8 import struct _DELTA = 0x9E3779B9 def _long2str(v, w): n = (len(v) - 1) << 2 if w: m = v[-1] if (m < n - 3) or (m > n): return '' n = m s = struct.pack('<%iL' % len(v), *v) return s[0:n] if w else s def _str2long(s, w): n = len(s) m = (4 - (n & 3) & 3) + n s = s.ljust(m, "\0") v = list(struct.unpack('<%iL' % (m >> 2), s)) if w: v.append(n) return v def encrypt(str, key): if str == '': return str v = _str2long(str, True) k = _str2long(key.ljust(16, "\0"), False) n = len(v) - 1 z = v[n] y = v[0] sum = 0 q = 6 + 52 // (n + 1) while q > 0: sum = (sum + _DELTA) & 0xffffffff e = sum >> 2 & 3 for p in xrange(n): y = v[p + 1] v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff z = v[p] y = v[0] v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff z = v[n] q -= 1 return _long2str(v, False) def decrypt(str, key): if str == '': return str v = _str2long(str, False) k = _str2long(key.ljust(16, "\0"), False) n = len(v) - 1 z = v[n] y = v[0] q = 6 + 52 // (n + 1) sum = (q * _DELTA) & 0xffffffff while (sum != 0): e = sum >> 2 & 3 for p in xrange(n, 0, -1): z = v[p - 1] v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff y = v[p] z = v[n] v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff y = v[0] sum = (sum - _DELTA) & 0xffffffff return _long2str(v, True) def xor(x ,y): return ord(x) ^ ord(y) # 转换为16进制 arr = 'CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA'.decode('hex') dec = '' # 因为加密时是正向加密,会用到加密之后的字符,因此解密需要逆向解密 for i in range(7,-1,-1): res = '' # 每3个为一组 for j in range(3): temp = ord(arr[i*3+j]) # 需要异或的值,例如第i组的值就是,arr[i*3+j]^(arr[n] for n in range(i)) for m in range(i): temp ^= ord(arr[m]) res += chr(temp) dec = res + dec # 原来的v18到v19数组是被打乱排序了的 num = [2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21] enc = [0] * 24 # key需要是16位 key = 'flag'+'\x00'*12 for i in range(24): enc[num[i]] = dec[i] dec2 = ''.join(enc) dec3 = decrypt(dec2, key) print dec4
5.get flag!
flag{CXX_and_++tea}
0x02 easyRE
测试文件:https://share.weiyun.com/5qzM6bU
1.准备
获取信息
- 64位文件
2.IDA打开
signed __int64 sub_4009C6() { char *v0; // rsi char *v1; // rdi signed __int64 result; // rax __int64 v3; // ST10_8 __int64 v4; // ST18_8 __int64 v5; // ST20_8 __int64 v6; // ST28_8 __int64 v7; // ST30_8 __int64 v8; // ST38_8 __int64 v9; // ST40_8 __int64 v10; // ST48_8 __int64 v11; // ST50_8 __int64 v12; // ST58_8 int i; // [rsp+Ch] [rbp-114h] char arraym[36]; // [rsp+60h] [rbp-C0h] char v15[32]; // [rsp+90h] [rbp-90h] int v16; // [rsp+B0h] [rbp-70h] char v17; // [rsp+B4h] [rbp-6Ch] char v18; // [rsp+C0h] [rbp-60h] char v19; // [rsp+E7h] [rbp-39h] char v20; // [rsp+100h] [rbp-20h] unsigned __int64 v21; // [rsp+108h] [rbp-18h] v21 = __readfsqword(0x28u); arraym[0] = 73; arraym[1] = 111; arraym[2] = 100; arraym[3] = 108; arraym[4] = 62; arraym[5] = 81; arraym[6] = 110; arraym[7] = 98; arraym[8] = 40; arraym[9] = 111; arraym[10] = 99; arraym[11] = 121; arraym[12] = 127; arraym[13] = 121; arraym[14] = 46; arraym[15] = 105; arraym[16] = 127; arraym[17] = 100; arraym[18] = 96; arraym[19] = 51; arraym[20] = 119; arraym[21] = 125; arraym[22] = 119; arraym[23] = 101; arraym[24] = 107; arraym[25] = 57; arraym[26] = 123; arraym[27] = 105; arraym[28] = 121; arraym[29] = 61; arraym[30] = 126; arraym[31] = 121; arraym[32] = 76; arraym[33] = 64; arraym[34] = 69; arraym[35] = 67; memset(v15, 0, sizeof(v15)); v16 = 0; v17 = 0; v0 = v15; sub_4406E0(0LL, (__int64)v15); v17 = 0; v1 = v15; if ( sub_424BA0(v15) == 36 ) { for ( i = 0; ; ++i ) { v1 = v15; if ( i >= (unsigned __int64)sub_424BA0(v15) ) break; if ( (unsigned __int8)(v15[i] ^ i) != arraym[i] ) { result = 4294967294LL; goto LABEL_13; } } sub_410CC0("continue!"); memset(&v18, 0, 0x40uLL); v20 = 0; v0 = &v18; sub_4406E0(0LL, (__int64)&v18); v19 = 0; v1 = &v18; if ( sub_424BA0(&v18) == 39 ) { v3 = sub_400E44(&v18); v4 = sub_400E44(v3); v5 = sub_400E44(v4); v6 = sub_400E44(v5); v7 = sub_400E44(v6); v8 = sub_400E44(v7); v9 = sub_400E44(v8); v10 = sub_400E44(v9); v11 = sub_400E44(v10); v12 = sub_400E44(v11); v0 = off_6CC090; v1 = (char *)v12; if ( !(unsigned int)sub_400360(v12, off_6CC090) ) { sub_410CC0("You found me!!!"); v1 = "bye bye~"; sub_410CC0("bye bye~"); } result = 0LL; } else { result = 4294967293LL; } } else { result = 0xFFFFFFFFLL; } LABEL_13: if ( __readfsqword(0x28u) != v21 ) sub_444020(v1, v0); return result; }
3.代码分析
首先有两次输入,第一次输入32位字符串,将每位字符异或后与已存在的marray数组比较,因此可以写出脚本,正确输入
arr = [73,111,100,108,62,81,110,98,40,111,99,121,127,121,46,105,127,100,96,51,119,125, 119,101,107,57,123,105,121,61,126,121,76,64,69,67] dec = '' for i in range(36): dec += chr(arr[i]^i) print(dec)
Info:The first four chars are `flag`
第二次输入,将输入的字符串进行10次base64加密后,与已知的字符串比较,反向解密就行
enc = "Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ==" for i in range(10): enc = enc.decode('base64') print (enc)
https://bbs.pediy.com/thread-254172.htm
在第二次输入加密后对比的常量下面,还发现了一个常量,在sub_400D35函数中调用
__int64 __fastcall sub_400D35(__int64 a1, __int64 a2) { __int64 v2; // rdi __int64 result; // rax unsigned __int64 v4; // rt1 unsigned int v5; // [rsp+Ch] [rbp-24h] signed int i; // [rsp+10h] [rbp-20h] signed int j; // [rsp+14h] [rbp-1Ch] unsigned int v8; // [rsp+24h] [rbp-Ch] unsigned __int64 v9; // [rsp+28h] [rbp-8h] v9 = __readfsqword(0x28u); v2 = 0LL; v5 = sub_43FD20(0LL) - qword_6CEE38; for ( i = 0; i <= 1233; ++i ) { v2 = v5; sub_40F790(v5); sub_40FE60(); sub_40FE60(); v5 = (unsigned __int64)sub_40FE60() ^ 0x98765432; } v8 = v5; if ( ((unsigned __int8)v5 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v8) ^ (unsigned __int8)byte_6CC0A3) == 'g' ) { for ( j = 0; j <= 24; ++j ) { v2 = (unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v8 + j % 4)); sub_410E90(v2); } } v4 = __readfsqword(0x28u); result = v4 ^ v9; if ( v4 != v9 ) sub_444020(v2, a2); return result; }
两段异或,第一段异或,能够通过'flag'和已知数组反向解出v5
第二段异或。通过已知数组和v5解出flag
key = '' enc1 = 'flag' dec = '' enc = [0x40,0x35,0x20,0x56,0x5D,0x18,0x22,0x45,0x17,0x2F,0x24,0x6E,0x62,0x3C,0x27,0x54,0x48,0x6C,0x24,0x6E,0x72,0x3C,0x32,0x45,0x5B] for i in range(4): key += chr(enc[i] ^ ord(enc1[i])) print (key) for i in range(len(enc)): dec += chr(enc[i] ^ ord(key[i%4])) print(dec)
4.get flag!
flag{Act1ve_Defen5e_Test}
0x03 calc
测试文件:https://www.lanzous.com/i7frprg
1准备
获取信息
- 64位文件
2.IDA打开
伪C代码
3.流程总结
整个过程,有三次输入,定义为变量x, y, z。在满足x < z and x > y的条件下,进行x**3+y**3+z**3=42,搜了一下有关“三次方42”的新闻
得到
(-80538738812075974)^3 + 80435758145817515^3 + 12602123297335631^3 = 42
根据x,y,z关系式得到
x=80435758145817515
y=12602123297335631
z=80538738812075974
将Sleep的时间全部改为0
写出脚本得到flag
4.get flag!
flag{951e27be2b2f10b7fa22a6dc8f4682bd}
0x04 childRE
测试文件:https://www.lanzous.com/i7h66wd
1.准备
- 64位文件
2.IDA代码分析
3.流程总结
- 因此总的运算流程就是:
- 输入长度为31的字符串
- 进行置换运算
- 取消修饰函数名
- 将未修饰函数名的商和余数与指定字符串比较
我们能够逆向操作来得到未修饰的函数名。
4.获取未修饰函数名
IDA动态调试
写出脚本
str1 = "([email protected]!08!6_0*[email protected]%%[email protected]=66!!974*3234=&0^3&[email protected]=&0908!6_0*&" str2 = "55565653255552225565565555243466334653663544426565555525555222" str3 = '[email protected]#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;,ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,./' name = '' for i in range(62): name += chr(str3.index(str1[i]) + str3.index(str2[i])*23 ) print (name)
得到:private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
使用C++写出一个上面函数的例子:
#include <iostream> class R0Pxx { public: R0Pxx() { My_Aut0_PWN((unsigned char*)"hello"); } private: char* __thiscall My_Aut0_PWN(unsigned char*); }; char* __thiscall R0Pxx::My_Aut0_PWN(unsigned char*) { std::cout << __FUNCDNAME__ << std::endl; return 0; } int main() { R0Pxx A; system("PAUSE"); return 0; }
得到:[email protected]@@[email protected]
5.置换运算
通过动态调试,发现乱序取值的数值是固定的,因此随便输入一组长度31的字符串(其中的字符不能重复)
反向操作,写出脚本来解决flag
from hashlib import md5 str1 = 'abcdefghijklmnopqrstuvwxyz12345' dec1 = '7071687273696474756A76776B656278796C7A316D6632336E34356F676361'.decode('hex') serial = [] print dec1 for i in dec1: serial.append(str1.index(i)) print serial name = '[email protected]@@[email protected]' enc = [''] * 31 for i in range(31): enc[serial[i]] = name[i] enc = ''.join(enc) print enc print md5(enc).hexdigest()
6.get flag!
flag{63b148e750fed3a33419168ac58083f5}
0x05 Snake
测试文件:https://www.lanzous.com/i7gol0d
Unity逆向
1.查看DLL文件
运行Snake,查看调用的DLL文件
2.DLL文件分析
使用ILSpy打开Interface.dll文件
发现了DLL文件使用的函数GameObject
使用IDA打开DLL文件
GameObject
判断出GameObject函数传入的参数,最大应该是199,因此直接写程序,调用DLL文件,**求flag
3.**求解
开多个进程,同时求解。
#include <Windows.h> #include <iostream> #include <libloaderapi.h> using namespace std; int main(int argc, char* argv[]) { const char* funcName = "GameObject"; HMODULE hDLL = LoadLibrary(TEXT("C:\\Users\\10245\\Desktop\\Snake\\Snake_Data\\Plugins\\Interface.dll")); if (hDLL != NULL) { cout << "Load Success!" << endl; typedef int(_cdecl *FuncPtr)(int); FuncPtr func = (FuncPtr)GetProcAddress(hDLL, funcName); func(atoi(argv[1])); } else { cout << "Load Failed!" << endl; } system("PAUSE"); return 0; }
4.get flag!
flag{[email protected]}
作者:Hk_Mayfly
出处:https://www.cnblogs.com/Mayfly-nymph/
个人博客:hkmayfly.com
****:https://blog.****.net/qq_39542714
如果你觉得博客对你有帮助的话,可以给博主一个(づ ̄3 ̄)づ╭❤~赞或者关注,谢谢!
博主目前大部分写作在博客园,欢迎大家来访,( ̄︶ ̄)↗也欢迎爱好写作的朋友和我交换友链。
附注:不支持转载!欢迎大家加我QQ,互相交流。Biubiubiu~顺便来关注我的公众号:Star Bottle叭!,哈哈哈!