本帖最后由 zhukai055 于 2018-7-16 09:09 编辑
REVERSE-ASONG:
0x01:
下载三个文件,分别是 asong linux程序 out输出 that_girl 原文
0x02:开始分析asong
Sub_400AAA:
int __fastcall sub_400AAA(const char *a1, __int64a2)//入栈内容:字符串指针,处理后的字符串长度{ int v2; // eax@3 int result; // eax@4 __int64 v4; // rcx@4 __int64 v5; //[sp+0h] [bp-20h]@1 char buf; //[sp+13h] [bp-Dh]@2 int fd; //[sp+14h] [bp-Ch]@1 __int64 v8; //[sp+18h] [bp-8h]@1 v8 = *MK_FP(__FS__, 40LL); fd = open(a1, 0, a2, a1);//打开that_girl文件 while ( read(fd, &buf, 1uLL) == 1 )//按单字符读入,读入成功,即循环 { v2 =sub_400936((unsigned int)buf, &buf);//读取到的内容进入sub_400936函数++*(_DWORD *)(4LL * v2 + v5);//v5是密码对应表的首地址,A[4*v2]++; 密码表用A数组表示。 } result = close(fd); v4 = *MK_FP(__FS__, 40LL) ^ v8; return result;
}
-----------------------------------------------------------------------------------__int64 __fastcall sub_400936(char a1)//一个算法 { __int64 result; // rax@1 result = (unsigned int)(a1 - 10); switch ( a1 ) { case 95: result = (unsigned int)(a1 - 49); break; case 10: result = (unsigned int)(a1 + 35); break; case 32: case 33: case 34: result = (unsigned int)(a1 + 10); break; case 39: result = (unsigned int)(a1 + 2); break; case 44: result = (unsigned int)(a1 - 4); break; case 46: result = (unsigned int)(a1 - 7); break; case 58: case 59: result = (unsigned int)(a1 - 21); break; case 63: result = (unsigned int)(a1 - 27); break; default: if ( a1 <= 47 || a1 > 57 ) { if ( a1 <= 64 || a1 > 90 ) { if ( a1 > 96 && a1 <= 122 ) result = (unsigned int)(a1 - 87); } else { result = (unsigned int)(a1 - 55); } } else { result = (unsigned int)(a1 - 48); } break; } return result;
}
-----------------------------------------------------------------------------------
__int64 __fastcall sub_400E54(const char *a1, __int64a2)//入栈内容:字符串指针,处理后的字符串长度{ int i; //[sp+18h] [bp-48h]@1 int v4; //[sp+1Ch] [bp-44h]@1 char v5[56]; //[sp+20h] [bp-40h]@3 __int64 v6; //[sp+58h] [bp-8h]@1 v6 = *MK_FP(__FS__, 40LL); v4 = strlen(a1); for ( i = 0; i < v4; ++i ) v5[i] = *(_DWORD *)(4LL * (signed int)sub_400936(a1[i]) + a2);//v5= A[4 * sub_400936(str1)];
sub_400D33(v5);//调整数组内位置函数 sub_400DB4((unsigned __int8 *)v5, v4); sub_400CC0(v5, "out", (unsigned int)v4); return *MK_FP(__FS__, 40LL) ^ v6;
}
-----------------------------------------------------------------------------------__int64 __fastcall sub_400D33(_BYTE *a1) { __int64 result; // rax@4 _BYTE v2[5]; //[sp+13h] [bp-5h]@1 v2[4] = 0; *(_DWORD *)v2 = *a1; while (dword_6020A0[*(signed int *)&v2[1]] ) { a1[*(signed int *)&v2[1]] = a1[dword_6020A0[*(signed int *)&v2[1]]]; *(_DWORD *)&v2[1] =dword_6020A0[*(signed int *)&v2[1]];
} result = (unsigned __int8)v2[0]; a1[*(signed int *)&v2[1]] = v2[0]; return result;
}//dword_6020A0在汇编上显示的是,4 * v1+地址,就是说,在一个数组中取一个下标为4*v1的内容。
//读内存,发现 其中的二位数组,按照这个表来 决定着下一个位置如何更换。
其结果是这样更换。
整理成C++代码
void sub_400D33(int asdstr[]) { int v1 = 0; int v0 = 0; int newstr[39]; v1 = B[(4 * v1) / 16][(4 * v1) % 16]; while (v1) { newstr[v0] = asdstr[v1]; cout << v0 << "" << v1 << endl; v0 = v1; v1 = B[(4 * v1) / 16][(4 * v1) % 16]; } newstr[1] = asdstr[0]; }
-----------------------------------------------------------------------------------
继续看下个函数
unsigned __int8 *__fastcallsub_400DB4(unsigned __int8 *a1, int a2)//最后一个算法,内容为,这一位和下一位经过左移和右移再或一下得到最终结果。{ unsigned __int8 *result; // rax@4 char v3; // [sp+17h] [bp-5h]@1 int i; // [sp+18h] [bp-4h]@1 v3 = *a1 >> 5; for ( i = 0; a2 - 1 > i; ++i ) a1[i] = 8 * a1[i] | (a1[i + 1] >> 5); result = &a1[i]; *result = 8 * *result | v3; return result; } 打开out文件,我们已经知道最终结果为:ec 29 e3 41 e1 f7 aa 1d 29 ed 29 99 39 f3 b7 a9 e7 ac 2b b7 ab40 9f a9 31 35 2c 29 ef A8 3d 4b b0 e9 e1 68 7b 41
开始我们的逆向爆破:
1. 爆破最后一个函数,
因为是或运算,所以得爆破前面一位和后面一位,才能确定中间的内容。最终得到的结果为。int key[] = { 61, 133, 60, 104, 60, 62, 245, 67,165, 61, 165, 51, 39, 62, 118, 245, 60, 245,133, 118, 245, 104, 19, 245, 38,38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104, 32 };这其中第一位没爆破出来,在可能的几个数字中一个一个试的。{29, 61, 93, 125, 157, 189, 221, 253 };2. 根据换位表,反推原数组 我是手动填数字的没用算法。 { 133, 67, 104, 133, 245, 38, 60, 61, 39, 245, 51,104, 62, 60, 118, 38, 245, 118, 165, 245, 19, 165, 61, 245, 62, 165, 45, 61,245, 7, 60, 118, 29, 60, 15, 104, 133, 169 }; 3. 爆破我们的字符串。
void sub_400E54baopo(){ int i; // [sp+18h][bp-48h]@1 int v4; // [sp+1Ch][bp-44h]@1 int v5[56]; // [sp+20h] [bp-40h]@3 __int64 v6; // [sp+58h] [bp-8h]@1 int ls; int DS[] = { 133, 67, 104, 133, 245, 38, 60, 61, 39, 245, 51, 104, 62, 60, 118, 38, 245, 118, 165, 245, 19, 165, 61, 245, 62, 165, 45, 61, 245, 7, 60, 118, 29, 60, 15, 104, 133, 169 }; v4 = 38; int adsad[] = { 29, 61, 93, 125, 157, 189, 221, 253 };//开始爆破的时候,不确定是哪个就一个一个试的。 for (int ia = 0; ia < 38; ia++) { for (i = 0; i < 360; i++) { ls = A[(4 * sub_400936(i))];//进入密码对应表。读取出结果。 if (ls == DS[ia]) { cout << (char)i << ""<<ia<<""<<endl; } } cout << endl; } }
爆破出来的结果发现,不区分大小写,都能通过。那就先来大写吧。 THAT_GIRL_SAYING_NO_FOR_YOUR_VINDICATE QCTF{THAT_GIRL_SAYING_NO_FOR_YOUR_VINDICATE} 4. 用VS c++ 恢复全部源码后,填入字符串,运行。 完全一致。Getflag,不区分大小写,都试了下。 QCTF{THAT_GIRL_SAYING_NO_FOR_YOUR_VINDICATE} QCTF{that_girl_saying_no_for_your_vindicate}
|