记一次GDB调试

目标文件: ciscn_2019_ne_5。

来源 :https://buuoj.cn/challenges

保护情况:保护是没有保护的

记一次GDB调试

 

 

 主要伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // [esp+0h] [ebp-100h]
  char src[4]; // [esp+4h] [ebp-FCh]
  char v5; // [esp+8h] [ebp-F8h]
  char s1[4]; // [esp+84h] [ebp-7Ch]
  char v7; // [esp+88h] [ebp-78h]
  int *v8; // [esp+F4h] [ebp-Ch]

  v8 = &argc;
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  fflush(stdout);
  *(_DWORD *)s1 = 48;
  memset(&v7, 0, 0x60u);
  *(_DWORD *)src = 48;
  memset(&v5, 0, 0x7Cu);
  puts("Welcome to use LFS.");
  printf("Please input admin password:");
  __isoc99_scanf("%100s", s1);                  // 输入密码: administrator
  if ( strcmp(s1, "administrator") )
  {
    puts("Password Error!");
    exit(0);
  }
  puts("Welcome!");
  while ( 1 )
  {
    puts("Input your operation:");
    puts("1.Add a log.");
    puts("2.Display all logs.");
    puts("3.Print all logs.");
    printf("0.Exit\n:");
    __isoc99_scanf("%d", &v3);
    switch ( v3 )
    {
      case 0:
        exit(0);                                // 退出
        return;
      case 1:
        AddLog((int)src);                       // 输入
        break;
      case 2:
        Display(src);                           // 删除输入的
        break;
      case 3:
        Print();
        break;
      case 4:
        GetFlag(src);                           // 将输入的输出
        break;
      default:
        continue;
    }
  }
}

 

 

int __cdecl AddLog(int a1)
{
  printf("Please input new log info:");
  return __isoc99_scanf("%128s", a1);
}

 

 

int __cdecl GetFlag(char *src)
{
  char dest[4]; // [esp+0h] [ebp-48h]
  char v3; // [esp+4h] [ebp-44h]

  *(_DWORD *)dest = 48;
  memset(&v3, 0, 0x3Cu);
  strcpy(dest, src);
  return printf("The flag is your log:%s\n", dest);
}

GetFlag的栈结构示意

记一次GDB调试

 

其它信息:

记一次GDB调试

 

记一次GDB调试

 

记一次GDB调试

 

或者这个 "sh" 字符串也可以用其他方式找到:

记一次GDB调试

 

调试目标 :构造GetFlag函数的栈溢出并借此开启一个shell。

 

先打上至少一个断点,不然待会干啥的白瞎

记一次GDB调试

 

也可以直接对函数名下断点(如果程序没开PIE的话)

记一次GDB调试

 

我将程序的执行断在了call进GetFlag前:

记一次GDB调试

 

很明显,0xFFFFD3FC就是输入字符串的保存地,0x804891B是这个call的返回值。

用 "s"命令跟进这个call,直到抬栈完毕。( 想步过这个call的话用  "n"就好。)

记一次GDB调试

 

先看看栈:

记一次GDB调试

 

 

 

 GetFlag函数中有个字符拷贝的行为,从GetFlag函数的栈顶开始写入0xFFFFD3FC之后的内容直到被 0 截断为止。

GetFlag函数的栈容量只有0x48个字节,再往后就是原 EBP 和返回值

 思路:将GetFlag函数的返回值覆盖为system函数的地址 ,并在其后写入字符串 "sh"的地址 0x80482EA 

接下来是利用 set 命令修改内存,但 set 命令一修改就是四个字节,有时候并不算太方便。

改原 EBP 到没什么讲究,只要四字节中没有为零的就好。

记一次GDB调试

 

 记一次GDB调试

 

 然后是覆盖返回值

记一次GDB调试

 

这个不重要,占位子的,四字节中没有为零的就好

记一次GDB调试

 

 写入参数地址

 记一次GDB调试

 

 至此,GetFlag的栈已经构造完成:

记一次GDB调试

 

 下面就是放飞自我的时候了( "go" 命令 :直接执行的下一个断点(但在这里是不可能的了)):

记一次GDB调试

 

 可以看到程序已经崩溃,并且如愿开启了shell。

 

上一篇:Vim编辑器-Windows


下一篇:【题解】CF1416E Split