小菜鸟一枚 发表于 2022-4-18 21:14

学破解第193天,《攻防世界reverse练习区re1-100》学习

前言:

  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:(https://www.52pojie.cn/thread-1582287-1-1.html)

**立帖为证!--------记录学习的点点滴滴**

## 0x1 收集信息
  1.首先,还是查壳,然后看一看文件格式,是windows平台还是linux平台的,是32位的,还是64位的。

!(https://s1.ax1x.com/2022/04/18/LdvZAU.png)

  2.将文件复制到kali中,然后将ida目录下的linux_server64也复制过来,防止一会远程调试,先备用。

!(https://s1.ax1x.com/2022/04/18/LdxeqP.png)

  3.运行一下程序,提示输入key,随便输入后提示wrong。

!(https://s1.ax1x.com/2022/04/18/LdxhJe.png)

  4.现在小菜鸟已经收集到一下信息了:

1)目标是64位linux程序,没有加壳
2)程序流程是输入key进行校验
3)我的目的是找到正确的key

## 0x2 静态分析
  1.将程序拖进ida,找到main函数,F5可以看到伪代码:
```
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__pid_t v3; // eax
size_t v4; // rax
ssize_t v5; // rbx
bool v6; // al
bool bCheckPtrace; //
ssize_t numRead; //
ssize_t numReada; //
char bufWrite; // BYREF
char bufParentRead; // BYREF
unsigned __int64 v12; //

v12 = __readfsqword(0x28u);
bCheckPtrace = detectDebugging();
if ( pipe(pParentWrite) == -1 )
    exit(1);
if ( pipe(pParentRead) == -1 )
    exit(1);
v3 = fork();
if ( v3 != -1 )
{
    if ( v3 )
    {
      close(pParentWrite);
      close(pParentRead);
      while ( 1 )
      {
      printf("Input key : ");
      memset(bufWrite, 0, sizeof(bufWrite));
      gets(bufWrite);
      v4 = strlen(bufWrite);
      v5 = write(pParentWrite, bufWrite, v4);
      if ( v5 != strlen(bufWrite) )
          printf("parent - partial/failed write");
      do
      {
          memset(bufParentRead, 0, sizeof(bufParentRead));
          numReada = read(pParentRead, bufParentRead, 0xC8uLL);
          v6 = bCheckPtrace || checkDebuggerProcessRunning();
          if ( !v6 && checkStringIsNumber(bufParentRead) && atoi(bufParentRead) )
          {
            puts("True");
            if ( close(pParentWrite) == -1 )
            exit(1);
            exit(0);
          }
          puts("Wrong !!!\n");
      }
      while ( numReada == -1 );
      }
    }
    close(pParentWrite);
    close(pParentRead);
    while ( 1 )
    {
      memset(bufParentRead, 0, sizeof(bufParentRead));
      numRead = read(pParentWrite, bufParentRead, 0xC8uLL);
      if ( numRead == -1 )
      break;
      if ( numRead )
      {
      if ( !childCheckDebugResult()
          && bufParentRead == 123
          && strlen(bufParentRead) == 42
          && !strncmp(&bufParentRead, "53fc275d81", 0xAuLL)
          && bufParentRead == 125
          && !strncmp(&bufParentRead, "4938ae4efd", 0xAuLL)
          && confuseKey(bufParentRead, 42)
          && !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
      {
          responseTrue();
      }
      else
      {
          responseFalse();
      }
      }
    }
    exit(1);
}
exit(1);
}
```

  2.看上去代码很多,先不慌,分段拆解,看这里面居然还有反调试checkDebuggerProcessRunning,找到wrong,看这里,似乎有点晕,提示wrong就循环,提示true就直接退出,那就说明这个if不该进去,直接把这段代码pass掉。
```
if ( !v6 && checkStringIsNumber(bufParentRead) && atoi(bufParentRead) )
          {
            puts("True");
            if ( close(pParentWrite) == -1 )
            exit(1);
            exit(0);
          }
          puts("Wrong !!!\n");
```

  3.write和read是读文件和写文件,这里pParentWrite存储着我输入的字符串,pParentRead读取字符串。
```
printf("Input key : ");
      memset(bufWrite, 0, sizeof(bufWrite));
      gets(bufWrite);
      v4 = strlen(bufWrite);
      v5 = write(pParentWrite, bufWrite, v4);
               
memset(&s, 0, 0xC8uLL);
          v9 = read(pParentRead, &s, 0xC8uLL);
```

  4.继续往后翻,这里将pParentWrite里面的内容复制给s。
```
memset(&s, 0, 0xC8uLL);
      v8 = read(pParentWrite, &s, 0xC8uLL);
```

  5.继续,看到关键的比较了,学数组的时候小菜鸟知道数组名就是数组首地址,这里先判断第一个字符是不是{,接着判断长度是不是42,然后晕圈,v12和v13咋来的?
```
if ( !(unsigned __int8)childCheckDebugResult()
          && s == 123
          && strlen(&s) == 42
          && !strncmp(v12, "53fc275d81", 0xAuLL)
          && *(&s + strlen(&s) - 1) == 125
          && !strncmp(v13, "4938ae4efd", 0xAuLL)
          && (unsigned __int8)confuseKey(&s, 42) == 1
          && !strncmp(&s, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
      {
          responseTrue();
      }
      else
      {
          responseFalse();
      }
```

  6.去反汇编窗口,按空格键,找到提示字符串,看到如下图所示信息,怀疑我的IDA F5反编译不准确,硬着头皮啃汇编吧,流程图中的汇编上面都是rbp+bufParentRead(我输入的字符串)进行比较,还是没看懂,重启IDA,出现一个提示框全勾上再说。

!(https://s1.ax1x.com/2022/04/18/LwV7OU.png)

```
if ( !childCheckDebugResult()
          && bufParentRead == 123
          && strlen(bufParentRead) == 42
          && !strncmp(&bufParentRead, "53fc275d81", 0xAuLL)
          && bufParentRead == 125
          && !strncmp(&bufParentRead, "4938ae4efd", 0xAuLL)
          && confuseKey(bufParentRead, 42)
          && !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
```

  7.这下子逻辑清晰了,输入字符串第一个字符必须是{,倒数第一个字符必须是},然后下标1-10对应的字符必须是"53fc275d81",下标31-40对应的字符必须是4938ae4efd,接着调用confuseKey函数将字符串变换,最后和{daf29f59034938ae4efd53fc275d81053ed5be8c}进行比较,看看confuseKey函数:
```
bool __cdecl confuseKey(char *szKey, int iKeyLength)
{
char szPart1; // BYREF
char szPart2; // BYREF
char szPart3; // BYREF
char szPart4; // BYREF
unsigned __int64 v7; //

v7 = __readfsqword(0x28u);
*(_QWORD *)szPart1 = 0LL;
*(_DWORD *)&szPart1 = 0;
*(_WORD *)&szPart1 = 0;
szPart1 = 0;
*(_QWORD *)szPart2 = 0LL;
*(_DWORD *)&szPart2 = 0;
*(_WORD *)&szPart2 = 0;
szPart2 = 0;
*(_QWORD *)szPart3 = 0LL;
*(_DWORD *)&szPart3 = 0;
*(_WORD *)&szPart3 = 0;
szPart3 = 0;
*(_QWORD *)szPart4 = 0LL;
*(_DWORD *)&szPart4 = 0;
*(_WORD *)&szPart4 = 0;
szPart4 = 0;
if ( iKeyLength != 42 )
    return 0;
if ( !szKey )
    return 0;
if ( strlen(szKey) != 42 )
    return 0;
if ( *szKey != 123 )
    return 0;
strncpy(szPart1, szKey + 1, 0xAuLL);
strncpy(szPart2, szKey + 11, 0xAuLL);
strncpy(szPart3, szKey + 21, 0xAuLL);
strncpy(szPart4, szKey + 31, 0xAuLL);
memset(szKey, 0, 0x2AuLL);
*szKey = 123;
strcat(szKey, szPart3);
strcat(szKey, szPart4);
strcat(szKey, szPart1);
strcat(szKey, szPart2);
szKey = 125;
return 1;
}
```

  8.前面都没啥,主要看strncpy赋值语句和strcat字符串连接语句,可以看到把字符串分为4段,每段长度位10,然后按照3412的顺序进行连接,先看看替换后的字符串:
```
daf29f5903    第3段
4938ae4efd    第4段
53fc275d81    第1段
053ed5be8c    第2段
```

  9.调整顺序为1234得到key,{53fc275d81053ed5be8cdaf29f59034938ae4efd},运行程序验证一下,成功。

!(https://s1.ax1x.com/2022/04/18/LwnhHU.png)

## 0x3 总结
  1.去攻防世界提交:flag{53fc275d81053ed5be8cdaf29f59034938ae4efd},居然提示错了,然后试{53fc275d81053ed5be8cdaf29f59034938ae4efd}还是错了,最后输入53fc275d81053ed5be8cdaf29f59034938ae4efd终于对了。

  2.这道题的逻辑其实很简单,主要是前面看伪代码懵圈了,然后想着IDA动态调试看参数的时候有点晕,一直断不下来,不知道是不是那个反调试的原因,真是奇了怪了。

hackerbob 发表于 2022-4-24 22:07

风鸣雨喧 发表于 2022-4-19 17:50
学破解第164天,楼主学习记录导航贴更新!

楼主,这个帖子是不是该更新了下啊,你最新的都没有,只到187 ...

这个帖子在哪?

hackerbob 发表于 2022-4-24 22:41

风鸣雨喧 发表于 2022-4-24 22:39
点我前面发的那个文字(蓝色的)就直接跳转过去了,那是一个链接

感谢,非常感谢,最近想试试破解

wei5383079 发表于 2022-4-18 22:58

能帮帮我吗?

天天go 发表于 2022-4-19 08:27

非常不错。。。。。。。

贪恋毅世的浮华 发表于 2022-4-19 08:49

感谢分享

NBW666 发表于 2022-4-19 10:13

感谢分享,学习技术。:lol

akhuoji 发表于 2022-4-19 10:45

大佬,厉害

o824 发表于 2022-4-19 11:29

感谢分享

LLJIZZ 发表于 2022-4-19 11:30

感谢分享,认真学习

Th52520 发表于 2022-4-19 16:19

感谢分享

风鸣雨喧 发表于 2022-4-19 17:50

学破解第164天,楼主学习记录导航贴更新!

楼主,这个帖子是不是该更新了下啊,你最新的都没有,只到187天,后面的呢?

https://s3.bmp.ovh/imgs/2022/04/19/92d3c77305747ba0.png
页: [1] 2
查看完整版本: 学破解第193天,《攻防世界reverse练习区re1-100》学习