base_on_rust
拖入IDA64,查看字符串,疑似base编码
跟进引用函数
发现这里并没有进行编码,只是初始化了base64,base32和base16的表
跟进到输入处理函数,根据base编码特征修改反编译代码,可得
提取出在off_140028AE8地址的字串RzQyVE1SSldHTTNUSU5SV0c1QkRNTVJXR0UzVEdOUlZHTTNER05CVklZM0RFTlJSRzRaVE1OSlRHTVpURU5LR0dZWkRNTUpYR00zREtNWlJHTTNES1JSV0dVM0VLTlJUR1pERE1OQldHVTJVTU5LR0dWRERPUkE9
解码可得:unctf{base64_base32_base16_encode___}
Trap
拖入IDA64,跟进main函数
puts("Welcome To UNCTF2020_RE_WORLD !!!");
printf("Plz Input Key: ", a2);
__isoc99_scanf("%s", s1);
strcpy(dest, s1);
sub_400CBE();
if ( !strcmp(s1, s2) )
{
puts("Success.");
for ( i = 0; i <= 8479; ++i ){
v3 = byte_6020E0[i];
byte_6020E0[i] = s1[i % strlen(s1)] ^ v3;
}
s = fopen("/tmp/libunctf.so", "wb");
fwrite(byte_6020E0, 1uLL, 0x2120uLL, s);
getchar();
handle = dlopen("/tmp/libunctf.so", 1)
if ( !handle ){
v5 = stderr;
v6 = dlerror();
fputs(v6, v5);
exit(1);
}
v7 = (void (__fastcall *)(__int16 *, char *))dlsym(handle, "jo_enc");
dlerror();
v15 = 0;
v16 = 0;
v17 = 0;
memset(&v18, 0, 0x28uLL);
printf("plz Input Answer: ", "jo_enc", &v18);
__isoc99_scanf("%s", &v15);
v7(&v15, dest);
}
else{
puts("Loser!!!");
}
大概意思是将处理后的输入和已有字串做对比,运行的时候输入正确的s1会异或解密整个动态链接库文件,然后写入文件并调用jo_enc函数对接下来的输入以v15(第二次输入的字串),dest(第一次输入的字串)的顺序进行调用检查
首先将输入与0x22异或, 然后创建了一个线程
v2 = strlen(s1);
for ( i = 0; i < v2; ++i )
s1[i] ^= 0x22u;
pthread_create(&th, 0LL, (void *(*)(void *))start_routine, 0LL);
return pthread_join(th, 0LL);
接着调用sub_400BC0函数将s2与0x33异或并写入文件
v3 = strlen(s2);
sub_400B76(v0);//反调试
for ( i = 0; ; ++i )
{
result = i;
if ((signed int)i >= v3 )
break;
s2[i] ^= 0x33u;
}
return result;
和调用sub_400C13函数对s1和s1长度做运算
for ( i = 0; ; ++i ){
result = (unsigned int)i;
if ( i >= v3 )
break;
sub_400C13(&s1[i], v3);
}
这里的sub_400C13有简单的花指令,
手动修复后其实就是个循环
__int64 __fastcall sub_400C13(_BYTE *a1, int a2)
{
if ( !a2 )
return 1LL;
++*a1;
return sub_400C13(a1, (unsigned int)(a2 - 1));
}
那么脚本就很好写了
s2=[26,23,18,23,17,44,124,27,46,45,125,124,125,46]
for i in s2:
print(chr(((i^0x33)-len(s2))^0x22),end='')
#941463c8-2bcb-
将生成的libunctf.so拖入ida64分析jo_enc函数
__int64 __fastcall jo_enc(char *a1, char *a2)
{
char *v2; // ST20_8
size_t v3; // ST10_8
int n; // [rsp+60h] [rbp-500h]
int m; // [rsp+64h] [rbp-4FCh]
int l; // [rsp+68h] [rbp-4F8h]
int k; // [rsp+6Ch] [rbp-4F4h]
int v9; // [rsp+70h] [rbp-4F0h]
int j; // [rsp+74h] [rbp-4ECh]
int v11; // [rsp+78h] [rbp-4E8h]
signed int i; // [rsp+7Ch] [rbp-4E4h]
int v13[48]; // [rsp+80h] [rbp-4E0h]
int odd_number[128]; // [rsp+140h] [rbp-420h]
int even_number[129]; // [rsp+340h] [rbp-220h]
int v16; // [rsp+544h] [rbp-1Ch]
char *input1; // [rsp+548h] [rbp-18h]
char *input2; // [rsp+550h] [rbp-10h]
input2 = a1;
input1 = a2;
v16 = 0;
memset(even_number, 0, 0x200uLL);
memset(odd_number, 0, 0x200uLL);
memset(v13, 0, 0xC0uLL);
for ( i = 0; i < 128; ++i )
{
even_number[i] = 2 * i;
odd_number[i] = 2 * i + 1;
}
v11 = strlen(input2);
for ( j = 0; j < v11; ++j )
{
v9 = input2[j];
if ( !(v9 % 2) )
{
for ( k = 0; k < v9; k += 2 )
v13[j] += even_number[k];
}
if ( v9 % 2 )
{
for ( l = 0; l < v9; l += 2 )
v13[j] += odd_number[l];
}
}
for ( m = 0; m < v11; ++m )
{
v2 = input1;
v3 = strlen(input1);
v13[m] = (16 * v2[m % v3] & 0xE827490C | ~(16 * v2[m % v3]) & 0x17D8B6F3) ^ (v13[m] & 0xE827490C | ~v13[m] & 0x17D8B6F3);
}
for ( n = 0; n < v11; ++n )
{
if ( v13[n] != *((_DWORD *)off_200FD8 + n) )
{
v16 = 0;
exit(1);
}
++v16;
}
if ( v16 == 22 )
puts("Win , Flag is unctf{input1+input2}");
return 0LL;
}
根据第一个输入可得,v9(即第二个输入的ascii码)范围在45~127,可以写爆破脚本
cipher_lst = [1668, 1646, 1856, 4118, 1899, 1752, 640, 2000, 4412, 1835, 820, 984, 968, 1189, 4353, 1646, 4348, 4561, 1564,1566, 5596, 1525]
input1 = '941463c8-2bcb-'
input2 = ''
even_number = [i * 2 for i in range(128)]
odd_number = [i * 2 + 1 for i in range(128)]
cipher_ = [((16 * ord(input1[m % 14])) & 0xE827490C | (~(16 * ord(input1[m % 14])) & 0x17D8B6F3)) ^ (cipher_lst[m] & 0xE827490C | ~cipher_lst[m] & 0x17D8B6F3) for m in range(22)]
for a in cipher_:
for n in range(45,127):
j = 0
if not n % 2:
for i in range(0, n, 2):
j += even_number[i]
else:
for i in range(0, n, 2):
j += odd_number[i]
if j == a:
input2 += chr(n)
print('unctf{' + input1 + input2 + '}')
之后经过师傅的点拨,原来 (str1 & random_hex1 | ~str1 & random_hex2) ^ (str2 & random_hex1 | ~str2 & random_hex2) 和 str1^str2是等价的