Manual Maplenen DLL'i gizlemek?

Durum
Üzgünüz bu konu cevaplar için kapatılmıştır...
Üye
Katılım
23 Eyl 2020
Mesajlar
36
Tepki puanı
27
5 HİZMET YILI
Giriş

Battleye, EAC gibi güçlü Anti-Cheat sistemleri manual maplenen DLLleri tespit etmek için query memory protection kullanırlar. Whitelist içersine alınmış modüller dışında allocate edilmiş Read/Write pageleri varsa bu process içerisinde manual maplenmiş bir DLL olduğunu gösterir. Bunu geçmenin kolay yolları vardır. Bu RWX pagelerini gizlemek için bizim izleyeceğimiz yollar şunlardır: VAD manipülasyonu ve NX bit değişimi.

NOT: DLL'inizi manual maplemek için bir Driver'a ihtiyacınız var.

RWX detection neden var?

RWX detection var çünkü DLL kodunu çalıştırabilmek için execute iznine ve memory ile birlikte çalışması için read ve write iznine ihtiyacınız var. Execute izinleri zaten kendi başlarına şüphelilerdir çünkü execute izni mevcut olan kodu gösterir.

1) VAD gizlemesi

VAD yani virtual address descriptor (Türkçesi sanal adres tanımlayıcısı) bir processin memory aralıklarının özelliklerini açıklayan Tree Data Structure'dır. VAD'ın kökü EPROCESS->vadRoot içerisinde bulunur. VirtualQuery Virtual Pageler için korumayı belirlemek için VAD'dan bilgi alır. VAD Tree'den geçerseniz ve allocate edilmiş sayfalarınızı tanımlayan VAD node'unun koruma değerini değiştirirseniz VirtualQuery Result ile uğraşabilirsiniz.

Kod:
 =
{
    PAGE_NOACCESS,
    PAGE_READONLY,
    PAGE_EXECUTE,
    PAGE_EXECUTE_READ,
    PAGE_READWRITE,
    PAGE_WRITECOPY,
    PAGE_EXECUTE_READWRITE,
    PAGE_EXECUTE_WRITECOPY,
    PAGE_NOACCESS,
    PAGE_NOCACHE | PAGE_READONLY,
    PAGE_NOCACHE | PAGE_EXECUTE,
    PAGE_NOCACHE | PAGE_EXECUTE_READ,
    PAGE_NOCACHE | PAGE_READWRITE,
    PAGE_NOCACHE | PAGE_WRITECOPY,
    PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
    PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
    PAGE_NOACCESS,
    PAGE_GUARD | PAGE_READONLY,
    PAGE_GUARD | PAGE_EXECUTE,
    PAGE_GUARD | PAGE_EXECUTE_READ,
    PAGE_GUARD | PAGE_READWRITE,
    PAGE_GUARD | PAGE_WRITECOPY,
    PAGE_GUARD | PAGE_EXECUTE_READWRITE,
    PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
    PAGE_NOACCESS,
    PAGE_WRITECOMBINE | PAGE_READONLY,
    PAGE_WRITECOMBINE | PAGE_EXECUTE,
    PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
    PAGE_WRITECOMBINE | PAGE_READWRITE,
    PAGE_WRITECOMBINE | PAGE_WRITECOPY,
    PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
    PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
};

/// <summary>
/// Change VAD protection flags
/// </summary>
/// <param name="pProcess">Target process object</param>
/// <param name="address">Target address</param>
/// <param name="prot">New protection flags</param>
/// <returns>Status code</returns>
NTSTATUS BBProtectVAD( IN PEPROCESS pProcess, IN ULONG_PTR address, IN ULONG prot )
{
    NTSTATUS status = STATUS_SUCCESS;
    PMMVAD_SHORT pVadShort = NULL;

    status = BBFindVAD( pProcess, address, &pVadShort );
    if (NT_SUCCESS( status ))
        pVadShort->u.VadFlags.Protection = prot;

    return status;
}

#pragma warning(disable : 4055)

/// <summary>
/// Hide memory from NtQueryVirtualMemory
/// </summary>
/// <param name="pProcess">Target process object</param>
/// <param name="address">Target address</param>
/// <returns>Status code</returns>
NTSTATUS BBUnlinkVAD( IN PEPROCESS pProcess, IN ULONG_PTR address )
{
    NTSTATUS status = STATUS_SUCCESS;
    PMMVAD_SHORT pVadShort = NULL;

    status = BBFindVAD( pProcess, address, &pVadShort );
    if (!NT_SUCCESS( status ))
        return status;

    // Erase image name
    if (pVadShort->u.VadFlags.VadType == VadImageMap)
    {
        PMMVAD pVadLong = (PMMVAD)pVadShort;
        if (pVadLong->Subsection && pVadLong->Subsection->ControlArea && pVadLong->Subsection->ControlArea->FilePointer.Object)
        {
            PFILE_OBJECT pFile = (PFILE_OBJECT)(pVadLong->Subsection->ControlArea->FilePointer.Value & ~0xF);
            pFile->FileName.Buffer[0] = L'\0';
            pFile->FileName.Length = 0;
        }
        else
            return STATUS_INVALID_ADDRESS;
    }
    // Make NO_ACCESS
    else if (pVadShort->u.VadFlags.VadType == VadDevicePhysicalMemory)
    {
        pVadShort->u.VadFlags.Protection = MM_ZERO_ACCESS;
    }
    // Invalid VAD type
    else
        status = STATUS_INVALID_PARAMETER;

    return status;
}

#pragma warning(default : 4055)

/// <summary>
/// Get region VAD type
/// </summary>
/// <param name="pProcess">Target process object</param>
/// <param name="address">Target address</param>
/// <param name="pType">Resulting VAD type</param>
/// <returns>Status code</returns>
NTSTATUS BBGetVadType( IN PEPROCESS pProcess, IN ULONG_PTR address, OUT PMI_VAD_TYPE pType )
{
    NTSTATUS status = STATUS_SUCCESS;
    PMMVAD_SHORT pVad = NULL;

    status = BBFindVAD( pProcess, address, &pVad );
    if (!NT_SUCCESS( status ))
        return status;

    *pType = pVad->u.VadFlags.VadType;

    return status;
}

/// <summary>
/// Find VAD that describes target address
/// </summary>
/// <param name="pProcess">Target process object</param>
/// <param name="address">Address to find</param>
/// <param name="pResult">Found VAD. NULL if not found</param>
/// <returns>Status code</returns>
NTSTATUS BBFindVAD( IN PEPROCESS pProcess, IN ULONG_PTR address, OUT PMMVAD_SHORT* pResult )
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG_PTR vpnStart = address >> PAGE_SHIFT;

    ASSERT( pProcess != NULL && pResult != NULL );
    if (pProcess == NULL || pResult == NULL)
        return STATUS_INVALID_PARAMETER;

    if (dynData.VadRoot == 0)
    {
        DPRINT( "BlackBone: %s: Invalid VadRoot offset\n", __FUNCTION__ );
        status = STATUS_INVALID_ADDRESS;
    }


    PMM_AVL_TABLE pTable = (PMM_AVL_TABLE)((PUCHAR)pProcess + dynData.VadRoot);
    PMM_AVL_NODE pNode = GET_VAD_ROOT( pTable );

    // Search VAD
    if (MiFindNodeOrParent( pTable, vpnStart, &pNode ) == TableFoundNode)
    {
        *pResult = (PMMVAD_SHORT)pNode;
    }
    else
    {
        DPRINT( "BlackBone: %s: VAD entry for address 0x%p not found\n", __FUNCTION__, address );
        status = STATUS_NOT_FOUND;
    }

    return status;
}

/// <summary>
/// Convert protection flags
/// </summary>
/// <param name="prot">Protection flags.</param>
/// <param name="fromPTE">If TRUE - convert to PTE protection, if FALSE - convert to Win32 protection</param>
/// <returns>Resulting protection flags</returns>
ULONG BBConvertProtection( IN ULONG prot, IN BOOLEAN fromPTE )
{
    if (fromPTE != FALSE)
    {
        // Sanity check
        if (prot < ARRAYSIZE( MmProtectToValue ))
            return MmProtectToValue[prot];
    }
    else
    {
        for (int i = 0; i < ARRAYSIZE( MmProtectToValue ); i++)
            if (MmProtectToValue[i] == prot)
                return i;
    }

    return 0;
}


Bağlantıları görmek için lütfen Giriş Yap


2) NX bitini değiştirme
Manual maplenen pageleri gizlemenin başka bir yolu pageleri sadece Read/Write izni ile açmak, allocate ettiğiniz pagelerin PTE'lerini (Page Table Entry) almak ve pageinize coverların altında execute izni eklemektir. Karmaşık gelebilir ama aslında kolay.
Sadece bir Page'in PTE'SİNİ yakalamak için bir fonksiyon yazmanız gerekir. Unexported MiGetPteAddress'i kullanabilirsiniz.

Eğer kafanız karıştıysa Page Table Entry hakkında araştırma yapabilirsiniz.

C++:
#pragma once
#include "Utils.h"


enum mapLevel
{
    PML4_LEVEL,
    PDPT_LEVEL,
    PD_LEVEL,
    PT_LEVEL,
    PTE_LEVEL,
};




namespace  Memory
{
    int                     myInt = 1312312;
    SIZE_T                  outSize;
    PHYSICAL_MEMORY_RANGE   physicalMemRange[10];
    static  int             numberOfRuns = 0;



    PT_ENTRY_64*     GetPte(VOID* VirtualAddress, CR3 HostCr3)
    {
        ADDRESS_TRANSLATION_HELPER helper;
        UINT32 level;
        PT_ENTRY_64* finalEntry;
        PML4E_64* pml4;
        PML4E_64* pml4e;
        PDPTE_64* pdpt;
        PDPTE_64* pdpte;
        PDE_64* pd;
        PDE_64* pde;
        PTE_64* pt;
        PTE_64* pte;

        helper.AsUInt64 = (UINT64)VirtualAddress;

        PHYSICAL_ADDRESS    addr;



        addr.QuadPart = HostCr3.AddressOfPageDirectory << PAGE_SHIFT;

        pml4 = (PML4E_64*)MmGetVirtualForPhysical(addr);

        pml4e = &pml4[helper.AsIndex.Pml4];

        if (pml4e->Present == FALSE)
        {
            finalEntry = (PT_ENTRY_64*)pml4e;
            goto Exit;
        }




        addr.QuadPart = pml4e->PageFrameNumber << PAGE_SHIFT;

        pdpt = (PDPTE_64*)MmGetVirtualForPhysical(addr);

        pdpte = &pdpt[helper.AsIndex.Pdpt];

        if ((pdpte->Present == FALSE) || (pdpte->LargePage != FALSE))
        {
            finalEntry = (PT_ENTRY_64*)pdpte;
            goto Exit;
        }



        addr.QuadPart = pdpte->PageFrameNumber << PAGE_SHIFT;

        pd = (PDE_64*)MmGetVirtualForPhysical(addr);

        pde = &pd[helper.AsIndex.Pd];

        if ((pde->Present == FALSE) || (pde->LargePage != FALSE))
        {
            finalEntry = (PT_ENTRY_64*)pde;
            goto Exit;
        }





        addr.QuadPart = pde->PageFrameNumber << PAGE_SHIFT;

        pt = (PTE_64*)MmGetVirtualForPhysical(addr);

        pte = &pt[helper.AsIndex.Pt];

        finalEntry = (PT_ENTRY_64*)pte;

        return  (PT_ENTRY_64*)pte;

    Exit:
        return finalEntry;
    }





    void scanPage(INPUT_STRUCT* context, PVOID64    virtualPage, ULONG     size, PHYSICAL_ADDRESS  addr)
    {

        if (virtualPage && MmIsAddressValid(virtualPage))
        {


            //MmCopyVirtualMemory(PsGetCurrentProcess(), &myInt, Globals::targetProcess, 0, sizeof(int), KernelMode, &outSize);
            __invlpg(virtualPage);


            PVOID64     foundAt = Utils::findPattern(context->serialNumber, context->serialLength, 'A',
                (ULONG64)virtualPage, (ULONG64)virtualPage + size);

            if (foundAt)
            {
                DbgPrint("[+] seri numarası fiziksel adreste tespit edildi %p !!\n",
                    (addr.QuadPart + ((DWORD64)foundAt - (DWORD64)virtualPage)));

                if (context->wide == false)
                {
                    Utils::print((char*)foundAt, 18);
                }
                else
                {
                    Utils::wprint((wchar_t*)foundAt, 18);
                }

                if (memcmp(Globals::signatureGuard, (PVOID)((DWORD64)(foundAt)+context->serialLength), 3) == 0)
                {
                
                }
                else
                {
                    RtlCopyMemory(foundAt, Globals::spoofString, context->serialLength);
                }
            }
            else
            {
            }

        }
    }




    void    getPhysicalMemoryRanges()
    {
        PPHYSICAL_MEMORY_RANGE MmPhysicalMemoryRange = MmGetPhysicalMemoryRanges();

        numberOfRuns = 0;

        for (int number_of_runs = 0;
            (MmPhysicalMemoryRange[number_of_runs].BaseAddress.QuadPart) || (MmPhysicalMemoryRange[number_of_runs].NumberOfBytes.QuadPart);
            number_of_runs++)
        {

            DbgPrint("base addr %llx, size %llx\n", MmPhysicalMemoryRange[number_of_runs].BaseAddress.QuadPart,
                MmPhysicalMemoryRange[number_of_runs].NumberOfBytes.QuadPart);


            physicalMemRange[number_of_runs] = MmPhysicalMemoryRange[number_of_runs];

            numberOfRuns += 1;


        }

        return;
    }


    bool    isAddressinPhysMemRange(PHYSICAL_ADDRESS  addr)
    {
        for (int i = 0; i < numberOfRuns; ++i)
        {
            if ((addr.QuadPart >= physicalMemRange[i].BaseAddress.QuadPart)
                && (addr.QuadPart <= (physicalMemRange[i].BaseAddress.QuadPart + physicalMemRange[i].NumberOfBytes.QuadPart)))
            {
                return true;
            }
        }

        return false;
    }



    void    scanPhysicalMemory(_In_ PVOID Context)
    {
    
        DbgPrint("thread başlatıldı! \n");

        getPhysicalMemoryRanges();

        INPUT_STRUCT* context = (INPUT_STRUCT*)Context;



        CR3     cr3;
        cr3.Flags = __readcr3();


        PHYSICAL_ADDRESS   addr;

        addr.QuadPart = cr3.AddressOfPageDirectory << PAGE_SHIFT;


        DbgPrint("Page dizinin.base adresi %p \n", addr.QuadPart);


        PML4E_64* pml4 = (PML4E_64*)Globals::pageManager.mapPage((PVOID)addr.QuadPart, PML4_LEVEL);




        if (MmIsAddressValid(pml4) == FALSE || pml4 == 0)
        {
            DbgPrint("Hata! pml4 geçerli bir adres değil \n");
            return;
        }


        for (int i = 0; i < 512; ++i)
        {

            if (pml4[i].Present == false)
            {
                continue;
            }

            addr.QuadPart = pml4[i].PageFrameNumber << PAGE_SHIFT;

            PDPTE_64* pdpt = (PDPTE_64*)Globals::pageManager.mapPage((PVOID)addr.QuadPart, PDPT_LEVEL);


            if (pdpt == 0 || (MmIsAddressValid(pdpt) == FALSE))
            {
                continue;
            }

            for (int ii = 0; ii < 512; ++ii)
            {

                addr.QuadPart = pdpt[ii].PageFrameNumber << PAGE_SHIFT;


                if (pdpt[ii].Present == false)
                {
                    continue;
                }
                if (pdpt[ii].LargePage == true)
                {

                    PDE_64* pageDir = (PDE_64*)MmGetVirtualForPhysical(addr);


                    if (pageDir == 0 || (MmIsAddressValid(pageDir) == FALSE))
                    {
                        continue;
                    }

                    for (int kkk = 0; kkk < 262144; ++kkk)
                    {

                        scanPage(context, (pageDir + (kkk * PAGE_SIZE)), PAGE_SIZE, addr);

                    }

                    continue;
                }


                PDE_64* pageDir = (PDE_64*)Globals::pageManager.mapPage((PVOID)addr.QuadPart, PD_LEVEL);


                if (pageDir == 0 || (MmIsAddressValid(pageDir)  == FALSE))
{
continue;
}

                for (int iii = 0; iii < 512; ++iii)
                {

                    addr.QuadPart = pageDir[iii].PageFrameNumber << PAGE_SHIFT;

                    if (pageDir[iii].Present == false)
                    {
                        continue;
                    }
                    if (pageDir[iii].LargePage == true)
                    {


                        PTE_64* pageTable = (PTE_64*)MmGetVirtualForPhysical(addr);


                        scanPage(context, pageTable, PAGE_SIZE * 512, addr);


                        continue;
                    }



                    PTE_64* pageTable = (PTE_64*)Globals::pageManager.mapPage((PVOID)addr.QuadPart, PT_LEVEL);


                    if (pageTable == 0 || (MmIsAddressValid(pageTable) == FALSE))
                    {
                        continue;
                    }




                    for (int iiii = 0; iiii < 512; ++iiii)
                    {
                        if (pageTable[iiii].Present == false || pageTable[iiii].Write == false || pageTable[iiii].ExecuteDisable == false)
                        {
                            continue;
                        }

                        addr.QuadPart = pageTable[iiii].PageFrameNumber << PAGE_SHIFT;



                        if (isAddressinPhysMemRange(addr) == TRUE)
                        {
                
                            PVOID64  virtualPage = (PVOID64)Globals::pageManager.mapPage((PVOID)addr.QuadPart, 4);

                            scanPage(context, virtualPage, PAGE_SIZE, addr);

                            Globals::pageManager.UnmapPage((PVOID)addr.QuadPart, 4);
                        }
                    }

                    Globals::pageManager.UnmapPage((PVOID)addr.QuadPart, 3);
                }

                Globals::pageManager.UnmapPage((PVOID)addr.QuadPart, 2);
            }

            Globals::pageManager.UnmapPage((PVOID)addr.QuadPart, 1);
        }

        Globals::pageManager.UnmapPage((PVOID)addr.QuadPart, 0);

        return;

    }
}


RWX pagelerini gizlemek için diğer yollar
- DLL'inizi imzalanmış ve packlenmiş bir DLL'de mapleyin.
- Birden fazla Page'i allocate etmeyin.

Yakında
Bağlantıları görmek için lütfen Giriş Yap
Driveri ile hazır kodu ekleyeceğim.
 
Son düzenleme:
Durum
Üzgünüz bu konu cevaplar için kapatılmıştır...
Üst