-
-
[原创]2019看雪CTF 团队赛 第五题 青梅竹马WP
-
发表于: 2019-3-21 16:39 3642
-
打开程序,随便输入点什么,弹出'no,wrong sn!!!"。
在MessageBoxA下断,回溯到402652,用IDA分析,下面的代码我已经分析过并重命名函数了。
//省略 GetDlgItemTextA(hDlg, 1000, &String, 3); // String总是等于0x4F v38 = GetDlgItemTextA(hDlg, 1001, &input, 255); sub_402A90(0, &Text); v39 = 0; v41 = 0; if ( v38 < 12 ) // len>12 v41 = 1; if ( input == 'A' && v30 == 'A' ) // 1和2位不可以为A ++v41; if ( v31 != 'V' || v32 != 'V' ) // 6和12位等于V ++v41; for ( i = 0; i < v38; ++i ) { if ( i != 5 && i != 11 ) // 当前位置不是6和12时 ...//这部分代码主要是组合input并且初始化变量,省略 v5 = String; // v5=0x4F sub_402889(80, &v13, 0x40u); // 生成base64table sub_402270(&v13); // 写到全局变量 len = base64decode((unsigned __int8 *)&code, &out);// 解码输入字符串 base64encode((unsigned __int8 *)&out, &encode, len);// 解码 if ( out ) { if ( !memcmp(&encode, &code, v39) ) // 比较编码后是否相同,相同则进入下一步 { v7 = check(100, v5, (int)&out, len); GetText(v7, &Text); // base64解码后返回 } } } MessageBoxA(hDlg, &Text, &Text, 0);
分析代码,可以知道flag是base64编码后在6和12位处插入'V',在解码时会将其删去。
//省略 GetDlgItemTextA(hDlg, 1000, &String, 3); // String总是等于0x4F v38 = GetDlgItemTextA(hDlg, 1001, &input, 255); sub_402A90(0, &Text); v39 = 0; v41 = 0; if ( v38 < 12 ) // len>12 v41 = 1; if ( input == 'A' && v30 == 'A' ) // 1和2位不可以为A ++v41; if ( v31 != 'V' || v32 != 'V' ) // 6和12位等于V ++v41; for ( i = 0; i < v38; ++i ) { if ( i != 5 && i != 11 ) // 当前位置不是6和12时 ...//这部分代码主要是组合input并且初始化变量,省略 v5 = String; // v5=0x4F sub_402889(80, &v13, 0x40u); // 生成base64table sub_402270(&v13); // 写到全局变量 len = base64decode((unsigned __int8 *)&code, &out);// 解码输入字符串 base64encode((unsigned __int8 *)&out, &encode, len);// 解码 if ( out ) { if ( !memcmp(&encode, &code, v39) ) // 比较编码后是否相同,相同则进入下一步 { v7 = check(100, v5, (int)&out, len); GetText(v7, &Text); // base64解码后返回 } } } MessageBoxA(hDlg, &Text, &Text, 0);
分析代码,可以知道flag是base64编码后在6和12位处插入'V',在解码时会将其删去。
看下check的代码
int __cdecl check(int a1, int value, int code, int len) { signed int v4; // ebx int *v5; // edi int prime_t; // [esp+Ch] [ebp-1C0h] int table[99]; // [esp+10h] [ebp-1BCh] char int2; // [esp+19Ch] [ebp-30h] char codeint; // [esp+1A8h] [ebp-24h] char result; // [esp+1B4h] [ebp-18h] char big_m; // [esp+1C0h] [ebp-Ch] int prime; // [esp+1D4h] [ebp+8h] int ret; // [esp+1E0h] [ebp+14h] prime_t = 0; memset(table, 0, sizeof(table)); prime = createprime(a1, &prime_t); // 生成质数数组 big_init(&result); big_init(&int2); big_setvalue((int)&result, 0); big_setvalue((int)&codeint, 0); v4 = 1; big_setvalue((int)&big_m, 1); big_setvalue((int)&int2, prime_t); // 第一位是2 big_fromint((int)&codeint, code, len); // 将code转换为bignum类型 ret = big_toint((int)&result); if ( prime > 1 ) { v5 = table; // table的值是prime的指针+1 while ( *v5 != value ) // value=0x4F { if ( *v5 ) sub_401B0A((int)&big_m, (int)&big_m, *v5);// 乘以table所有成员,结果在big_m,结果是0x41CD66ACC237B22681A18067 ++v4; ++v5; if ( v4 >= prime ) goto LABEL_9; } prime = table[v4]; // 取得value所在位置后一位的成员,在这个程序里一直等于0x53 } LABEL_9: if ( big_cmp(&codeint, &int2) >= 0 && big_cmp(&codeint, &big_m) <= 0 ) { codevaild((int)&result, (int)&codeint, prime, (int)&big_m); if ( big_cmp(&result, &int2) >= 0 && big_cmp(&result, &big_m) <= 0 )// result为2 ret = big_toint((int)&result); } free((int)&result); free((int)&int2); return ret; }
看过GetText就知道,这个函数的返回值必须为2
int __cdecl check(int a1, int value, int code, int len) { signed int v4; // ebx int *v5; // edi int prime_t; // [esp+Ch] [ebp-1C0h] int table[99]; // [esp+10h] [ebp-1BCh] char int2; // [esp+19Ch] [ebp-30h] char codeint; // [esp+1A8h] [ebp-24h] char result; // [esp+1B4h] [ebp-18h] char big_m; // [esp+1C0h] [ebp-Ch] int prime; // [esp+1D4h] [ebp+8h] int ret; // [esp+1E0h] [ebp+14h] prime_t = 0; memset(table, 0, sizeof(table)); prime = createprime(a1, &prime_t); // 生成质数数组 big_init(&result); big_init(&int2); big_setvalue((int)&result, 0); big_setvalue((int)&codeint, 0); v4 = 1; big_setvalue((int)&big_m, 1); big_setvalue((int)&int2, prime_t); // 第一位是2 big_fromint((int)&codeint, code, len); // 将code转换为bignum类型 ret = big_toint((int)&result); if ( prime > 1 ) { v5 = table; // table的值是prime的指针+1 while ( *v5 != value ) // value=0x4F { if ( *v5 ) sub_401B0A((int)&big_m, (int)&big_m, *v5);// 乘以table所有成员,结果在big_m,结果是0x41CD66ACC237B22681A18067 ++v4; ++v5; if ( v4 >= prime ) goto LABEL_9; } prime = table[v4]; // 取得value所在位置后一位的成员,在这个程序里一直等于0x53 } LABEL_9: if ( big_cmp(&codeint, &int2) >= 0 && big_cmp(&codeint, &big_m) <= 0 ) { codevaild((int)&result, (int)&codeint, prime, (int)&big_m); if ( big_cmp(&result, &int2) >= 0 && big_cmp(&result, &big_m) <= 0 )// result为2 ret = big_toint((int)&result); } free((int)&result); free((int)&int2); return ret; }
看过GetText就知道,这个函数的返回值必须为2
if ( a1 ) { if ( a1 == 1 ) { v2 = aBwzxzsb3b3jrth; // more work to do! } else if ( a1 == 2 ) { v2 = aSwe9lcbjb3jxzw; // yes, correct sn! } else { v2 = aC24azgzlc24n8c; // sn doesn't work! } } else { v2 = aBmdeth8xb2unth; // no, wrong sn!!!! }
我们继续跟进codevaild
if ( a1 ) { if ( a1 == 1 ) { v2 = aBwzxzsb3b3jrth; // more work to do! } else if ( a1 == 2 ) { v2 = aSwe9lcbjb3jxzw; // yes, correct sn! } else { v2 = aC24azgzlc24n8c; // sn doesn't work! } } else { v2 = aBmdeth8xb2unth; // no, wrong sn!!!! }
我们继续跟进codevaild
int __cdecl codevaild(int a1, int big_code, unsigned int a3, int big_m) { unsigned int i; // ebx char ret; // [esp+4h] [ebp-24h] char mulret; // [esp+10h] [ebp-18h] char tmp; // [esp+1Ch] [ebp-Ch] big_init(&ret); mod(&tmp, big_code, (_DWORD *)big_m); // 取模,结果给tmp big_setvalue((int)&ret, 1); for ( i = a3; i; i >>= 1 ) // i=0x53 { if ( i & 1 ) { mul_0(&mulret, &ret, &tmp); // 相乘 mod(&ret, (int)&mulret, (_DWORD *)big_m); } mul_0(&mulret, &tmp, &tmp); mod(&tmp, (int)&mulret, (_DWORD *)big_m); } copy((_DWORD *)a1, (int)&ret); free((int)&ret); return 0; }
将过程列成数学表达式
int __cdecl codevaild(int a1, int big_code, unsigned int a3, int big_m) { unsigned int i; // ebx char ret; // [esp+4h] [ebp-24h] char mulret; // [esp+10h] [ebp-18h] char tmp; // [esp+1Ch] [ebp-Ch] big_init(&ret); mod(&tmp, big_code, (_DWORD *)big_m); // 取模,结果给tmp big_setvalue((int)&ret, 1); for ( i = a3; i; i >>= 1 ) // i=0x53 { if ( i & 1 ) { mul_0(&mulret, &ret, &tmp); // 相乘 mod(&ret, (int)&mulret, (_DWORD *)big_m); } mul_0(&mulret, &tmp, &tmp); mod(&tmp, (int)&mulret, (_DWORD *)big_m); } copy((_DWORD *)a1, (int)&ret); free((int)&ret); return 0; }
将过程列成数学表达式
ret1=(code*ret)%0x41CD66ACC237B22681A18067 //code^1 code1=(code*code)%0x41CD66ACC237B22681A18067 //2 ret2=(code1*ret1)%0x41CD66ACC237B22681A18067 //3 code2=(code1*code1)%0x41CD66ACC237B22681A18067 //4 code3=(code2*code2)%0x41CD66ACC237B22681A18067 //8 code4=(code3*code3)%0x41CD66ACC237B22681A18067 //16 ret5=(code4*ret2)%0x41CD66ACC237B22681A18067 //19 code5=(code4*code4)%0x41CD66ACC237B22681A18067 //32 code6=(code5*code5)%0x41CD66ACC237B22681A18067 //64 ret7=(code6*ret5)%0x41CD66ACC237B22681A18067 //83 ret7=2
ret1=(code*ret)%0x41CD66ACC237B22681A18067 //code^1 code1=(code*code)%0x41CD66ACC237B22681A18067 //2 ret2=(code1*ret1)%0x41CD66ACC237B22681A18067 //3 code2=(code1*code1)%0x41CD66ACC237B22681A18067 //4 code3=(code2*code2)%0x41CD66ACC237B22681A18067 //8 code4=(code3*code3)%0x41CD66ACC237B22681A18067 //16 ret5=(code4*ret2)%0x41CD66ACC237B22681A18067 //19 code5=(code4*code4)%0x41CD66ACC237B22681A18067 //32 code6=(code5*code5)%0x41CD66ACC237B22681A18067 //64 ret7=(code6*ret5)%0x41CD66ACC237B22681A18067 //83 ret7=2
可以把表达式整理成(flag^83) = 2 mod 0x41CD66ACC237B22681A18067
求解该高次剩余问题就能得到flag,下面用P代称
0x41CD66ACC237B22681A18067
一般高次剩余问题中P是质数,但是这个地方不同,P是合数,P是check函数的质数数组{0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,0x3D,0x43,0x47,0x49}相乘得到的,所以需要先质数分解P,
求解flag1^83 = 2 mod 3,flag2^83 = 2 mod 5, flagn^83 = 2 mod Pn ,再用中国剩余定理合并flagn。
求解高次剩余的文章和代码网上有很多,后面我会附上链接,可以解得flagn={2,3,4,7,7,8,13,4,15,4,5,5,22,32,5,10,17,63,38,32}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
看原图
赞赏
雪币:
留言: