SICNUCTF RSA逆向
分析主程序
int main_0()
{
int result; // eax
char input_in; // [esp+4Ch] [ebp-F0h]
char v2; // [esp+4Dh] [ebp-EFh]
int v3; // [esp+68h] [ebp-D4h]
char v4; // [esp+6Ch] [ebp-D0h]
char v5; // [esp+6Dh] [ebp-CFh]
char v6; // [esp+9Dh] [ebp-9Fh]
char v7; // [esp+A0h] [ebp-9Ch]
char v8; // [esp+A1h] [ebp-9Bh]
char v9; // [esp+D1h] [ebp-6Bh]
char v10; // [esp+D4h] [ebp-68h]
char v11; // [esp+D5h] [ebp-67h]
char v12; // [esp+105h] [ebp-37h]
char input; // [esp+108h] [ebp-34h]
char v14; // [esp+109h] [ebp-33h]
int v15; // [esp+125h] [ebp-17h]
char v16; // [esp+139h] [ebp-3h]
input = 0;
memset(&v14, 0, 0x30u);
v16 = 0;
v10 = 0;
memset(&v11, 0, 0x30u);
v12 = 0;
v7 = 0;
memset(&v8, 0, 0x30u);
v9 = 0;
v4 = 0;
memset(&v5, 0, 0x30u);
v6 = 0;
v3 = 0;
printf("Welcome to 8th SICNUCTF!\nPlz input : ");
scanf("%s", &input);
if ( strlen(&input) == 30 && !memcmp("sicnuctf{", &input, 9u) && (unsigned __int8)v15 == 125 )
{
input_in = 0;
memset(&v2, 0, 0x18u);
sub_401023((int)&input, &input_in);
v3 = sub_40100A((int)&input_in);
if ( v3 == 66 )
sub_401005((int)off_432A34[0], (int)&v4);
else
sub_401005((int)off_432A38, (int)&v4);
printf("%s\n", &v4);
result = 0;
}
else
{
sub_401005((int)off_432A38, (int)&v4);
printf("%s\n", &v4);
result = 0;
}
return result;
}
首先拿到题目要求你输入长度位30,且必须为sicnuctf{}所包裹,继续向下分析401023处函数是将中间的20个字符拷贝到另一个地址,之后就是较为关键的40100A函数了,进入该函数分析
int __cdecl sub_401680(int input_in)
{
int v2; // [esp+4Ch] [ebp-150h]
int i; // [esp+50h] [ebp-14Ch]
int v4; // [esp+54h] [ebp-148h]
char v5; // [esp+58h] [ebp-144h]
int v6; // [esp+11Ch] [ebp-80h]
_DWORD *v7; // [esp+120h] [ebp-7Ch]
int v8; // [esp+124h] [ebp-78h]
int *v9; // [esp+128h] [ebp-74h]
int v10; // [esp+12Ch] [ebp-70h]
int v11; // [esp+130h] [ebp-6Ch]
char v12; // [esp+134h] [ebp-68h]
char v13; // [esp+135h] [ebp-67h]
char v14; // [esp+165h] [ebp-37h]
char encode_input; // [esp+168h] [ebp-34h]
char v16; // [esp+169h] [ebp-33h]
char v17; // [esp+199h] [ebp-3h]
encode_input = 0;
memset(&v16, 0, 0x30u);
v17 = 0;
v12 = 0;
memset(&v13, 0, 0x30u);
v14 = 0;
v10 = sub_4025A0(400, 0x10u);
v9 = (int *)sub_402350(0);
v8 = sub_402350(0);
v7 = (_DWORD *)sub_402350(1);
v6 = sub_402350(233);
*(_DWORD *)(v10 + 564) = 16;
v4 = 0;
memset(&v5, 0, 0xC4u);
v2 = sub_40101E(100, (int)&v4);
for ( i = 0; i < v2; ++i )
sub_405970((int)v7, *(&v4 + i), v7);
sub_401005(input_in, (int)&encode_input);
sub_405E10(15, &encode_input, v9);
sub_4046E0((int)v9, v6, (int)v7, v8);
if ( *(_DWORD *)v8 <= 2u )
{
v11 = **(_DWORD **)(v8 + 4);
}
else
{
sub_401005((int)off_432A38, (int)&v12);
v11 = 0;
}
sub_402CA0(v9);
sub_402CA0(v8);
sub_402CA0(v7);
sub_402CA0(v6);
sub_402CC0();
return v11;
}
变种base64
与输入有关的函数为函数401005,进入该函数
signed int __cdecl sub_401110(int input_in, int a2)
{
signed int v3; // [esp+4Ch] [ebp-18h]
unsigned __int8 v4; // [esp+50h] [ebp-14h]
int v5; // [esp+54h] [ebp-10h]
int v6; // [esp+58h] [ebp-Ch]
int v7; // [esp+5Ch] [ebp-8h]
int v8; // [esp+60h] [ebp-4h]
v3 = 3;
sub_40100F();
v8 = 0;
v5 = 0;
v6 = 0;
v7 = 0;
while ( *(_BYTE *)(v7 + input_in) )
{
v4 = byte_436B58[*(unsigned __int8 *)(v7++ + input_in)];// 取映射
if ( v4 == 0xFF )
return -1;
if ( v4 != 0xFD )
{
if ( v4 == 0xFE )
{
v4 = 0;
--v3;
}
v8 = v4 | (v8 << 6);
if ( ++v6 == 4 ) // 以4个为一组
{
*(_BYTE *)(v5++ + a2) = BYTE2(v8);
if ( v3 > 1 )
*(_BYTE *)(v5++ + a2) = BYTE1(v8);
if ( v3 > 2 )
*(_BYTE *)(v5++ + a2) = v8;
v8 = 0;
v6 = 0;
}
}
}
return v5;
}
该函数将输入的函数ASCII码作为436B58处的值将其重新生成一个值,并且新值以4个为一组每个取其6位重新合并成一个24位的数字,之后又把数字拆成3组。感觉有点像base64解密。
看看436B58处有啥
我们可以正常输入的字符为
*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
正好64位。。。而且偏移取的字符最大为3F。。。。
可以判断这是一个变种的base64解密 字母表被替换,将字母表整理一下
a=[0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3A,0xFF,0xFF,0xFF,0xFF,0xFF,0x06,0x3E,0x1D,0x24,0x05,0x25,0x07,0x26,0x3C,0x3D,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF
,0xFF,0x0C,0x15,0x16,0x17,0x1A,0x1B,0x30,0x08,0x31,0x32,0x33,0x18,0x19,0x0A,0x2A,0x1E,0x1F,0x2F,0x0F,0x10,0x09,0x11,0x0D,0x0E,0x0B,0x27,0xFF,0xFF,0xFF,0xFF,0x1C
,0xFF,0x28,0x29,0x02,0x12,0x13,0x14,0x34,0x35,0x01,0x36,0x37,0x38,0x39,0x03,0x3F,0x3B,0x20,0x21,0x00,0x22,0x04,0x23,0x2B,0x2C,0x2D,0x2E,0xFF,0xFF,0xFF,0xFF,0xFF
,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
c=''
for x in range (0,64):
b=a.index(x)
c+=chr(b)
print c
打印出字母表如下
sicnu406HUNYAWXSTVdefBCDLMEF_2PQqrtv357ZabOwxyzRGIJKghjklm*p891o
继续向下分析main函数的405E10,由于函数比较长,查看运算结果发现是将字符变成了大数
再继续看函数main函数的4046E0,函数更长,于是放到PEID的插件查一下他的加密。
一个是mirvar(大数计算)还有一个powmod且powmod函数的地址就是4046E0,很容易想到这是RSA,放入IDA中查看一下powmod函数的参数。
第一个参数意义不明。。
第二个参数为DE04A372DD8B9DE12E25C098D58813 一串很长的数字应该是n,用python打印出10进制
前往网站进行分解整数
分解出100以内的质数
第三个参数可能就是E9了。
第四个参数是输入测试数据的base64解密的结果
一切都已经明了于是开始RSA解密
解密流程如下
RSA解密
base64变种加密
import base64
base='\x4c\x03\x4D\xb4\xa7\xcc\x8a\xe9\x85\xe4\x63\x4c\xf1\x40\xbb'
jiemi=base64.b64encode(base)
base64_table='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
base64_table2='sicnu406HUNYAWXSTVdefBCDLMEF_2PQqrtv357ZabOwxyzRGIJKghjklm*p891o'
flag=''
for x in range(0,20):
index=base64_table.find(jiemi[x:x+1])
flag+=base64_table2[index:index+1]
print flag
最终flag为
sicnuctf{esWWyNQAtz74m0WA8fcp}