Search code examples
cportable-executable

Understanding hook_finder


I'm Trying to understand the PE Format & the source code of "hook_finder" in here "https://github.com/Mr-Un1k0d3r/EDRs/blob/main/hook_finder64.c"

in this snippet I now it's trying to calculate Export_Table offset:

VOID DumpListOfExport(VOID *lib, BOOL bNt) {
    DWORD dwIter = 0;
    CHAR* base = (CHAR*)lib;
    CHAR* PE = base + (unsigned char)*(base + 0x3c); 
    DWORD ExportDirectoryOffset = *((DWORD*)PE + (0x8a / 4));
    CHAR* ExportDirectory = base + ExportDirectoryOffset;
    DWORD dwFunctionsCount = *((DWORD*)ExportDirectory + (0x14 / 4));
    DWORD OffsetNamesTableOffset = *((DWORD*)ExportDirectory + (0x20 / 4));
    CHAR* OffsetNamesTable = base + OffsetNamesTableOffset;

    printf("------------------------------------------\nBASE\t\t\t0x%p\t%s\nPE\t\t\t0x%p\t%s\nExportTableOffset\t0x%p\nOffsetNameTable\t\t0x%p\nFunctions Count\t\t0x%x (%d)\n------------------------------------------\n",
    base, base, PE, PE, ExportDirectory, OffsetNamesTable, dwFunctionsCount, dwFunctionsCount);

    for(dwIter; dwIter < dwFunctionsCount - 1; dwIter++) {
        DWORD64 offset = *((DWORD*)OffsetNamesTable + dwIter);
        CHAR* current = base + offset;
        GetBytesByName((HANDLE)lib, current, bNt);
    }
}

ox3c is e_lfnew offset. However, can't understand what's other hex values and why it's divided by 4 byte?

Further,

VOID GetBytesByName(HANDLE hDll, CHAR *name, BOOL bNt) {
    FARPROC ptr = GetProcAddress((HMODULE)hDll, name);
    DWORD* opcode = (DWORD*)*ptr;

    if(bNt) {
        if(name[0] != 'N' && name[1] != 't') {
            return;
        }
    }
    
    if((*opcode << 24) >> 24 == 0xe9) {
        if(!IsFalsePositive(name)) {
            printf("%s is hooked\n", name);
        }
    }
}

what's been exactly left & right shifting and Why 24 specifically? From my understanding of EDRs, it adds a JMP instruction at the very beginning of the function and that's why the condition is trying to check if it's (0xe9), but how does it follow and be certain about the function flow?

and is this applicable only for ntdll.dll?

Sorry I'm starting to study the PE behavior and trying to make things very clear.

Thank you in advance


Solution

  • The function DumpListOfExport assumes that NtHeaders start at the offset 0x3c from the base but, this is not always the case depending on the size of the DOS stub. Probably, this code makes that assumption for ntdll.dll.

    And in the function GetBytesByName, if first byte of the procedure starts with a JMP(in that case, it is near, relative jmp whose opcode starts with "E9") instruction and the procedure name is not in the false positives list, then the function makes decision that the function is hooked.

    Let be the value of the 4-bytes pointed to by opcode 0xca0e4be9, left shifting it by 24 will result in 0xe9000000, and then right shifting by 24 the result will be 0x000000e9 which is the value of the first byte at ptr.

    That procedure can be simplified as follows.

    VOID GetBytesByName(HANDLE hDll, CHAR *name, BOOL bNt) {
        FARPROC ptr = GetProcAddress((HMODULE)hDll, name);
        BYTE* opcode = (BYTE*)ptr;
    
        if(bNt) {
            if(name[0] != 'N' && name[1] != 't') {
                return;
            }
        }
        
        if(!IsFalsePositive(name) && *opcode == 0xe9) {
            printf("%s is hooked\n", name);
        }
    }
    

    As a note : I can say that the code isn't written well, and doesn't follow any good coding style.