首页
社区
课程
招聘
[原创]Buuctf-reverse-crackMe
发表于: 2022-4-6 11:32 12627

[原创]Buuctf-reverse-crackMe

2022-4-6 11:32
12627

ps:学习了一下大家写文章的方式,以后就都这么写了,之前的第一篇就懒得改了,参考了网上师傅的wp

题目链接:https://buuoj.cn/challenges#crackMe

img

通过题目内容可以了解到,用户名是welcomebeijing,密码MD5的小写哈希就是我们的flag。点开程序,可以看到关键字符串,大致的思路就是通过这个字符串可以定位到关键函数,再进行进一步分析。

先用查壳软件看一下程序情况

img

是一个无壳的32位PE文件,用IDA32打开,定位到主函数

对于一些不是很重要的函数的作用我都直接在上面的函数注明了,比较重要的,我会在下文着重分析。

这个函数就是对user进行了一系列操作,最后把值赋给了byte_416050这个数组,我看着很像RC4加密,后面可以直接动调出来

一下子就能看见有好几个反调试的语句,第一个while循环里的意思就是将输入的密码,例如123456,按0x12,0x34,0x56这样两位切割,然后放在v14数组里面。接下来重点就是第二个循环,我们先来看这个sub_401710函数

在上面这个函数中 v4是user用户名的长度,a3是两位一组后的密码分组个数计数,v4是永远大于a3的。else if里面的操作就是把V6和用户名异或再把值存放在v16里,为了区别,这里将新的v16记作v16'。然后就是sub_401470,我们通过return语句可以推出,v13 == 0xAB94,通过这个我们就可以推出v16'的值。

这里面的a2就是v16',只要v16'的每一位值能等于这里面if判断语句的每一个值就会输出v13 == 0xAB94,所以v16'=dbappfsec。然后我们就可以通过v16'和用户名异或得出v16,再把v16和byte_416050数组异或就可以得出我们要的两位一组分割后的密码,最后拼起来就行。现在我们唯一缺的就是byte_416050数组,这里可以通过动态调试得出

动态调试之前需要先将前面的反调试语句处理一下,将下图处的jz改为jmp就行了

img

然后我们再去看一下关键位置的汇编代码

img

img

可以看见我们只需要观察ecx的值就可以得出byte_416050数组的值,动调得出值为0x2A, 0xD7, 0x92, 0xE9, 0x53, 0xE2, 0xC4, 0xCD。ps:这里动调的值,不知道是程序原因还是什么原因,动调出来的值跟网上动调出来的值不一样,最后搬的网上师傅动调出来的值,有师傅知道的可以在评论区指点一手。

奇怪的是这个结果程序是可以通过的,但是提交到Buuctf没通过,网上的一些wp的结果提交到Buuctf是可以的,但是通不过程序的检验,不知道是不是Buuctf的flag错了。网上其他wp的结果是4eb5f3992391a1ae,大家可以自己试一下。

img

 
 
 
 
int wmain()
{
    FILE *v0; // eax
    FILE *v1; // eax
    char v3; // [esp+3h] [ebp-405h]
    char v4; // [esp+4h] [ebp-404h] BYREF
    char v5[255]; // [esp+5h] [ebp-403h] BYREF
    char Format; // [esp+104h] [ebp-304h] BYREF
    char v7[255]; // [esp+105h] [ebp-303h] BYREF
    char v8; // [esp+204h] [ebp-204h] BYREF
    char v9[255]; // [esp+205h] [ebp-203h] BYREF
    char v10; // [esp+304h] [ebp-104h] BYREF
    char v11[255]; // [esp+305h] [ebp-103h] BYREF
 
    printf("Come one! Crack Me~~~\n");
    v10 = 0;
    memset(v11, 0, sizeof(v11));
    v8 = 0;
    memset(v9, 0, sizeof(v9));
    while ( 1 )
    {
        do
        {
            do
            {
                printf("user(6-16 letters or numbers):");
                scanf("%s", &v10);           #接收user
                    v0 = (FILE *)sub_4024BE();
                fflush(v0);
            }
            while ( !(unsigned __int8)sub_401000(&v10) );
            printf("password(6-16 letters or numbers):");
            scanf("%s", &v8);         #接收password
                v1 = (FILE *)sub_4024BE();
            fflush(v1);       #这几个并没有对接收的字符串做什么操作,就不管了
            }
        while ( !(unsigned __int8)sub_401000(&v8) ); #检查是不是字母或者数字
            sub_401090(&v10);
        Format = 0;
        memset(v7, 0, sizeof(v7));
        v4 = 0;
        memset(v5, 0, sizeof(v5));
        v3 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&Format, &v4); #这个应该是判断后输出再试一次的字符串
            if ( (unsigned __int8)sub_401830(&v10, &v8) ) #关键的验证函数
            {
                if ( v3 )
                    break;
            }
        printf(&v4);
    }
    printf(&Format);
    return 0;
}
int wmain()
{
    FILE *v0; // eax
    FILE *v1; // eax
    char v3; // [esp+3h] [ebp-405h]
    char v4; // [esp+4h] [ebp-404h] BYREF
    char v5[255]; // [esp+5h] [ebp-403h] BYREF
    char Format; // [esp+104h] [ebp-304h] BYREF
    char v7[255]; // [esp+105h] [ebp-303h] BYREF
    char v8; // [esp+204h] [ebp-204h] BYREF
    char v9[255]; // [esp+205h] [ebp-203h] BYREF
    char v10; // [esp+304h] [ebp-104h] BYREF
    char v11[255]; // [esp+305h] [ebp-103h] BYREF
 
    printf("Come one! Crack Me~~~\n");
    v10 = 0;
    memset(v11, 0, sizeof(v11));
    v8 = 0;
    memset(v9, 0, sizeof(v9));
    while ( 1 )
    {
        do
        {
            do
            {
                printf("user(6-16 letters or numbers):");
                scanf("%s", &v10);           #接收user
                    v0 = (FILE *)sub_4024BE();
                fflush(v0);
            }
            while ( !(unsigned __int8)sub_401000(&v10) );
            printf("password(6-16 letters or numbers):");
            scanf("%s", &v8);         #接收password
                v1 = (FILE *)sub_4024BE();
            fflush(v1);       #这几个并没有对接收的字符串做什么操作,就不管了
            }
        while ( !(unsigned __int8)sub_401000(&v8) ); #检查是不是字母或者数字
            sub_401090(&v10);
        Format = 0;
        memset(v7, 0, sizeof(v7));
        v4 = 0;
        memset(v5, 0, sizeof(v5));
        v3 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&Format, &v4); #这个应该是判断后输出再试一次的字符串
            if ( (unsigned __int8)sub_401830(&v10, &v8) ) #关键的验证函数
            {
                if ( v3 )
                    break;
            }
        printf(&v4);
    }
    printf(&Format);
    return 0;
}
_BYTE *__cdecl sub_401090(_BYTE *a1)
{
  _BYTE *result; // eax
  int v2; // [esp+Ch] [ebp-18h]
  int v3; // [esp+10h] [ebp-14h]
  _BYTE *v4; // [esp+14h] [ebp-10h]
  int i; // [esp+18h] [ebp-Ch]
  char v7; // [esp+20h] [ebp-4h]
  char v8; // [esp+22h] [ebp-2h]
  unsigned __int8 v9; // [esp+23h] [ebp-1h]
 
  for ( i = 0; i < 256; ++i )
    byte_416050[i] = i;
  v2 = 0;
  v9 = 0;
  v3 = 0;
  result = a1;
  v4 = a1;
  do
    LOBYTE(result) = *v4;
  while ( *v4++ );
  while ( v2 < 256 )
  {
    v8 = byte_416050[v2];
    v9 += v8 + a1[v3];
    v7 = byte_416050[v9];
    ++v3;
    byte_416050[v9] = v8;
    byte_416050[v2] = v7;
    result = (_BYTE *)v3;
    if ( v3 >= v4 - (a1 + 1) )
      v3 = 0;
    ++v2;
  }
  return result;
}
_BYTE *__cdecl sub_401090(_BYTE *a1)
{
  _BYTE *result; // eax
  int v2; // [esp+Ch] [ebp-18h]
  int v3; // [esp+10h] [ebp-14h]
  _BYTE *v4; // [esp+14h] [ebp-10h]
  int i; // [esp+18h] [ebp-Ch]
  char v7; // [esp+20h] [ebp-4h]
  char v8; // [esp+22h] [ebp-2h]
  unsigned __int8 v9; // [esp+23h] [ebp-1h]
 
  for ( i = 0; i < 256; ++i )
    byte_416050[i] = i;
  v2 = 0;
  v9 = 0;
  v3 = 0;
  result = a1;
  v4 = a1;
  do
    LOBYTE(result) = *v4;
  while ( *v4++ );
  while ( v2 < 256 )
  {
    v8 = byte_416050[v2];
    v9 += v8 + a1[v3];
    v7 = byte_416050[v9];
    ++v3;
    byte_416050[v9] = v8;
    byte_416050[v2] = v7;
    result = (_BYTE *)v3;
    if ( v3 >= v4 - (a1 + 1) )
      v3 = 0;
    ++v2;
  }
  return result;
}
bool __cdecl sub_401830(int a1, const char *a2)
{
    int v3; // [esp+18h] [ebp-22Ch]
    int v4; // [esp+1Ch] [ebp-228h]
    int v5; // [esp+28h] [ebp-21Ch]
    unsigned int v6; // [esp+30h] [ebp-214h]
    char v7; // [esp+36h] [ebp-20Eh]
    char v8; // [esp+37h] [ebp-20Dh]
    char v9; // [esp+38h] [ebp-20Ch]
    unsigned __int8 v10; // [esp+39h] [ebp-20Bh]
    unsigned __int8 v11; // [esp+3Ah] [ebp-20Ah]
    char v12; // [esp+3Bh] [ebp-209h]
    int v13; // [esp+3Ch] [ebp-208h] BYREF
    char v14; // [esp+40h] [ebp-204h] BYREF
    char v15[255]; // [esp+41h] [ebp-203h] BYREF
    char v16; // [esp+140h] [ebp-104h] BYREF
    char v17[255]; // [esp+141h] [ebp-103h] BYREF
 
    v4 = 0;
    v5 = 0;
    v11 = 0;
    v10 = 0;
    v16 = 0;
    memset(v17, 0, sizeof(v17));
    v14 = 0;
    memset(v15, 0, sizeof(v15));
    v9 = 0;
    v6 = 0;
    v3 = 0;
    while ( v6 < strlen(a2) )
    {
        if ( isdigit(a2[v6]) )
        {
            v8 = a2[v6] - 48#这里将输入的字符0-9转化成十进制的0-9,就是转换一下类型
            }
        else if ( isxdigit(a2[v6]) )
        {
            if ( *((_DWORD *)NtCurrentPeb()->SubSystemData + 3) != 2 )#反调试的
                a2[v6] = 34;
            v8 = (a2[v6] | 0x20) - 87#意思是减87加32,也就是将字母,例如a转化成16进制数a(就是10)
            }
        else
        {
            v8 = ((a2[v6] | 0x20) - 97) % 6 + 10; #将其他的字符转化到十六进制数a-f之间(10-16)
            }
        __rdtsc();
        __rdtsc();
        v9 = v8 + 16 * v9;
        if ( !((int)(v6 + 1) % 2) ) #将输入的密码两位一组,存放在v14数组里面
        {
            *(&v14 + v3++) = v9;
            v9 = 0;
        }
        ++v6;
    }
    while ( v5 < 8 )
    {
        v10 += byte_416050[++v11];
        v12 = byte_416050[v11];
        v7 = byte_416050[v10];
        byte_416050[v10] = v12;
        byte_416050[v11] = v7;
        if ( ((int)NtCurrentPeb()->UnicodeCaseTableData & 0x70) != 0 ) #反调试
            v12 = v10 + v11;
        *(&v16 + v5) = byte_416050[(unsigned __int8)(v7 + v12)] ^ *(&v14 + v4); #关键的异或操作
            if ( (unsigned __int8)*(_DWORD *)&NtCurrentPeb()->BeingDebugged )#反调试
            {
                v10 = -83;
                v11 = 43;
            }
        sub_401710(&v16, a1, v5++);
        v4 = v5;
        if ( v5 >= (unsigned int)(&v14 + strlen(&v14) + 1 - v15) )
            v4 = 0;
    }
    v13 = 0;
    sub_401470(&v16, &v13);
    return v13 == 0xAB94;
}
bool __cdecl sub_401830(int a1, const char *a2)
{
    int v3; // [esp+18h] [ebp-22Ch]
    int v4; // [esp+1Ch] [ebp-228h]
    int v5; // [esp+28h] [ebp-21Ch]
    unsigned int v6; // [esp+30h] [ebp-214h]
    char v7; // [esp+36h] [ebp-20Eh]
    char v8; // [esp+37h] [ebp-20Dh]
    char v9; // [esp+38h] [ebp-20Ch]
    unsigned __int8 v10; // [esp+39h] [ebp-20Bh]
    unsigned __int8 v11; // [esp+3Ah] [ebp-20Ah]
    char v12; // [esp+3Bh] [ebp-209h]
    int v13; // [esp+3Ch] [ebp-208h] BYREF
    char v14; // [esp+40h] [ebp-204h] BYREF
    char v15[255]; // [esp+41h] [ebp-203h] BYREF
    char v16; // [esp+140h] [ebp-104h] BYREF
    char v17[255]; // [esp+141h] [ebp-103h] BYREF
 
    v4 = 0;
    v5 = 0;
    v11 = 0;
    v10 = 0;
    v16 = 0;
    memset(v17, 0, sizeof(v17));
    v14 = 0;
    memset(v15, 0, sizeof(v15));
    v9 = 0;
    v6 = 0;
    v3 = 0;
    while ( v6 < strlen(a2) )
    {
        if ( isdigit(a2[v6]) )
        {
            v8 = a2[v6] - 48#这里将输入的字符0-9转化成十进制的0-9,就是转换一下类型
            }
        else if ( isxdigit(a2[v6]) )
        {
            if ( *((_DWORD *)NtCurrentPeb()->SubSystemData + 3) != 2 )#反调试的
                a2[v6] = 34;
            v8 = (a2[v6] | 0x20) - 87#意思是减87加32,也就是将字母,例如a转化成16进制数a(就是10)
            }
        else
        {
            v8 = ((a2[v6] | 0x20) - 97) % 6 + 10; #将其他的字符转化到十六进制数a-f之间(10-16)
            }
        __rdtsc();
        __rdtsc();

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//