首页
社区
课程
招聘
[原创]2019KCTF总决赛 第四题:西部乐园 WP
发表于: 2019-12-12 16:52 3854

[原创]2019KCTF总决赛 第四题:西部乐园 WP

2019-12-12 16:52
3854

一道驱动题,先把主程序拉到IDA里面看看。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  LPSTR v3; // rax
  DWORD v4; // eax
  DWORD v5; // eax
  LPSTR v7; // rax
  __int64 (*v8)(); // [rsp+40h] [rbp-878h]
  __int128 password; // [rsp+48h] [rbp-870h]
  DWORD dwIoControlCode; // [rsp+58h] [rbp-860h]
  BOOL v11; // [rsp+5Ch] [rbp-85Ch]
  HANDLE hDevice; // [rsp+60h] [rbp-858h]
  __int64 dwSize; // [rsp+68h] [rbp-850h]
  DWORD flOldProtect; // [rsp+70h] [rbp-848h]
  DWORD BytesReturned; // [rsp+74h] [rbp-844h]
  __int64 v16; // [rsp+78h] [rbp-840h]
  __int64 v17; // [rsp+80h] [rbp-838h]
  LPCWSTR lpFileName; // [rsp+88h] [rbp-830h]
  char *Address; // [rsp+90h] [rbp-828h]
  __int64 (*v20)(); // [rsp+98h] [rbp-820h]
  __int64 v21; // [rsp+A0h] [rbp-818h]
  __int64 v22; // [rsp+A8h] [rbp-810h]
  char v23; // [rsp+B0h] [rbp-808h]
  char OutBuffer; // [rsp+490h] [rbp-428h]

  v8 = 0i64;
  v17 = 0i64;
  v16 = 0i64;
  password = 0ui64;
  dwSize = 0i64;
  v3 = sub_1400014D0(aKerneloadriven_0);
  LoadDriver((__int64)v3);
  sub_140002030();
  print(asc_1400062F8);
  scanf(Format, &password);
  if ( (signed __int64)password <= 96000 && (signed __int64)password >= 90000 )// 判断输入的密码的范围
  {
    v16 = 0x42i64;
    v17 = 0xA9i64;
    dwSize = 0xE7i64;
    *((_QWORD *)&password + 1) = &unk_1400060B0;
    VirtualProtect_0(&unk_1400060B0, 0xE7);
    VirtualProtect_0(&OutBuffer, 1024);
    sub_14000256E(&v8);
    v8 = (__int64 (*)())((char *)v8 + 2);
    sub_140001760(*((__int64 *)&password + 1), 0i64, (__int64)&unk_140006B60);
    dwIoControlCode = 0x222041;
    VirtualProtect(&Address, 0xAui64, 0x40u, &flOldProtect);
    Address = &OutBuffer;
    v8 = Callback_Wrong;                        // 密码错误时的回调函数
    v20 = Callback_Wrong;
    v4 = GetCurrentProcessId();
    v8 = (__int64 (*)())v4;
    v21 = v4;
    v22 = password;
    qmemcpy(&v23, *((const void **)&password + 1), 0xE7ui64);
    lpFileName = aKerneloadrivev;
    hDevice = CreateFileW(aKerneloadrivev, 0xC0000000, 0, 0i64, 3u, 0x80u, 0i64);
    if ( hDevice == (HANDLE)-1i64 )
    {
      v5 = GetLastError();
      print(aFailedToObtain, aMywdmdevice, v5);
      return 1;
    }                                           // 跟驱动通信,Address存放了密码,错误回调函数等等
    v11 = DeviceIoControl(hDevice, dwIoControlCode, &Address, 0x107u, &OutBuffer, 0x400u, &BytesReturned, 0i64);
    if ( !v11 )
    {
      MessageBoxW(0i64, L"wrong!", L"err", 0);
      return 0;
    }
    CloseHandle(hDevice);
  }
  else
  {
    MessageBoxW(0i64, L"wrong!", L"err", 0);
  }
  SleepEx(0x3E8u, 1);
  v7 = sub_1400014D0(aKerneloadriven_1);
  UnloadDriver((__int64)v7);
  system(pause);
  return 0;
}

从main函数可以看出来password的范围为[90000,96000],接下来看看驱动。
int __cdecl main(int argc, const char **argv, const char **envp)
{
  LPSTR v3; // rax
  DWORD v4; // eax
  DWORD v5; // eax
  LPSTR v7; // rax
  __int64 (*v8)(); // [rsp+40h] [rbp-878h]
  __int128 password; // [rsp+48h] [rbp-870h]
  DWORD dwIoControlCode; // [rsp+58h] [rbp-860h]
  BOOL v11; // [rsp+5Ch] [rbp-85Ch]
  HANDLE hDevice; // [rsp+60h] [rbp-858h]
  __int64 dwSize; // [rsp+68h] [rbp-850h]
  DWORD flOldProtect; // [rsp+70h] [rbp-848h]
  DWORD BytesReturned; // [rsp+74h] [rbp-844h]
  __int64 v16; // [rsp+78h] [rbp-840h]
  __int64 v17; // [rsp+80h] [rbp-838h]
  LPCWSTR lpFileName; // [rsp+88h] [rbp-830h]
  char *Address; // [rsp+90h] [rbp-828h]
  __int64 (*v20)(); // [rsp+98h] [rbp-820h]
  __int64 v21; // [rsp+A0h] [rbp-818h]
  __int64 v22; // [rsp+A8h] [rbp-810h]
  char v23; // [rsp+B0h] [rbp-808h]
  char OutBuffer; // [rsp+490h] [rbp-428h]

  v8 = 0i64;
  v17 = 0i64;
  v16 = 0i64;
  password = 0ui64;
  dwSize = 0i64;
  v3 = sub_1400014D0(aKerneloadriven_0);
  LoadDriver((__int64)v3);
  sub_140002030();
  print(asc_1400062F8);
  scanf(Format, &password);
  if ( (signed __int64)password <= 96000 && (signed __int64)password >= 90000 )// 判断输入的密码的范围
  {
    v16 = 0x42i64;
    v17 = 0xA9i64;
    dwSize = 0xE7i64;
    *((_QWORD *)&password + 1) = &unk_1400060B0;
    VirtualProtect_0(&unk_1400060B0, 0xE7);
    VirtualProtect_0(&OutBuffer, 1024);
    sub_14000256E(&v8);
    v8 = (__int64 (*)())((char *)v8 + 2);
    sub_140001760(*((__int64 *)&password + 1), 0i64, (__int64)&unk_140006B60);
    dwIoControlCode = 0x222041;
    VirtualProtect(&Address, 0xAui64, 0x40u, &flOldProtect);
    Address = &OutBuffer;
    v8 = Callback_Wrong;                        // 密码错误时的回调函数
    v20 = Callback_Wrong;
    v4 = GetCurrentProcessId();
    v8 = (__int64 (*)())v4;
    v21 = v4;
    v22 = password;
    qmemcpy(&v23, *((const void **)&password + 1), 0xE7ui64);
    lpFileName = aKerneloadrivev;
    hDevice = CreateFileW(aKerneloadrivev, 0xC0000000, 0, 0i64, 3u, 0x80u, 0i64);
    if ( hDevice == (HANDLE)-1i64 )
    {
      v5 = GetLastError();
      print(aFailedToObtain, aMywdmdevice, v5);
      return 1;
    }                                           // 跟驱动通信,Address存放了密码,错误回调函数等等
    v11 = DeviceIoControl(hDevice, dwIoControlCode, &Address, 0x107u, &OutBuffer, 0x400u, &BytesReturned, 0i64);
    if ( !v11 )
    {
      MessageBoxW(0i64, L"wrong!", L"err", 0);
      return 0;
    }
    CloseHandle(hDevice);
  }
  else
  {
    MessageBoxW(0i64, L"wrong!", L"err", 0);
  }
  SleepEx(0x3E8u, 1);
  v7 = sub_1400014D0(aKerneloadriven_1);
  UnloadDriver((__int64)v7);
  system(pause);
  return 0;
}

从main函数可以看出来password的范围为[90000,96000],接下来看看驱动。
在DriverEntry顺藤摸瓜,找到sub_140005080,伪代码如下。
__int64 __fastcall sub_140005080(__int64 a1, _IRP *a2)
{
  _IO_STACK_LOCATION *v2; // rax
  _IRP *v3; // rbp
  __int64 v4; // r13
  PMDL v5; // rcx
  _IRP *v6; // r15
  PVOID v7; // r12
  PMDL v8; // r14
  _IRP *v9; // rdi
  int v10; // eax
  __int64 v11; // rbx
  int v12; // esi
  __int64 v13; // rcx
  __int64 v15; // [rsp+68h] [rbp+10h]

  v2 = a2->Tail.Overlay.CurrentStackLocation;
  v3 = a2;
  v4 = v2->Parameters.Create.Options;
  if ( v2->Parameters.Read.ByteOffset.LowPart == 0x222041 )
  {
    v5 = a2->MdlAddress;
    v6 = a2->AssociatedIrp.MasterIrp;
    if ( v5->MdlFlags & 5 )
      v7 = v5->MappedSystemVa;
    else
      v7 = MmMapLockedPagesSpecifyCache(v5, 0, MmCached, 0i64, 0, NormalPagePriority);
    v8 = v6->MdlAddress;
    v9 = v6->AssociatedIrp.MasterIrp;
    v15 = *(_QWORD *)&v6->Type;
    v10 = sub_14000110D();
    v11 = v10;
    v12 = v4 - (v10 + 36);
    DecryptCode((_BYTE *)&v6->ThreadListEntry.Flink + v10 + 4, v4 - (v10 + 36), (int)v9);// 以password为key解密shellcode
    if ( *(_DWORD *)((char *)&v6->ThreadListEntry.Flink + v11 + 4) == v12 )// 若解密成功,则将sub_140001131处的代码以及shellcode复制到R3的内存中
    {
      memcpy(v7, &v6->ThreadListEntry, v4 - 32);
      v13 = v15;
LABEL_9:
      InsertApc(v13, 0i64, 0i64, 0i64);         // 将内存地址插入到线程APC,等待执行,如果解密失败,则将错误回调函数插入到APC
      goto LABEL_10;
    }
    if ( v8 )
    {
      v13 = (__int64)v8;
      goto LABEL_9;
    }
  }
LABEL_10:
  v3->IoStatus.Status = 0;
  v3->IoStatus.Information = 0i64;
  IofCompleteRequest(v3, 0);
  return 0i64;
}

__int64 __fastcall sub_140005080(__int64 a1, _IRP *a2)
{
  _IO_STACK_LOCATION *v2; // rax
  _IRP *v3; // rbp
  __int64 v4; // r13
  PMDL v5; // rcx
  _IRP *v6; // r15
  PVOID v7; // r12
  PMDL v8; // r14
  _IRP *v9; // rdi
  int v10; // eax
  __int64 v11; // rbx
  int v12; // esi
  __int64 v13; // rcx
  __int64 v15; // [rsp+68h] [rbp+10h]

  v2 = a2->Tail.Overlay.CurrentStackLocation;
  v3 = a2;
  v4 = v2->Parameters.Create.Options;
  if ( v2->Parameters.Read.ByteOffset.LowPart == 0x222041 )
  {
    v5 = a2->MdlAddress;
    v6 = a2->AssociatedIrp.MasterIrp;
    if ( v5->MdlFlags & 5 )
      v7 = v5->MappedSystemVa;
    else
      v7 = MmMapLockedPagesSpecifyCache(v5, 0, MmCached, 0i64, 0, NormalPagePriority);
    v8 = v6->MdlAddress;
    v9 = v6->AssociatedIrp.MasterIrp;
    v15 = *(_QWORD *)&v6->Type;
    v10 = sub_14000110D();
    v11 = v10;
    v12 = v4 - (v10 + 36);
    DecryptCode((_BYTE *)&v6->ThreadListEntry.Flink + v10 + 4, v4 - (v10 + 36), (int)v9);// 以password为key解密shellcode
    if ( *(_DWORD *)((char *)&v6->ThreadListEntry.Flink + v11 + 4) == v12 )// 若解密成功,则将sub_140001131处的代码以及shellcode复制到R3的内存中
    {
      memcpy(v7, &v6->ThreadListEntry, v4 - 32);
      v13 = v15;
LABEL_9:
      InsertApc(v13, 0i64, 0i64, 0i64);         // 将内存地址插入到线程APC,等待执行,如果解密失败,则将错误回调函数插入到APC
      goto LABEL_10;
    }
    if ( v8 )
    {
      v13 = (__int64)v8;
      goto LABEL_9;
    }
  }
LABEL_10:
  v3->IoStatus.Status = 0;
  v3->IoStatus.Information = 0i64;
  IofCompleteRequest(v3, 0);
  return 0i64;
}

驱动部分的主要流程:
以输入的password解密一段代码,解密成功则将代码写到程序里,并将地址插入到APC。
在程序走到"SleepEx(0x3E8u, 1);"的时候,插入到APC的地址就会被调用。

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

最后于 2019-12-14 21:31 被梦游枪手编辑 ,原因: 补图
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//