题目链接: https://ctf.pediy.com/itembank.htm
标下看雪上写着难度,有想学习的伙伴可以根据难度了解下,上次异想天开的难度是169。
传统查壳艺能
无壳,放心干。
拖OD后,发现左边的函数地址很奇怪,距离0x00400000很远,那么这个程序很有可能设置了动态基址。
设置了动态基址的话,OD跟IDA的地址是不对应的,我们需要干掉他。
PE文件->Nt头->可选头的有个成员可以设置掉。
借鉴了下writeup,mfc程序可以使用xspy工具,它查到MFC程序的响应事件函数。
这个函数比onclick更底层些,也就是说点击按钮后,先执行 onclick其次在可能执行OnCommand
下断后随便输入个flags,点确定就断下了。那就证明这个函数是正确的,我们到IDA看下伪代码
伪代码的主体就是一个if语句,可能是成功或失败的if。
我们随便输入一个flalgs然后按确定,让他断下关键跳的位置
改了下逻辑果然成功了。
所以决定判断成功或失败的函数就是 sub_402600。
我们进去看一下
发现伪代码有个很奇怪的东西。
我怀疑汇编代码加了花指令。
往下翻了翻还真有
在IDA
鼠标对着地址按C 键可以将数据转换成指令
这两个可以更改机器码
一共有很多处这样的花指令,其代码都跟上面讲述的一模一样,我们进行处理后,F5呈现出来的伪代码是这样的(记得那两个子函数也要处理 sub_402CA0 与 sub_402E80)
值得注意的是,在处理sub_402E80的时候
完全脱花之后呈现出来的时这样的。
[C++] 纯文本查看 复制代码 void __cdecl sub_402CA0(_BYTE *a1, int a2, unsigned int a3)
{
int v3; // [esp+8h] [ebp-114h]
char v4; // [esp+Fh] [ebp-10Dh]
int i; // [esp+10h] [ebp-10Ch]
int j; // [esp+10h] [ebp-10Ch]
char v7; // [esp+18h] [ebp-104h]
char v8[255]; // [esp+19h] [ebp-103h] BYREF
v3 = 0;
v7 = 0;
memset(v8, 0, sizeof(v8));
for ( i = 0; i < 256; ++i )
{
a1[i] = i;
*(&v7 + i) = *(_BYTE *)(a2 + i % a3);
}
for ( j = 0; j < 256; ++j )
{
v3 = (*(&v7 + j) + v3 + (unsigned __int8)a1[j]) % 256;
v4 = a1[j];
a1[j] = a1[v3];
a1[v3] = v4;
}
}
[C++] 纯文本查看 复制代码 void __stdcall sub_402E80(int a1, int a2, unsigned int a3)
{
int v3; // [esp+Ch] [ebp-18h]
unsigned int i; // [esp+10h] [ebp-14h]
int v5; // [esp+14h] [ebp-10h]
char v6; // [esp+1Bh] [ebp-9h]
v5 = 0;
v3 = 0;
for ( i = 0; i < a3; ++i )
{
v5 = (v5 + 1) % 256;
v3 = (v3 + *(unsigned __int8 *)(v5 + a1)) % 256;
v6 = *(_BYTE *)(v5 + a1);
*(_BYTE *)(v5 + a1) = *(_BYTE *)(v3 + a1);
*(_BYTE *)(v3 + a1) = v6;
*(_BYTE *)(i + a2) ^= *(_BYTE *)((*(unsigned __int8 *)(v3 + a1) + *(unsigned __int8 *)(v5 + a1)) % 256 + a1);
}
}
[C++] 纯文本查看 复制代码
char __cdecl sub_402600(int a1)
{
int v1; // ecx
const WCHAR *v2; // eax
void *v3; // eax
int v5; // [esp-18h] [ebp-4DCh]
int v6; // [esp-10h] [ebp-4D4h]
char v7; // [esp+8h] [ebp-4BCh]
char v8[511]; // [esp+9h] [ebp-4BBh] BYREF
int v9; // [esp+208h] [ebp-2BCh]
char *v10; // [esp+20Ch] [ebp-2B8h]
int v11; // [esp+210h] [ebp-2B4h]
size_t Count; // [esp+214h] [ebp-2B0h]
int v13; // [esp+218h] [ebp-2ACh]
size_t v14; // [esp+21Ch] [ebp-2A8h]
char *v15; // [esp+220h] [ebp-2A4h]
char *v16; // [esp+224h] [ebp-2A0h]
int v17; // [esp+228h] [ebp-29Ch]
char v18[4]; // [esp+22Ch] [ebp-298h] BYREF
char *Source; // [esp+230h] [ebp-294h]
int v20; // [esp+234h] [ebp-290h]
char v21; // [esp+238h] [ebp-28Ch]
char v22; // [esp+239h] [ebp-28Bh]
char v23; // [esp+23Ah] [ebp-28Ah]
char v24; // [esp+23Bh] [ebp-289h]
char v25; // [esp+23Ch] [ebp-288h]
char v26; // [esp+23Dh] [ebp-287h]
char v27; // [esp+23Eh] [ebp-286h]
char v28; // [esp+23Fh] [ebp-285h]
char v29; // [esp+240h] [ebp-284h]
char v30; // [esp+241h] [ebp-283h]
char v31; // [esp+242h] [ebp-282h]
char v32; // [esp+243h] [ebp-281h]
char v33; // [esp+244h] [ebp-280h]
char v34; // [esp+245h] [ebp-27Fh]
char v35; // [esp+246h] [ebp-27Eh]
char v36; // [esp+247h] [ebp-27Dh]
char v37; // [esp+248h] [ebp-27Ch]
char v38; // [esp+249h] [ebp-27Bh]
char v39; // [esp+24Ah] [ebp-27Ah]
char v40; // [esp+24Bh] [ebp-279h]
char v41; // [esp+24Ch] [ebp-278h]
char v42; // [esp+24Dh] [ebp-277h]
char v43; // [esp+24Eh] [ebp-276h]
char v44; // [esp+24Fh] [ebp-275h]
char v45; // [esp+250h] [ebp-274h]
char v46; // [esp+251h] [ebp-273h]
char v47; // [esp+252h] [ebp-272h]
char v48; // [esp+253h] [ebp-271h]
char v49; // [esp+254h] [ebp-270h]
char v50; // [esp+255h] [ebp-26Fh]
char v51; // [esp+256h] [ebp-26Eh]
char v52; // [esp+257h] [ebp-26Dh]
const char *v53; // [esp+258h] [ebp-26Ch]
char *v54; // [esp+25Ch] [ebp-268h]
int i; // [esp+260h] [ebp-264h]
char *v56; // [esp+264h] [ebp-260h]
char v57; // [esp+26Dh] [ebp-257h]
char v58; // [esp+26Eh] [ebp-256h]
char v59; // [esp+26Fh] [ebp-255h]
char v60[24]; // [esp+270h] [ebp-254h] BYREF
int v61; // [esp+288h] [ebp-23Ch]
char v62; // [esp+28Ch] [ebp-238h] BYREF
char v63[255]; // [esp+28Dh] [ebp-237h] BYREF
char v64[256]; // [esp+38Ch] [ebp-138h] BYREF
char Destination; // [esp+48Ch] [ebp-38h] BYREF
char v66[39]; // [esp+48Dh] [ebp-37h] BYREF
int v67; // [esp+4C0h] [ebp-4h]
int savedregs; // [esp+4C4h] [ebp+0h] BYREF
v20 = v1;
v67 = 3;
v21 = 91;
v22 = -42;
v23 = -48;
v24 = 38;
v25 = -56;
v26 = -35;
v27 = 25;
v28 = 126;
v29 = 110;
v30 = 62;
v31 = -53;
v32 = 22;
v33 = -111;
v34 = 125;
v35 = -1;
v36 = -81;
v37 = -35;
v38 = 118;
v39 = 100;
v40 = -80;
v41 = -9;
v42 = -27;
v43 = -119;
v44 = 87;
v45 = -126;
v46 = -97;
v47 = 12;
v48 = 0;
v49 = -98;
v50 = -48;
v51 = 69;
v52 = -6;
v2 = (const WCHAR *)sub_401570(&a1);
v17 = sub_4030A0(v2);
v13 = v17;
v3 = (void *)sub_401570(v17);
sub_403000(v3);
sub_4012A0(v18);
Source = (char *)unknown_libname_1(v60);
v54 = Source;
v16 = Source + 1;
v54 += strlen(v54);
v14 = ++v54 - (Source + 1);
Count = v14;
Destination = 0;
memset(v66, 0, sizeof(v66));
strncpy(&Destination, Source, v14); // Destination就是我们输入的Flags
if ( sub_402AF0(&Destination) ) // 判断长度
{
v57 = 0;
LABEL_7:
v58 = v59;
}
else
{
strcpy(v64, "qwertyuiop"); // 验证成功,下面是算法
memset(&v64[11], 0, 0xF5u);
v62 = 0;
memset(v63, 0, sizeof(v63));
v7 = 0;
memset(v8, 0, sizeof(v8));
v53 = v64;
v10 = &v64[1];
v53 += strlen(v53);
v9 = ++v53 - &v64[1];
sub_402CA0(&v62, (int)v64, v53 - &v64[1]); // RC4算法,这里是初始化
v61 = v6;
v56 = &Destination;
v15 = v66;
v56 += strlen(v56);
v11 = ++v56 - v66;
sub_402E80((int)&v62, (int)&Destination, v56 - v66);// 加密
for ( i = 31; i >= 0; --i )
{
if ( *(&Destination + i) != *((char *)&savedregs + i + (_DWORD)&loc_4026B7 - 4204867) )
{
v61 = v5;
v59 = 0;
goto LABEL_7;
}
}
v58 = 1;
}
LOBYTE(v67) = 0;
sub_403060(v60);
v67 = -1;
sub_4012A0(&a1);
return v58;
}
已经很明显了,对我们输入的数值进行CR4加密,KEY是“qwertyuiop”
加密后进行对比
[C++] 纯文本查看 复制代码 if ( *(&Destination + i) != *((char *)&savedregs + i + (_DWORD)&loc_4026B7 - 4204867) )
这个对比在伪代码不怎么详细,我通过OD动态调试之后,大概能得出大概是这样
[C++] 纯文本查看 复制代码 for (int i = 31; i >= 0; --i)
{
if (pData[i] != Array[31 - i])
{
//失败
}
}
这个array是函数初始化的一个数组
通过还原后
[C++] 纯文本查看 复制代码
char Array[32] = { 0xfa, 0x45, 0xd0, 0x9e, 0, 0xc, 0x9f, 0x82, 0x57, 0x89, 0xe5, 0xf7, 0xb0, 0x64, 0x76
, 0xdd, 0xaf, 0xff, 0x7d, 0x91, 0x16, 0xcb, 0x3e, 0x6e, 0x7e, 0x19, 0xdd, 0xc8, 0x26, 0xd0, 0xd6
, 0x5b };
所以大致的流程
[C] 纯文本查看 复制代码 UCHAR Array[32] = { 0xFA, 0x45, 0x0D0, 0x9E, 0, 0x0c, 0x9F, 0x82, 0x57, 0x89, 0xE5, 0xF7, 0xB0, 0x64, 0x76, 0x0DD, 0x0AF, 0x0FF,0x7D, 0x91, 0x16, 0x0CB, 0x3E, 0x6E, 0x7E, 0x19, 0x0DD, 0x0C8, 0x26, 0xD0, 0xD6, 0x5B };
unsigned char s[256] = { 0 };
char key[256] = { "qwertyuiop" };
char pData[512] = "输入的缓冲区";
ULONG len = strlen(pData);
rc4_init(s, (unsigned char*)key, strlen(key)); //已经完成了初始化
rc4_crypt(s, (unsigned char*)pData, len);//加密
for (int i = 31; i >= 0; --i)
{
if (pData[i] != Array[31 - i])
{
//失败
}
}
有key跟加密后的数据,我们进行解密即可
flag计算代码
[C++] 纯文本查看 复制代码 void rc4_init(UCHAR* a1, UCHAR * a2, unsigned int a3){
int v3; // [esp+8h] [ebp-114h]
char v4; // [esp+Fh] [ebp-10Dh]
int i; // [esp+10h] [ebp-10Ch]
int j; // [esp+10h] [ebp-10Ch]
char v7[256] = {0}; // [esp+18h] [ebp-104h]
v3 = 0;
//v7 = 0;
for (i = 0; i < 256; ++i)
{
a1[i] = i;
*(v7 + i) = *(char*)(a2 + i % a3);
}
for (j = 0; j < 256; ++j)
{
v3 = (*(v7 + j) + v3 + (unsigned __int8)a1[j]) % 256;
v4 = a1[j];
a1[j] = a1[v3];
a1[v3] = v4;
}
return;
}
void rc4_crypt(UCHAR * a1, UCHAR* a2, unsigned int a3){
int v3; // [esp+Ch] [ebp-18h]
unsigned int i; // [esp+10h] [ebp-14h]
int v5; // [esp+14h] [ebp-10h]
char v6; // [esp+1Bh] [ebp-9h]
v5 = 0;
v3 = 0;
for (i = 0; i < a3; ++i)
{
v5 = (v5 + 1) % 256;
v3 = (v3 + *(unsigned __int8 *)(v5 + a1)) % 256;
v6 = *(char *)(v5 + a1);
*(char *)(v5 + a1) = *(char *)(v3 + a1);
*(char *)(v3 + a1) = v6;
*(char *)(i + a2) ^= *(char *)((*(unsigned __int8 *)(v3 + a1) + *(unsigned __int8 *)(v5 + a1)) % 256 + a1);
}
return;
}
int main(){
char Array[32] = { 0xfa, 0x45, 0xd0, 0x9e, 0, 0xc, 0x9f, 0x82, 0x57, 0x89, 0xe5, 0xf7, 0xb0, 0x64, 0x76
, 0xdd, 0xaf, 0xff, 0x7d, 0x91, 0x16, 0xcb, 0x3e, 0x6e, 0x7e, 0x19, 0xdd, 0xc8, 0x26, 0xd0, 0xd6
, 0x5b };
//*****
unsigned char s[256] = { 0 }, s2[256] = { 0 };//S-box
char key[256] = { "qwertyuiop" };
char pData[256] = "0";
char realData[256] = "0";
for (int i = 31; i >= 0; i--)//倒序,还原加密后的data
{
pData[i] = Array[31 - i];
}
ULONG len = 32;//长度我自己写了,因为Array数组有个元素是0,在计算strlen的时候会截短掉。
rc4_init(s, (unsigned char*)key, strlen(key));//初始化 s-box
rc4_crypt(s, (unsigned char*)pData, len);//解密
//解密后的数据,因为是输入到程序中的,输入都程序中还会在进行倒序的检查,所以我们还需要将得到后的数据倒过来
ULONG readDataLen = strlen(pData);
readDataLen--;
for (int i = 0; i <= readDataLen; i++)
{
realData[i] = pData[readDataLen - i];
}
printf("%s", pData);
system("pause");
return 1;
}
当我得出这个结果时,输入进去它还显示错误,然后我去那道题的评论区看了看。
发现需要加上flag{ }才正确。
|