diff --git a/CAPE/CAPE.c b/CAPE/CAPE.c index 78457f26..fc138361 100644 --- a/CAPE/CAPE.c +++ b/CAPE/CAPE.c @@ -109,9 +109,9 @@ typedef struct _hook_info_t { } hook_info_t; typedef SIZE_T (WINAPI *_RtlCompareMemory)( - _In_ const VOID* Source1, - _In_ const VOID* Source2, - _In_ SIZE_T Length + _In_ const VOID* Source1, + _In_ const VOID* Source2, + _In_ SIZE_T Length ); extern _RtlCompareMemory pRtlCompareMemory; @@ -259,33 +259,33 @@ PVOID GetHookCallerBase() void SanitiseString(char *Dst, const char *Src, size_t Size) //************************************************************************************** { - size_t Length = strlen(Src); - size_t NewLength = 0; + size_t Length = strlen(Src); + size_t NewLength = 0; - for (size_t i = 0; i < Length && NewLength < Size - 1; ++i) - if (Src[i] == '%') - NewLength += 2; - else - NewLength++; + for (size_t i = 0; i < Length && NewLength < Size - 1; ++i) + if (Src[i] == '%') + NewLength += 2; + else + NewLength++; - if (NewLength >= Size) - NewLength = Size - 1; + if (NewLength >= Size) + NewLength = Size - 1; - Dst[NewLength] = '\0'; + Dst[NewLength] = '\0'; - for (int i = (int)Length - 1, j = (int)NewLength - 1; i >= 0 && j >= 0; --i) - if (Src[i] == '%') - { - if (j >= 1) - { - Dst[j--] = '%'; - Dst[j--] = '%'; - } - } - else if ((unsigned char)Src[i] < 0x0a || (unsigned char)Src[i] > 0x7E) - Dst[j--] = '?'; - else - Dst[j--] = Src[i]; + for (int i = (int)Length - 1, j = (int)NewLength - 1; i >= 0 && j >= 0; --i) + if (Src[i] == '%') + { + if (j >= 1) + { + Dst[j--] = '%'; + Dst[j--] = '%'; + } + } + else if ((unsigned char)Src[i] < 0x0a || (unsigned char)Src[i] > 0x7E) + Dst[j--] = '?'; + else + Dst[j--] = Src[i]; } //************************************************************************************** @@ -655,18 +655,18 @@ PVOID GetNonExportedFunctionAddress(HMODULE ModuleBase, PCHAR ExportName, int Of if (!ModuleBase || !ExportName) return NULL; - DWORD ExportRVA = (DWORD)((ULONG_PTR)GetProcAddress(ModuleBase, ExportName) - (ULONG_PTR)ModuleBase); - if (!ExportRVA) - return NULL; + DWORD ExportRVA = (DWORD)((ULONG_PTR)GetProcAddress(ModuleBase, ExportName) - (ULONG_PTR)ModuleBase); + if (!ExportRVA) + return NULL; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)ModuleBase + (ULONG)((PIMAGE_DOS_HEADER)ModuleBase)->e_lfanew); - PIMAGE_RUNTIME_FUNCTION_ENTRY Table = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((PUCHAR)ModuleBase + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress); + PIMAGE_RUNTIME_FUNCTION_ENTRY Table = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((PUCHAR)ModuleBase + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress); for (unsigned int i = 0; Table[i].BeginAddress; i++) if (Table[i].BeginAddress == ExportRVA) return (PVOID)((PBYTE)ModuleBase + Table[Offset + i].BeginAddress); - return NULL; + return NULL; } #endif @@ -688,23 +688,60 @@ PVOID GetFunctionByName(HMODULE ModuleBase, PCHAR FunctionName) {"LdrpCallInitRoutine", "RtlActivateActivationContextUnsafeFast", 1}, }; - if ((OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion > 1) || OSVersion.dwMajorVersion > 6) + if ((OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion > 1) || (OSVersion.dwMajorVersion > 6 && OSVersion.dwBuildNumber < 26000)) { for (int i = 0; i < sizeof(RuntimeTable) / sizeof(RuntimeTableMapping); i++) if (strcmp(RuntimeTable[i].FunctionName, FunctionName) == 0) return GetNonExportedFunctionAddress(ModuleBase, RuntimeTable[i].ExportName, RuntimeTable[i].Offset); } #endif - const char *YaraFunctions[] = + char *YaraFunctions[] = { "LdrpCallInitRoutine", + "WMI_ExecQuery", + "WMI_ExecMethod", + "WMI_ExecQueryAsync", + "WMI_ExecMethodAsync", + "WMI_GetObject", + "WMI_GetObjectAsync", + "vDbgPrintExWithPrefixInternal", + NULL }; - for (int i = 0; i < sizeof(YaraFunctions) / sizeof(YaraFunctions[0]); i++) - if (strcmp(YaraFunctions[i], FunctionName) == 0) - return GetAddressByYara(ModuleBase, FunctionName); + BOOL InYaraFunctions = FALSE; + for (SIZE_T i = 0; YaraFunctions[i]; i++) + { + if (!strcmp(YaraFunctions[i], FunctionName)) + { + InYaraFunctions = TRUE; + break; + } + } - return NULL; + if (!InYaraFunctions) + return NULL; + + SIZE_T FoundCount = 0, FuncCount = sizeof(YaraFunctions) / sizeof(YaraFunctions[0]); + NameByAddress* results = GetAddressesByYara(ModuleBase, YaraFunctions, FuncCount, &FoundCount); + + if (!results || FoundCount == 0) + { + if (results) + free(results); + return NULL; + } + + PVOID Address = NULL; + for (SIZE_T j = 0; j < FoundCount; j++) + if (results[j].FunctionName && results[j].Address && !strcmp(results[j].FunctionName, FunctionName)) + { + Address = results[j].Address; + break; + } + + free(results); + + return Address; } //************************************************************************************** @@ -1054,10 +1091,6 @@ PTRACKEDREGION AddTrackedRegion(PVOID Address, ULONG Protect) if (TrackedRegion->EntryPoint) { TrackedRegion->MinPESize = GetMinPESize(TrackedRegion->AllocationBase); - if (TrackedRegion->MinPESize) - DebugOutput("AddTrackedRegion: Min PE size 0x%x", TrackedRegion->MinPESize); - //else - // DebugOutput("AddTrackedRegion: GetMinPESize failed"); #ifdef DEBUG_COMMENTS if (!PageAlreadyTracked) DebugOutput("AddTrackedRegion: New region at 0x%p added to tracked regions: EntryPoint 0x%x, Entropy %e\n", TrackedRegion->AllocationBase, TrackedRegion->EntryPoint, TrackedRegion->Entropy); @@ -2048,41 +2081,102 @@ PCHAR ScanForExport(PVOID Address, SIZE_T ScanMax) __try { PVOID Base = GetAllocationBase(Address); - if (!Base) + if (!Base || !IsAddressAccessible(Base)) + return NULL; + + PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Base; + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL; - PIMAGE_NT_HEADERS pNtHeader = pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)Base + (ULONG)((PIMAGE_DOS_HEADER)Base)->e_lfanew); - if (!pNtHeader) + PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)Base + DosHeader->e_lfanew); + if (NtHeader->Signature != IMAGE_NT_SIGNATURE) return NULL; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)Base + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - if (!ExportDirectory) + IMAGE_DATA_DIRECTORY ExportDirEntry = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if (ExportDirEntry.VirtualAddress == 0 || ExportDirEntry.Size == 0) return NULL; - PDWORD AddressOfNames = (PDWORD)((PUCHAR)Base + ExportDirectory->AddressOfNames); - if (!AddressOfNames) + PIMAGE_EXPORT_DIRECTORY ExportDir = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)Base + ExportDirEntry.VirtualAddress); + if (!IsAddressAccessible(ExportDir)) return NULL; - PDWORD AddressOfFunctions = (PDWORD)((PUCHAR)Base + ExportDirectory->AddressOfFunctions); - if (!AddressOfFunctions) + if (ExportDir->NumberOfFunctions > 0x100000) return NULL; - PWORD AddressOfNameOrdinals = (PWORD)((PUCHAR)Base + ExportDirectory->AddressOfNameOrdinals); - if (!AddressOfNameOrdinals) + PDWORD AddressOfFunctions = (PDWORD)((PUCHAR)Base + ExportDir->AddressOfFunctions); + if (!IsAddressAccessible(AddressOfFunctions)) return NULL; - for (unsigned int j = 0; j < ExportDirectory->NumberOfFunctions; j++) + PDWORD AddressOfNames = (PDWORD)((PUCHAR)Base + ExportDir->AddressOfNames); + PWORD AddressOfNameOrdinals = (PWORD)((PUCHAR)Base + ExportDir->AddressOfNameOrdinals); + + for (DWORD i = 0; i < ExportDir->NumberOfNames; i++) { - if ((PUCHAR)Address - (PUCHAR)Base > (int)AddressOfFunctions[AddressOfNameOrdinals[j]] - && (PUCHAR)Address - (PUCHAR)Base - AddressOfFunctions[AddressOfNameOrdinals[j]] <= (int)ScanMax) - return (PCHAR)Base + AddressOfNames[j]; + if (!IsAddressAccessible(&AddressOfNameOrdinals[i]) || !IsAddressAccessible(&AddressOfNames[i])) + continue; + + WORD Ordinal = AddressOfNameOrdinals[i]; + if (Ordinal >= ExportDir->NumberOfFunctions) + continue; + + DWORD FunctionRva = AddressOfFunctions[Ordinal]; + if (FunctionRva == 0) + continue; + + if ((ULONG_PTR)Address - (ULONG_PTR)Base >= FunctionRva && (ULONG_PTR)Address - (ULONG_PTR)Base - FunctionRva <= ScanMax) + { + PCHAR Name = (PCHAR)((PUCHAR)Base + AddressOfNames[i]); + if (IsAddressAccessible(Name)) + return Name; + } } } __except(EXCEPTION_EXECUTE_HANDLER) { return NULL; } - return NULL; + + return NULL; +} + +//************************************************************************************** +PCHAR GetExportDirectory(PVOID Address) +//************************************************************************************** +{ + if (!Address) + return NULL; + + __try + { + PVOID Base = GetAllocationBase(Address); + if (!Base || !IsAddressAccessible(Base)) + return NULL; + + PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Base; + if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return NULL; + + PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)Base + DosHeader->e_lfanew); + if (NtHeader->Signature != IMAGE_NT_SIGNATURE) + return NULL; + + IMAGE_DATA_DIRECTORY ExportDirEntry = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if (ExportDirEntry.VirtualAddress == 0 || ExportDirEntry.Size == 0) + return NULL; + + PIMAGE_EXPORT_DIRECTORY ExportDir = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)Base + ExportDirEntry.VirtualAddress); + if (!IsAddressAccessible(ExportDir)) + return NULL; + + if (ExportDir && ExportDir->Name) + return ((PCHAR)Base + ExportDir->Name); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return NULL; + } + + return NULL; } //************************************************************************************** @@ -2167,13 +2261,23 @@ BOOL TestPERequirements(PIMAGE_NT_HEADERS pNtHeader) } //************************************************************************************** -SIZE_T GetMinPESize(PIMAGE_NT_HEADERS pNtHeader) +SIZE_T GetMinPESize(PIMAGE_DOS_HEADER pDosHeader) //************************************************************************************** { - SIZE_T MinSize; + SIZE_T MinSize = 0; + PIMAGE_NT_HEADERS pNtHeader = NULL; __try { + if (!IsAddressAccessible(pDosHeader)) + return 0; + + if (pDosHeader->e_lfanew && (ULONG)pDosHeader->e_lfanew < PE_HEADER_LIMIT && ((ULONG)pDosHeader->e_lfanew & 3) == 0) + pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + (ULONG)pDosHeader->e_lfanew); + + if (!pNtHeader || !TestPERequirements(pNtHeader)) + return 0; + PIMAGE_SECTION_HEADER NtSection; if ((pNtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) && (pNtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)) @@ -2421,12 +2525,13 @@ DWORD GetTimeStamp(PVOID Address) int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) //************************************************************************************** { - IMAGE_DOS_HEADER DosHeader; - IMAGE_NT_HEADERS NtHeaders; + IMAGE_DOS_HEADER DosHeader; + IMAGE_NT_HEADERS NtHeaders; PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_SECTION_HEADER SectionHeaders = NULL; - DWORD bytesRead; + PBYTE EntryPointBytes = NULL; + DWORD bytesRead; int RetVal = -1; @@ -2467,18 +2572,19 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) DWORD SizeOfHeaders = pDosHeader->e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader; PIMAGE_SECTION_HEADER pSectionHeaders = (PIMAGE_SECTION_HEADER)((PBYTE)ImageBase + SizeOfHeaders); + PBYTE pEntryPointBytes = (PBYTE)ImageBase + pNtHeader->OptionalHeader.AddressOfEntryPoint; - HANDLE hFile = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) + if (hFile == INVALID_HANDLE_VALUE) { #ifdef DEBUG_COMMENTS ErrorOutput("VerifyHeaders: Error opening file %ws", Path); #endif return RetVal; - } + } - if (!ReadFile(hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL)) + if (!ReadFile(hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL)) { #ifdef DEBUG_COMMENTS ErrorOutput("VerifyHeaders: Error reading file %ws", Path); @@ -2486,7 +2592,7 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) goto end; } - if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) + if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyHeaders: IMAGE_DOS_SIGNATURE"); @@ -2496,7 +2602,7 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) SetFilePointer(hFile, DosHeader.e_lfanew, 0, FILE_BEGIN); - if (!ReadFile(hFile, &NtHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL)) + if (!ReadFile(hFile, &NtHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL)) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyHeaders: Error reading header of %ws", Path); @@ -2504,13 +2610,13 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) goto end; } - if (NtHeaders.FileHeader.NumberOfSections != pNtHeader->FileHeader.NumberOfSections) + if (NtHeaders.FileHeader.NumberOfSections != pNtHeader->FileHeader.NumberOfSections) { DebugOutput("VerifyHeaders: Number of sections mismatch: %d vs %d", NtHeaders.FileHeader.NumberOfSections, pNtHeader->FileHeader.NumberOfSections); goto end; } - if (!NtHeaders.FileHeader.NumberOfSections) + if (!NtHeaders.FileHeader.NumberOfSections) { DebugOutput("VerifyHeaders: Number of sections zero"); goto end; @@ -2518,16 +2624,16 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) SetFilePointer(hFile, SizeOfHeaders, 0, FILE_BEGIN); - SectionHeaders = calloc(NtHeaders.FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)); - if (SectionHeaders == NULL) + SectionHeaders = calloc(NtHeaders.FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)); + if (SectionHeaders == NULL) { DebugOutput("VerifyHeaders: Error allocating memory for %d section headers", NtHeaders.FileHeader.NumberOfSections); return RetVal; - } + } SIZE_T SizeOfSectionHeaders = sizeof(IMAGE_SECTION_HEADER) * NtHeaders.FileHeader.NumberOfSections; - if (!ReadFile(hFile, SectionHeaders, (DWORD)SizeOfSectionHeaders, &bytesRead, NULL)) + if (!ReadFile(hFile, SectionHeaders, (DWORD)SizeOfSectionHeaders, &bytesRead, NULL)) { DebugOutput("VerifyHeaders: Error reading section headers of %ws", Path); goto end; @@ -2535,25 +2641,58 @@ int VerifyHeaders(PVOID ImageBase, LPCWSTR Path) SIZE_T Matching = pRtlCompareMemory((PVOID)SectionHeaders, pSectionHeaders, SizeOfSectionHeaders); - if (Matching == SizeOfSectionHeaders) + if (Matching == SizeOfSectionHeaders) { #ifdef DEBUG_COMMENTS - DebugOutput("VerifyHeaders: PE header matches.\n"); + DebugOutput("VerifyHeaders: PE header matches.\n"); #endif RetVal = 1; - } + } else { - DebugOutput("VerifyHeaders: PE header does not match, 0x%x of 0x%x matching\n", Matching, SizeOfSectionHeaders); + DebugOutput("VerifyHeaders: PE header does not match, 0x%x of 0x%x matching\n", Matching, SizeOfSectionHeaders); RetVal = 0; - } + } + + SetFilePointer(hFile, NtHeaders.OptionalHeader.AddressOfEntryPoint, 0, FILE_BEGIN); + + unsigned int ChunkSize = 0x10; + EntryPointBytes = calloc(ChunkSize, sizeof(BYTE)); + if (EntryPointBytes == NULL) + { + DebugOutput("VerifyHeaders: Error allocating memory for entry point check"); + return RetVal; + } + + if (!ReadFile(hFile, EntryPointBytes, (DWORD)ChunkSize, &bytesRead, NULL)) + { + DebugOutput("VerifyHeaders: Error reading section headers of %ws", Path); + goto end; + } + + Matching = pRtlCompareMemory((PVOID)EntryPointBytes, pEntryPointBytes, ChunkSize); + + if (Matching == ChunkSize) + { +#ifdef DEBUG_COMMENTS + DebugOutput("VerifyHeaders: Entry point matches.\n"); +#endif + RetVal = 1; + } + else + { + DebugOutput("VerifyHeaders: Entry point does not match, 0x%x of 0x%x matching\n", Matching, ChunkSize); + RetVal = 0; + } end: if (SectionHeaders) free(SectionHeaders); - CloseHandle(hFile); + if (EntryPointBytes) + free(EntryPointBytes); + CloseHandle(hFile); - return RetVal; + return RetVal; } //************************************************************************************** @@ -2610,17 +2749,17 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) DWORD SizeOfHeaders = pDosHeader->e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader; PIMAGE_SECTION_HEADER pFirstSectionHeader = (PIMAGE_SECTION_HEADER)((PBYTE)ImageBase + SizeOfHeaders); - HANDLE hFile = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) + if (hFile == INVALID_HANDLE_VALUE) { #ifdef DEBUG_COMMENTS ErrorOutput("VerifyCodeSection: Error opening file %ws", Path); #endif return RetVal; - } + } - if (!ReadFile(hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL)) + if (!ReadFile(hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL)) { #ifdef DEBUG_COMMENTS ErrorOutput("VerifyCodeSection: Error reading file %ws", Path); @@ -2628,7 +2767,7 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) goto end; } - if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) + if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyCodeSection: IMAGE_DOS_SIGNATURE"); @@ -2638,7 +2777,7 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) SetFilePointer(hFile, DosHeader.e_lfanew, 0, FILE_BEGIN); - if (!ReadFile(hFile, &NtHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL)) + if (!ReadFile(hFile, &NtHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL)) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyCodeSection: Error reading header of %ws", Path); @@ -2648,8 +2787,8 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) SetFilePointer(hFile, SizeOfHeaders, 0, FILE_BEGIN); - IMAGE_SECTION_HEADER FirstSectionHeader; - if (!ReadFile(hFile, &FirstSectionHeader, sizeof(IMAGE_SECTION_HEADER), &bytesRead, NULL)) + IMAGE_SECTION_HEADER FirstSectionHeader; + if (!ReadFile(hFile, &FirstSectionHeader, sizeof(IMAGE_SECTION_HEADER), &bytesRead, NULL)) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyCodeSection: Error reading first section of %ws", Path); @@ -2657,31 +2796,31 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) goto end; } - if (!FirstSectionHeader.SizeOfRawData) + if (!FirstSectionHeader.SizeOfRawData) { DebugOutput("VerifyCodeSection: SizeOfRawData zero.\n"); goto end; } - CodeSectionBuffer = (PBYTE)calloc(FirstSectionHeader.SizeOfRawData, sizeof(BYTE)); - if (CodeSectionBuffer == NULL) + CodeSectionBuffer = (PBYTE)calloc(FirstSectionHeader.SizeOfRawData, sizeof(BYTE)); + if (CodeSectionBuffer == NULL) { #ifdef DEBUG_COMMENTS DebugOutput("VerifyCodeSection: Error allocating memory"); #endif return RetVal; - } + } SetFilePointer(hFile, FirstSectionHeader.PointerToRawData, 0, FILE_BEGIN); - DWORD BytesReadInSection; - if (!ReadFile(hFile, CodeSectionBuffer, FirstSectionHeader.SizeOfRawData, &BytesReadInSection, NULL)) + DWORD BytesReadInSection; + if (!ReadFile(hFile, CodeSectionBuffer, FirstSectionHeader.SizeOfRawData, &BytesReadInSection, NULL)) { #ifdef DEBUG_COMMENTS ErrorOutput("VerifyCodeSection: Error reading code section of %ws", Path); #endif return RetVal; - } + } Relocations = (PIMAGE_BASE_RELOCATION)((PBYTE)ImageBase + NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); RelocationSize = NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; @@ -2765,25 +2904,25 @@ int VerifyCodeSection(PVOID ImageBase, LPCWSTR Path) SIZE_T Matching = pRtlCompareMemory((PVOID)CodeSectionBuffer, pFirstSection, SizeOfSection); - if (Matching == SizeOfSection) + if (Matching == SizeOfSection) { #ifdef DEBUG_COMMENTS - DebugOutput("VerifyCodeSection: Executable code matches.\n"); + DebugOutput("VerifyCodeSection: Executable code matches.\n"); #endif RetVal = 1; - } + } else { - DebugOutput("VerifyCodeSection: Executable code does not match, 0x%x of 0x%x matching\n", Matching, SizeOfSection); + DebugOutput("VerifyCodeSection: Executable code does not match, 0x%x of 0x%x matching\n", Matching, SizeOfSection); RetVal = 0; - } + } end: if (CodeSectionBuffer) free(CodeSectionBuffer); - CloseHandle(hFile); + CloseHandle(hFile); - return RetVal; + return RetVal; } //************************************************************************************** @@ -3137,57 +3276,57 @@ int DumpImageInCurrentProcess(PVOID Address) return 0; } - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE || (*(DWORD*)((BYTE*)pDosHeader + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE)) - { - // We want to fix the PE header in the dump (for e.g. disassembly etc) + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE || (*(DWORD*)((BYTE*)pDosHeader + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE)) + { + // We want to fix the PE header in the dump (for e.g. disassembly etc) SIZE_T RegionSize = GetAccessibleSize(Address); - RegionCopy = calloc(RegionSize, sizeof(BYTE)); - - if (!RegionCopy) - { - ErrorOutput("DumpImageInCurrentProcess: Failed to allocate memory page for PE header.\n"); - return 0; - } - - __try - { - memcpy(RegionCopy, Address, RegionSize); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - DebugOutput("DumpImageInCurrentProcess: Exception occured copying PE header at 0x%p\n", Address); - free(RegionCopy); - return 0; - } - - pDosHeader = (PIMAGE_DOS_HEADER)RegionCopy; - - DebugOutput("DumpImageInCurrentProcess: Disguised PE image (bad MZ and/or PE headers) at 0x%p\n", Address); - - if (!pDosHeader->e_lfanew) - { - // In case the header until and including 'PE' has been zeroed - WORD* MachineProbe = (WORD*)&pDosHeader->e_lfanew; - while ((PUCHAR)MachineProbe < (PUCHAR)pDosHeader + (PE_HEADER_LIMIT - offsetof(IMAGE_DOS_HEADER, e_lfanew))) - { - if (*MachineProbe == IMAGE_FILE_MACHINE_I386 || *MachineProbe == IMAGE_FILE_MACHINE_AMD64) - { - if ((PUCHAR)MachineProbe > (PUCHAR)pDosHeader + 3) - pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)MachineProbe - 4); - } - MachineProbe += sizeof(WORD); - } - - if (pNtHeader) - pDosHeader->e_lfanew = (LONG)((PUCHAR)pNtHeader - (PUCHAR)pDosHeader); - } - - if (!pDosHeader->e_lfanew || pDosHeader->e_lfanew > PE_MAX_SIZE) - { - DebugOutput("DumpImageInCurrentProcess: Bad e_lfanew 0x%x\n", pDosHeader->e_lfanew); - goto end; - } + RegionCopy = calloc(RegionSize, sizeof(BYTE)); + + if (!RegionCopy) + { + ErrorOutput("DumpImageInCurrentProcess: Failed to allocate memory page for PE header.\n"); + return 0; + } + + __try + { + memcpy(RegionCopy, Address, RegionSize); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + DebugOutput("DumpImageInCurrentProcess: Exception occured copying PE header at 0x%p\n", Address); + free(RegionCopy); + return 0; + } + + pDosHeader = (PIMAGE_DOS_HEADER)RegionCopy; + + DebugOutput("DumpImageInCurrentProcess: Disguised PE image (bad MZ and/or PE headers) at 0x%p\n", Address); + + if (!pDosHeader->e_lfanew) + { + // In case the header until and including 'PE' has been zeroed + WORD* MachineProbe = (WORD*)&pDosHeader->e_lfanew; + while ((PUCHAR)MachineProbe < (PUCHAR)pDosHeader + (PE_HEADER_LIMIT - offsetof(IMAGE_DOS_HEADER, e_lfanew))) + { + if (*MachineProbe == IMAGE_FILE_MACHINE_I386 || *MachineProbe == IMAGE_FILE_MACHINE_AMD64) + { + if ((PUCHAR)MachineProbe > (PUCHAR)pDosHeader + 3) + pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)MachineProbe - 4); + } + MachineProbe += sizeof(WORD); + } + + if (pNtHeader) + pDosHeader->e_lfanew = (LONG)((PUCHAR)pNtHeader - (PUCHAR)pDosHeader); + } + + if (!pDosHeader->e_lfanew || pDosHeader->e_lfanew > PE_MAX_SIZE) + { + DebugOutput("DumpImageInCurrentProcess: Bad e_lfanew 0x%x\n", pDosHeader->e_lfanew); + goto end; + } *(WORD*)pDosHeader = IMAGE_DOS_SIGNATURE; *(DWORD*)((PUCHAR)pDosHeader + pDosHeader->e_lfanew) = IMAGE_NT_SIGNATURE; @@ -3280,8 +3419,6 @@ void DumpInterestingRegions(MEMORY_BASIC_INFORMATION MemInfo) if (lookup_get(&g_dotnet_jit, (ULONG_PTR)MemInfo.BaseAddress, 0)) { - DebugOutput("DumpInterestingRegions: Dumping .NET JIT native cache at 0x%p.\n", MemInfo.BaseAddress); - CapeMetaData->ModulePath = NULL; CapeMetaData->DumpType = 0; #ifdef _WIN64 @@ -3291,7 +3428,15 @@ void DumpInterestingRegions(MEMORY_BASIC_INFORMATION MemInfo) #endif CapeMetaData->Address = MemInfo.BaseAddress; - DumpMemory(MemInfo.BaseAddress, GetAccessibleSize(MemInfo.BaseAddress)); + if (DotNetCacheDumpCount < g_config.jit_dumps && DumpMemory(MemInfo.BaseAddress, GetAccessibleSize(MemInfo.BaseAddress))) + { + DebugOutput("DumpInterestingRegions: Dumped .NET JIT native cache at 0x%p.\n", MemInfo.BaseAddress); + DotNetCacheDumpCount++; + } + else if (g_config.jit_dumps && DotNetCacheDumpCount >= g_config.jit_dumps) + DebugOutput("DumpInterestingRegions: .NET JIT native cache dump limit hit: %d", g_config.jit_dumps); + else if (!g_config.jit_dumps) + DebugOutput("DumpInterestingRegions: Skipping .NET JIT native cache at 0x%p (jit-dumps=0)\n", MemInfo.BaseAddress); } } @@ -3565,6 +3710,32 @@ void RestoreHeaders() DebugOutput("RestoreHeaders: Restored original import table.\n"); } +static void EnableLoaderSnaps() +{ +#ifdef _WIN64 + PBYTE _fltused = (PBYTE)GetProcAddress(GetModuleHandle("ntdll"), "_fltused"); + if (_fltused == NULL) + return; + DWORD* LdrpDebugFlags = (DWORD*)(_fltused - 0x10); + if (!*LdrpDebugFlags) + *LdrpDebugFlags = 1; +#else + PBYTE LdrGetDllHandleEx = (PBYTE)GetProcAddress(GetModuleHandle("ntdll"), "LdrGetDllHandleEx"); + if (LdrGetDllHandleEx == NULL) + return; + DWORD* ShowSnaps = NULL; + for (PBYTE p = LdrGetDllHandleEx; p < LdrGetDllHandleEx + 50; p++) + { + if (p[0] == 0xf6 && p[1] == 0x05 && p[6] == 0x09) + ShowSnaps = *(DWORD**)(p+2); + } + if (!ShowSnaps) + return; + if (!*ShowSnaps) + *ShowSnaps = 1; +#endif +} + void CAPE_post_init() { if (g_config.syscall && ((OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion > 1) || OSVersion.dwMajorVersion > 6)) @@ -3592,9 +3763,6 @@ void CAPE_post_init() void CAPE_init() { - // Initialise CAPE global variables - // - //if (!g_config.standalone) CapeMetaData = (PCAPEMETADATA)calloc(sizeof(CAPEMETADATA), sizeof(BYTE)); CapeMetaData->Pid = GetCurrentProcessId(); CapeMetaData->PPid = parent_process_id(); @@ -3612,8 +3780,8 @@ void CAPE_init() ProcessDumped = FALSE; DumpCount = 0; - // Cuckoo debug output level for development (0=none, 2=max) - // g_config.debug = 2; + if (g_config.snaps) + EnableLoaderSnaps(); YaraInit(); diff --git a/CAPE/CAPE.h b/CAPE/CAPE.h index c61880fe..23927f06 100644 --- a/CAPE/CAPE.h +++ b/CAPE/CAPE.h @@ -42,11 +42,12 @@ PVOID GetAllocationBase(PVOID Address); SIZE_T GetRegionSize(PVOID Address); SIZE_T GetAllocationSize(PVOID Address); SIZE_T GetAccessibleSize(PVOID Address); +PVOID GetFunctionByName(HMODULE ModuleBase, PCHAR FunctionName); PVOID GetFunctionAddress(HMODULE ModuleBase, PCHAR FunctionName); BOOL IsAddressAccessible(PVOID Address); BOOL IsAddressExecutable(PVOID Address); BOOL TestPERequirements(PIMAGE_NT_HEADERS pNtHeader); -SIZE_T GetMinPESize(PIMAGE_NT_HEADERS pNtHeader); +SIZE_T GetMinPESize(PIMAGE_DOS_HEADER pDosHeader); double GetEntropy(PUCHAR Buffer); void SanitiseString(char *Dst, const char *Src, size_t Size); PCHAR TranslatePathFromDeviceToLetter(PCHAR DeviceFilePath); @@ -77,9 +78,10 @@ int DumpImageInCurrentProcess(PVOID ImageBase); void DumpSectionViewsForPid(DWORD Pid); BOOL DumpRange(PVOID Address, SIZE_T Size); BOOL DumpStackRegion(void); +void DumpStrings(void); BOOL ProcessDumped; -unsigned int DumpCount; +unsigned int DumpCount, DotNetCacheDumpCount; SYSTEM_INFO SystemInfo; PVOID CallingModule; diff --git a/CAPE/Debugger.c b/CAPE/Debugger.c index 7179c5a6..acfd0178 100644 --- a/CAPE/Debugger.c +++ b/CAPE/Debugger.c @@ -46,22 +46,21 @@ extern char *convert_address_to_dll_name_and_offset(ULONG_PTR addr, unsigned int extern PCHAR GetNameBySsn(unsigned int Number); extern void log_direct_syscall(const char *function, PVOID addr); extern unsigned int address_is_in_stack(DWORD Address); -extern BOOL WoW64fix(void); -extern BOOL WoW64PatchBreakpoint(unsigned int Register); -extern BOOL WoW64UnpatchBreakpoint(unsigned int Register); extern BOOL SetInitialBreakpoints(PVOID ImageBase), Trace(struct _EXCEPTION_POINTERS* ExceptionInfo), SoftwareBreakpointCallback(struct _EXCEPTION_POINTERS* ExceptionInfo); extern BOOL BreakpointCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINTERS* ExceptionInfo); +extern BOOL GuardPageCallback(struct _EXCEPTION_POINTERS* ExceptionInfo); extern void DebuggerOutput(_In_ LPCTSTR lpOutputString, ...), DoTraceOutput(PVOID Address); -extern BOOL TraceRunning, BreakpointsSet, BreakpointsHit, StopTrace, BreakOnNtContinue, SyscallBreakpointSet; +extern BOOL TraceRunning, BreakpointsSet, BreakpointsHit, StopTrace, SyscallBreakpointSet; extern PVECTORED_EXCEPTION_HANDLER SampleVectoredHandler; extern int StepOverRegister; extern int process_shutting_down; extern HANDLE DebuggerLog; +extern PVOID GuardedPages; struct ThreadBreakpoints *MainThreadBreakpointList; unsigned int TrapIndex, DepthCount; -PVOID _KiUserExceptionDispatcher; -BOOL SetSingleStepMode(PCONTEXT Context, PVOID Handler), ClearSingleStepMode(PCONTEXT Context); +PVOID KiUserExceptionDispatcher; +BOOL BreakOnNtContinue, SetSingleStepMode(PCONTEXT Context, PVOID Handler), ClearSingleStepMode(PCONTEXT Context); lookup_t SoftBPs, SyscallBPs; SOFTBP SyscallBP; @@ -104,32 +103,43 @@ HANDLE GetThreadHandle(DWORD ThreadId) } //************************************************************************************** -BOOL PatchByte(LPVOID Address, BYTE Byte) +BOOL PatchBytes(LPVOID Address, const char* HexBytes) //************************************************************************************** { - DWORD OldProtect; + if (!Address || !HexBytes || !IsAddressAccessible(Address)) + { + DebugOutput("PatchBytes: Invalid address or hex string"); + return FALSE; + } - if (!Address || !IsAddressAccessible(Address)) - return FALSE; + SIZE_T HexLen = strlen(HexBytes); + if (HexLen == 0 || HexLen % 2 != 0) + { + DebugOutput("PatchBytes: Invalid hex string length %d", HexLen); + return FALSE; + } - if (!VirtualProtect(Address, 1, PAGE_EXECUTE_READWRITE, &OldProtect)) + SIZE_T ByteCount = HexLen / 2; + DWORD OldProtect; + if (!VirtualProtect(Address, ByteCount, PAGE_EXECUTE_READWRITE, &OldProtect)) { - DebugOutput("PatchByte: Unable to change memory protection at 0x%p", Address); - return FALSE; - } + DebugOutput("PatchBytes: Failed to change memory protection at 0x%p", Address); + return FALSE; + } -#ifdef DEBUG_COMMENTS - DebugOutput("PatchByte: Changed memory protection at 0x%p", Address); -#endif + PBYTE Dest = (PBYTE)Address; + for (SIZE_T i = 0; i < HexLen; i += 2) + { + char HexByte[3] = { HexBytes[i], HexBytes[i + 1], '\0' }; + BYTE Byte = (BYTE)strtoul(HexByte, NULL, 16); + *Dest++ = Byte; + } - *(PBYTE)Address = Byte; + VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); -#ifdef DEBUG_COMMENTS - DebugOutput("PatchByte: New instruction byte at 0x%p: 0x%x", Address, *(PBYTE)Address); -#endif - VirtualProtect(Address, 1, OldProtect, &OldProtect); + DebugOutput("PatchBytes: Patched %zu bytes at 0x%p: %s", ByteCount, Address, HexBytes); - return TRUE; + return TRUE; } //************************************************************************************** @@ -496,7 +506,7 @@ BOOL SyscallBreakpointHandler(struct _EXCEPTION_POINTERS* ExceptionInfo) } #ifdef DEBUG_COMMENTS else - DebugOutput("SyscallBreakpointHandler: Calling SSN 0x%x -> 0x%p: %s\n", SSN, Function, FunctionName); + DebugOutput("SyscallBreakpointHandler: Syscall at 0x%p, SSN 0x%x -> 0x%p: %s\n", ExceptionInfo->ExceptionRecord->ExceptionAddress, SSN, Function, FunctionName); #endif log_direct_syscall(FunctionName, (PVOID)CIP); @@ -603,85 +613,31 @@ LONG WINAPI CAPEExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) if (bp == 0 && ((DWORD_PTR)pBreakpointInfo->Address != ExceptionInfo->ContextRecord->Dr0)) { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp0 address (0x%p) different to BreakpointInfo (0x%x)!\n", ExceptionInfo->ContextRecord->Dr0, pBreakpointInfo->Address); + if (pBreakpointInfo->Address) + DebugOutput("CAPEExceptionFilter: Anomaly detected! bp0 address 0x%p different to internal breakpoint 0x%p\n", ExceptionInfo->ContextRecord->Dr0, pBreakpointInfo->Address); return EXCEPTION_CONTINUE_SEARCH; } if (bp == 1 && ((DWORD_PTR)pBreakpointInfo->Address != ExceptionInfo->ContextRecord->Dr1)) { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp1 address (0x%p) different to BreakpointInfo (0x%x)!\n", ExceptionInfo->ContextRecord->Dr1, pBreakpointInfo->Address); + if (pBreakpointInfo->Address) + DebugOutput("CAPEExceptionFilter: Anomaly detected! bp1 address 0x%p different to internal breakpoint 0x%p\n", ExceptionInfo->ContextRecord->Dr1, pBreakpointInfo->Address); return EXCEPTION_CONTINUE_SEARCH; } if (bp == 2 && ((DWORD_PTR)pBreakpointInfo->Address != ExceptionInfo->ContextRecord->Dr2)) { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp2 address (0x%p) different to BreakpointInfo (0x%x)!\n", ExceptionInfo->ContextRecord->Dr2, pBreakpointInfo->Address); + if (pBreakpointInfo->Address) + DebugOutput("CAPEExceptionFilter: Anomaly detected! bp2 address 0x%p different to internal breakpoint 0x%p\n", ExceptionInfo->ContextRecord->Dr2, pBreakpointInfo->Address); return EXCEPTION_CONTINUE_SEARCH; } if (bp == 3 && ((DWORD_PTR)pBreakpointInfo->Address != ExceptionInfo->ContextRecord->Dr3)) { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp3 address (0x%p) different to BreakpointInfo (0x%x)!\n", ExceptionInfo->ContextRecord->Dr3, pBreakpointInfo->Address); + if (pBreakpointInfo->Address) + DebugOutput("CAPEExceptionFilter: Anomaly detected! bp3 address 0x%p different to internal breakpoint 0x%p\n", ExceptionInfo->ContextRecord->Dr3, pBreakpointInfo->Address); return EXCEPTION_CONTINUE_SEARCH; } -#ifndef _WIN64 - if (bp == 0 && ((DWORD_PTR)pBreakpointInfo->Type != ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE0)) - { - if (pBreakpointInfo->Type == BP_READWRITE && ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE0 == BP_WRITE && address_is_in_stack((DWORD_PTR)pBreakpointInfo->Address)) - { - DebugOutput("CAPEExceptionFilter: Reinstated BP_READWRITE on breakpoint %d (WoW64 workaround)\n", pBreakpointInfo->Register); - - ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, pBreakpointInfo->Register, pBreakpointInfo->Size, (BYTE*)pBreakpointInfo->Address, pBreakpointInfo->Type, pBreakpointInfo->HitCount, pBreakpointInfo->Callback); - } - else - { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp0 type (0x%x) different to BreakpointInfo (0x%x)!\n", ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE0, pBreakpointInfo->Type); - CheckDebugRegisters(0, ExceptionInfo->ContextRecord); - } - } - if (bp == 1 && ((DWORD)pBreakpointInfo->Type != ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE1)) - { - if (pBreakpointInfo->Type == BP_READWRITE && ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE1 == BP_WRITE && address_is_in_stack((DWORD_PTR)pBreakpointInfo->Address)) - { - DebugOutput("CAPEExceptionFilter: Reinstated BP_READWRITE on breakpoint %d (WoW64 workaround)\n", pBreakpointInfo->Register); - - ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, pBreakpointInfo->Register, pBreakpointInfo->Size, (BYTE*)pBreakpointInfo->Address, pBreakpointInfo->Type, pBreakpointInfo->HitCount, pBreakpointInfo->Callback); - } - else - { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp1 type (0x%x) different to BreakpointInfo (0x%x)!\n", ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE1, pBreakpointInfo->Type); - CheckDebugRegisters(0, ExceptionInfo->ContextRecord); - } - } - if (bp == 2 && ((DWORD)pBreakpointInfo->Type != ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE2)) - { - if (pBreakpointInfo->Type == BP_READWRITE && ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE2 == BP_WRITE && address_is_in_stack((DWORD_PTR)pBreakpointInfo->Address)) - { - DebugOutput("CAPEExceptionFilter: Reinstated BP_READWRITE on stack breakpoint %d (WoW64 workaround)\n", pBreakpointInfo->Register); - - ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, pBreakpointInfo->Register, pBreakpointInfo->Size, (BYTE*)pBreakpointInfo->Address, pBreakpointInfo->Type, pBreakpointInfo->HitCount, pBreakpointInfo->Callback); - } - else - { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp2 type (0x%x) different to BreakpointInfo (0x%x)!\n", ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE2, pBreakpointInfo->Type); - CheckDebugRegisters(0, ExceptionInfo->ContextRecord); - } - } - if (bp == 3 && ((DWORD)pBreakpointInfo->Type != ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE3)) - { - if (pBreakpointInfo->Type == BP_READWRITE && ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE3 == BP_WRITE && address_is_in_stack((DWORD_PTR)pBreakpointInfo->Address)) - { - DebugOutput("CAPEExceptionFilter: Reinstated BP_READWRITE on breakpoint %d (WoW64 workaround)\n", pBreakpointInfo->Register); - - ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, pBreakpointInfo->Register, pBreakpointInfo->Size, (BYTE*)pBreakpointInfo->Address, pBreakpointInfo->Type, pBreakpointInfo->HitCount, pBreakpointInfo->Callback); - } - else - { - DebugOutput("CAPEExceptionFilter: Anomaly detected! bp3 type (0x%x) different to BreakpointInfo (0x%x)!\n", ((PDR7)&(ExceptionInfo->ContextRecord->Dr7))->RWE3, pBreakpointInfo->Type); - CheckDebugRegisters(0, ExceptionInfo->ContextRecord); - } - } -#endif // !_WIN64 if (pBreakpointInfo->HitCount) { @@ -810,6 +766,14 @@ LONG WINAPI CAPEExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) #endif #endif } + else if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) + { +#ifdef DEBUG_COMMENTS + DebugOutput("CAPEExceptionFilter: Guard page violation at 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress); +#endif + if (GetAllocationBase(ExceptionInfo->ExceptionRecord->ExceptionAddress) == GuardedPages && GuardPageCallback(ExceptionInfo)) + return EXCEPTION_CONTINUE_EXECUTION; + } // Exceptions in capemon if ((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress >= g_our_dll_base && (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress < (g_our_dll_base + g_our_dll_size)) @@ -917,11 +881,6 @@ BOOL ContextSetDebugRegisterEx if (Type == BP_EXEC) Length = 0; -#ifndef _WIN64 - if (Type == BP_READWRITE && address_is_in_stack((DWORD_PTR)Address)) - WoW64PatchBreakpoint(Register); -#endif - if (Register == 0) { *Dr0 = (DWORD_PTR)Address; @@ -1063,11 +1022,6 @@ BOOL SetDebugRegister if (Type == BP_EXEC) Length = 0; -#ifndef _WIN64 - if (Type == BP_READWRITE && address_is_in_stack((DWORD_PTR)Address)) - WoW64PatchBreakpoint(Register); -#endif - if (Register == 0) { *Dr0 = (DWORD_PTR)Address; @@ -1264,59 +1218,8 @@ BOOL ContextClearAllBreakpoints(PCONTEXT Context) BOOL ClearAllBreakpoints() //************************************************************************************** { - CONTEXT Context; - PTHREADBREAKPOINTS ThreadBreakpoints; - unsigned int Register; - - ThreadBreakpoints = MainThreadBreakpointList; - - while (ThreadBreakpoints) - { - if (!ThreadBreakpoints->ThreadId) - { - DebugOutput("ClearAllBreakpoints: Error: no thread id for thread breakpoints 0x%x.\n", ThreadBreakpoints); - return FALSE; - } - - if (!ThreadBreakpoints->ThreadHandle) - { - DebugOutput("ClearAllBreakpoints: Error no thread handle for thread %d.\n", ThreadBreakpoints->ThreadId); - return FALSE; - } - - for (Register = 0; Register < NUMBER_OF_DEBUG_REGISTERS; Register++) - { - ThreadBreakpoints->BreakpointInfo[Register].Size = 0; - ThreadBreakpoints->BreakpointInfo[Register].Address = NULL; - ThreadBreakpoints->BreakpointInfo[Register].Type = 0; - ThreadBreakpoints->BreakpointInfo[Register].HitCount = 0; - ThreadBreakpoints->BreakpointInfo[Register].Callback = NULL; - } - - memset(&Context, 0, sizeof(CONTEXT)); - Context.ContextFlags = CONTEXT_DEBUG_REGISTERS; - - Context.Dr0 = 0; - Context.Dr1 = 0; - Context.Dr2 = 0; - Context.Dr3 = 0; - Context.Dr6 = 0; - Context.Dr7 = 0; - - if (!SetThreadContext(ThreadBreakpoints->ThreadHandle, &Context)) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ClearAllBreakpoints: Error setting thread context (thread %d).\n", ThreadBreakpoints->ThreadId); -#endif - return FALSE; - } -#ifdef DEBUG_COMMENTS - else - DebugOutput("ClearAllBreakpoints: Cleared breakpoints for thread %d (handle 0x%x).\n", ThreadBreakpoints->ThreadId, ThreadBreakpoints->ThreadHandle); -#endif - - ThreadBreakpoints = ThreadBreakpoints->NextThreadBreakpoints; - } + for (unsigned int Register = 0; Register < NUMBER_OF_DEBUG_REGISTERS; Register++) + ClearBreakpoint(Register); ClearSoftwareBreakpoints(); @@ -1372,11 +1275,6 @@ BOOL ContextClearBreakpointEx(PCONTEXT Context, PBREAKPOINTINFO pBreakpointInfo, Dr7->L3 = 0; } -#ifndef _WIN64 - if (pBreakpointInfo->Type == BP_READWRITE && address_is_in_stack((DWORD_PTR)pBreakpointInfo->Address)) - WoW64UnpatchBreakpoint(pBreakpointInfo->Register); -#endif - pBreakpointInfo->Address = 0; pBreakpointInfo->Size = 0; pBreakpointInfo->Type = 0; @@ -1864,11 +1762,6 @@ BOOL ClearDebugRegister Dr7->L3 = 0; } -#ifndef _WIN64 - if (Type == BP_READWRITE && address_is_in_stack((DWORD_PTR)Address)) - WoW64UnpatchBreakpoint(Register); -#endif - Context.ContextFlags = CONTEXT_DEBUG_REGISTERS; if (!SetThreadContext(hThread, &Context)) @@ -2736,27 +2629,22 @@ BOOL InitialiseDebugger(void) } // Store address of KiUserExceptionDispatcher - _KiUserExceptionDispatcher = GetProcAddress(GetModuleHandle("ntdll"), "KiUserExceptionDispatcher"); + HMODULE ntdll = GetModuleHandle("ntdll"); + KiUserExceptionDispatcher = GetProcAddress(ntdll, "KiUserExceptionDispatcher"); - if (_KiUserExceptionDispatcher == NULL) + if (KiUserExceptionDispatcher == NULL) { DebugOutput("InitialiseDebugger error: could not resolve ntdll::KiUserExceptionDispatcher.\n"); return FALSE; } #ifdef DEBUG_COMMENTS - else DebugOutput("InitialiseDebugger: ntdll::KiUserExceptionDispatcher = 0x%p\n", _KiUserExceptionDispatcher); + else DebugOutput("InitialiseDebugger: ntdll::KiUserExceptionDispatcher = 0x%p\n", KiUserExceptionDispatcher); #endif // Initialise global variables ChildProcessId = 0; SingleStepHandler = NULL; -#ifndef _WIN64 - // Ensure wow64 patch is installed if needed - if (!g_config.msi) - WoW64fix(); -#endif - g_config.debugger = 1; DebuggerInitialised = TRUE; @@ -2802,7 +2690,7 @@ void NtContinueHandler(PCONTEXT ThreadContext) TrackExecution(CIP); - if (BreakpointsSet) + if (g_config.debugger) { DWORD ThreadId = GetCurrentThreadId(); PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(ThreadId); diff --git a/CAPE/Debugger.h b/CAPE/Debugger.h index a0f69dfa..0b990c6f 100644 --- a/CAPE/Debugger.h +++ b/CAPE/Debugger.h @@ -181,7 +181,7 @@ BOOL InitialiseDebugger(void); BOOL ResumeFromBreakpoint(PCONTEXT Context); void OutputThreadBreakpoints(DWORD ThreadId); void DebugOutputThreadBreakpoints(); -BOOL PatchByte(LPVOID Address, BYTE Byte); +BOOL PatchBytes(LPVOID Address, const char* HexBytes); void ShowStack(DWORD_PTR StackPointer, unsigned int NumberOfRecords); diff --git a/CAPE/Injection.c b/CAPE/Injection.c index 248b0836..050ff6b0 100644 --- a/CAPE/Injection.c +++ b/CAPE/Injection.c @@ -46,6 +46,8 @@ extern char *our_process_name; extern void hook_disable(); extern void hook_enable(); +void ProcessMessage(DWORD ProcessId, DWORD ThreadId); + //************************************************************************************** PINJECTIONINFO GetInjectionInfo(DWORD ProcessId) //************************************************************************************** @@ -514,8 +516,11 @@ void DumpSectionViewsForHandle(HANDLE SectionHandle) return; } -void GetThreadContextHandler(DWORD Pid, LPCONTEXT Context) +__declspec(noinline) void GetThreadContextHandler(HANDLE ThreadHandle, LPCONTEXT Context) { + DWORD Pid = pid_from_thread_handle(ThreadHandle); + DWORD Tid = tid_from_thread_handle(ThreadHandle); + if (Context && Context->ContextFlags & CONTEXT_CONTROL) { struct InjectionInfo *CurrentInjectionInfo = GetInjectionInfo(Pid); @@ -527,13 +532,74 @@ void GetThreadContextHandler(DWORD Pid, LPCONTEXT Context) CurrentInjectionInfo->StackPointer = (LPVOID)Context->Esp; #endif } + + if (g_config.debugger && Pid == GetCurrentProcessId()) + { + PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(Tid); + if (ThreadBreakpoints) + { + if (ThreadBreakpoints->BreakpointInfo[0].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[0].Address == Context->Dr0) + { + Context->Dr0 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[1].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[1].Address == Context->Dr1) + { + Context->Dr1 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[2].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[2].Address == Context->Dr2) + { + Context->Dr2 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[3].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[3].Address == Context->Dr3) + { + Context->Dr3 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + } + } } -void SetThreadContextHandler(DWORD Pid, const CONTEXT *Context) +__declspec(noinline) void SetThreadContextHandler(HANDLE ThreadHandle, CONTEXT *Context) { if (!Context || !(Context->ContextFlags & CONTEXT_CONTROL)) return; + DWORD Pid = pid_from_thread_handle(ThreadHandle); + DWORD Tid = tid_from_thread_handle(ThreadHandle); + + if (Pid != GetCurrentProcessId()) + ProcessMessage(Pid, 0); + + if (g_config.debugger && Pid == GetCurrentProcessId()) + { + PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(Tid); + if (ThreadBreakpoints) + { + if + ( + (ThreadBreakpoints->BreakpointInfo[0].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[0].Address != Context->Dr0) || + (ThreadBreakpoints->BreakpointInfo[1].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[1].Address != Context->Dr1) || + (ThreadBreakpoints->BreakpointInfo[2].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[2].Address != Context->Dr2) || + (ThreadBreakpoints->BreakpointInfo[3].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[3].Address != Context->Dr3) + ) + { + DebugOutput("SetThreadContextHandler: Protecting breakpoints for thread %d: 0x%p, 0x%p, 0x%p, 0x%p.\n", Tid, ThreadBreakpoints->BreakpointInfo[0].Address, ThreadBreakpoints->BreakpointInfo[1].Address, ThreadBreakpoints->BreakpointInfo[2].Address, ThreadBreakpoints->BreakpointInfo[3].Address); + ContextSetThreadBreakpointsEx(Context, ThreadBreakpoints, TRUE); + } + } +#ifdef DEBUG_COMMENTS + else + DebugOutput("SetThreadContextHandler hook: No breakpoints to protect for thread %d.\n", Tid); +#endif + } + MEMORY_BASIC_INFORMATION MemoryInfo; struct InjectionInfo *CurrentInjectionInfo = GetInjectionInfo(Pid); @@ -579,6 +645,112 @@ void SetThreadContextHandler(DWORD Pid, const CONTEXT *Context) #endif } +#ifdef _WIN64 +__declspec(noinline) void Wow64GetThreadContextHandler(HANDLE ThreadHandle, PWOW64_CONTEXT Context) +{ + DWORD Pid = pid_from_thread_handle(ThreadHandle); + DWORD Tid = tid_from_thread_handle(ThreadHandle); + + if (Context && Context->ContextFlags & CONTEXT_CONTROL) + { + struct InjectionInfo *CurrentInjectionInfo = GetInjectionInfo(Pid); + if (CurrentInjectionInfo && CurrentInjectionInfo->ProcessId == Pid) + CurrentInjectionInfo->StackPointer = (PVOID)(DWORD_PTR)Context->Esp; + } + + if (g_config.debugger && Pid == GetCurrentProcessId()) + { + PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(Tid); + if (ThreadBreakpoints) + { + if (ThreadBreakpoints->BreakpointInfo[0].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[0].Address == Context->Dr0) + { + Context->Dr0 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[1].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[1].Address == Context->Dr1) + { + Context->Dr1 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[2].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[2].Address == Context->Dr2) + { + Context->Dr2 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + if (ThreadBreakpoints->BreakpointInfo[3].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[3].Address == Context->Dr3) + { + Context->Dr3 = 0; + Context->Dr6 = 0; + Context->Dr7 = 0; + } + } + } +} + +__declspec(noinline) void Wow64SetThreadContextHandler(HANDLE ThreadHandle, PWOW64_CONTEXT Context) +{ + if (!Context || !(Context->ContextFlags & CONTEXT_CONTROL)) + return; + + DWORD Pid = pid_from_thread_handle(ThreadHandle); + DWORD Tid = tid_from_thread_handle(ThreadHandle); + + if (Pid != GetCurrentProcessId()) + ProcessMessage(Pid, 0); + + if (g_config.debugger && Pid == GetCurrentProcessId()) + { + PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(Tid); + if (ThreadBreakpoints) + { + if + ( + (ThreadBreakpoints->BreakpointInfo[0].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[0].Address != Context->Dr0) || + (ThreadBreakpoints->BreakpointInfo[1].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[1].Address != Context->Dr1) || + (ThreadBreakpoints->BreakpointInfo[2].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[2].Address != Context->Dr2) || + (ThreadBreakpoints->BreakpointInfo[3].Address && (DWORD_PTR)ThreadBreakpoints->BreakpointInfo[3].Address != Context->Dr3) + ) + { + DebugOutput("Wow64SetThreadContextHandler: Protecting breakpoints for thread %d: 0x%p, 0x%p, 0x%p, 0x%p.\n", Tid, ThreadBreakpoints->BreakpointInfo[0].Address, ThreadBreakpoints->BreakpointInfo[1].Address, ThreadBreakpoints->BreakpointInfo[2].Address, ThreadBreakpoints->BreakpointInfo[3].Address); + //ContextSetThreadBreakpointsEx(Context, ThreadBreakpoints, TRUE); + } + } +#ifdef DEBUG_COMMENTS + else + DebugOutput("Wow64SetThreadContextHandler hook: No breakpoints to protect for thread %d.\n", Tid); +#endif + } + + MEMORY_BASIC_INFORMATION MemoryInfo; + struct InjectionInfo *CurrentInjectionInfo = GetInjectionInfo(Pid); + + if (!CurrentInjectionInfo) + return; + + if (VirtualQueryEx(CurrentInjectionInfo->ProcessHandle, (PVOID)(DWORD_PTR)Context->Eax, &MemoryInfo, sizeof(MemoryInfo))) + CurrentInjectionInfo->ImageBase = (DWORD_PTR)MemoryInfo.AllocationBase; + else + { + ErrorOutput("Wow64SetThreadContextHandler: Failed to query target process memory at address 0x%x", Context->Eax); + return; + } + + if (!CurrentInjectionInfo || CurrentInjectionInfo->ProcessId != Pid) + return; + + CurrentInjectionInfo->EntryPoint = Context->Eax - CurrentInjectionInfo->ImageBase; // eax holds ep on 32-bit + + if (Context->Eip == (DWORD_PTR)GetProcAddress(GetModuleHandle("ntdll"), "NtMapViewOfSection")) + DebugOutput("Wow64SetThreadContextHandler: Hollow process entry point set to NtMapViewOfSection (process %d).\n", Pid); + else + DebugOutput("Wow64SetThreadContextHandler: Hollow process entry point reset via NtSetContextThread to 0x%p (process %d).\n", CurrentInjectionInfo->EntryPoint, Pid); +} +#endif + BOOL CheckDontMonitorList(WCHAR* TargetProcess) { const wchar_t *DontMonitorList[] = @@ -937,7 +1109,7 @@ void WriteMemoryHandler(HANDLE ProcessHandle, LPVOID BaseAddress, LPCVOID Buffer if (IsDisguisedPEHeader((PVOID)Buffer)) { CurrentInjectionInfo->ImageBase = (DWORD_PTR)BaseAddress; - DebugOutput("WriteMemoryHandler: Executable binary injected into process %d (ImageBase 0x%x)\n", Pid, CurrentInjectionInfo->ImageBase); + DebugOutput("WriteMemoryHandler: Executable binary injected from 0x%p (size 0x%x) into process %d at 0x%p.\n", Buffer, NumberOfBytesWritten, Pid, BaseAddress); if (CurrentInjectionInfo->ImageDumped == FALSE) { @@ -975,11 +1147,12 @@ void WriteMemoryHandler(HANDLE ProcessHandle, LPVOID BaseAddress, LPCVOID Buffer { // Looks like a previously dumped PE image is being written a section at a time to the target process. // We don't want to dump these writes. - DebugOutput("WriteMemoryHandler: injection of section of PE image which has already been dumped.\n"); + if (NumberOfBytesWritten >= 0x1000) + DebugOutput("WriteMemoryHandler: injection of section of PE image which has already been dumped.\n"); } else { - DebugOutput("WriteMemoryHandler: shellcode at 0x%p (size 0x%x) injected into process %d.\n", Buffer, NumberOfBytesWritten, Pid); + DebugOutput("WriteMemoryHandler: shellcode at 0x%p (size 0x%x) injected into process %d at 0x%p.\n", Buffer, NumberOfBytesWritten, Pid, BaseAddress); // dump injected code/data CapeMetaData->DumpType = INJECTION_SHELLCODE; @@ -1111,11 +1284,26 @@ void TerminateHandler() } } +#define ProcessMessageLimit 0x20 +DWORD PreviousPid; +unsigned int ProcessMessageCount; + void ProcessMessage(DWORD ProcessId, DWORD ThreadId) { if (ProcessId == GetCurrentProcessId()) return; + if (ProcessId == PreviousPid) + ProcessMessageCount++; + else + { + PreviousPid = ProcessId; + ProcessMessageCount = 0; + } + + if (ProcessMessageCount >= ProcessMessageLimit) + return; + PINJECTIONINFO CurrentInjectionInfo = GetInjectionInfo(ProcessId); if (CurrentInjectionInfo && !ThreadId) @@ -1207,5 +1395,5 @@ void ProcessMessage(DWORD ProcessId, DWORD ThreadId) hook_enable(); } else - pipe("PROCESS:0:%d,%d", ProcessId, ThreadId); + pipe("PROCESS:%d,%d", ProcessId, ThreadId); } \ No newline at end of file diff --git a/CAPE/Injection.h b/CAPE/Injection.h index 54d4f650..934294a7 100644 --- a/CAPE/Injection.h +++ b/CAPE/Injection.h @@ -71,4 +71,12 @@ void ResumeProcessHandler(HANDLE ProcessHandle, DWORD Pid); void MapSectionViewHandler(HANDLE ProcessHandle, HANDLE SectionHandle, PVOID BaseAddress, SIZE_T ViewSize); void UnmapSectionViewHandler(PVOID BaseAddress); void WriteMemoryHandler(HANDLE ProcessHandle, LPVOID BaseAddress, LPCVOID Buffer, SIZE_T NumberOfBytesWritten); +void GetThreadContextHandler(HANDLE ThreadHandle, LPCONTEXT Context); +void SetThreadContextHandler(HANDLE ThreadHandle, LPCONTEXT Context); +#ifdef _WIN64 +void Wow64GetThreadContextHandler(HANDLE ThreadHandle, PWOW64_CONTEXT Context); +void Wow64SetThreadContextHandler(HANDLE ThreadHandle, PWOW64_CONTEXT Context); +#endif +void ResumeThreadHandler(DWORD Pid); +void CreateRemoteThreadHandler(DWORD Pid); void TerminateHandler(); diff --git a/CAPE/InstrCallback.c b/CAPE/InstrCallback.c index 3c4ed62e..1a3fd08f 100644 --- a/CAPE/InstrCallback.c +++ b/CAPE/InstrCallback.c @@ -170,8 +170,9 @@ VOID InstrumentationCallback(PVOID CIP, unsigned int ReturnValue) if (!TrackedRegion) DebugOutput("InstrumentationCallback: Failed to add region at 0x%p to tracked regions list (thread %d).\n", AllocationBase, GetCurrentThreadId()); else { - DebugOutput("InstrumentationCallback: Added region at 0x%p to tracked regions list (thread %d).\n", AllocationBase, GetCurrentThreadId()); - TrackedRegion->Address = (PVOID)CIP; + DebugOutput("InstrumentationCallback: Added region at 0x%p (base 0x%p) to tracked regions list (thread %d).\n", CIP, AllocationBase, GetCurrentThreadId()); + TrackedRegion->Caller = (PVOID)CIP; + TrackedRegion->Address = AllocationBase; } } if (TrackedRegion) diff --git a/CAPE/Output.c b/CAPE/Output.c index e2ca289b..db547ada 100644 --- a/CAPE/Output.c +++ b/CAPE/Output.c @@ -344,7 +344,7 @@ void StringsOutput(_In_ LPCTSTR lpOutputString, ...) Character++; } - DebuggerOutput(StringsLine); + DebuggerOutput("%.64s", StringsLine); if (strlen(StringsLine) + 1 < MAX_PATH) *Character = 0x0a; diff --git a/CAPE/Scylla/ApiReader.cpp b/CAPE/Scylla/ApiReader.cpp index e3df4163..e7c7e380 100644 --- a/CAPE/Scylla/ApiReader.cpp +++ b/CAPE/Scylla/ApiReader.cpp @@ -12,7 +12,7 @@ extern "C" void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern "C" void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); extern "C" SIZE_T GetAllocationSize(PVOID Address); -stdext::hash_multimap ApiReader::apiList; //api look up table +ApiReader::ApiList ApiReader::apiList; //api look up table std::map * ApiReader::moduleThunkList; //store found apis DWORD_PTR ApiReader::minApiAddress = (DWORD_PTR)-1; @@ -690,7 +690,7 @@ bool ApiReader::isApiAddressValid(DWORD_PTR virtualAddress) ApiInfo * ApiReader::getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect) { - stdext::hash_multimap::iterator it1, it2; + ApiList::iterator it1; size_t c = 0; size_t countDuplicates = apiList.count(virtualAddress); int countHighPriority = 0; @@ -773,7 +773,7 @@ ApiInfo * ApiReader::getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isS return (ApiInfo *) 1; } -ApiInfo * ApiReader::getScoredApi(stdext::hash_multimap::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ) +ApiInfo * ApiReader::getScoredApi(ApiList::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ) { ApiInfo * foundApi = 0; ApiInfo * foundMatchingApi = 0; @@ -1116,9 +1116,9 @@ void ApiReader::clearAll() minApiAddress = (DWORD_PTR)-1; maxApiAddress = 0; - for ( stdext::hash_map::iterator it = apiList.begin(); it != apiList.end(); ++it ) + for (const auto& pair : apiList) { - delete it->second; + delete pair.second; } apiList.clear(); diff --git a/CAPE/Scylla/ApiReader.h b/CAPE/Scylla/ApiReader.h index 8028ed5f..7702f049 100644 --- a/CAPE/Scylla/ApiReader.h +++ b/CAPE/Scylla/ApiReader.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include "ProcessAccessHelp.h" #include "Thunks.h" @@ -11,7 +11,8 @@ typedef std::pair API_Pair; class ApiReader : public ProcessAccessHelp { public: - static stdext::hash_multimap apiList; //api look up table + using ApiList = std::unordered_multimap; + static ApiList apiList; //api look up table static std::map * moduleThunkList; //store found apis @@ -70,6 +71,6 @@ class ApiReader : public ProcessAccessHelp bool isApiBlacklisted( const char * functionName ); bool isWinSxSModule( ModuleInfo * module ); - ApiInfo * getScoredApi(stdext::hash_map::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ); + ApiInfo * getScoredApi(ApiList::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ); }; diff --git a/CAPE/Scylla/ImportsHandling.h b/CAPE/Scylla/ImportsHandling.h index 3c1ee9bf..e34ef0cf 100644 --- a/CAPE/Scylla/ImportsHandling.h +++ b/CAPE/Scylla/ImportsHandling.h @@ -2,7 +2,7 @@ #include #include -#include +#include class ImportThunk; class ImportModuleThunk; diff --git a/CAPE/Scylla/PeParser.cpp b/CAPE/Scylla/PeParser.cpp index 20b9f0fb..9e656fb6 100644 --- a/CAPE/Scylla/PeParser.cpp +++ b/CAPE/Scylla/PeParser.cpp @@ -554,11 +554,6 @@ void PeParser::getDosAndNtHeader(BYTE* memory, LONG size) //DebugOutput("PeParser: getDosAndNtHeader: dosStubSize size 0x%x.\n", dosStubSize); #endif } - else if (pDosHeader->e_lfanew < sizeof(IMAGE_DOS_HEADER)) - { - //Overlapped Headers, e.g. Spack (by Bagie) - pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER); - } } if (!pDosHeader->e_lfanew) diff --git a/CAPE/ScyllaHarness.cpp b/CAPE/ScyllaHarness.cpp index 389e1ab8..c2ee8096 100644 --- a/CAPE/ScyllaHarness.cpp +++ b/CAPE/ScyllaHarness.cpp @@ -179,8 +179,8 @@ extern "C" int ScyllaDumpProcess(HANDLE hProcess, DWORD_PTR ModuleBase, DWORD_PT } else { - DebugOutput("DumpProcess: Module entry point VA is 0x%p.\n", entrypoint); entrypoint = entrypoint + (DWORD_PTR)ModuleBase; + DebugOutput("DumpProcess: Module entry point VA is 0x%p.\n", entrypoint); } if (!FixImports) @@ -706,196 +706,3 @@ extern "C" int IsPeImageRaw(DWORD_PTR Buffer) delete peFile; return 0; } - -//************************************************************************************** -extern "C" BOOL ScyllaGetSectionByName(PVOID ImageBase, char* Name, PVOID* SectionData, SIZE_T* SectionSize) -//************************************************************************************** -{ - ScyllaInit(NULL); - - PeParser *peFile = new PeParser((DWORD_PTR)ImageBase, true); - - if (!peFile->isValidPeFile()) - { - DebugOutput("ScyllaGetSectionByName: Invalid PE image.\n"); - return 0; - } - - if (!peFile->readPeSectionsFromProcess()) - { - DebugOutput("ScyllaGetSectionByName: Failed to read PE sections from image.\n"); - return 0; - } - - unsigned int NumberOfSections = peFile->getNumberOfSections(); - - for (unsigned int i = 0; i < NumberOfSections; i++) - { - if (!strcmp((char*)peFile->listPeSection[i].sectionHeader.Name, Name)) - { - *SectionData = peFile->listPeSection[i].sectionHeader.VirtualAddress + (PUCHAR)ImageBase; - *SectionSize = peFile->listPeSection[i].sectionHeader.Misc.VirtualSize; - DebugOutput("ScyllaGetSectionByName: %s section at 0x%p size 0x%x.\n", Name, *SectionData, *SectionSize); - return TRUE; - } - } - - return FALSE; -} - -//************************************************************************************** -extern "C" PCHAR ScyllaGetExportNameByScan(PVOID Address, PCHAR* ModuleName, SIZE_T ScanSize) -//************************************************************************************** -{ - ApiReader apiReader; - ApiInfo* apiInfo = NULL; - unsigned int ModuleIndex = 0; - bool dummy = 0; - - ScyllaInit(NULL); - - for (unsigned int i = 0; i < ProcessAccessHelp::ownModuleList.size(); i++) { - if ((DWORD_PTR)Address >= ProcessAccessHelp::ownModuleList[i].modBaseAddr && (DWORD_PTR)Address < (ProcessAccessHelp::ownModuleList[i].modBaseAddr + ProcessAccessHelp::ownModuleList[i].modBaseSize)) - ModuleIndex = i+1; - } - - if (!ModuleIndex) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportNameByScan: Address 0x%p not within loaded modules.\n", Address); -#endif - return NULL; - } - - PVOID ModuleBase = GetAllocationBase(Address); - - if (!ModuleBase) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportNameByScan: GetAllocationBase failed for 0x%p.\n", Address); -#endif - return NULL; - } -#ifdef DEBUG_COMMENTS - else - DebugOutput("ScyllaGetExportNameByScan: AllocationBase 0x%p for 0x%p.\n", ModuleBase, Address); -#endif - - PeParser *peFile = new PeParser((DWORD_PTR)ModuleBase, true); - - if (!peFile->isValidPeFile()) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportNameByScan: Invalid PE image at 0x%p.\n", Address); -#endif - delete peFile; - return NULL; - } - - if (!peFile->hasExportDirectory()) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportNameByScan: Module has no exports.\n"); -#endif - delete peFile; - return NULL; - } - - apiReader.clearAll(); - - // This creates moduleInfo->apiList - apiReader.parseModuleWithOwnProcess(&ProcessAccessHelp::ownModuleList[ModuleIndex-1]); - - for (unsigned int i=0; i < ScanSize; i++) - { - apiInfo = apiReader.getApiByVirtualAddress((DWORD_PTR)Address-i, &dummy); - if (apiInfo) - break; - } - - if (apiInfo) - { - if (ModuleName) - *ModuleName = apiInfo->module->fullPath; -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportNameByScan: Located function %s within module %s.\n", apiInfo->name, apiInfo->module->fullPath); -#endif - delete peFile; - return (PCHAR)apiInfo->name; - } -#ifdef DEBUG_COMMENTS - else - DebugOutput("ScyllaGetExportNameByScan: Failed to locate function among module exports.\n"); -#endif - - delete peFile; - return NULL; -} - -//************************************************************************************** -extern "C" PCHAR ScyllaGetExportNameByAddress(PVOID Address, PCHAR* ModuleName) -//************************************************************************************** -{ - return ScyllaGetExportNameByScan(Address, ModuleName, 1); -} - -//************************************************************************************** -extern "C" PCHAR ScyllaGetExportDirectory(PVOID Address) -//************************************************************************************** -{ - unsigned int ModuleIndex = 0; - - ScyllaInit(NULL); - - for (unsigned int i = 0; i < ProcessAccessHelp::ownModuleList.size(); i++) { - if ((DWORD_PTR)Address >= ProcessAccessHelp::ownModuleList[i].modBaseAddr && (DWORD_PTR)Address < (ProcessAccessHelp::ownModuleList[i].modBaseAddr + ProcessAccessHelp::ownModuleList[i].modBaseSize)) - ModuleIndex = i+1; - } - - if (!ModuleIndex) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportDirectory: Address 0x%p not within loaded modules.\n", Address); -#endif - return NULL; - } - - PVOID ModuleBase = GetAllocationBase(Address); - - if (!ModuleBase) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportDirectory: GetAllocationBase failed for 0x%p.\n", Address); -#endif - return NULL; - } - - PeParser *peFile = new PeParser((DWORD_PTR)ModuleBase, true); - - if (!peFile->isValidPeFile()) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportDirectory: Invalid PE image at 0x%p.\n", Address); -#endif - delete peFile; - return NULL; - } - - char* DirectoryName = peFile->getExportDirectory(); - - if (DirectoryName) - { -#ifdef DEBUG_COMMENTS - DebugOutput("ScyllaGetExportDirectory: Export directory name %s.\n", DirectoryName); -#endif - delete peFile; - return (PCHAR)DirectoryName; - } -#ifdef DEBUG_COMMENTS - else - DebugOutput("ScyllaGetExportDirectory: Failed to locate export directory name.\n"); -#endif - - delete peFile; - return NULL; -} diff --git a/CAPE/Solo.c b/CAPE/Solo.c new file mode 100644 index 00000000..e4270e5f --- /dev/null +++ b/CAPE/Solo.c @@ -0,0 +1,1565 @@ +/* +CAPE - Config And Payload Extraction +Copyright(C) 2019 kevoreilly@gmail.com + +This program is free software : you can redistribute it and / or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.If not, see . +*/ +//#define DEBUG_COMMENTS +#include +#include +#include "..\hooking.h" +#include +#include "..\misc.h" +#include "Debugger.h" +#include "CAPE.h" +#include "uthash.h" + +#define SOLO_PIPE "\\\\.\\pipe\\debugger_pipe" +#define BUFFER_SIZE 1024*65 +#define BYTES_PER_LINE 16 +#define MAX_LINES 256 +#define MAX_STACK_SLOTS 512 +#define SLOTS_BEFORE 255 +#define EST_LINE 80 +#define INITIAL_CAPACITY 16 +#define MAX_ENTRIES ((BUFFER_SIZE - 16) / sizeof(MBIEntry)) +#define PAGE_SIZE 4096 +#define OUTPUT_BUFFER_SIZE 2048 +#define CHUNKSIZE 16 + +// Structure for MBI entry +typedef struct +{ + uintptr_t BaseAddress; + SIZE_T RegionSize; + DWORD Protect; +} MBIEntry; + +typedef struct +{ + MBIEntry* data; + size_t size; + size_t capacity; +} MBIEntryArray; + +typedef const char* (*CmdHandler)(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data); + +typedef struct +{ + char name[3]; + CmdHandler handler; + UT_hash_handle hh; +} CommandEntry; + +static BOOL CommandMapInitialized = FALSE; +static CommandEntry* CommandMap = NULL; + +extern DWORD g_terminate_event_thread_id, g_procname_watcher_thread_id, g_unhook_detect_thread_id, g_unhook_watcher_thread_id; +extern _NtQueryInformationThread pNtQueryInformationThread; +extern int StepOverRegister; +extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); + +BOOL InteractiveBreakpointCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINTERS* ExceptionInfo); +char* InteractiveDebuggerPipe(_In_ LPCTSTR lpOutputString, ...); +char* DumpMemoryView(HANDLE hProcess, PCONTEXT ctx, ULONG_PTR RequestedAddr, int numLines); +char* GetStackWindowView(HANDLE hProcess, PCONTEXT ctx, int numSlots); +void PushBack(MBIEntryArray* array, MBIEntry entry); +void FreeArray(MBIEntryArray* array); +static BOOL SetRegister(PCONTEXT Context, char* RegString, PVOID Target); +uint32_t GetPageChecksum(HANDLE hProcess, uintptr_t Address); +BOOL InteractiveTrace(struct _EXCEPTION_POINTERS* ExceptionInfo); +void InitCommands(void); +const char* DispatchCommand(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* Command); +void RegisterCommand(const char* name, CmdHandler func); + +static CONTEXT LastContext; + +static const uint32_t crc32Table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +void RegisterCommand(const char* name, CmdHandler func) +{ + if (!name || strlen(name) != 2) return; + + CommandEntry* entry = (CommandEntry*)malloc(sizeof(CommandEntry)); + snprintf(entry->name, sizeof(entry->name), "%.2s", name); + entry->name[2] = '\0'; + entry->handler = func; + HASH_ADD_STR(CommandMap, name, entry); +} + +const char* DispatchCommand(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* Command) +{ + char DbgCmd[3] = { 0 }; + char* CmdData = NULL; + + if (Command && *Command) + { + char* Sep = strchr(Command, ':'); + if (Sep && Sep - Command >= 2) + { + DbgCmd[0] = Command[0]; + DbgCmd[1] = Command[1]; + DbgCmd[2] = '\0'; + CmdData = Sep + 1; + + if (!CmdData) + { + return InteractiveDebuggerPipe("Memory allocation failed for command data.\n"); + } + } + else + { + return InteractiveDebuggerPipe("Malformed command: %s\n", Command); + } + } + else + { + return InteractiveDebuggerPipe("Empty command received.\n"); + } + + CommandEntry* Entry = NULL; + HASH_FIND_STR(CommandMap, DbgCmd, Entry); + + if (Entry && Entry->handler) + { + return Entry->handler(ExceptionInfo, CmdData); + } + else + { + return InteractiveDebuggerPipe("Unknown command: %s\n", DbgCmd); + } +} + + +static BOOL ParseHex(const char* input, ULONG_PTR* output) +{ + int base = 16; + + if (!input || !*input) return FALSE; + + char* endp = NULL; + unsigned long long tmp = strtoull(input, &endp, base); + if (endp == input || *endp != '\0') return FALSE; + + *output = (ULONG_PTR)tmp; + return TRUE; +} + +void VerifyCommandMapInitialized(void) +{ + if (!CommandMapInitialized) + { + InitCommands(); + CommandMapInitialized = TRUE; + } +} + +char* InteractiveDebuggerPipe(_In_ LPCTSTR lpOutputString, ...) +{ + va_list args; + va_start(args, lpOutputString); + + CHAR DebuggerLine[BUFFER_SIZE]; + static CHAR DebuggerCommand[BUFFER_SIZE]; + CHAR TempBuffer[BUFFER_SIZE]; + int BytesRead = 0; + + memset(DebuggerLine, 0, sizeof(DebuggerLine)); + memset(TempBuffer, 0, sizeof(TempBuffer)); + memset(DebuggerCommand, 0, sizeof(DebuggerCommand)); + + _vsnprintf_s(TempBuffer, BUFFER_SIZE, _TRUNCATE, lpOutputString, args); + _snprintf_s(DebuggerLine, BUFFER_SIZE, _TRUNCATE, "BREAK:%s", TempBuffer); + + + char* Character = DebuggerLine; + while (*Character) + { // Restrict to ASCII range + if (*Character < 0x0a || *Character > 0x7E) + *Character = 0x3F; // '?' + Character++; + } + + int Length = (int)strlen(DebuggerLine); + + CallNamedPipe(SOLO_PIPE, DebuggerLine, Length, DebuggerCommand, BUFFER_SIZE, (unsigned long*)&BytesRead, NMPWAIT_WAIT_FOREVER); + + va_end(args); + + return DebuggerCommand; +} + +char* OutputRegisters(PCONTEXT Context) +{ + static char OutputBuffer[OUTPUT_BUFFER_SIZE]; + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + void* teb = NtCurrentTeb(); + +#ifdef _WIN64 + size_t len = _snprintf_s(OutputBuffer, sizeof(OutputBuffer), _TRUNCATE, + "RAX: %016I64X CF:%d\n" + "RBX: %016I64X PF:%d\n" + "RCX: %016I64X AF:%d\n" + "RDX: %016I64X ZF:%d\n" + "RSI: %016I64X SF:%d\n" + "RDI: %016I64X TF:%d\n" + "RSP: %016I64X IF:%d\n" + "RBP: %016I64X DF:%d\n" + "R8 : %016I64X OF:%d\n" + "R9 : %016I64X ID:%d\n" + "R10: %016I64X NT:%d\n" + "R11: %016I64X RF:%d\n" + "R12: %016I64X VM:%d\n" + "R13: %016I64X AC:%d\n" + "R14: %016I64X VIF:%d\n" + "R15: %016I64X VIP:%d\n" + "RIP: %016I64X IOPL:%d\n\n" + "GS: %p\n\n", + Context->Rax, (Context->EFlags & 0x00000001) ? 1 : 0, + Context->Rbx, (Context->EFlags & 0x00000004) ? 1 : 0, + Context->Rcx, (Context->EFlags & 0x00000010) ? 1 : 0, + Context->Rdx, (Context->EFlags & 0x00000040) ? 1 : 0, + Context->Rsi, (Context->EFlags & 0x00000080) ? 1 : 0, + Context->Rdi, (Context->EFlags & 0x00000100) ? 1 : 0, + Context->Rsp, (Context->EFlags & 0x00000200) ? 1 : 0, + Context->Rbp, (Context->EFlags & 0x00000400) ? 1 : 0, + Context->R8, (Context->EFlags & 0x00000800) ? 1 : 0, + Context->R9, (Context->EFlags & 0x00200000) ? 1 : 0, + Context->R10, (Context->EFlags & 0x00004000) ? 1 : 0, + Context->R11, (Context->EFlags & 0x00010000) ? 1 : 0, + Context->R12, (Context->EFlags & 0x00020000) ? 1 : 0, + Context->R13, (Context->EFlags & 0x00040000) ? 1 : 0, + Context->R14, (Context->EFlags & 0x00080000) ? 1 : 0, + Context->R15, (Context->EFlags & 0x00100000) ? 1 : 0, + Context->Rip, (int)((Context->EFlags >> 12) & 0x3), + teb + ); + + const M128A* xmm = &Context->Xmm0; + for (int i = 0; i < 16; ++i) + { + size_t remaining = sizeof(OutputBuffer) > len ? sizeof(OutputBuffer) - len : 0; + if (remaining) + { + len += _snprintf_s(OutputBuffer + len, remaining, _TRUNCATE, + "XMM%02d.Low : %016I64X XMM%02d.High: %016I64X\n", + i, (unsigned __int64)xmm[i].Low, i, (unsigned __int64)xmm[i].High); + } + } +#else + size_t len = _snprintf_s(OutputBuffer, sizeof(OutputBuffer), _TRUNCATE, + "EAX: %08X CF:%d\n" + "EBX: %08X AF:%d\n" + "ECX: %08X SF:%d\n" + "EDX: %08X IF:%d\n" + "ESI: %08X OF:%d\n" + "EDI: %08X NT:%d\n" + "ESP: %08X PF:%d\n" + "EBP: %08X ZF:%d\n" + "EIP: %08X TF:%d IOPL:%d\n\n" + "FS: %p\n\n", + Context->Eax, (Context->EFlags & 0x00000001) ? 1 : 0, + Context->Ebx, (Context->EFlags & 0x00000010) ? 1 : 0, + Context->Ecx, (Context->EFlags & 0x00000080) ? 1 : 0, + Context->Edx, (Context->EFlags & 0x00000200) ? 1 : 0, + Context->Esi, (Context->EFlags & 0x00000800) ? 1 : 0, + Context->Edi, (Context->EFlags & 0x00004000) ? 1 : 0, + Context->Esp, (Context->EFlags & 0x00000004) ? 1 : 0, + Context->Ebp, (Context->EFlags & 0x00000040) ? 1 : 0, + Context->Eip, (Context->EFlags & 0x00000100) ? 1 : 0, + (int)((Context->EFlags >> 12) & 0x3), + teb + ); + + const M128A* xmm = (const M128A*)Context->ExtendedRegisters; + for (int i = 0; i < 8; ++i) + { + len += _snprintf_s(OutputBuffer + len, sizeof(OutputBuffer) - len, _TRUNCATE, + "XMM%02d.Low : %016I64X XMM%02d.High: %016I64X\n", i, xmm[i].Low, i, xmm[i].High); + } +#endif + + return InteractiveDebuggerPipe("%s\n", OutputBuffer); +} + + +uint32_t ComputeCRC32(const uint8_t* buf, size_t len) +{ + uint32_t crc = 0xFFFFFFFFU; + for (size_t i = 0; i < len; ++i) + { + crc = (crc >> 8) ^ crc32Table[(crc ^ buf[i]) & 0xFF]; + } + + return crc ^ 0xFFFFFFFFU; +} + +uint32_t GetPageChecksum(HANDLE hProcess, uintptr_t Address) { + uintptr_t base = Address & ~(PAGE_SIZE - 1); + unsigned char page[PAGE_SIZE]; + SIZE_T BytesRead = 0; + if (!ReadProcessMemory(hProcess, (LPCVOID)base, page, PAGE_SIZE, &BytesRead) || BytesRead != PAGE_SIZE) return 0; + return ComputeCRC32(page, PAGE_SIZE); +} + + +char* GetStackWindowView(HANDLE hProcess, PCONTEXT ctx, int numSlots) +{ + if (numSlots > MAX_STACK_SLOTS) numSlots = MAX_STACK_SLOTS; + + int estimate = numSlots * EST_LINE + 1; + char* out = (char*)malloc(estimate); + if (!out) return NULL; + out[0] = '\0'; + + char* p = out; + int rem = estimate; + ULONG_PTR sp; + +#ifdef _WIN64 + sp = ctx->Rsp; +#else + sp = ctx->Esp; +#endif + + ULONG_PTR offset = (ULONG_PTR)SLOTS_BEFORE * sizeof(ULONG_PTR); + ULONG_PTR base = (sp > offset) ? (sp - offset) : (ULONG_PTR)0; + + + for (int i = 0; i < numSlots; i++) + { + ULONG_PTR addr = base + (ULONG_PTR)i * sizeof(ULONG_PTR); + ULONG_PTR val = 0; + SIZE_T rd = 0; + + if (!ReadProcessMemory(hProcess, (LPCVOID)addr, &val, sizeof(val), &rd) || rd != sizeof(val)) break; + +#ifdef _WIN64 + int n = _snprintf_s(p, rem, _TRUNCATE, "%016I64X,%016I64X\n", (unsigned __int64)addr, (unsigned __int64)val); +#else + int n = _snprintf_s(p, rem, _TRUNCATE, "%08X,%08X\n", (unsigned)addr, (unsigned)val); +#endif + if (n <= 0) break; + + p += n; + rem -= n; + } + + return out; +} + +char* DumpMemoryView(HANDLE hProcess, PCONTEXT ctx, ULONG_PTR RequestedAddr, int numLines) +{ + ULONG_PTR base = RequestedAddr; + unsigned char probe; + SIZE_T rd; + + if (base == 0 || ReadProcessMemory(hProcess, (LPCVOID)base, &probe, 1, &rd) != TRUE || rd != 1) + { +#ifdef _WIN64 + base = ctx->Rsp; +#else + base = ctx->Esp; +#endif + } + + int estimate = numLines * (20 + BYTES_PER_LINE * 3 + 1) + 1; + char* out = (char*)malloc(estimate); + if (!out) return NULL; + + out[0] = '\0'; + + char* p = out; + int rem = estimate; + unsigned char buf[BYTES_PER_LINE]; + + for (int line = 0; line < numLines; line++) + { + ULONG_PTR addr = base + (ULONG_PTR)line * BYTES_PER_LINE; + SIZE_T BytesRead = 0; + + if (!ReadProcessMemory(hProcess, (LPCVOID)addr, buf, BYTES_PER_LINE, &BytesRead) || BytesRead == 0) break; + +#ifdef _WIN64 + int n = _snprintf_s(p, rem, _TRUNCATE, "%016I64X,", (unsigned __int64)addr); +#else + int n = _snprintf_s(p, rem, _TRUNCATE, "%08X,", (unsigned)addr); +#endif + if (n <= 0) break; + p += n; rem -= n; + + for (int i = 0; i < BYTES_PER_LINE; i++) + { + if (i < (int)BytesRead) + n = _snprintf_s(p, rem, _TRUNCATE, "%02X ", buf[i]); + else + n = _snprintf_s(p, rem, _TRUNCATE, " "); + if (n <= 0) break; + p += n; rem -= n; + } + + if (rem < 2) break; + *p++ = '\n'; + *p = '\0'; + rem--; + } + + return out; +} + +char* RetrievePage(HANDLE hProcess, uintptr_t Address) { + uintptr_t base = Address & ~(PAGE_SIZE - 1); + unsigned char page[PAGE_SIZE]; + SIZE_T BytesRead = 0; + + if (!ReadProcessMemory(hProcess, (LPCVOID)base, page, PAGE_SIZE, &BytesRead) || BytesRead != PAGE_SIZE) return NULL; + + char* hexPage = malloc(PAGE_SIZE * 2 + 1); + if (!hexPage) return NULL; + + for (SIZE_T i = 0; i < PAGE_SIZE; ++i) + sprintf(hexPage + i * 2, "%02X", page[i]); + + hexPage[PAGE_SIZE * 2] = '\0'; + + return hexPage; +} + +// Helper functions for dynamic array +static MBIEntryArray CreateArray(void) +{ + MBIEntryArray array = + { + .data = (MBIEntry*)malloc(INITIAL_CAPACITY * sizeof(MBIEntry)), + .size = 0, + .capacity = INITIAL_CAPACITY, + }; + return array; +} + +void PushBack(MBIEntryArray* array, MBIEntry entry) +{ + if (array->size >= MAX_ENTRIES) return; + + if (array->size + 1 >= array->capacity) + { + size_t newCap = array->capacity * 2; + if (newCap > MAX_ENTRIES) newCap = MAX_ENTRIES; + + MBIEntry* p = realloc(array->data, newCap * sizeof(MBIEntry)); + if (!p) return; + + array->data = p; + array->capacity = newCap; + } + + array->data[array->size++] = entry; +} + +void FreeArray(MBIEntryArray* array) +{ + free(array->data); + array->data = NULL; + array->size = 0; + array->capacity = 0; +} + +static BOOL SetRegister(PCONTEXT Context, char* RegString, PVOID Target) +{ + if (!Context || !RegString) + return; + + __try + { +#ifdef _WIN64 + if (!stricmp(RegString, "eax")) + (PVOID)Context->Rax = Target; + else if (!stricmp(RegString, "ebx")) + (PVOID)Context->Rbx = Target; + else if (!stricmp(RegString, "ecx")) + (PVOID)Context->Rcx = Target; + else if (!stricmp(RegString, "edx")) + (PVOID)Context->Rdx = Target; + else if (!stricmp(RegString, "esi")) + (PVOID)Context->Rsi = Target; + else if (!stricmp(RegString, "edi")) + (PVOID)Context->Rdi = Target; + else if (!stricmp(RegString, "esp")) + (PVOID)Context->Rsp = Target; + else if (!stricmp(RegString, "ebp")) + (PVOID)Context->Rbp = Target; + else if (!stricmp(RegString, "eip")) + (PVOID)Context->Rip = Target; + else if (!stricmp(RegString, "rax")) + (PVOID)Context->Rax = Target; + else if (!stricmp(RegString, "rbx")) + (PVOID)Context->Rbx = Target; + else if (!stricmp(RegString, "rcx")) + (PVOID)Context->Rcx = Target; + else if (!stricmp(RegString, "rdx")) + (PVOID)Context->Rdx = Target; + else if (!stricmp(RegString, "rsi")) + (PVOID)Context->Rsi = Target; + else if (!stricmp(RegString, "rdi")) + (PVOID)Context->Rdi = Target; + else if (!stricmp(RegString, "rsp")) + (PVOID)Context->Rsp = Target; + else if (!stricmp(RegString, "rbp")) + (PVOID)Context->Rbp = Target; + else if (!stricmp(RegString, "rip")) + (PVOID)Context->Rip = Target; + else if (!strnicmp(RegString, "r8", 2)) + (PVOID)Context->R8 = Target; + else if (!strnicmp(RegString, "r9", 2)) + (PVOID)Context->R9 = Target; + else if (!strnicmp(RegString, "r10", 3)) + (PVOID)Context->R10 = Target; + else if (!strnicmp(RegString, "r11", 3)) + (PVOID)Context->R11 = Target; + else if (!strnicmp(RegString, "r12", 3)) + (PVOID)Context->R13 = Target; + else if (!strnicmp(RegString, "r13", 3)) + (PVOID)Context->R13 = Target; + else if (!strnicmp(RegString, "r14", 3)) + (PVOID)Context->R14 = Target; + else if (!strnicmp(RegString, "r15", 3)) + (PVOID)Context->R15 = Target; +#else + if (!stricmp(RegString, "eax")) + (PVOID)Context->Eax = Target; + else if (!stricmp(RegString, "ebx")) + (PVOID)Context->Ebx = Target; + else if (!stricmp(RegString, "ecx")) + (PVOID)Context->Ecx = Target; + else if (!stricmp(RegString, "edx")) + (PVOID)Context->Edx = Target; + else if (!stricmp(RegString, "esi")) + (PVOID)Context->Esi = Target; + else if (!stricmp(RegString, "edi")) + (PVOID)Context->Edi = Target; + else if (!stricmp(RegString, "esp")) + (PVOID)Context->Esp = Target; + else if (!stricmp(RegString, "ebp")) + (PVOID)Context->Ebp = Target; + else if (!stricmp(RegString, "eip")) + (PVOID)Context->Eip = Target; +#endif + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DebugOutput("Failed to set register %s.\n", RegString); + return FALSE; + } + + return TRUE; +} + +void InteractiveCommandHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, char* InitialCommand) +{ + LastContext = *ExceptionInfo->ContextRecord; + const char* Command = InitialCommand; + while (Command && *Command) + { + const char* NextCommand = DispatchCommand(ExceptionInfo, Command); + if (strcmp(NextCommand, "__DONE__") == 0) + break; + + Command = NextCommand; + Sleep(100); + } +} + +const char* HandleInstructionPage(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + ULONG_PTR RequestedAddr = 0; + SIZE_T rd = 0; + unsigned char probe = 0; + HANDLE DebuggerProcessHandle = GetCurrentProcess(); + + if (data && *data) { + if (!ParseHex(data, &RequestedAddr)) + { + return InteractiveDebuggerPipe("Failed with invalid instruction address: %s", data); + } + + if (!ReadProcessMemory(DebuggerProcessHandle, (LPCVOID)RequestedAddr, &probe, 1, &rd) || rd != 1) + { + return InteractiveDebuggerPipe("%p|UNREADABLE", RequestedAddr); + } + } + + char* InstructionPage = RetrievePage(DebuggerProcessHandle, RequestedAddr); + if (InstructionPage) + { + const char* Command = InteractiveDebuggerPipe("%p|%s", RequestedAddr, InstructionPage); + free(InstructionPage); + return Command; + } + else + { + return InteractiveDebuggerPipe("%p|NODATA", RequestedAddr); + } +} + +const char* HandlePageMap(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + MEMORY_BASIC_INFORMATION mbi; + MBIEntryArray entries = CreateArray(); + PBYTE address = NULL; + + while (VirtualQueryEx(GetCurrentProcess(), (LPCVOID)address, &mbi, sizeof(mbi)) == sizeof(mbi)) + { + MBIEntry entry = { (uintptr_t)mbi.BaseAddress, mbi.RegionSize, mbi.Protect }; + PushBack(&entries, entry); + address = (PBYTE)mbi.BaseAddress + mbi.RegionSize; + } + + const char* Command = NULL; + + if (entries.size > 0) + { + size_t cap = entries.size * 64 + 1; + char* payload = malloc(cap); + if (payload) + { + char* p = payload; + for (size_t i = 0; i < entries.size; ++i) + { + int n = sprintf(p, "0x%Ix,%Iu,0x%x", entries.data[i].BaseAddress, entries.data[i].RegionSize, entries.data[i].Protect); + p += n; + if (i + 1 < entries.size) *p++ = '|'; + } + + *p = '\0'; + Command = InteractiveDebuggerPipe("%s\n", payload); + free(payload); + } + } + else + { + return InteractiveDebuggerPipe("Failed with no memory regions found.\n"); + } + + FreeArray(&entries); + return Command; +} + +const char* HandleRegisters(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + return OutputRegisters(&LastContext); +} + +const char* HandleContinue(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + ClearSingleStepMode(ExceptionInfo->ContextRecord); + CommandEntry* entry; + CommandEntry* tmp; + + HASH_ITER(hh, CommandMap, entry, tmp) + { + HASH_DEL(CommandMap, entry); + free(entry); + } + + CommandMap = NULL; + CommandMapInitialized = FALSE; + return "__DONE__"; +} + +const char* HandleStepIn(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) { + if (SetSingleStepMode(ExceptionInfo->ContextRecord, InteractiveTrace)) + { + LastContext = *ExceptionInfo->ContextRecord; + return "__DONE__"; + } + return InteractiveDebuggerPipe("Failed to set single step mode.\n"); +} + +const char* HandleStepOver(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + PVOID cip; +#ifdef _WIN64 + cip = (PVOID)ExceptionInfo->ContextRecord->Rip; +#else + cip = (PVOID)ExceptionInfo->ContextRecord->Eip; +#endif + _DecodedInst inst; + unsigned int count = 0; + + _DecodeResult res = distorm_decode(0, (const unsigned char*)cip, 32, sizeof(void*) == 8 ? Decode64Bits : Decode32Bits, &inst, 1, &count); + + if (inst.size == 0) + { + return InteractiveDebuggerPipe("Failed could not disassemble instruction at 0x%p\n", cip); + } + + PVOID RetAddr = (PVOID)((PUCHAR)cip + inst.size); + if (ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)RetAddr, 0, 1, InteractiveBreakpointCallback)) + { + LastContext = *ExceptionInfo->ContextRecord; + ClearSingleStepMode(ExceptionInfo->ContextRecord); + RetAddr = NULL; + return "__DONE__"; + } + else + { + return InteractiveDebuggerPipe("Failed to set step-over breakpoint at 0x%p\n", RetAddr); + } +} + +const char* HandleStepOut(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ +#ifdef _WIN64 + PVOID StackPtr = (PVOID)ExceptionInfo->ContextRecord->Rsp; + SIZE_T AddressSize = sizeof(ULONG64); +#else + PVOID StackPtr = (PVOID)ExceptionInfo->ContextRecord->Esp; + SIZE_T AddressSize = sizeof(ULONG32); +#endif + + ULONG_PTR ReturnAddress = 0; + SIZE_T BytesRead = 0; + + if (!ReadProcessMemory(GetCurrentProcess(), StackPtr, &ReturnAddress, AddressSize, &BytesRead) || BytesRead != AddressSize) + { + return InteractiveDebuggerPipe("Failed to read return address\n"); + } + + if (ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)ReturnAddress, 0, 1, InteractiveBreakpointCallback)) + { + ClearSingleStepMode(ExceptionInfo->ContextRecord); + LastContext = *ExceptionInfo->ContextRecord; + return "__DONE__"; + } + + return InteractiveDebuggerPipe("Failed to set step-out breakpoint at 0x%p\n", (PVOID)ReturnAddress); +} + +const char* HandleMemoryDump(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + ULONG_PTR RequestedAddr = 0; + SIZE_T RequestedSize = 0; + SIZE_T BytesRead = 0; + unsigned char Probe = 0; + HANDLE ProcessHandle = GetCurrentProcess(); + + if (data && *data) + { + char* SizeSep = strchr(data, '|'); + if (SizeSep) *SizeSep++ = '\0'; + + if (!ParseHex(data, &RequestedAddr)) + return InteractiveDebuggerPipe("Failed with invalid dump address: %s\n", data); + + if (SizeSep && *SizeSep) + { + if (!ParseHex(SizeSep, &RequestedSize)) + return InteractiveDebuggerPipe("Failed with invalid dump size: %s\n", SizeSep); + + if (RequestedSize > OUTPUT_BUFFER_SIZE) + return InteractiveDebuggerPipe("Failed: requested size %zu exceeds max buffer size %d.\n", RequestedSize, OUTPUT_BUFFER_SIZE); + + unsigned char* Buffer = (unsigned char*)malloc(RequestedSize); + if (!Buffer) + return InteractiveDebuggerPipe("Failed with memory allocation.\n"); + + if (!ReadProcessMemory(ProcessHandle, (LPCVOID)RequestedAddr, Buffer, RequestedSize, &BytesRead) || BytesRead != RequestedSize) + { + free(Buffer); + return InteractiveDebuggerPipe("0x%p|Failed with unreadable memory\n", (PVOID)RequestedAddr); + } + + char* HexOutput = (char*)malloc(RequestedSize * 2 + 1); + if (!HexOutput) + { + free(Buffer); + return InteractiveDebuggerPipe("0x%p|Failed with hex formatting.\n", (PVOID)RequestedAddr); + } + + for (SIZE_T I = 0; I < RequestedSize; ++I) + { + sprintf(HexOutput + I * 2, "%02X", Buffer[I]); + } + + const char* Command = InteractiveDebuggerPipe("0x%p|%s\n", (PVOID)RequestedAddr, HexOutput); + free(HexOutput); + free(Buffer); + return Command; + } + + if (!ReadProcessMemory(ProcessHandle, (LPCVOID)RequestedAddr, &Probe, 1, &BytesRead) || BytesRead != 1) + { + return InteractiveDebuggerPipe("0x%p|Failed with unreadable dump address\n", (PVOID)RequestedAddr); + } + } + + char* MemDump = DumpMemoryView(ProcessHandle, ExceptionInfo->ContextRecord, RequestedAddr, MAX_LINES); + if (MemDump) + { + const char* Command = InteractiveDebuggerPipe("0x%p|%s\n", (PVOID)RequestedAddr, MemDump); + free(MemDump); + return Command; + } + + return InteractiveDebuggerPipe("Failed to dump memory.\n"); +} + + +const char* HandleStackView(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + HANDLE ProcessHandle = GetCurrentProcess(); + char* StackDump = GetStackWindowView(ProcessHandle, ExceptionInfo->ContextRecord, MAX_STACK_SLOTS); + + if (StackDump) + { + const char* Command = InteractiveDebuggerPipe("%s\n", StackDump); + free(StackDump); + return Command; + } + + return InteractiveDebuggerPipe("Failed to dump stack view.\n"); +} + +const char* HandleListBreakpoints(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + CONTEXT* ctx = ExceptionInfo->ContextRecord; + int len = 0; + const int MaxPerLine = 32; + const int MaxEntries = 4; + + + size_t BufSize = MaxEntries * MaxPerLine + 1; + char* Output = (char*)malloc(BufSize); + if (!Output) + { + return InteractiveDebuggerPipe("Failed to allocate memory.\n"); + } + + ULONG_PTR dr[4] = + { + (ULONG_PTR)ctx->Dr0, + (ULONG_PTR)ctx->Dr1, + (ULONG_PTR)ctx->Dr2, + (ULONG_PTR)ctx->Dr3 + }; + + for (int i = 0; i < 4; ++i) + { + if (dr[i]) + { + len += sprintf(Output + len, "%d,%p|", i, (PVOID)dr[i]); + } + } + + if (len == 0) + { + free(Output); + return InteractiveDebuggerPipe("No hardware breakpoints set.\n"); + } + + if (Output[len - 1] == '|') Output[len - 1] = '\0'; + + const char* Command = InteractiveDebuggerPipe("%s\n", Output); + free(Output); + return Command; +} + +const char* HandleFlagMod(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data || !*data) + { + return InteractiveDebuggerPipe("Failed missing flag directive.\n"); + } + + if (_stricmp(data, "ClearZeroFlag") == 0) ClearZeroFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "SetZeroFlag") == 0) SetZeroFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "FlipZeroFlag") == 0) FlipZeroFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "ClearSignFlag") == 0) ClearSignFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "SetSignFlag") == 0) SetSignFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "FlipSignFlag") == 0) FlipSignFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "ClearCarryFlag") == 0) ClearCarryFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "SetCarryFlag") == 0) SetCarryFlag(ExceptionInfo->ContextRecord); + else if (_stricmp(data, "FlipCarryFlag") == 0) FlipCarryFlag(ExceptionInfo->ContextRecord); + else + { + return InteractiveDebuggerPipe("Failed invalid flag modifier: %s\n", data); + } + + return OutputRegisters(ExceptionInfo->ContextRecord); +} + +const char* HandleRunUntil(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + ULONG_PTR Addr = 0; + if (!data || !ParseHex(data, &Addr)) + { + return InteractiveDebuggerPipe("Failed with invalid run-until address: %s\n", data); + } + + if (ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)Addr, 0, 1, InteractiveBreakpointCallback)) + { + ClearSingleStepMode(ExceptionInfo->ContextRecord); + LastContext = *ExceptionInfo->ContextRecord; + return "__DONE__"; + } + + return InteractiveDebuggerPipe("Failed to set run-until breakpoint at 0x%p\n", (PVOID)Addr); +} + +const char* HandleListThreads(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + DWORD ExcludeTids[] = + { + g_terminate_event_thread_id, + g_procname_watcher_thread_id, + g_unhook_detect_thread_id, + g_unhook_watcher_thread_id + }; + + size_t ExcludeTidCount = sizeof(ExcludeTids) / sizeof(ExcludeTids[0]); + + typedef enum _THREADINFOCLASS + { + ThreadBasicInformation, + ThreadQuerySetWin32StartAddress = 9 + } THREADINFOCLASS; + + typedef NTSTATUS(NTAPI* PFN_NTQIT)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); + + PFN_NTQIT pNtQIT = (PFN_NTQIT)pNtQueryInformationThread; + + DWORD pid = GetCurrentProcessId(); + DWORD CurrentTid = GetCurrentThreadId(); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + THREADENTRY32 te = { sizeof(te) }; + NTSTATUS status; + + if (hSnap == INVALID_HANDLE_VALUE) + { + return InteractiveDebuggerPipe("Failed to snapshot threads.\n"); + } + + size_t capacity = OUTPUT_BUFFER_SIZE; + char* Output = (char*)malloc(capacity); + + if (!Output) + { + CloseHandle(hSnap); + return InteractiveDebuggerPipe("Faile memory allocation failed.\n"); + } + + int len = 0; + + for (BOOL ok = Thread32First(hSnap, &te); ok; ok = Thread32Next(hSnap, &te)) + { + if (te.th32OwnerProcessID != pid) continue; + + BOOL skip = FALSE; + for (size_t i = 0; i < ExcludeTidCount; ++i) + { + if (te.th32ThreadID == ExcludeTids[i]) + { + skip = TRUE; + break; + } + } + + if (skip) continue; + + const char* mark = (te.th32ThreadID == CurrentTid) ? "+" : "-"; + + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION, FALSE, te.th32ThreadID); + PVOID StartAddr = NULL; + + if (hThread && pNtQIT) + { + status = pNtQIT(hThread, ThreadQuerySetWin32StartAddress, &StartAddr, sizeof(StartAddr), NULL); + if (!NT_SUCCESS(status)) StartAddr = NULL; + } + + int needed = snprintf(NULL, 0, "%s|%lu|%p\n", mark, te.th32ThreadID, StartAddr); + if ((size_t)(len + needed + 1) >= capacity) + { + capacity *= 2; + char* NewOutput = (char*)realloc(Output, capacity); + if (!NewOutput) + { + free(Output); + CloseHandle(hSnap); + return InteractiveDebuggerPipe("Buffer reallocation failed.\n"); + } + Output = NewOutput; + } + + len += sprintf(Output + len, "%s|%lu|%p\n", mark, te.th32ThreadID, StartAddr); + } + + CloseHandle(hSnap); + + if (len > 0) + { + const char* Command = InteractiveDebuggerPipe("%s\n", Output); + free(Output); + return Command; + } + else + { + free(Output); + return InteractiveDebuggerPipe("Failed no modules found.\n"); + } +} + +const char* HandleListModules(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + DWORD Pid = GetCurrentProcessId(); + HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, Pid); + if (Snapshot == INVALID_HANDLE_VALUE) + return InteractiveDebuggerPipe("Failed to snapshot modules.\n"); + + MODULEENTRY32 Me = { 0 }; + Me.dwSize = sizeof(Me); + + size_t capacity = OUTPUT_BUFFER_SIZE * 2; + char* Output = (char*)malloc(capacity); + if (!Output) + { + CloseHandle(Snapshot); + return InteractiveDebuggerPipe("Memory allocation failed.\n"); + } + + int Len = 0; + + if (Module32First(Snapshot, &Me)) + { + do + { + int needed = snprintf( + NULL, + 0, + "%p,%08X,%s,%s|", + Me.modBaseAddr, + Me.modBaseSize, + Me.szModule, + Me.szExePath + ); + + if ((size_t)(Len + needed + 1) >= capacity) + { + capacity *= 2; + char* NewOutput = (char*)realloc(Output, capacity); + if (!NewOutput) + { + free(Output); + CloseHandle(Snapshot); + return InteractiveDebuggerPipe("Buffer reallocation failed.\n"); + } + Output = NewOutput; + } + + Len += sprintf( + Output + Len, + "%p,%08X,%s,%s|", + Me.modBaseAddr, + Me.modBaseSize, + Me.szModule, + Me.szExePath + ); + + } while (Module32Next(Snapshot, &Me)); + } + + CloseHandle(Snapshot); + + if (Len > 0) + { + if (Output[Len - 1] == '|') Output[Len - 1] = '\0'; + const char* Command = InteractiveDebuggerPipe("%s\n", Output); + free(Output); + return Command; + } + else + { + free(Output); + return InteractiveDebuggerPipe("Failed no modules found.\n"); + } +} + + +const char* HandleSetBreakpoint(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data && !*data) + return InteractiveDebuggerPipe("Invalid breakpoint command: missing input.\n"); + + char Input[MAX_PATH]; + strncpy_s(Input, sizeof(Input), data, _TRUNCATE); + char* Sep = strchr(Input, '|'); + if (!Sep) + return InteractiveDebuggerPipe("Failed with malformed breakpoint command (missing '|')\n"); + + *Sep = '\0'; + const char* RegStr = Input; + const char* AddrStr = Sep + 1; + int Register = -1; + + if (strcmp(RegStr, "next") != 0) + { + char* Endp = NULL; + long r = strtol(RegStr, &Endp, 0); + if (Endp == RegStr || *Endp != '\0' || r < 0 || r > 3) + return InteractiveDebuggerPipe("Failed with invalid register: %s\n", RegStr); + + Register = (int)r; + } + + char* Endp = NULL; + unsigned long long addr = strtoull(AddrStr, &Endp, 0); + if (Endp == AddrStr || *Endp != '\0') + return InteractiveDebuggerPipe("Failed with invalid breakpoint address: %s\n", AddrStr); + + ULONG_PTR BpAddress = (ULONG_PTR)addr; + if (Register == -1) + { + if (ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)BpAddress, BP_EXEC, 0, InteractiveBreakpointCallback)) + { + return InteractiveDebuggerPipe("Breakpoint %d set at 0x%p\n", StepOverRegister, (PVOID)BpAddress); + } + else + { + return InteractiveDebuggerPipe("Failed to set breakpoint %d at 0x%p\n", StepOverRegister, (PVOID)BpAddress); + } + } + else + { + if (ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, Register, 0, (BYTE*)BpAddress, BP_EXEC, 0, InteractiveBreakpointCallback)) + { + return InteractiveDebuggerPipe("Breakpoint %d set at 0x%p\n", Register, (PVOID)BpAddress); + } + else + { + return InteractiveDebuggerPipe("Failed to set breakpoint %d at 0x%p\n", Register, (PVOID)BpAddress); + } + } +} + +const char* HandleDeleteBreakpoint(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data || !*data) + return InteractiveDebuggerPipe("Failed to delete breakpoint: missing input.\n"); + + char* Endptr = NULL; + unsigned long idx = strtoul(data, &Endptr, 0); + if (Endptr == data || *Endptr != '\0' || idx > 3) + return InteractiveDebuggerPipe("Failed to delete breakpoint: invalid breakpoint index '%s'\n", data); + + int index = (int)idx; + CONTEXT* ctx = ExceptionInfo->ContextRecord; + ULONG_PTR BpAddress = 0; + switch (index) { + case 0: BpAddress = ctx->Dr0; break; + case 1: BpAddress = ctx->Dr1; break; + case 2: BpAddress = ctx->Dr2; break; + case 3: BpAddress = ctx->Dr3; break; + } + + if (!ContextClearBreakpoint(ExceptionInfo->ContextRecord, index)) + return InteractiveDebuggerPipe("Failed to clear breakpoint index %d\n", index); + + return InteractiveDebuggerPipe("Breakpoint %d cleared at 0x%p\n", index, (PVOID)BpAddress); +} + +const char* HandleExports(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data || !*data) + return InteractiveDebuggerPipe("Failed to get exports: missing input.\n"); + + char input[MAX_PATH]; + strncpy_s(input, sizeof(input), data, _TRUNCATE); + char* sep = strchr(input, '|'); + + if (!sep) + { + return InteractiveDebuggerPipe("Failed to get exports: malformed command.\n"); + } + + *sep = '\0'; + char OriginalModName[MAX_MODULE_NAME32]; + strcpy_s(OriginalModName, sizeof(OriginalModName), input); + int page = atoi(sep + 1); + char modulePath[MAX_PATH] = { 0 }; + BOOL found = FALSE; + + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, GetCurrentProcessId()); + MODULEENTRY32 me = { .dwSize = sizeof(me) }; + + if (Module32First(hSnap, &me)) + { + do + { + char modNameLower[MAX_MODULE_NAME32]; + strcpy_s(modNameLower, sizeof(modNameLower), OriginalModName); + _strlwr_s(modNameLower, sizeof(modNameLower)); + + char snapNameLower[MAX_MODULE_NAME32]; + strcpy_s(snapNameLower, sizeof(snapNameLower), me.szModule); + _strlwr_s(snapNameLower, sizeof(snapNameLower)); + + if (strcmp(snapNameLower, modNameLower) == 0) + { + strcpy_s(modulePath, sizeof(modulePath), me.szExePath); + found = TRUE; + break; + } + } while (Module32Next(hSnap, &me)); + } + CloseHandle(hSnap); + + if (!found) + { + return InteractiveDebuggerPipe("Failed to get exports: module not found.\n"); + } + + HMODULE hMod = LoadLibraryExA(modulePath, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (!hMod) + { + return InteractiveDebuggerPipe("Failed to get exports: module not loaded.\n"); + } + + const int MaxSymbols = BUFFER_SIZE; + const size_t MaxEntryLen = 256; + char** entries = (char**)malloc(MaxSymbols * sizeof(char*)); + int EntryCount = 0; + + BYTE* base = (BYTE*)hMod; + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base; + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew); + + DWORD rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + DWORD RvaSize = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + + if (rva && RvaSize && !IsBadReadPtr(base + rva, RvaSize)) + { + PIMAGE_EXPORT_DIRECTORY ed = (PIMAGE_EXPORT_DIRECTORY)(base + rva); + DWORD* FuncRvas = (DWORD*)(base + ed->AddressOfFunctions); + DWORD* NameRvas = (DWORD*)(base + ed->AddressOfNames); + WORD* ordinals = (WORD*)(base + ed->AddressOfNameOrdinals); + + for (DWORD i = 0; i < ed->NumberOfFunctions && EntryCount < MaxSymbols; ++i) + { + DWORD FuncRva = FuncRvas[i]; + if (!FuncRva) continue; + + const char* name = NULL; + for (DWORD j = 0; j < ed->NumberOfNames; ++j) + { + if (ordinals[j] == i) + { + DWORD NameOffset = NameRvas[j]; + if (NameOffset > nt->OptionalHeader.SizeOfImage) break; + const char* TestName = (const char*)(base + NameOffset); + if (!IsBadReadPtr(TestName, 1)) + name = TestName; + break; + } + } + + char fallback[32]; + if (!name) + { + snprintf(fallback, sizeof(fallback), "ord_%04u", ed->Base + i); + name = fallback; + } + + uintptr_t AbsAddr = (uintptr_t)me.modBaseAddr + FuncRva; + + char* entry = (char*)malloc(MaxEntryLen); + if (!entry) continue; + + int written = snprintf(entry, MaxEntryLen, "%llu,%s", (unsigned long long)AbsAddr, name); + if (written <= 0 || written >= MaxEntryLen) + { + free(entry); + continue; + } + + entries[EntryCount++] = entry; + } + } + + FreeLibrary(hMod); + + const int SymbolsPerPage = 512; + int start = page * SymbolsPerPage; + int end = start + SymbolsPerPage; + int CurLen = 0; + + char* PageBuffer = (char*)malloc(BUFFER_SIZE); + if (!PageBuffer) + { + for (int i = 0; i < EntryCount; i++) free(entries[i]); + free(entries); + return InteractiveDebuggerPipe("Failed memory allocation failed.\n"); + } + + for (int i = start; i < EntryCount && i < end; i++) + { + int len = (int)strlen(entries[i]); + if (CurLen + len + 1 >= BUFFER_SIZE - 64) + break; + + if (CurLen > 0) PageBuffer[CurLen++] = '|'; + memcpy(PageBuffer + CurLen, entries[i], len); + CurLen += len; + } + + BOOL HasMore = (end < EntryCount); + strcat_s(PageBuffer, BUFFER_SIZE, HasMore ? "||MORE" : "||END"); + + for (int i = 0; i < EntryCount; i++) free(entries[i]); + free(entries); + + char* FinalBuffer = (char*)malloc(BUFFER_SIZE); + if (!FinalBuffer) + { + free(PageBuffer); + return InteractiveDebuggerPipe("Memory allocation failed.\n"); + } + + _snprintf_s(FinalBuffer, BUFFER_SIZE, _TRUNCATE, "%s||%s", OriginalModName, PageBuffer); + + free(PageBuffer); + if (strlen(FinalBuffer) >= BUFFER_SIZE - 1) + DebugOutput("Failed: Exports final buffer overflow for %s\n", OriginalModName); + + const char* Command = InteractiveDebuggerPipe("%s\n", FinalBuffer); + free(FinalBuffer); + return Command; +} + +const char* HandleSetRegister(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data || !*data) + return InteractiveDebuggerPipe("Failed with invalid input data.\n"); + + char InputBuffer[MAX_PATH]; + strncpy_s(InputBuffer, sizeof(InputBuffer), data, _TRUNCATE); + + char* Sep = strchr(InputBuffer, '|'); + if (!Sep) + return InteractiveDebuggerPipe("Failed with malformed command, expected REGISTER|VALUE.\n"); + + *Sep = '\0'; + char RegName[16]; + char ValueStr[32]; + + strcpy_s(RegName, sizeof(RegName), InputBuffer); + strcpy_s(ValueStr, sizeof(ValueStr), Sep + 1); + + ULONG_PTR NewValue = 0; + if (!ParseHex(ValueStr, &NewValue)) + return InteractiveDebuggerPipe("Failed with invalid value for register %s: %s.", RegName, ValueStr); + + LastContext = *ExceptionInfo->ContextRecord; + BOOL result = SetRegister(&LastContext, RegName, NewValue); + if (!result) + return InteractiveDebuggerPipe("Failed to set register %s to %llu.\n", RegName, NewValue); + + return OutputRegisters(&LastContext); +} + +const char* HandleNopInstruction(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + ULONG_PTR Address = 0; + if (!data || !ParseHex(data, &Address)) + return InteractiveDebuggerPipe("Invalid instruction address: %s\n", data); + + _DecodeType DecodeType; + _DecodeResult Result; + _OffsetType Offset = 0; + _DecodedInst DecodedInstruction; + unsigned int DecodedInstructionsCount = 0; + DWORD OldProtect; + +#ifdef _WIN64 + DecodeType = Decode64Bits; +#else + DecodeType = Decode32Bits; +#endif + + if (Address) + Result = distorm_decode(Offset, (const unsigned char*)Address, CHUNKSIZE, DecodeType, &DecodedInstruction, 1, &DecodedInstructionsCount); + + if (!DecodedInstruction.size) + return InteractiveDebuggerPipe("Failed Nop instruction at 0x%p\n", Address); + + VirtualProtect(Address, DecodedInstruction.size, PAGE_EXECUTE_READWRITE, &OldProtect); + for (unsigned int i = 0; i < DecodedInstruction.size; i++) + *((BYTE*)Address + i) = 0x90; + + VirtualProtect(Address, DecodedInstruction.size, OldProtect, &OldProtect); + return InteractiveDebuggerPipe("%p|%u\n", Address, DecodedInstruction.size); +} + +const char* HandlePatchBytes(struct _EXCEPTION_POINTERS* ExceptionInfo, const char* data) +{ + if (!data && !*data) + return InteractiveDebuggerPipe("Failed with invalid input data.\n"); + + char* Sep = strchr(data, '|'); + if (!Sep) + return InteractiveDebuggerPipe("Failed with bad data format\n"); + + *Sep++ = '\0'; + ULONG_PTR Address = 0; + + if (!ParseHex(data, &Address)) + return InteractiveDebuggerPipe("Failed with invalid patch address: %s\n", data); + + size_t HexLen = strlen(Sep); + size_t MaxBytes = HexLen / 2 + 1; + BYTE* Patch = (BYTE*)malloc(MaxBytes); + if (!Patch) + return InteractiveDebuggerPipe("Failed with memory allocation.\n"); + + size_t ByteCount = 0; + char* HexPtr = Sep; + while (HexPtr) + { + while (*HexPtr && isspace((unsigned char)*HexPtr)) HexPtr++; + + if (!HexPtr) break; + + if (!isxdigit((unsigned char)*HexPtr) || !isxdigit((unsigned char)*(HexPtr + 1))) break; + + char HexByte[3] = { *HexPtr, *(HexPtr + 1), '\0' }; + Patch[ByteCount++] = (BYTE)strtol(HexByte, NULL, 16); + HexPtr += 2; + } + + if (ByteCount == 0) + { + free(Patch); + return InteractiveDebuggerPipe("Failed with no bytes to patch.\n"); + } + + if (!Address || !IsAddressAccessible(Address)) + return InteractiveDebuggerPipe("Failed address is not accessible: 0x%p", Address); + + DWORD OldProtect; + if (!VirtualProtect(Address, ByteCount, PAGE_EXECUTE_READWRITE, &OldProtect)) + return InteractiveDebuggerPipe("Failed unable to change memory protection at 0x%p", Address); + + BYTE* dest = (BYTE*)Address; + BYTE* src = Patch; + for (size_t i = 0; i < ByteCount; ++i, ++dest, ++src) + { + *dest = *src; + } + + VirtualProtect(Address, ByteCount, OldProtect, &OldProtect); + free(Patch); + + return InteractiveDebuggerPipe("Patched %p|%u\n", Address, ByteCount); +} + +void InitCommands(void) +{ + RegisterCommand("IN", HandleInstructionPage); + RegisterCommand("PM", HandlePageMap); + RegisterCommand("RG", HandleRegisters); + RegisterCommand("CT", HandleContinue); + RegisterCommand("SI", HandleStepIn); + RegisterCommand("SO", HandleStepOver); + RegisterCommand("OU", HandleStepOut); + RegisterCommand("SK", HandleStackView); + RegisterCommand("MD", HandleMemoryDump); + RegisterCommand("LB", HandleListBreakpoints); + RegisterCommand("FL", HandleFlagMod); + RegisterCommand("RU", HandleRunUntil); + RegisterCommand("TH", HandleListThreads); + RegisterCommand("LM", HandleListModules); + RegisterCommand("BP", HandleSetBreakpoint); + RegisterCommand("DB", HandleDeleteBreakpoint); + RegisterCommand("EX", HandleExports); + RegisterCommand("SR", HandleSetRegister); + RegisterCommand("NI", HandleNopInstruction); + RegisterCommand("PB", HandlePatchBytes); +} + +BOOL InteractiveTrace(struct _EXCEPTION_POINTERS* ExceptionInfo) +{ + return InteractiveBreakpointCallback(NULL, ExceptionInfo); +} + +BOOL InteractiveBreakpointCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINTERS* ExceptionInfo) +{ + PVOID CIP; + char* Command = NULL; + +#ifdef _WIN64 + CIP = (PVOID)ExceptionInfo->ContextRecord->Rip; +#else + CIP = (PVOID)ExceptionInfo->ContextRecord->Eip; +#endif + + if (pBreakpointInfo) + { + if (StepOverRegister != -1 && pBreakpointInfo->Register == StepOverRegister) + { + StepOverRegister = -1; + } + Command = InteractiveDebuggerPipe("Breakpoint %i => 0x%p\n", pBreakpointInfo->Register, CIP); + } + else + { + Command = InteractiveDebuggerPipe("Single step at 0x%p\n", CIP); + } + + VerifyCommandMapInitialized(); + InteractiveCommandHandler(ExceptionInfo, Command); + + return TRUE; +} \ No newline at end of file diff --git a/CAPE/Trace.c b/CAPE/Trace.c index 566359cf..4d03131f 100644 --- a/CAPE/Trace.c +++ b/CAPE/Trace.c @@ -45,18 +45,18 @@ extern char *convert_address_to_dll_name_and_offset(ULONG_PTR addr, unsigned int extern BOOL is_in_dll_range(ULONG_PTR addr); extern DWORD_PTR FileOffsetToVA(DWORD_PTR ModuleBase, DWORD_PTR dwOffset); extern DWORD_PTR GetEntryPointVA(DWORD_PTR ModuleBase); -extern PCHAR ScyllaGetExportNameByAddress(PVOID Address, PCHAR* ModuleName); extern ULONG_PTR g_our_dll_base; extern BOOL inside_hook(LPVOID Address); extern void loq(int index, const char *category, const char *name, int is_success, ULONG_PTR return_value, const char *fmt, ...); extern void log_flush(); -extern PVOID _KiUserExceptionDispatcher; +extern PVOID KiUserExceptionDispatcher; extern lookup_t SoftBPs, SyscallBPs; +extern BOOL InteractiveBreakpointCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINTERS *ExceptionInfo); char *ModuleName, *PreviousModuleName; -PVOID ModuleBase, DumpAddress, ReturnAddress, BreakOnReturnAddress, BreakOnNtContinueCallback, PreviousJumps[4]; -BOOL BreakpointsSet, BreakpointsHit, FilterTrace, StopTrace, ReDisassemble, SyscallBreakpointSet, TraceRunning, BreakOnNtContinue; +PVOID ModuleBase, DumpAddress, ReturnAddress, BreakOnReturnAddress, PreviousJumps[4], GuardedPages; +BOOL BreakpointsSet, BreakpointsHit, FilterTrace, StopTrace, ReDisassemble, SyscallBreakpointSet, TraceRunning; unsigned int Correction, StepCount, StepLimit, TraceDepthLimit, BreakOnReturnRegister, JumpCount; char Action0[MAX_PATH], Action1[MAX_PATH], Action2[MAX_PATH], Action3[MAX_PATH]; char *Instruction0, *Instruction1, *Instruction2, *Instruction3, *procname0; @@ -207,7 +207,7 @@ SIZE_T StrTestW(PWCHAR StrCandidate, PWCHAR OutputBuffer, SIZE_T BufferSize) void StringCheck(PVOID PossibleString) { - PCHAR ExportName = ScyllaGetExportNameByAddress(PossibleString, NULL); + PCHAR ExportName = GetExportNameByAddress(PossibleString); if (ExportName) { DebuggerOutput(" %s ", ExportName); @@ -273,8 +273,6 @@ void DoOutputString(PVOID PossibleString) StringsOutput("%.256ws...", (PWCHAR)OutputBufferW); else if (Size > 1) StringsOutput("%.256ws", (PWCHAR)OutputBufferW); - else - StringsOutput(""); } } @@ -588,6 +586,36 @@ PVOID GetTarget(PCONTEXT Context, _DecodedInst DecodedInstruction) return Target; } +void OutputFlagChanges(DWORD OldFlags, DWORD NewFlags) +{ + if (OldFlags == NewFlags) return; + + char FlagChanges[32] = {0}; + int pos = 0; + + #define CHECK_FLAG(flag, ch) \ + if ((OldFlags & flag) != (NewFlags & flag)) \ + FlagChanges[pos++] = (NewFlags & flag) ? toupper(ch) : tolower(ch) + + CHECK_FLAG(FL_CF, 'c'); // Carry: C/c + CHECK_FLAG(FL_PF, 'p'); // Parity: P/p + CHECK_FLAG(FL_AF, 'a'); // Aux: A/a + CHECK_FLAG(FL_ZF, 'z'); // Zero: Z/z + CHECK_FLAG(FL_SF, 's'); // Sign: S/s + CHECK_FLAG(FL_TF, 't'); // Trap: T/t + CHECK_FLAG(FL_IF, 'i'); // Interrupt: I/i + CHECK_FLAG(FL_DF, 'd'); // Direction: D/d + CHECK_FLAG(FL_OF, 'o'); // Overflow: O/o + + #undef CHECK_FLAG + + if (pos > 0) + { + FlagChanges[pos] = '\0'; + DebuggerOutput(" %s", FlagChanges); + } +} + OutputRegisterChanges(PCONTEXT Context) { #ifdef _WIN64 @@ -771,6 +799,9 @@ OutputRegisterChanges(PCONTEXT Context) } } #endif + + OutputFlagChanges(LastContext.EFlags, Context->EFlags); + if (g_config.trace_times) { FILETIME CurrentTime; @@ -1475,6 +1506,27 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De DebuggerOutput("ActionDispatcher: Nothing to print at 0x%p\n", Target); } #endif + else if (!stricmp(Action, "Guard")) + { + DWORD OldProtect; + MEMORY_BASIC_INFORMATION MemInfo; + if (!SystemInfo.dwPageSize) + GetSystemInfo(&SystemInfo); + SIZE_T GuardedSize = SystemInfo.dwPageSize; + GuardedPages = GetAllocationBase(CIP); + if (VirtualQuery(GuardedPages, &MemInfo, GuardedSize)) + { + if (VirtualProtect(GuardedPages, GuardedSize, MemInfo.Protect | PAGE_GUARD, &OldProtect)) + DebuggerOutput("ActionDispatcher: Instated guard page(s) at 0x%p size 0x%x", GuardedPages, GuardedSize); + else + { + ErrorOutput("ActionDispatcher: Failed to instate guard page(s) at 0x%p size 0x%x", GuardedPages, GuardedSize); + DebuggerOutput("ActionDispatcher: Failed to instate guard page(s) at 0x%p size 0x%x", GuardedPages, GuardedSize); + } + } + else + DebuggerOutput("Problem calling VirtualQuery on 0x%p", GuardedPages); + } else if (!stricmp(Action, "DumpImage")) { #ifdef _WIN64 @@ -1507,6 +1559,18 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De else DebuggerOutput("ActionDispatcher: Failed to dump breaking module at 0x%p.\n", CallingModule); } + else if (!strnicmp(Action, "DumpImage:", 10)) + { + if (Target) + { + if (DumpImageInCurrentProcess(Target)) + DebuggerOutput("ActionDispatcher: Dumped image at 0x%p.\n", Target); + else + DebuggerOutput("ActionDispatcher: Failed to dump image at 0x%p.\n", Target); + } + else + DebuggerOutput("ActionDispatcher: Failed to dump image - target missing or invalid.\n"); + } else if (!strnicmp(Action, "DumpSize:", 9)) { if (Target) @@ -1601,6 +1665,10 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De DumpAddress = 0; DumpSize = 0; } + else if (!stricmp(Action, "DumpStrings")) + { + DumpStrings(); + } else if (!stricmp(Action, "Step2OEP")) { SetSingleStepMode(ExceptionInfo->ContextRecord, ProcessOEP); @@ -1619,8 +1687,6 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De PVOID Base = GetAllocationBase(CIP); if (Target) { - if ((PUCHAR)Target < (PUCHAR)Base) - Target = (PVOID)((PUCHAR)Target + (DWORD_PTR)Base); ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, 0, 0, Target, BP_EXEC, 0, BreakpointCallback); DebuggerOutput("SetBp0: Breakpoint 0 set to 0x%p.\n", Target); } @@ -1632,8 +1698,6 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De PVOID Base = GetAllocationBase(CIP); if (Target) { - if ((PUCHAR)Target < (PUCHAR)Base) - Target = (PVOID)((PUCHAR)Target + (DWORD_PTR)Base); ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, 1, 0, Target, BP_EXEC, 0, BreakpointCallback); DebuggerOutput("SetBp1: Breakpoint 1 set to 0x%p.\n", Target); } @@ -1645,8 +1709,6 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De PVOID Base = GetAllocationBase(CIP); if (Target) { - if ((PUCHAR)Target < (PUCHAR)Base) - Target = (PVOID)((PUCHAR)Target + (DWORD_PTR)Base); ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, 2, 0, Target, BP_EXEC, 0, BreakpointCallback); DebuggerOutput("SetBp2: Breakpoint 2 set to 0x%p.\n", Target); } @@ -1658,8 +1720,6 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De PVOID Base = GetAllocationBase(CIP); if (Target) { - if ((PUCHAR)Target < (PUCHAR)Base) - Target = (PVOID)((PUCHAR)Target + (DWORD_PTR)Base); ContextSetThreadBreakpoint(ExceptionInfo->ContextRecord, 3, 0, Target, BP_EXEC, 0, BreakpointCallback); DebuggerOutput("SetBp3: Breakpoint 3 set to 0x%p.\n", Target); } @@ -1728,6 +1788,11 @@ void ActionDispatcher(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst De DebuggerOutput("ActionDispatcher: Terminating process.\n"); New_NtTerminateProcess(NULL, 1); } + else if (!stricmp(Action, "hook-watch")) + { + g_config.hook_watch = 1; + DebuggerOutput("ActionDispatcher: Hook watch enabled.\n"); + } else if (stricmp(Action, "custom")) DebuggerOutput("ActionDispatcher: Unrecognised action: (%s)\n", Action); @@ -1773,7 +1838,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst } else if (CallTarget && !ExportName) { - ExportName = ScyllaGetExportNameByAddress(CallTarget, NULL); + ExportName = GetExportNameByAddress(CallTarget); if (!ExportName && (!FilterTrace || g_config.trace_all)) TraceOutputFuncAddress(CIP, DecodedInstruction, CallTarget); @@ -1790,7 +1855,9 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst if (!FilterTrace || g_config.trace_all) TraceOutputFuncName(CIP, DecodedInstruction, ExportName); - *StepOver = TRUE; + if (is_in_dll_range((ULONG_PTR)CallTarget) && !g_config.trace_all) + *StepOver = TRUE; + *ForceStepOver = DoStepOver(ExportName); for (unsigned int i = 0; i < ARRAYSIZE(g_config.trace_into_api); i++) @@ -1816,7 +1883,8 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst if (ReturnAddress && (unsigned int)abs(TraceDepthCount) >= TraceDepthLimit) *StepOver = TRUE; - else + + if (*StepOver == FALSE && *ForceStepOver == FALSE) TraceDepthCount++; } else if (!strcmp(DecodedInstruction.mnemonic.p, "JMP")) @@ -1837,7 +1905,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst } else { - ExportName = ScyllaGetExportNameByAddress(JumpTarget, NULL); + ExportName = GetExportNameByAddress(JumpTarget); if (ExportName) { @@ -1882,12 +1950,12 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst } else if (!strnicmp(DecodedInstruction.mnemonic.p, "j", 1)) { - int JumpOffset = (int)*((PCHAR)CIP + 1); - PVOID JumpTarget = (PVOID)((PUCHAR)CIP + DecodedInstruction.size + JumpOffset); + PVOID JumpTarget = GetTarget(ExceptionInfo->ContextRecord, DecodedInstruction); if (!FilterTrace || g_config.trace_all) TraceOutputFuncAddress(CIP, DecodedInstruction, JumpTarget); if (g_config.loopskip) { + int JumpOffset = (int)((PUCHAR)JumpTarget - (PUCHAR)CIP); for (unsigned int i = 0; i < 4; i++) { if (JumpOffset < 0 && PreviousJumps[i] == CIP) @@ -1991,7 +2059,7 @@ void InstructionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo, _DecodedInst #endif SkipInstruction(ExceptionInfo->ContextRecord); if (lookup_get(&SoftBPs, (ULONG_PTR)CIP, 0)) - PatchByte(CIP, 0xCC); + PatchBytes(CIP, "CC"); } else if (!strcmp(DecodedInstruction.mnemonic.p, "RET")) { @@ -2101,21 +2169,10 @@ BOOL Trace(struct _EXCEPTION_POINTERS* ExceptionInfo) DebuggerOutput("\n"); } - PCHAR FunctionName = NULL; - __try - { - FunctionName = ScyllaGetExportNameByAddress(CIP, NULL); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - DebugOutput("Trace: Error dereferencing instruction pointer 0x%p.\n", CIP); - FunctionName = NULL; - } ModuleName = convert_address_to_dll_name_and_offset((ULONG_PTR)CIP, &DllRVA); - if (ModuleName) { - if (CIP == (PVOID)((PCHAR)_KiUserExceptionDispatcher+1)) + if (CIP == (PVOID)((PCHAR)KiUserExceptionDispatcher+1)) { DebugOutput("Trace: Stepping out of KiUserExceptionDispatcher\n"); ForceStepOver = TRUE; @@ -2126,6 +2183,7 @@ BOOL Trace(struct _EXCEPTION_POINTERS* ExceptionInfo) PVOID ImageBase = (PVOID)((PUCHAR)CIP - DllRVA); if (FilterTrace) DebuggerOutput("\n"); + PCHAR FunctionName = GetExportNameByAddress(CIP); if (FunctionName) { DebuggerOutput("Break at 0x%p in %s::%s (RVA 0x%x, thread %d, Stack 0x%p-0x%p, ImageBase 0x%p)\n", CIP, ModuleName, FunctionName, DllRVA, GetCurrentThreadId(), get_stack_bottom(), get_stack_top(), ImageBase); @@ -2333,19 +2391,10 @@ BOOL BreakpointCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINT { if (!PreviousModuleName || strncmp(ModuleName, PreviousModuleName, strlen(ModuleName))) { - PCHAR FunctionName; - PVOID ImageBase = (PVOID)((PUCHAR)CIP - DllRVA); - - __try - { - FunctionName = ScyllaGetExportNameByAddress(CIP, NULL); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - DebugOutput("BreakpointCallback: Error dereferencing instruction pointer 0x%p.\n", CIP); - } if (FilterTrace) DebuggerOutput("\n"); + PVOID ImageBase = (PVOID)((PUCHAR)CIP - DllRVA); + PCHAR FunctionName = GetExportNameByAddress(CIP); if (FunctionName) { DebuggerOutput("Break at 0x%p in %s::%s (RVA 0x%x, thread %d, Stack 0x%p-0x%p, ImageBase 0x%p)\n", CIP, ModuleName, FunctionName, DllRVA, GetCurrentThreadId(), get_stack_bottom(), get_stack_top(), ImageBase); @@ -2574,9 +2623,108 @@ BOOL SoftwareBreakpointCallback(struct _EXCEPTION_POINTERS* ExceptionInfo) return TRUE; } +BOOL GuardPageCallback(struct _EXCEPTION_POINTERS* ExceptionInfo) +{ + PVOID CIP; + _DecodeType DecodeType; + _DecodeResult Result; + _OffsetType Offset = 0; + _DecodedInst DecodedInstruction; + unsigned int DecodedInstructionsCount = 0; + BOOL StepOver = FALSE, ForceStepOver = FALSE; + + StopTrace = FALSE; + + BreakpointsHit = TRUE; + + DebuggerOutput("Guard break hit by instruction at 0x%p (thread %d)", ExceptionInfo->ExceptionRecord->ExceptionAddress, GetCurrentThreadId()); + +#ifdef _WIN64 + CIP = (PVOID)ExceptionInfo->ContextRecord->Rip; + DecodeType = Decode64Bits; +#else + CIP = (PVOID)ExceptionInfo->ContextRecord->Eip; + DecodeType = Decode32Bits; +#endif + + if (g_config.log_breakpoints) + { + // Log breakpoint to behavior log + memset(DebuggerBuffer, 0, MAX_PATH*sizeof(CHAR)); + _snprintf_s(DebuggerBuffer, MAX_PATH, _TRUNCATE, "Breakpoint hit at 0x%p", CIP); + log_breakpoint("Debugger", DebuggerBuffer); + } + + FilterTrace = FALSE; + + if (InsideMonitor(NULL, CIP) && g_config.trace_all == 1) + FilterTrace = TRUE; + + if (inside_hook(CIP) && !g_config.trace_all) + FilterTrace = TRUE; + + if (is_in_dll_range((ULONG_PTR)CIP) && !g_config.trace_all) + FilterTrace = TRUE; + + //StepCount++; + + OutputRegisterChanges(ExceptionInfo->ContextRecord); + + if (!FilterTrace) + DebuggerOutput("\n"); + + if (CIP) + Result = distorm_decode(Offset, (const unsigned char*)CIP, CHUNKSIZE, DecodeType, &DecodedInstruction, 1, &DecodedInstructionsCount); + + // Instruction handling + InstructionHandler(ExceptionInfo, DecodedInstruction, &StepOver, &ForceStepOver); + + LastContext = *ExceptionInfo->ContextRecord; + + if (!StepLimit || StepCount > StepLimit || StopTrace) + { + if (StepLimit) + DebuggerOutput("\nGuardPageCallback: Single-step limit reached (%d), releasing.\n", StepLimit); + memset(&LastContext, 0, sizeof(CONTEXT)); + StopTrace = TRUE; + StepCount = 0; + TraceRunning = FALSE; + ReturnAddress = NULL; + } + else if (ReturnAddress && (StepOver == TRUE && !g_config.trace_all) || ForceStepOver) + { + if (ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)ReturnAddress, BP_EXEC, 1, BreakpointCallback)) + { +#ifdef DEBUG_COMMENTS + DebugOutput("GuardPageCallback: Set breakpoint on return address 0x%p\n", ReturnAddress); +#endif + ReturnAddress = NULL; + } + else + DebugOutput("GuardPageCallback: Failed to set breakpoint on return address 0x%p\n", ReturnAddress); + } + else + { + DWORD OldProtect; + MEMORY_BASIC_INFORMATION MemInfo; + GuardedPages = GetAllocationBase(CIP); + SIZE_T AllocationSize = GetAllocationSize(GuardedPages); + if (VirtualQuery(GuardedPages, &MemInfo, AllocationSize)) + { + if (VirtualProtect(GuardedPages, AllocationSize, MemInfo.Protect | PAGE_GUARD, &OldProtect)) + DebuggerOutput("GuardPageCallback: Instated guard page(s) at 0x%p size 0x%x", GuardedPages, AllocationSize); + else + DebuggerOutput("GuardPageCallback: Failed to instate guard page(s) at 0x%p size 0x%x", GuardedPages, AllocationSize); + } + } + + return TRUE; +} + BOOL BreakOnReturnCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_POINTERS* ExceptionInfo) { PVOID CIP; + int Register; unsigned int DllRVA; BreakpointsHit = TRUE; @@ -2593,22 +2741,14 @@ BOOL BreakOnReturnCallback(PBREAKPOINTINFO pBreakpointInfo, struct _EXCEPTION_PO if (ModuleName) { - PCHAR FunctionName; - __try - { - FunctionName = ScyllaGetExportNameByAddress(CIP, NULL); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - DebugOutput("BreakOnReturnCallback: Error dereferencing instruction pointer 0x%p.\n", CIP); - } + PCHAR FunctionName = GetExportNameByAddress(CIP); if (FunctionName) DebuggerOutput("\nBreak at 0x%p in %s::%s (RVA 0x%x, thread %d), releasing until return address 0x%p\n", CIP, ModuleName, FunctionName, DllRVA, GetCurrentThreadId(), ReturnAddress); else DebuggerOutput("\nBreak at 0x%p in %s (RVA 0x%x, thread %d), releasing until return address 0x%p\n", CIP, ModuleName, DllRVA, GetCurrentThreadId(), ReturnAddress); } - if (!ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &StepOverRegister, 0, (BYTE*)ReturnAddress, BP_EXEC, 1, BreakpointCallback)) + if (!ContextSetNextAvailableBreakpoint(ExceptionInfo->ContextRecord, &Register, 0, (BYTE*)ReturnAddress, BP_EXEC, 1, BreakpointCallback)) DebugOutput("BreakOnReturnCallback: Failed to set breakpoint on return address at 0x%p.\n", ReturnAddress); ReturnAddress = NULL; @@ -2692,7 +2832,12 @@ BOOL SetConfigBP(PVOID ImageBase, DWORD Register, PVOID Address) HitCount = g_config.hc3; } - if (SetBreakpoint(Register, 0, BreakpointVA, Type, HitCount, BreakpointCallback)) + PVOID Callback = BreakpointCallback; + + if (g_config.idbg) + Callback = InteractiveBreakpointCallback; + + if (SetBreakpoint(Register, 0, BreakpointVA, Type, HitCount, Callback)) { DebugOutput("SetInitialBreakpoints: Breakpoint %d set on address 0x%p (RVA 0x%x, type %d, hit count %d, thread %d)\n", Register, BreakpointVA, Address, Type, HitCount, GetCurrentThreadId()); BreakpointsSet = TRUE; @@ -2748,7 +2893,12 @@ BOOL SetInitialBreakpoints(PVOID ImageBase) // break-on-entrypoint uses bp0 Register = EntryPointRegister - 1; - if (SetBreakpoint(Register, 0, (BYTE*)EntryPoint, BP_EXEC, 1, BreakpointCallback)) + PVOID Callback = BreakpointCallback; + + if (g_config.idbg) + Callback = InteractiveBreakpointCallback; + + if (SetBreakpoint(Register, 0, (BYTE*)EntryPoint, BP_EXEC, 1, Callback)) { DebuggerOutput("Breakpoint %d set on entry point at 0x%p\n", Register, EntryPoint); BreakpointsSet = TRUE; @@ -2884,7 +3034,10 @@ BOOL SetInitialBreakpoints(PVOID ImageBase) { BreakpointVA = (PVOID)((DWORD_PTR)ImageBase + (DWORD_PTR)g_config.bp[i]); if (SetSoftwareBreakpoint(&SoftBPs, BreakpointVA)) + { DebugOutput("SetInitialBreakpoints: Software breakpoint %d set at 0x%p", i, BreakpointVA); + BreakpointsSet = TRUE; + } g_config.bp[i] = 0; } } diff --git a/CAPE/YaraHarness.c b/CAPE/YaraHarness.c index f89442df..e5d2935f 100644 --- a/CAPE/YaraHarness.c +++ b/CAPE/YaraHarness.c @@ -23,6 +23,7 @@ along with this program.If not, see . #include "Debugger.h" #include "YaraHarness.h" #include "..\config.h" +#include "..\alloc.h" extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); @@ -43,17 +44,53 @@ extern PVOID LdrpInvertedFunctionTableSRWLock; static char NewLine[MAX_PATH]; char InternalYara[] = + "rule capemon" + "{strings:$hash = {d3 b9 46 1d 9a 14 bc 44 a1 61 c3 47 6a 0e 35 90 00 2c 28 81 dc a0 36 dc 2c 92 0c 7c b6 84 39 59}" + "condition:all of them}" +#ifdef _WIN64 + "rule vDbgPrintExWithPrefixInternal" + "{strings:$10_0_26100_3476 = {48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 54 41 56 41 57 48 83 EC 40 44 8A BC 24}" + "$function = {40 55 53 56 41 54 41 55 41 56 41 57 48 81 EC 20 01 00 00 48 8D 6C 24 20 48 8B 05 [4] 48 33 C5 48 89 85 ?? 00 00 00 4C 89 4D ?? 44 89 45 ?? 44 8B E2 89 55 ?? 48 8B D1 48 89 4D ?? 48 8B 85}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule FindFixAndRun" + "{strings:$function = {48 89 5C 24 10 48 89 74 24 18 57 41 54 41 55 41 56 41 57 48 81 EC [80-110] 85 C0 0F 88 [2] 00 00 49 8B 4? 70 66 83 (78|79) 02 3A 0F 84 [2] 00 00 48 8D 54 24 20 49 8B CE E8}" + "condition:uint16(0) == 0x5a4d and any of them}" +#else + "rule vDbgPrintExWithPrefixInternal" + "{strings:$function = {68 90 00 00 00 68 [4] E8 [4] 89 95 [4] 89 8D [4] 8B 45 ?? 89 85 [4] 8B 45 ?? 89 85 [4] 64 A1 18 00 00 00 89 45 ?? 83 FA FF 0F 84}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule FindFixAndRun" + "{strings:$function = {8B FF 55 8B EC 6A FE 68 [4] 68 [4] 64 A1 00 00 00 00 50 81 EC [90-96] 00 0F 84 [4] B8 E7 7F 00 00 50 8D 8D [4] E8 [4] 85 C0 0F 88 [4] 8B 4? 38 66 83 7? 02 3A 0F 84}" + "condition:uint16(0) == 0x5a4d and any of them}" +#endif "rule RtlInsertInvertedFunctionTable" - "{strings:$10_0_19041_662 = {48 8D 0D [4] E8 [4] [7] 8B 44 24 ?? 44 8B CB 4C 8B 44 24 ?? 48 8B D7 89 44 24 ?? E8}" + "{strings:$10_0_26100_3476 = {48 8D 0D [4] 49 F7 D8 48 8B F8 1B DB 23 5C 24 ?? E8 [4] 33 C9 E8}" + "$10_0_19041_662 = {48 8D 0D [4] E8 [4] [7] 8B 44 24 ?? 44 8B CB 4C 8B 44 24 ?? 48 8B D7 89 44 24 ?? E8}" "$10_0_18362_1350 = {48 8D 0D [4] 33 D2 85 C0 48 0F 48 DA E8 [4] 33 C9 E8 [4] 8B 44 24 ?? 44 8B CF 4C 8B C3 89 44 24 ?? 48 8B D6 E8}" "$10_0_10240_16384 = {48 8D 0D [4] 48 8B E8 E8 [4] 33 C9 E8 [4] 8B 15 [4] 3B 15 [4] 0F 84}" "condition:uint16(0) == 0x5a4d and any of them}" "rule LdrpCallInitRoutine" - "{strings:$function = {55 8B EC 56 57 53 8B F4 [0-2] FF 75 14 FF 75 10 FF 75 0C FF 55 08 8B E6 5B 5F 5E 5D C2 10 00}" + "{strings:$10_0_26100_3476 = {40 53 56 57 41 54 41 55 [65-85] 84 03 FE 7F}" + "$function = {55 8B EC 56 57 53 8B F4 [0-2] FF 75 14 FF 75 10 FF 75 0C FF 55 08 8B E6 5B 5F 5E 5D C2 10 00}" "condition:uint16(0) == 0x5a4d and any of them}" - "rule capemon" - "{strings:$hash = {d3 b9 46 1d 9a 14 bc 44 a1 61 c3 47 6a 0e 35 90 00 2c 28 81 dc a0 36 dc 2c 92 0c 7c b6 84 39 59}" - "condition:all of them}"; + "rule WMI_ExecQuery" + "{strings:$function = {4C 8B DC 56 57 41 54 41 56 41 57 48 83 EC 60 49 C7 43 B8 FE FF FF FF 49 89 5B 10 49 89 6B 18 45 8B E1 4D 8B F0 4C 8B F9 48 8B 41 08 48 83 78 20 00 0F 84}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule WMI_ExecMethod" + "{strings:$function = {48 8B C4 56 57 41 54 41 56 41 57 48 83 EC 70 48 C7 40 B8 FE FF FF FF 48 89 58 10 48 89 68 18 45 8B E1 4D 8B F0 48 8B EA 4C 8B F9 48 8B 41 08 48 83 78 20 00 75 0A B8 08 01 01 80 E9}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule WMI_ExecQueryAsync" + "{strings:$function = {4C 8B DC 56 57 41 54 41 56 41 57 48 83 EC 60 49 C7 43 B8 FE FF FF FF 49 89 5B 10 49 89 6B 18 45 8B E1 4D 8B F0 48 8B E9 48 8B 41 08 48 83 78 20 00 0F 84 [4] 49 83 63 08 00 4D 8D 43 08 E8}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule WMI_ExecMethodAsync" + "{strings:$function = {48 8B C4 57 41 54 41 55 41 56 41 57 48 83 EC 60 48 C7 40 B8 FE FF FF FF 48 89 58 10 48 89 68 18 48 89 70 20 45 8B E9 4D 8B F8 4C 8B F2 48 8B E9 48 8B 41 08 48 83 78 20 00 75 0A B8 08 01 01 80 E9}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule WMI_GetObject" + "{strings:$function = {4C 8B DC 56 57 41 54 41 56 41 57 48 83 EC 50 49 C7 43 ?? FE FF FF FF 49 89 5B 10 49 89 6B 18 4D 8B F9 45 8B E0 48 8B EA 4C 8B F1 48 8B 41 08 48 83 78 20 00 0F 84 12 AB 02 00 49 83 63 08 00 4D 8D 43 08 E8}" + "condition:uint16(0) == 0x5a4d and any of them}" + "rule WMI_GetObjectAsync" + "{strings:$function = {48 8B C4 56 57 41 54 41 56 41 57 48 83 EC 40 48 C7 40 C8 FE FF FF FF 48 89 58 10 48 89 68 18 4D 8B F9 45 8B E0 48 8B EA 48 8B F1 48 8B 41 08 48 83 78 20 00 75 0A B8 08 01 01 80 E9}" + "condition:uint16(0) == 0x5a4d and any of them}"; void ScannerError(int Error) { @@ -90,21 +127,25 @@ void ScannerError(int Error) } } -BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) +void ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match, void* user_data) { char *Value, *Key, *p, *q, *r, c = 0; - unsigned int ValueLength; - int delta=0; + ULONG_PTR delta=0; + SIZE_T ValueLength = 0; + if (!Line || !Identifier) - return FALSE; + return; + p = strchr(Line, '$'); if (!p) - return FALSE; + return; + p = strchr(Line, '='); if (!p) - return FALSE; + return; + r = strchr(p, ':'); - if (r) + if (r && *(r + 1) == '$') Value = r + 1; else Value = p + 1; @@ -127,12 +168,12 @@ BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) } if (q) { - ValueLength = (unsigned int)(DWORD_PTR)(q-(DWORD_PTR)Value); + ValueLength = (SIZE_T)(DWORD_PTR)(q-(DWORD_PTR)Value); if (*(q-1) == '*') ValueLength--; } else - ValueLength = (unsigned int)strlen(Value); + ValueLength = (SIZE_T)strlen(Value); if (*(Value+ValueLength-1) == '*') { @@ -140,28 +181,44 @@ BOOL ParseOptionLine(char* Line, char* Identifier, YR_MATCH* Match) delta += Match->match_length - 1; } - if (strncmp(Value, Identifier, ValueLength)) - return FALSE; + SIZE_T IdentifierLength = strlen(Identifier); + if (strncmp(Value, Identifier, IdentifierLength < ValueLength ? IdentifierLength : ValueLength)) + return; Key = Line; - if (r) { + if (r && *(r + 1) == '$') + { c = *r; *r = 0; } - else { + else + { c = *p; *p = 0; } + + if (_strnicmp(Line, "bp", 2) && strncmp(Line, "br", 2) && strncmp(Line, "sysbp", 5)) + delta += (ULONG_PTR)user_data; + memset(NewLine, 0, sizeof(NewLine)); - sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); if (r) + sprintf(NewLine, "%s%c0x%p%s\0", Key, c, (PUCHAR)Match->offset+delta, r); + else + sprintf(NewLine, "%s%c0x%p\0", Key, c, (PUCHAR)Match->offset+delta); + + if (r && *(r + 1) == '$') *r = c; else *p = c; - p = strchr(NewLine, '$'); - if (p) - return FALSE; - return TRUE; + +#ifdef DEBUG_COMMENTS + DebugOutput("ParseOptionLine: %s", NewLine); +#endif + + if (!strchr(NewLine, '$')) + parse_config_line(NewLine); + + return; } int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) @@ -181,6 +238,9 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void YR_META* Meta; YR_RULE* Rule = (YR_RULE*)message_data; + if (!strcmp(Rule->identifier, "capemon")) + return CALLBACK_CONTINUE; + DebugOutput("YaraScan hit: %s\n", Rule->identifier); // Process cape_options metadata @@ -195,29 +255,28 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void char *p = strchr(OptionLine, ','); if (p) *p = 0; - yr_rule_strings_foreach(Rule, String) + if (!strchr(OptionLine, '$')) + parse_config_line(OptionLine); + else { - yr_string_matches_foreach(context, String, Match) + yr_rule_strings_foreach(Rule, String) { -#ifdef DEBUG_COMMENTS - DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); -#endif - if (ParseOptionLine(OptionLine, (char*)String->identifier, Match)) + yr_string_matches_foreach(context, String, Match) { #ifdef DEBUG_COMMENTS - DebugOutput("YaraScan: NewLine %s", NewLine); + DebugOutput("YaraScan match: %s, %s (0x%x)", OptionLine, String->identifier, Match->offset); #endif - parse_config_line(NewLine); - SetBreakpoints = TRUE; + ParseOptionLine(OptionLine, (char*)String->identifier, Match, user_data); } - else if (!strchr(OptionLine, '$') && _strnicmp(OptionLine, "bp", 2) || strncmp(OptionLine, "br", 2)) - SetBreakpoints = TRUE; - } } - + if (!_strnicmp(OptionLine, "bp", 2) || !strncmp(OptionLine, "br", 2) || !strncmp(OptionLine, "sysbp", 5)) + SetBreakpoints = TRUE; if (!_stricmp("dump", OptionLine)) - DoDumpRegion = TRUE; + { + DebugOutput("YaraScan: Dump of region at 0x%p triggered by Yara.", user_data); + DumpRegion(user_data); + } if (!_stricmp("clear", OptionLine)) { BreakpointsHit = FALSE; @@ -234,8 +293,6 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void memset(Action2, 0, MAX_PATH); memset(Action3, 0, MAX_PATH); } - if (!strchr(OptionLine, '$')) - parse_config_line(OptionLine); if (p) { *p = ','; @@ -250,121 +307,139 @@ int YaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void if (DebuggerInitialised && SetBreakpoints) SetInitialBreakpoints(user_data); - if (DoDumpRegion) - { - DebugOutput("YaraScan: Dump of region at 0x%p triggered by Yara.", user_data); - DumpRegion(user_data); - } - return CALLBACK_CONTINUE; } return CALLBACK_ERROR; } -typedef struct -{ - PCHAR FunctionName; - PVOID Address; -} NameByAddress; - -int GetAddressByYaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) +int GetAddressesByYaraCallback(YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) { - switch(message) - { - case CALLBACK_MSG_RULE_NOT_MATCHING: + switch(message) + { + case CALLBACK_MSG_RULE_NOT_MATCHING: #ifdef DEBUG_COMMENTS - DebugOutput("YaraScan rule did not match."); + DebugOutput("YaraScan rule did not match."); #endif - case CALLBACK_MSG_IMPORT_MODULE: - return CALLBACK_CONTINUE; - case CALLBACK_MSG_RULE_MATCHING: - YR_MATCH* Match; - YR_STRING* String; - YR_RULE* Rule = (YR_RULE*)message_data; + case CALLBACK_MSG_IMPORT_MODULE: + return CALLBACK_CONTINUE; -#ifdef DEBUG_COMMENTS - DebugOutput("GetAddressByYaraCallback hit: %s\n", Rule->identifier); -#endif + case CALLBACK_MSG_RULE_MATCHING: + YR_MATCH* Match; + YR_STRING* String; + YR_RULE* Rule = (YR_RULE*)message_data; + NameByAddress* AddressInfos = (NameByAddress*)user_data; - yr_rule_strings_foreach(Rule, String) - { - yr_string_matches_foreach(context, String, Match) - { - NameByAddress *AddressInfo = user_data; - - if (!strcmp(Rule->identifier, AddressInfo->FunctionName)) - { #ifdef DEBUG_COMMENTS - DebugOutput("GetAddressByYaraCallback: Function %s found at RVA 0x%x", AddressInfo->FunctionName, Match->offset); + DebugOutput("GetAddressesByYaraCallback hit: %s\n", Rule->identifier); #endif - AddressInfo->Address = (PVOID)Match->offset; - break; - } + + yr_rule_strings_foreach(Rule, String) + { + yr_string_matches_foreach(context, String, Match) + { + for (SIZE_T i = 0; AddressInfos[i].FunctionName != NULL; i++) + { + if (!strcmp(Rule->identifier, AddressInfos[i].FunctionName)) + { #ifdef DEBUG_COMMENTS - else - DebugOutput("GetAddressByYaraCallback: Function %s not found", AddressInfo->FunctionName); + DebugOutput("GetAddressesByYaraCallback: Found %s at RVA 0x%x", AddressInfos[i].FunctionName, Match->offset); #endif - } - } - return CALLBACK_CONTINUE; - } + AddressInfos[i].Address = (PVOID)Match->offset; + break; + } + } + } + } + return CALLBACK_CONTINUE; + } + + return CALLBACK_ERROR; +} - return CALLBACK_ERROR; +NameByAddress* GetAddressesByYara(HMODULE ModuleBase, PCHAR FunctionNames[], SIZE_T FunctionCount, SIZE_T* OutFoundCount) +{ + if (!YaraActivated || !FunctionNames || FunctionCount == 0) + return NULL; + + SIZE_T Size = GetAccessibleSize(ModuleBase); + if (!Size) + return NULL; + + Size = (SIZE_T)ReverseScanForNonZero(ModuleBase, Size); + if (!Size) + { + if (YaraLogging) + DebugOutput("GetAddressesByYara: Nothing to scan at 0x%p!\n", ModuleBase); + return NULL; + } + + NameByAddress* AddressInfos = (NameByAddress*)calloc(FunctionCount + 1, sizeof(NameByAddress)); + if (!AddressInfos) + return NULL; + + for (SIZE_T i = 0; i < FunctionCount; i++) + { + AddressInfos[i].FunctionName = FunctionNames[i]; + AddressInfos[i].Address = NULL; + } + + int Flags = 0, Timeout = 1, Result = ERROR_SUCCESS; + __try + { + Result = yr_rules_scan_mem(Rules, (PVOID)ModuleBase, Size, Flags, GetAddressesByYaraCallback, AddressInfos, Timeout); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + if (YaraLogging) + DebugOutput("GetAddressesByYara: Unable to scan 0x%p\n", ModuleBase); + free(AddressInfos); + return NULL; + } + + if (Result != ERROR_SUCCESS) + { + if (YaraLogging) + ScannerError(Result); + free(AddressInfos); + return NULL; + } + + SIZE_T FoundCount = 0; + for (SIZE_T i = 0; i < FunctionCount; i++) + { + if (AddressInfos[i].Address) + { + AddressInfos[i].Address = (PVOID)((ULONG_PTR)ModuleBase + (ULONG_PTR)AddressInfos[i].Address); + FoundCount++; + } + } + + if (OutFoundCount) + *OutFoundCount = FoundCount; + + return AddressInfos; } PVOID GetAddressByYara(HMODULE ModuleBase, PCHAR FunctionName) { - if (!YaraActivated) - return NULL; - - int Flags = 0, Timeout = 1, Result = ERROR_SUCCESS; - - SIZE_T Size = GetAccessibleSize(ModuleBase); - - if (!Size) - return NULL; - - Size = (SIZE_T)ReverseScanForNonZero(ModuleBase, Size); - - if (!Size) - { - if (YaraLogging) - DebugOutput("GetAddressByYara: Nothing to scan at 0x%p!\n", ModuleBase); - return NULL; - } + if (!YaraActivated) + return NULL; - NameByAddress AddressInfo; - AddressInfo.FunctionName = FunctionName; - AddressInfo.Address = NULL; + PCHAR FunctionNames[] = {FunctionName, NULL}; + SIZE_T FunctionCount = 1; + SIZE_T FoundCount = 0; - __try - { - Result = yr_rules_scan_mem(Rules, (PVOID)ModuleBase, Size, Flags, GetAddressByYaraCallback, &AddressInfo, Timeout); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - if (YaraLogging) - DebugOutput("GetAddressByYara: Unable to scan 0x%p\n", ModuleBase); - return NULL; - } + NameByAddress* Results = GetAddressesByYara(ModuleBase, FunctionNames, FunctionCount, &FoundCount); - if (Result != ERROR_SUCCESS) - if (YaraLogging) - ScannerError(Result); -#ifdef DEBUG_COMMENTS - else - DebugOutput("GetAddressByYara: successfully scanned 0x%p\n", ModuleBase); -#endif + PVOID FoundAddress = NULL; + if (Results && FoundCount > 0) + { + FoundAddress = Results[0].Address; + free(Results); + } - if (!AddressInfo.Address) - return NULL; - -#ifdef DEBUG_COMMENTS - DebugOutput("GetAddressByYara: %s found at 0x%x", FunctionName, (ULONG_PTR)ModuleBase + (ULONG_PTR)AddressInfo.Address); -#endif - - return (PVOID)((ULONG_PTR)ModuleBase + (ULONG_PTR)AddressInfo.Address); + return FoundAddress; } void YaraShutdown() @@ -451,7 +526,10 @@ BOOL ScanForRulesCanary(PVOID Address, SIZE_T Size) YaraLogging = FALSE; BOOL CapemonRulesDetected = FALSE; if (GetAddressByYara(Address, "capemon")) + { CapemonRulesDetected = TRUE; + DebugOutput("ScanForRulesCanary: capemon rules detected"); + } YaraLogging = PreviousYaraLogging; return CapemonRulesDetected; } @@ -498,6 +576,10 @@ BOOL YaraInit() goto exit; } + // Add 'internal' yara first + if (yr_compiler_add_string(Compiler, InternalYara, NULL) != 0) + DebugOutput("YaraInit: Failed to add internal yara rules.\n", compiled_rules); + if (g_config.yarascan) { char FindString[MAX_PATH]; @@ -551,10 +633,6 @@ BOOL YaraInit() DebugOutput("YaraInit: Found no Yara rules in %s\n", yara_dir); } - // Add 'internal' yara - if (yr_compiler_add_string(Compiler, InternalYara, NULL) != 0) - DebugOutput("YaraInit: Failed to add internal yara rules.\n", compiled_rules); - Result = yr_compiler_get_rules(Compiler, &Rules); if (Result != ERROR_SUCCESS) diff --git a/CAPE/YaraHarness.h b/CAPE/YaraHarness.h index fcec62f0..c5ebcafa 100644 --- a/CAPE/YaraHarness.h +++ b/CAPE/YaraHarness.h @@ -4,9 +4,16 @@ #include "yara.h" +typedef struct +{ + PCHAR FunctionName; + PVOID Address; +} NameByAddress; + BOOL YaraInit(); BOOL ScanForRulesCanary(PVOID Address, SIZE_T Size); void YaraScan(PVOID Address, SIZE_T Size); void SilentYaraScan(PVOID Address, SIZE_T Size); PVOID GetAddressByYara(HMODULE ModuleBase, PCHAR FunctionName); +NameByAddress* GetAddressesByYara(HMODULE ModuleBase, PCHAR FunctionNames[], SIZE_T FunctionCount, SIZE_T* OutFoundCount); void YaraShutdown(); \ No newline at end of file diff --git a/CAPE/uthash.h b/CAPE/uthash.h new file mode 100644 index 00000000..07ae09e0 --- /dev/null +++ b/CAPE/uthash.h @@ -0,0 +1,1137 @@ +/* +Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_NO_STDINT) && HASH_NO_STDINT +/* The user doesn't have , and must figure out their own way + to provide definitions for uint8_t and uint32_t. */ +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) 1 +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ \ No newline at end of file diff --git a/CAPE/w64wow64/internal.h b/CAPE/w64wow64/internal.h deleted file mode 100644 index de4fccf5..00000000 --- a/CAPE/w64wow64/internal.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * - * WOW64Ext Library - * - * Copyright (c) 2012 ReWolf - * http://blog.rewolf.pl/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - */ -#pragma once - -#define EMIT(a) __asm __emit (a) - -#define X64_Start_with_CS(_cs) \ - { \ - EMIT(0x6A) EMIT(_cs) /* push _cs */ \ - EMIT(0xE8) EMIT(0) EMIT(0) EMIT(0) EMIT(0) /* call $+5 */ \ - EMIT(0x83) EMIT(4) EMIT(0x24) EMIT(5) /* add dword [esp], 5 */ \ - EMIT(0xCB) /* retf */ \ - } - -#define X64_End_with_CS(_cs) \ - { \ - EMIT(0xE8) EMIT(0) EMIT(0) EMIT(0) EMIT(0) /* call $+5 */ \ - EMIT(0xC7) EMIT(0x44) EMIT(0x24) EMIT(4) EMIT(_cs) EMIT(0) EMIT(0) EMIT(0) /* mov dword [rsp + 4], _cs */ \ - EMIT(0x83) EMIT(4) EMIT(0x24) EMIT(0xD) /* add dword [rsp], 0xD */ \ - EMIT(0xCB) /* retf */ \ - } - -#define X64_Start() X64_Start_with_CS(0x33) -#define X64_End() X64_End_with_CS(0x23) - -#define _RAX 0 -#define _RCX 1 -#define _RDX 2 -#define _RBX 3 -#define _RSP 4 -#define _RBP 5 -#define _RSI 6 -#define _RDI 7 -#define _R8 8 -#define _R9 9 -#define _R10 10 -#define _R11 11 -#define _R12 12 -#define _R13 13 -#define _R14 14 -#define _R15 15 - -#define X64_Push(r) EMIT(0x48 | ((r) >> 3)) EMIT(0x50 | ((r) & 7)) -#define X64_Pop(r) EMIT(0x48 | ((r) >> 3)) EMIT(0x58 | ((r) & 7)) - -#define REX_W EMIT(0x48) __asm - -//to fool M$ inline asm compiler I'm using 2 DWORDs instead of DWORD64 -//use of DWORD64 will generate wrong 'pop word ptr[]' and it will break stack -union reg64 -{ - DWORD64 v; - DWORD dw[2]; -}; diff --git a/CAPE/w64wow64/w64wow64.c b/CAPE/w64wow64/w64wow64.c deleted file mode 100644 index c40a34e3..00000000 --- a/CAPE/w64wow64/w64wow64.c +++ /dev/null @@ -1,469 +0,0 @@ -#ifndef _WIN64 -/* -W64oWoW64 -Copyright (C) 2012 George Nicolaou - -This file is part of W64oWoW64. - -W64oWoW64 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -W64oWoW64 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with W64oWoW64. If not, see . -*/ -#include -#include "internal.h" -#include "w64wow64.h" -#include "w64wow64defs.h" -#include "windef.h" - -extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); -extern void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); - -FUNCTIONPTRS sFunctions = { 0 }; - -/** -* -* X64Call Part of WOW64Ext Library -* See internals.h -*/ -extern unsigned __int64 X64Call(DWORD64 func, int argC, ...) -{ - va_list args; - va_start(args, argC); - union reg64 _rcx = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; - union reg64 _rdx = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; - union reg64 _r8 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; - union reg64 _r9 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; - union reg64 _rax = { 0 }; - - union reg64 restArgs = { (DWORD64)&va_arg(args, DWORD64) }; - - // conversion to QWORD for easier use in inline assembly - union reg64 _argC = { (DWORD64)argC }; - DWORD back_esp = 0; - WORD back_fs = 0; - - __asm - { - ;// reset FS segment, to properly handle RFG - mov back_fs, fs - mov eax, 0x2B - mov fs, ax - - ;// keep original esp in back_esp variable - mov back_esp, esp - - ;// align esp to 0x10, without aligned stack some syscalls may return errors ! - ;// (actually, for syscalls it is sufficient to align to 8, but SSE opcodes - ;// requires 0x10 alignment), it will be further adjusted according to the - ;// number of arguments above 4 - and esp, 0xFFFFFFF0 - - X64_Start(); - - ;// below code is compiled as x86 inline asm, but it is executed as x64 code - ;// that's why it need sometimes REX_W() macro, right column contains detailed - ;// transcription how it will be interpreted by CPU - - ;// fill first four arguments - REX_W mov ecx, _rcx.dw[0] ;// mov rcx, qword ptr [_rcx] - REX_W mov edx, _rdx.dw[0] ;// mov rdx, qword ptr [_rdx] - push _r8.v ;// push qword ptr [_r8] - X64_Pop(_R8); ;// pop r8 - push _r9.v ;// push qword ptr [_r9] - X64_Pop(_R9); ;// pop r9 - ;// - REX_W mov eax, _argC.dw[0] ;// mov rax, qword ptr [_argC] - ;// - ;// final stack adjustment, according to the ;// - ;// number of arguments above 4 ;// - test al, 1 ;// test al, 1 - jnz _no_adjust ;// jnz _no_adjust - sub esp, 8 ;// sub rsp, 8 -_no_adjust: ;// - ;// - push edi ;// push rdi - REX_W mov edi, restArgs.dw[0] ;// mov rdi, qword ptr [restArgs] - ;// - ;// put rest of arguments on the stack ;// - REX_W test eax, eax ;// test rax, rax - jz _ls_e ;// je _ls_e - REX_W lea edi, dword ptr [edi + 8*eax - 8] ;// lea rdi, [rdi + rax*8 - 8] - ;// -_ls: ;// - REX_W test eax, eax ;// test rax, rax - jz _ls_e ;// je _ls_e - push dword ptr [edi] ;// push qword ptr [rdi] - REX_W sub edi, 8 ;// sub rdi, 8 - REX_W sub eax, 1 ;// sub rax, 1 - jmp _ls ;// jmp _ls -_ls_e: ;// - ;// - ;// create stack space for spilling registers ;// - REX_W sub esp, 0x20 ;// sub rsp, 20h - ;// - call func ;// call qword ptr [func] - ;// - ;// cleanup stack ;// - REX_W mov ecx, _argC.dw[0] ;// mov rcx, qword ptr [_argC] - REX_W lea esp, dword ptr [esp + 8*ecx + 0x20] ;// lea rsp, [rsp + rcx*8 + 20h] - ;// - pop edi ;// pop rdi - ;// - // set return value ;// - REX_W mov _rax.dw[0], eax ;// mov qword ptr [_rax], rax - - X64_End(); - - mov ax, ds - mov ss, ax - mov esp, back_esp - - ;// restore FS segment - mov ax, back_fs - mov fs, ax - } - return _rax.v; -} -#pragma warning(pop) - -PTEB64 NtTeb64( void ) -{ - X64_Start(); - GETTEB(); - X64_End(); -} - -PLDR_DATA_TABLE_ENTRY64 GetModule64LdrTable( wchar_t * lwcModuleName ) -{ - PTEB64 psTeb = NtTeb64(); - //PPEB64 psPeb = - PPEB_LDR_DATA Ldr = psTeb->ProcessEnvironmentBlock->Ldr; - PLDR_DATA_TABLE_ENTRY64 psDataEntryStart = - (PLDR_DATA_TABLE_ENTRY64)Ldr->InLoadOrderModuleList.Flink; - PLDR_DATA_TABLE_ENTRY64 psDataEntryCurrent = psDataEntryStart; - - do { - if( memcmp( (DWORD64)psDataEntryCurrent->BaseDllName.Buffer, lwcModuleName, - psDataEntryCurrent->BaseDllName.Length ) == 0 ) { - return psDataEntryCurrent; - } - psDataEntryCurrent = - (PLDR_DATA_TABLE_ENTRY64)psDataEntryCurrent->InLoadOrderLinks.Flink; - } while( psDataEntryStart != psDataEntryCurrent && psDataEntryCurrent ); - return NULL; -} - -extern void __cdecl SetLastErrorFromX64Call(DWORD64 status) -{ - typedef ULONG (WINAPI *RtlNtStatusToDosError_t)(NTSTATUS Status); - typedef ULONG (WINAPI *RtlSetLastWin32Error_t)(NTSTATUS Status); - - static RtlNtStatusToDosError_t RtlNtStatusToDosError = NULL; - static RtlSetLastWin32Error_t RtlSetLastWin32Error = NULL; - - if ((NULL == RtlNtStatusToDosError) || (NULL == RtlSetLastWin32Error)) - { - HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); - RtlNtStatusToDosError = (RtlNtStatusToDosError_t)GetProcAddress(ntdll, "RtlNtStatusToDosError"); - RtlSetLastWin32Error = (RtlSetLastWin32Error_t)GetProcAddress(ntdll, "RtlSetLastWin32Error"); - } - - if ((NULL != RtlNtStatusToDosError) && (NULL != RtlSetLastWin32Error)) - { - RtlSetLastWin32Error(RtlNtStatusToDosError((DWORD)status)); - } -} - -extern SIZE_T VirtualQueryEx64(HANDLE hProcess, DWORD64 lpAddress, MEMORY_BASIC_INFORMATION64* lpBuffer, SIZE_T dwLength) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD ntqvm = 0; - if (0 == ntqvm) - { - ntqvm = (DWORD)GetProcAddress64(lvpNtdll, "NtQueryVirtualMemory"); - if (0 == ntqvm) - return 0; - } - DWORD64 ret = 0; - X64Call(ntqvm, 6, (DWORD64)hProcess, lpAddress, (DWORD64)0, (DWORD64)lpBuffer, (DWORD64)dwLength, (DWORD64)&ret); - return (SIZE_T)ret; -} - -#pragma warning(push) -#pragma warning(disable : 4244) -extern DWORD64 VirtualAllocEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD64 ntavm = 0; - if (0 == ntavm) - { - ntavm = GetProcAddress64(lvpNtdll, "NtAllocateVirtualMemory"); - if (0 == ntavm) - return 0; - } - - DWORD64 tmpAddr = lpAddress; - DWORD64 tmpSize = dwSize; - DWORD64 ret = X64Call(ntavm, 6, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)0, (DWORD64)&tmpSize, (DWORD64)flAllocationType, (DWORD64)flProtect); - if (STATUS_SUCCESS != ret) - { - SetLastErrorFromX64Call(ret); - return FALSE; - } - else - return tmpAddr; -} - -extern BOOL VirtualFreeEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD dwFreeType) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD64 ntfvm = 0; - if (0 == ntfvm) - { - ntfvm = GetProcAddress64(lvpNtdll, "NtFreeVirtualMemory"); - if (0 == ntfvm) - return 0; - } - - DWORD64 tmpAddr = lpAddress; - DWORD64 tmpSize = dwSize; - DWORD64 ret = X64Call(ntfvm, 4, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)&tmpSize, (DWORD64)dwFreeType); - if (STATUS_SUCCESS != ret) - { - SetLastErrorFromX64Call(ret); - return FALSE; - } - else - return TRUE; -} - -extern BOOL VirtualProtectEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD* lpflOldProtect) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD64 ntpvm = 0; - if (0 == ntpvm) - { - ntpvm = GetProcAddress64(lvpNtdll, "NtProtectVirtualMemory"); - if (0 == ntpvm) - return 0; - } - - DWORD64 tmpAddr = lpAddress; - DWORD64 tmpSize = dwSize; - DWORD64 ret = X64Call(ntpvm, 5, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)&tmpSize, (DWORD64)flNewProtect, (DWORD64)lpflOldProtect); - if (STATUS_SUCCESS != ret) - { - SetLastErrorFromX64Call(ret); - return FALSE; - } - else - return TRUE; -} -#pragma warning(pop) - -extern BOOL ReadProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD nrvm = 0; - if (0 == nrvm) - { - nrvm = (DWORD)GetProcAddress64(lvpNtdll, "NtReadVirtualMemory"); - if (0 == nrvm) - return 0; - } - DWORD64 ret = X64Call(nrvm, 5, (DWORD64)hProcess, lpBaseAddress, (DWORD64)lpBuffer, (DWORD64)nSize, (DWORD64)lpNumberOfBytesRead); - if (STATUS_SUCCESS != ret) - return FALSE; - else - return TRUE; -} - -extern BOOL WriteProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten) -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - static DWORD nrvm = 0; - if (0 == nrvm) - { - nrvm = (DWORD)GetProcAddress64(lvpNtdll, "NtWriteVirtualMemory"); - if (0 == nrvm) - return 0; - } - DWORD64 ret = X64Call(nrvm, 5, (DWORD64)hProcess, lpBaseAddress, (DWORD64)lpBuffer, (DWORD64)nSize, (DWORD64)lpNumberOfBytesWritten); - if (STATUS_SUCCESS != ret) - return FALSE; - else - return TRUE; -} - -DWORD64 GetModuleBase64( wchar_t * lwcModuleName ) -{ - PLDR_DATA_TABLE_ENTRY64 LdrEntry = GetModule64LdrTable( lwcModuleName ); - return (DWORD64)LdrEntry->DllBase; -} - -PIMAGE_NT_HEADERS64 GetModule64NtHeader( DWORD64 lvpBaseAddress ) -{ - PIMAGE_DOS_HEADER psDosHeader = (PIMAGE_DOS_HEADER)lvpBaseAddress; - return (PIMAGE_NT_HEADERS64)( ((__int8 *)lvpBaseAddress) + - psDosHeader->e_lfanew ); -} - -DWORD64 GetModule64PEBaseAddress( DWORD64 lvpBaseAddress ) -{ - PIMAGE_NT_HEADERS64 psNtHeader = GetModule64NtHeader( lvpBaseAddress ); - return (DWORD64)psNtHeader->OptionalHeader.ImageBase; -} - -DWORD64 GetModule64EntryRVA( DWORD64 lvpBaseAddress ) -{ - PIMAGE_NT_HEADERS64 psNtHeader = GetModule64NtHeader( lvpBaseAddress ); - return (DWORD64)psNtHeader->OptionalHeader.AddressOfEntryPoint; -} - -extern DWORD64 GetProcAddress64( DWORD64 lvpBaseAddress, char * lpszProcName ) -{ - PIMAGE_NT_HEADERS64 psNtHeader = GetModule64NtHeader( lvpBaseAddress ); - char * lpcModBase = (char *)lvpBaseAddress; - PIMAGE_EXPORT_DIRECTORY psExportDir = (PIMAGE_EXPORT_DIRECTORY)( lpcModBase + - psNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress ); - - int nNumberOfNames = psExportDir->NumberOfNames; - unsigned long * lpulFunctions = - (unsigned long *)( lpcModBase + psExportDir->AddressOfFunctions ); - - unsigned long * lpulNames = - (unsigned long *)( lpcModBase + psExportDir->AddressOfNames ); - - unsigned short * lpusOrdinals = - (unsigned short *) ( lpcModBase + psExportDir->AddressOfNameOrdinals ); - - int i; - char * lpszFunctionName; - for( i = 0; i < nNumberOfNames; i++ ) { - lpszFunctionName = ((__int8 *)lpulNames[i]) + (int)lvpBaseAddress; - if( strcmp( lpszFunctionName, lpszProcName ) == 0 ) { - return ( (__int8 *)lvpBaseAddress ) + - lpulFunctions[ lpusOrdinals[i] ]; - } - } - return NULL; -} - -BOOL FreeKnownDllPage( wchar_t * lpwzKnownDllName ) -{ - DWORD64 hSection = 0; - DWORD64 lvpBaseAddress = 0; - DWORD64 lvpRealBaseAddress = 0; - DWORD64 stViewSize = 0; - DWORD64 stRegionSize = 0; - PTEB64 psTeb; - X64Call( sFunctions.LdrGetKnownDllSectionHandle, 3, - (DWORD64)lpwzKnownDllName, - (DWORD64)0, - (DWORD64)&hSection ); - - psTeb = NtTeb64(); - psTeb->NtTib.ArbitraryUserPointer = (DWORD64)lpwzKnownDllName; - - X64Call( sFunctions.NtMapViewOfSection, 10, - (DWORD64)hSection, - (DWORD64)-1, - (DWORD64)&lvpBaseAddress, - (DWORD64)0, - (DWORD64)0, - (DWORD64)0, - (DWORD64)&stViewSize, - (DWORD64)ViewUnmap, - (DWORD64)0, - (DWORD64)PAGE_READONLY ); - - lvpRealBaseAddress = - (DWORD64)GetModule64PEBaseAddress( (DWORD64)lvpBaseAddress ); - - X64Call( sFunctions.NtFreeVirtualMemory, 4, - (DWORD64)-1, - (DWORD64)&lvpRealBaseAddress, - (DWORD64)&stRegionSize, - (DWORD64)MEM_RELEASE ); - - X64Call( sFunctions.NtUnmapViewOfSection, 2, (DWORD64)-1, - (DWORD64)lvpBaseAddress ); - return TRUE; -} - -extern DWORD64 LoadLibrary64A( char * lpcLibraryName ) -{ - if( sFunctions.LoadLibraryA == NULL ) { - sFunctions.LoadLibraryA = - GetProcAddress64( GetModuleBase64( L"kernel32.dll" ), "LoadLibraryA" ); - } - return (DWORD64)X64Call( sFunctions.LoadLibraryA, 1, (DWORD64)lpcLibraryName ); -} - -extern BOOL InitializeW64oWoW64() -{ - DWORD64 lvpNtdll = GetModuleBase64( L"ntdll.dll" ); - UNICODE_STRING64 sUnicodeString; - __int8 * lvpKernelBaseBase; - __int8 * lvpKernel32Base; - PLDR_DATA_TABLE_ENTRY64 lpsKernel32Ldr; - PLDR_DATA_TABLE_ENTRY64 lpsKernelBaseLdr; - - sFunctions.LdrGetKnownDllSectionHandle = GetProcAddress64( lvpNtdll, - "LdrGetKnownDllSectionHandle" ); - sFunctions.NtFreeVirtualMemory = GetProcAddress64( lvpNtdll, - "NtFreeVirtualMemory" ); - sFunctions.NtMapViewOfSection = GetProcAddress64( lvpNtdll, - "NtMapViewOfSection" ); - sFunctions.NtUnmapViewOfSection = GetProcAddress64( lvpNtdll, - "NtUnmapViewOfSection" ); - - if( FreeKnownDllPage( L"kernel32.dll" ) == FALSE) return FALSE; - if( FreeKnownDllPage( L"user32.dll" ) == FALSE ) return FALSE; - - sUnicodeString.Length = 0x18; - sUnicodeString.MaximumLength = 0x1a; - sUnicodeString.Buffer = (DWORD64)L"kernel32.dll"; - if( X64Call( GetProcAddress64( lvpNtdll, "LdrLoadDll" ), 4, - (DWORD64)0, - (DWORD64)0, - (DWORD64)&sUnicodeString, - (DWORD64)&lvpKernel32Base ) != NULL ) { - ErrorOutput("Failed to load 64-bit kernel32.dll"); - return FALSE; - } - - lvpKernelBaseBase = (__int8 *)GetModuleBase64( L"KERNELBASE.dll"); - X64Call( ( lvpKernelBaseBase + (int)GetModule64EntryRVA( lvpKernelBaseBase ) ), - 3, - (DWORD64)lvpKernelBaseBase, - (DWORD64)DLL_PROCESS_ATTACH, - (DWORD64)0 ); - - X64Call( ( lvpKernel32Base + (int)GetModule64EntryRVA( lvpKernel32Base ) ), - 3, - (DWORD64)lvpKernel32Base, - (DWORD64)DLL_PROCESS_ATTACH, - (DWORD64)0 ); - - lpsKernel32Ldr = GetModule64LdrTable( L"kernel32.dll" ); - lpsKernel32Ldr->LoadCount = 0xffff; - lpsKernel32Ldr->Flags += LDRP_ENTRY_PROCESSED | LDRP_PROCESS_ATTACH_CALLED; - - lpsKernelBaseLdr = GetModule64LdrTable( L"KERNELBASE.dll" ); - lpsKernelBaseLdr->LoadCount = 0xffff; - lpsKernelBaseLdr->Flags += LDRP_ENTRY_PROCESSED | LDRP_PROCESS_ATTACH_CALLED; - - return TRUE; -} - -#endif \ No newline at end of file diff --git a/CAPE/w64wow64/w64wow64.h b/CAPE/w64wow64/w64wow64.h deleted file mode 100644 index ffdbe375..00000000 --- a/CAPE/w64wow64/w64wow64.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -W64oWoW64 -Copyright (C) 2012 George Nicolaou - -This file is part of W64oWoW64. - -W64oWoW64 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -W64oWoW64 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with W64oWoW64. If not, see . -*/ -#pragma once - -#include - -#ifndef STATUS_SUCCESS -# define STATUS_SUCCESS 0 -#endif - -#ifndef __W64WOW64_H_ -#define __W64WOW64_H_ - -unsigned __int64 X64Call( DWORD64 lvpFunctionPtr, int nArgc, ... ); -void __cdecl SetLastErrorFromX64Call(DWORD64 status); -DWORD64 GetProcAddress64( DWORD64 lvpBaseAddress, char * lpszProcName ); -DWORD64 LoadLibrary64A( char * lpcLibraryName ); -DWORD64 GetModuleBase64( wchar_t * lwcModuleName ); - -DWORD64 VirtualAllocEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -BOOL VirtualFreeEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD* lpflOldProtect); -BOOL VirtualProtectEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD* lpflOldProtect); -BOOL ReadProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); -BOOL WriteProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); - -extern BOOL InitializeW64oWoW64(void); -#endif \ No newline at end of file diff --git a/CAPE/w64wow64/w64wow64defs.h b/CAPE/w64wow64/w64wow64defs.h deleted file mode 100644 index d5cd3780..00000000 --- a/CAPE/w64wow64/w64wow64defs.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -W64oWoW64 -Copyright (C) 2012 George Nicolaou - -This file is part of W64oWoW64. - -W64oWoW64 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -W64oWoW64 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with W64oWoW64. If not, see . -*/ -#define GETTEB() \ -{ \ - EMIT(0x65) EMIT(0x48) EMIT(0x8b) EMIT(0x04) EMIT(0x25) EMIT(0x30) EMIT(0x00) EMIT(0x00) EMIT(0x00) \ -} - - -typedef struct { - void * LdrGetKnownDllSectionHandle; - void * NtMapViewOfSection; - void * NtFreeVirtualMemory; - void * NtUnmapViewOfSection; - void * LoadLibraryA; -} FUNCTIONPTRS; - -BOOL InitializeW64oWoW64( void ); \ No newline at end of file diff --git a/CAPE/w64wow64/windef.h b/CAPE/w64wow64/windef.h deleted file mode 100644 index fd7b4872..00000000 --- a/CAPE/w64wow64/windef.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -W64oWoW64 -Copyright (C) 2012 George Nicolaou - -This file is part of W64oWoW64. - -W64oWoW64 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -W64oWoW64 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with W64oWoW64. If not, see . -*/ -#include - -typedef struct _CLIENT_ID64 -{ - DWORD64 UniqueProcess; - DWORD64 UniqueThread; -} CLIENT_ID64, *PCLIENT_ID64; - - -typedef struct _PEB_LDR_DATA -{ - ULONG Length; - UCHAR Initialized; - DWORD64 SsHandle; - LIST_ENTRY64 InLoadOrderModuleList; - LIST_ENTRY64 InMemoryOrderModuleList; - LIST_ENTRY64 InInitializationOrderModuleList; - DWORD64 EntryInProgress; -} PEB_LDR_DATA, *PPEB_LDR_DATA; - - -typedef struct _PEB64 -{ - UCHAR InheritedAddressSpace; - UCHAR ReadImageFileExecOptions; - UCHAR BeingDebugged; - UCHAR BitField; - ULONG ImageUsesLargePages: 1; - ULONG IsProtectedProcess: 1; - ULONG IsLegacyProcess: 1; - ULONG IsImageDynamicallyRelocated: 1; - ULONG SpareBits: 4; - DWORD64 Mutant; - DWORD64 ImageBaseAddress; - PPEB_LDR_DATA Ldr; -} PEB64, *PPEB64; - -typedef struct _LSA_UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - DWORD64 Buffer; -} UNICODE_STRING64, * PUNICODE_STRING64; - -typedef struct _TEB64 -{ - NT_TIB64 NtTib; - DWORD64 EnvironmentPointer; - CLIENT_ID64 ClientId; - DWORD64 ActiveRpcHandle; - DWORD64 ThreadLocalStoragePointer; - PPEB64 ProcessEnvironmentBlock; -} TEB64, *PTEB64; - -typedef struct _LDR_DATA_TABLE_ENTRY64 -{ - LIST_ENTRY64 InLoadOrderLinks; - LIST_ENTRY64 InMemoryOrderLinks; - LIST_ENTRY64 InInitializationOrderLinks; - DWORD64 DllBase; - DWORD64 EntryPoint; - ULONG SizeOfImage; - UNICODE_STRING64 FullDllName; - UNICODE_STRING64 BaseDllName; - ULONG Flags; - WORD LoadCount; -} LDR_DATA_TABLE_ENTRY54, *PLDR_DATA_TABLE_ENTRY64; - -#define LDRP_PROCESS_ATTACH_CALLED 0x000080000 -#define LDRP_ENTRY_PROCESSED 0x000004000 - -typedef enum _SECTION_INHERIT { - ViewShare = 1, - ViewUnmap = 2 -} SECTION_INHERIT, * PSECTION_INHERIT; \ No newline at end of file diff --git a/CAPE/wow64_fix.c b/CAPE/wow64_fix.c deleted file mode 100644 index fa7457c8..00000000 --- a/CAPE/wow64_fix.c +++ /dev/null @@ -1,225 +0,0 @@ -/* -CAPE - Config And Payload Extraction -Copyright(C) 2015, 2016 Context Information Security. (kevin.oreilly@contextis.com) - -This program is free software : you can redistribute it and / or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program.If not, see . -*/ -#ifndef _WIN64 -#include "w64wow64\w64wow64.h" -// Based upon ReWolf's wow64ext library: -// https://github.com/rwfpl/rewolf-wow64ext - -#define DR7_MASK_RWE0 0xFFFDFFFF // 11111111111111011111111111111111 -#define DR7_MASK_RWE1 0xFFDFFFFF // 11111111110111111111111111111111 -#define DR7_MASK_RWE2 0xFDFFFFFF // 11111101111111111111111111111111 -#define DR7_MASK_RWE3 0xDFFFFFFF // 11011111111111111111111111111111 - -const int PAGE_SIZE = 0x1000; - -extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); -extern void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); - -BOOL WoW64HookInstalled; - -DWORD64 lpHookCode = (DWORD64)NULL; -DWORD64 lpNewJumpLocation; - -DWORD64 pfnKiUserExceptionDispatcher; -DWORD64 pfnNtSetContextThread; - -//************************************************************************************** -extern BOOL WoW64PatchBreakpoint(unsigned int Register) -//************************************************************************************** -{ - if (WoW64HookInstalled == FALSE) - return FALSE; - - DebugOutput("WoW64PatchBreakpoint entry, debug register: %d, current DR7 mask = 0x%x\n", Register, *(DWORD*)(((PBYTE)lpNewJumpLocation)+37)); - - switch(Register) - { - case 0: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) & DR7_MASK_RWE0); - break; - case 1: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) & DR7_MASK_RWE1); - break; - case 2: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) & DR7_MASK_RWE2); - break; - case 3: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) & DR7_MASK_RWE3); - break; - } - - DebugOutput("WoW64PatchBreakpoint: patched DR7 mask = 0x%x\n", *(DWORD*)(((PBYTE)lpNewJumpLocation)+37)); - - return TRUE; -} - -//************************************************************************************** -extern BOOL WoW64UnpatchBreakpoint(unsigned int Register) -//************************************************************************************** -{ - if (WoW64HookInstalled == FALSE) - return FALSE; - - DebugOutput("WoW64UnpatchBreakpoint entry, debug register: %d, current DR7 mask = 0x%x\n", Register, *(DWORD*)(((PBYTE)lpNewJumpLocation)+37)); - - switch(Register) - { - case 0: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) | ~DR7_MASK_RWE0); - break; - case 1: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) | ~DR7_MASK_RWE1); - break; - case 2: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) | ~DR7_MASK_RWE2); - break; - case 3: - *(DWORD*)(((PBYTE)lpNewJumpLocation)+37) = (*(DWORD*)(((PBYTE)lpNewJumpLocation)+37) | ~DR7_MASK_RWE3); - break; - } - - DebugOutput("WoW64UnpatchBreakpoint: unpatched DR7 mask = 0x%x\n", *(DWORD*)(((PBYTE)lpNewJumpLocation)+37)); - - return TRUE; -} - -//************************************************************************************** -const DWORD64 CreateHook(const DWORD_PTR pKiUserExceptionDispatcher, const DWORD_PTR pNtSetContextThread64, const DWORD_PTR pWow64PrepareForException) -//************************************************************************************** -// credit to Omega Red http://pastebin.ca/raw/475547 -{ - unsigned char HookBytes[] = - { - 0x81, 0xBC, 0x24, 0xF0, 0x04, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x40, //cmp dword [rsp+0x4f0], 0x4000001e ; wow64 single step? 0 - 0x75, 0x37, //jne hook_end 11 - - 0x49, 0x89, 0xCC, //mov r12, rcx 13 - 0x49, 0x89, 0xD5, //mov r13, rdx 16 - 0x4D, 0x89, 0xC6, //mov r14, r8 19 - 0x4D, 0x89, 0xCF, //mov r15, r9 22 - 0xC7, 0x44, 0x24, 0x30, 0x10, 0x00, 0x10, 0x00, //mov dword ptr [rsp+30h], 100010h 25 - 0x81, 0x64, 0x24, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, //and dword ptr [rsp+70h], 0xFFFFFFFF 33 - 0x48, 0xC7, 0xC1, 0xFE, 0xFF, 0xFF, 0xFF, //mov rcx, 0FFFFFFFFFFFFFFFEh 41 - 0x48, 0x89, 0xE2, //mov rdx, rsp 48 - 0xE8, 0xBE, 0x07, 0xAF, 0x77, //call near ptr Xh 51 - 0x4C, 0x89, 0xE1, //mov rcx, r12 56 - 0x4C, 0x89, 0xEA, //mov rdx, r13 59 - 0x4D, 0x89, 0xF0, //mov r8, r14 62 - 0x4D, 0x89, 0xF9, //mov r9, r15 65 -//hook_end - 0xFC, //cld - first two instructions from KiUserExceptionDispatcher 68 - 0x48, 0xB8, 0xDD, 0xCC, 0xBB, 0xAA, 0x00, 0x00, 0x00, 0x00, //mov rax, 0AABBCCDDh 69 - 0x50, //push rax - jump back to KiUserExceptionDispatcher+8 79 - 0x48, 0xB8, 0xDD, 0xCC, 0xBB, 0xAA, 0x00, 0x00, 0x00, 0x00, //mov rax, 0AABBCCDDh 80 - 0x48, 0x87, 0x04, 0x24, //xchg rax, [rsp] 90 - 0xC3 //ret 94 - }; // 86 - - lpHookCode = VirtualAllocEx64((HANDLE) -1, (DWORD64)NULL, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - - //insert relative address of NtSetContextThread64 from instruction after call - DWORD RelativeOffset = pNtSetContextThread64 - ((DWORD)lpHookCode + 56); - memcpy(&HookBytes[52], &RelativeOffset, sizeof(DWORD_PTR)); - - //insert VA of Wow64PrepareForException - memcpy(&HookBytes[71], &pWow64PrepareForException, sizeof(DWORD_PTR)); - - //insert address to return to from hook code at 8 bytes into KiUserExceptionDispatcher - DWORD ReturnAddress = pKiUserExceptionDispatcher + 8; - memcpy(&HookBytes[82], &ReturnAddress, sizeof(DWORD_PTR)); //(8 is address of third instruction) - - //copy it to newly created page - memcpy((LPVOID)lpHookCode, (const void *)HookBytes, sizeof(HookBytes)); - - return lpHookCode; -} - -//************************************************************************************** -const void EnableWow64Hook() -//************************************************************************************** -{ - unsigned char trampolineBytes[] = - { - 0xE9, 0xDD, 0xCC, 0xBB, 0xAA, // jmp +0xAABBCCDDEE+5 - 0xCC, 0xCC, 0xCC // - }; - DWORD pNew = (DWORD)lpNewJumpLocation; - DWORD pOrig = (DWORD)pfnKiUserExceptionDispatcher + 5; - DWORD RelativeOffset = pNew - pOrig; - memcpy(&trampolineBytes[1], (PVOID)&RelativeOffset, sizeof(DWORD_PTR)); - - DWORD dwOldProtect = 0; - if (!VirtualProtectEx64((HANDLE)-1, (DWORD64)pfnKiUserExceptionDispatcher, PAGE_SIZE, PAGE_EXECUTE_READWRITE, &dwOldProtect)) - { - ErrorOutput("VirtualProtectEx64 failed to set PAGE_EXECUTE_READWRITE"); - return; - } - - memcpy((PVOID)pfnKiUserExceptionDispatcher, &trampolineBytes, sizeof(trampolineBytes)); - - if (!VirtualProtectEx64((HANDLE)-1, (DWORD64)pfnKiUserExceptionDispatcher, PAGE_SIZE, dwOldProtect, &dwOldProtect)) - { - ErrorOutput("VirtualProtect failed to restore dwOldProtect"); - return; - } -} - -//************************************************************************************** -extern BOOL WoW64fix(void) -//************************************************************************************** -{ - OSVERSIONINFO OSVersion; - OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - -#pragma warning(suppress : 4996) - if (!GetVersionEx(&OSVersion)) - { - ErrorOutput("WoW64fix: Failed to get OS version"); - return FALSE; - } - - if ((OSVersion.dwMajorVersion == 6 && OSVersion.dwMinorVersion > 1) || OSVersion.dwMajorVersion > 6) - { - DebugOutput("WoW64fix: Windows version %d.%d not supported.\n", OSVersion.dwMajorVersion, OSVersion.dwMinorVersion); - return FALSE; - } - - IsWow64Process(GetCurrentProcess(), &WoW64HookInstalled); - if (WoW64HookInstalled == FALSE) - { - DebugOutput("WoW64 not detected.\n"); - return FALSE; - } - - DWORD64 ntdll64 = GetModuleBase64(L"ntdll.dll"); - DWORD64 wow64dll = GetModuleBase64(L"wow64.dll"); - DWORD64 pfnWow64PrepareForException = GetProcAddress64(wow64dll, "Wow64PrepareForException"); - pfnKiUserExceptionDispatcher = GetProcAddress64(ntdll64, "KiUserExceptionDispatcher"); - pfnNtSetContextThread = GetProcAddress64(ntdll64, "NtSetContextThread"); - - DebugOutput("WoW64 detected: 64-bit ntdll base: 0x%x, KiUserExceptionDispatcher: 0x%x, NtSetContextThread: 0x%x, Wow64PrepareForException: 0x%x\n", ntdll64, pfnKiUserExceptionDispatcher, pfnNtSetContextThread, pfnWow64PrepareForException); - - lpNewJumpLocation = CreateHook((DWORD_PTR)pfnKiUserExceptionDispatcher, (DWORD_PTR)pfnNtSetContextThread, (DWORD_PTR)pfnWow64PrepareForException); - - EnableWow64Hook(); - - DebugOutput("WoW64 workaround: KiUserExceptionDispatcher hook installed at: 0x%x\n", lpNewJumpLocation); - - return TRUE; -} -#endif \ No newline at end of file diff --git a/capemon.c b/capemon.c index 5ab1ad16..d2b0c8ec 100644 --- a/capemon.c +++ b/capemon.c @@ -59,8 +59,8 @@ extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern LONG WINAPI CAPEExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo); extern ULONG_PTR base_of_dll_of_interest; extern BOOL BreakpointsHit, SetInitialBreakpoints(PVOID ImageBase); -extern PCHAR ScyllaGetExportDirectory(PVOID Address); -extern PCHAR ScyllaGetExportNameByScan(PVOID Address, PCHAR* ModuleName, SIZE_T ScanSize); +extern PCHAR GetExportDirectory(PVOID Address); +extern PCHAR ScanForExport(PVOID Address, SIZE_T ScanMax); extern void YaraScan(PVOID Address, SIZE_T Size); extern BOOL IsAddressAccessible(PVOID Address); @@ -164,7 +164,7 @@ VOID CALLBACK New_DllLoadNotification( add_dll_range((ULONG_PTR)NotificationData->Loaded.DllBase, (ULONG_PTR)NotificationData->Loaded.DllBase + GetAllocationSize(NotificationData->Loaded.DllBase)); if (!set_hooks_dll(dllname)) { - exportdirectory = ScyllaGetExportDirectory(NotificationData->Loaded.DllBase); + exportdirectory = GetExportDirectory(NotificationData->Loaded.DllBase); if (exportdirectory) { size = strlen(exportdirectory); mbstowcs_s(&numconverted, exportdirectory_w, MAX_PATH, exportdirectory, size+1); @@ -196,12 +196,7 @@ static int parse_stack_trace(void *msg, ULONG_PTR addr) char *buf = convert_address_to_dll_name_and_offset(addr, &offset); if (buf) { PCHAR funcname; - __try { - funcname = ScyllaGetExportNameByScan((PVOID)addr, NULL, 0x50); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - ; - } + funcname = ScanForExport((PVOID)addr, 0x50); if (funcname) snprintf((char *)msg + strlen(msg), sizeof(msg) - strlen(msg) - 1, "%s::%s(0x%x)\n", buf, funcname, offset); else @@ -341,9 +336,12 @@ LONG WINAPI capemon_exception_handler(__in struct _EXCEPTION_POINTERS *Exception stack = (ULONG_PTR *)(ULONG_PTR)(ExceptionInfo->ContextRecord->Esp); ebp_or_rip = (ULONG_PTR)(ExceptionInfo->ContextRecord->Ebp); { - DWORD *tebtmp = (DWORD *)NtCurrentTeb(); - if (tebtmp[0] != 0xffffffff) - seh = ((DWORD *)tebtmp[0])[1]; + PTEB teb = NtCurrentTeb(); + if (teb) { + PEXCEPTION_REGISTRATION_RECORD sehrecord = teb->NtTib.ExceptionList; + if (sehrecord && sehrecord != EXCEPTION_CHAIN_END) + seh = (ULONG_PTR)sehrecord->Handler; + } } #endif @@ -372,12 +370,7 @@ LONG WINAPI capemon_exception_handler(__in struct _EXCEPTION_POINTERS *Exception sprintf(msg, "Exception Caught! PID: %u EIP:", GetCurrentProcessId()); if (dllname) { PCHAR FunctionName; - __try { - FunctionName = ScyllaGetExportNameByScan((PVOID)eip, NULL, 0x50); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - ; - } + FunctionName = ScanForExport((PVOID)eip, 0x50); if (FunctionName) snprintf(msg + strlen(msg), sizeof(msg) - strlen(msg) - 1, " %s::%s(0x%x)", dllname, FunctionName, offset); else @@ -418,12 +411,7 @@ LONG WINAPI capemon_exception_handler(__in struct _EXCEPTION_POINTERS *Exception char *buf = convert_address_to_dll_name_and_offset(stack[i], &offset); if (buf) { PCHAR funcname = NULL; - __try { - funcname = ScyllaGetExportNameByScan((PVOID)eip, NULL, 0x50); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - ; - } + funcname = ScanForExport((PVOID)eip, 0x50); if (funcname) snprintf(msg + strlen(msg), sizeof(msg) - strlen(msg) - 1, " %s::%s(0x%x)\n", buf, funcname, offset); else @@ -455,13 +443,9 @@ LONG WINAPI capemon_exception_handler(__in struct _EXCEPTION_POINTERS *Exception #endif Result = distorm_decode(Offset, (const unsigned char*)eip, 0x100, DecodeType, &DecodedInstruction, 1, &DecodedInstructionsCount); - if (dllname) { - __try { - FunctionName = ScyllaGetExportNameByScan((PVOID)eip, NULL, 0x40); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - ; - } + if (dllname) + { + FunctionName = ScanForExport((PVOID)eip, 0x40); if (FunctionName) { DebugOutput("%s::%s (`) %-20s %-6s%-4s%-30s\n", dllname, FunctionName, (DWORD_PTR)eip, (char*)DecodedInstruction.instructionHex.p, (char*)DecodedInstruction.mnemonic.p, DecodedInstruction.operands.length != 0 ? " " : "", (char*)DecodedInstruction.operands.p); @@ -618,26 +602,24 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) get_our_commandline(); - if (g_config.standalone) { - // initialise CAPE - CAPE_init(); - DebugOutput("Standalone mode initialised.\n"); - return TRUE; - } - InitializeCriticalSection(&g_mutex); InitializeCriticalSection(&g_writing_log_buffer_mutex); // read the config settings - if (!read_config()) -#if CUCKOODBG - ; - else - DebugOutput("Config loaded.\n"); -#else - // if we're not debugging, then failure to read the capemon config should be a critical error - goto abort; -#endif + read_config(); + + if (g_config.standalone) { + // initialize these because some hooks behave badly when they are empty + if (!g_config.w_analyzer[0]) { + for (i = 0; i < ARRAYSIZE(g_config.analyzer); i++) + g_config.w_analyzer[i] = (wchar_t)(unsigned short)g_config.analyzer[i]; + } + if (!g_config.w_results[0]) { + for (i = 0; i < ARRAYSIZE(g_config.results); i++) + g_config.w_results[i] = (wchar_t)(unsigned short)g_config.results[i]; + } + DebugOutput("Running in standalone mode.\n"); + } // don't inject into our own binaries run out of the analyzer directory unless they're the first process (intended) if (wcslen(g_config.w_analyzer) && !wcsnicmp(our_process_path_w, g_config.w_analyzer, wcslen(g_config.w_analyzer)) && !g_config.first_process) diff --git a/capemon.vcxproj b/capemon.vcxproj index 72aa0e63..40811cd7 100644 --- a/capemon.vcxproj +++ b/capemon.vcxproj @@ -97,7 +97,7 @@ - CUCKOODBG;WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS_DEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions);_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS + CUCKOODBG;WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS_DEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions) MultiThreadedDebug Level3 ProgramDatabase @@ -122,7 +122,7 @@ - CUCKOODBG;WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS_DEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions);_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS + CUCKOODBG;WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS_DEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions) MultiThreadedDebug Level3 ProgramDatabase @@ -145,7 +145,7 @@ - WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSNDEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions);_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS + WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSNDEBUG;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions) MultiThreaded Level3 ProgramDatabase @@ -180,7 +180,7 @@ - WIN32;_WIN64;_CRT_SECURE_NO_WARNINGS;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) + WIN32;_WIN64;_CRT_SECURE_NO_WARNINGS;_WINDOWS;_USRDLL;MONGO_HAVE_STDINT;MONGO_STATIC_BUILD;%(PreprocessorDefinitions) MultiThreaded Level3 ProgramDatabase @@ -228,10 +228,9 @@ + - - @@ -266,6 +265,7 @@ + @@ -445,6 +445,7 @@ + diff --git a/capemon.vcxproj.filters b/capemon.vcxproj.filters index efeb5826..223642a4 100644 --- a/capemon.vcxproj.filters +++ b/capemon.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -31,9 +31,6 @@ {2870ff64-a1ae-437b-b93d-b815724102e4} - - {cf36b66e-1611-4be7-abe4-482a5fbacbe2} - @@ -261,12 +258,6 @@ Source Files\CAPE\Scylla - - Source Files\CAPE\w64wow64 - - - Source Files\CAPE - Source Files\CAPE @@ -303,6 +294,12 @@ Source Files + + Source Files\CAPE + + + Source Files + @@ -449,6 +446,12 @@ Header Files\CAPE + + Header Files + + + Header Files\CAPE + diff --git a/config.c b/config.c index f9eb15ed..ebaffbd7 100644 --- a/config.c +++ b/config.c @@ -26,6 +26,7 @@ along with this program. If not, see . #include "unhook.h" #include "Shlwapi.h" #include "CAPE\CAPE.h" +#include "CAPE\Debugger.h" #define SINGLE_STEP_LIMIT 0x4000 // default unless specified in web ui #define DROPPED_LIMIT 100 @@ -40,9 +41,9 @@ along with this program. If not, see . #define PrintEAX 3 extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); +extern void ProcessMessage(DWORD ProcessId, DWORD ThreadId); extern char *our_dll_path; extern char *our_process_name; -extern BOOL PatchByte(LPVOID Address, BYTE Byte); extern wchar_t *our_process_path_w; extern int EntryPointRegister; extern unsigned int TraceDepthLimit, StepLimit, Type0, Type1, Type2; @@ -132,15 +133,39 @@ void parse_config_line(char* line) g_config.hook_range = atoi(value); DebugOutput("Config: hook range limit set to %d", g_config.hook_range); } - else if (!strcmp(key, "hook-type")) { //Valid for 32-bit analyses only. Specifies the hook type to use: direct, indirect, or safe. Safe attempts a Detours-style hook. + else if (!strcmp(key, "hook-type")) { + if (!strcmp(value, "indirect")) { + g_config.hook_type = HOOK_JMP_INDIRECT; + DebugOutput("Config: Indirect hooking selected.\n"); + } + else if (!strcmp(value, "pushret")) { + g_config.hook_type = HOOK_PUSH_RETN; + DebugOutput("Config: Push-ret hooking selected.\n"); + } #ifndef _WIN64 - if (!strcmp(value, "direct")) + else if (!strcmp(value, "direct")) { g_config.hook_type = HOOK_JMP_DIRECT; - else if (!strcmp(value, "indirect")) - g_config.hook_type = HOOK_JMP_INDIRECT; - else if (!strcmp(value, "safe")) + DebugOutput("Config: Direct hooking selected.\n"); + } + else if (!strcmp(value, "safe")) { g_config.hook_type = HOOK_SAFEST; + DebugOutput("Config: Safest hooking selected.\n"); + } +#endif + } +#ifdef _WIN64 + else if (!stricmp(key, "hook-low")) { + g_config.hook_low = value[0]; + if (g_config.hook_low) { + DebugOutput("Config: Hook 'low' enabled (trampoline address < 2GB)\n"); + g_config.hook_type = HOOK_PUSH_RETN; + } + } #endif + else if (!stricmp(key, "hook-restore")) { + g_config.hook_restore = value[0] == '1'; + if (g_config.hook_restore) + DebugOutput("Config: Attempt hook restoration of modifications detected by unhook thread\n"); } else if (!strcmp(key, "disable_hook_content")) { //Set to 1 to remove functionality of all hooks except those critical for monitoring other processes. Set to 2 to apply to all hooks. g_config.disable_hook_content = atoi(value); @@ -225,7 +250,10 @@ void parse_config_line(char* line) } else if (!strcmp(key, "dump-limit")) { //Override the default dump limit of 10 payloads g_config.dump_limit = (unsigned int)strtoul(value, NULL, 10); - DebugOutput("Dropped file limit set to %d.\n", g_config.dump_limit); + if (g_config.dump_limit) + DebugOutput("Payload capture limit set to %d.\n", g_config.dump_limit); + else + DebugOutput("Payload capture limit disabled.\n"); } else if (!strcmp(key, "dropped-limit")) { //Override the default dropped file limit of 100 files g_config.dropped_limit = (unsigned int)strtoul(value, NULL, 10); @@ -240,10 +268,24 @@ void parse_config_line(char* line) } else if (!strcmp(key, "ntdll-protect")) { g_config.ntdll_protect = (unsigned int)strtoul(value, NULL, 10); - if (g_config.ntdll_protect) - DebugOutput("Config: ntdll write protection enabled."); + if (g_config.ntdll_protect) + DebugOutput("Config: ntdll write protection enabled."); + else + DebugOutput("Config: ntdll write protection disabled."); + } + else if (!strcmp(key, "ntdll-unhook")) { + g_config.ntdll_unhook = (unsigned int)strtoul(value, NULL, 10); + if (g_config.ntdll_unhook) + DebugOutput("Config: ntdll unhook protection enabled."); + else + DebugOutput("Config: ntdll unhook protection disabled."); + } + else if (!strcmp(key, "hook-protect")) { + g_config.hook_protect = (unsigned int)strtoul(value, NULL, 10); + if (g_config.hook_protect) + DebugOutput("Config: hook write protection enabled."); else - DebugOutput("Config: ntdll write protection disabled."); + DebugOutput("Config: hook write protection disabled."); } else if (!strcmp(key, "ntdll-remap")) { g_config.ntdll_remap = (unsigned int)strtoul(value, NULL, 10); @@ -366,32 +408,31 @@ void parse_config_line(char* line) if (p) { *p = '\0'; char *p2 = p+1; - unsigned int byte = strtoul(value, NULL, 0); int delta=0; - p = strchr(p2, '+'); + p = strchr(value, '+'); if (p) { delta = strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } else { - p = strchr(p2, '-'); + p = strchr(value, '-'); if (p) { delta = - (int)strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } } - PVOID address = (PVOID)(DWORD_PTR)strtoul(p2, NULL, 0); + PVOID address = (PVOID)(DWORD_PTR)strtoull(value, NULL, 0); if (address) { - DebugOutput("Config: patching address 0x%p with byte 0x%x", address, byte); - PatchByte(address, (BYTE)byte); + DebugOutput("Config: patching address 0x%p with bytes %s", address, p2); + PatchBytes(address, p2); } else - DebugOutput("Config: patch address missing invalid: %s", value); + DebugOutput("Config: patch address missing or invalid: %s", value); } else - DebugOutput("Config: patch byte missing"); + DebugOutput("Config: patch bytes missing"); } else if (!stricmp(key, "bp")) { unsigned int x = 0; @@ -804,43 +845,64 @@ void parse_config_line(char* line) } } else if (!stricmp(key, "br1")) { - int delta=0; - p = strchr(value, '+'); - if (p) { - delta = strtoul(p+1, NULL, 0); - DebugOutput("Config: Delta 0x%x.\n", delta); + p = strchr(value, ':'); + if (p && *(p+1) == ':') { + g_config.br1 = 0; *p = '\0'; + *(p+1) = '\0'; + HANDLE Module = NULL; + if (!stricmp(value, "capemon")) + Module = (HANDLE)g_our_dll_base; + else + Module = GetModuleHandle(value); + g_config.break_on_apiname = strdup(p+2); + g_config.break_on_modname = strdup(value); + if (Module) + g_config.br1 = GetProcAddress(Module, p+2); + else + DebugOutput("Config: Failed to get base for module (%s).\n", g_config.break_on_modname); + if (g_config.br1) { + g_config.debugger = 1; + g_config.bpva1 = 1; + DebugOutput("Config: br1 set to 0x%p (%s::%s).\n", g_config.br1, g_config.break_on_modname, g_config.break_on_apiname); + } + else if (Module) { + unsigned int delta = strtoul(p+2, NULL, 0); + if (delta) { + g_config.br1 = (PBYTE)Module + delta; + g_config.debugger = 1; + g_config.bpva1 = 1; + DebugOutput("Config: br1 set to 0x%p (%s::%s).\n", g_config.br1, g_config.break_on_modname, g_config.break_on_apiname); + } + else + DebugOutput("Config: Failed to get address for function %s::%s\n", g_config.break_on_modname, p+2); + } } else { - p = strchr(value, '-'); + int delta=0; + p = strchr(value, '+'); if (p) { - delta = - (int)strtoul(p+1, NULL, 0); + delta = strtoul(p+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); *p = '\0'; } - } - g_config.br1 = (PVOID)(DWORD_PTR)strtoul(value, NULL, 0); - if (g_config.br1) { - g_config.debugger = 1; - if (delta) { - DebugOutput("Config: br1 was 0x%x (delta 0x%x).\n", g_config.br1, delta); - g_config.br1 = (PVOID)(DWORD_PTR)((PUCHAR)g_config.br1 + delta); + else { + p = strchr(value, '-'); + if (p) { + delta = - (int)strtoul(p+1, NULL, 0); + DebugOutput("Config: Delta 0x%x.\n", delta); + *p = '\0'; + } + } + g_config.br1 = (PVOID)(DWORD_PTR)strtoul(value, NULL, 0); + if (g_config.br1) { + g_config.debugger = 1; + if (delta) { + DebugOutput("Config: br1 was 0x%x (delta 0x%x).\n", g_config.br1, delta); + g_config.br1 = (PVOID)(DWORD_PTR)((PUCHAR)g_config.br1 + delta); + } + DebugOutput("Config: br1 set to 0x%x (break-on-return)\n", g_config.br1); } - DebugOutput("Config: br1 set to 0x%x (break-on-return)\n", g_config.br1); - } - } - else if (!stricmp(key, "br2")) { - g_config.br2 = (PVOID)(DWORD_PTR)strtoul(value, NULL, 0); - if (g_config.br2) { - g_config.debugger = 1; - DebugOutput("Config: br2 set to 0x%x (break-on-return)\n", g_config.br2); - } - } - else if (!stricmp(key, "br3")) { - g_config.br3 = (PVOID)(DWORD_PTR)strtoul(value, NULL, 0); - if (g_config.br3) { - g_config.debugger = 1; - DebugOutput("Config: br3 set to 0x%x (break-on-return)\n", g_config.br3); } } else if (!stricmp(key, "sysbp")) { @@ -853,18 +915,18 @@ void parse_config_line(char* line) *p2 = '\0'; } int delta=0; - p2 = strchr(value, '+'); - if (p2) { - delta = strtoul(p2+1, NULL, 0); + char *p3 = strchr(value, '+'); + if (p3) { + delta = strtoul(p3+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); - *p2 = '\0'; + *p3 = '\0'; } else { - p2 = strchr(value, '-'); - if (p2) { - delta = - (int)strtoul(p2+1, NULL, 0); + p3 = strchr(value, '-'); + if (p3) { + delta = - (int)strtoul(p3+1, NULL, 0); DebugOutput("Config: Delta 0x%x.\n", delta); - *p2 = '\0'; + *p3 = '\0'; } } for (unsigned int i = 0; i < ARRAYSIZE(g_config.sysbp); i++) { @@ -1262,8 +1324,11 @@ void parse_config_line(char* line) g_config.amsidump = value[0] == '1'; if (g_config.amsidump) DebugOutput("AMSI dumping enabled.\n"); - else - DebugOutput("AMSI dumping disabled.\n"); + } + else if (!stricmp(key, "jit-dumps")) { + g_config.jit_dumps = (unsigned int)strtoul(value, NULL, 10); + if (g_config.jit_dumps) + DebugOutput(".NET JIT cache dumps enabled, limit %d\n", g_config.jit_dumps); } else if (!stricmp(key, "minhook")) { g_config.minhook = value[0] == '1'; @@ -1275,6 +1340,11 @@ void parse_config_line(char* line) if (g_config.zerohook) DebugOutput("All* hooks disabled (*except essential)\n"); } + else if (!stricmp(key, "native")) { + g_config.native = value[0] == '1'; + if (g_config.native) + DebugOutput("Native hooks only (ntdll)\n"); + } else if (!stricmp(key, "tlsdump")) { g_config.tlsdump = value[0] == '1'; if (g_config.tlsdump) { @@ -1323,6 +1393,30 @@ void parse_config_line(char* line) if (g_config.interactive == 1) DebugOutput("Interactive desktop enabled.\n"); } + else if (!stricmp(key, "idbg")) { + g_config.idbg = value[0] == '1'; + if (g_config.idbg) + DebugOutput("Interactive debugger enabled (CAPEsolo)\n"); + } + else if (!stricmp(key, "snaps")) { + g_config.snaps = value[0] == '1'; + if (g_config.snaps) + DebugOutput("Loader snaps enabled.\n"); + } + else if (!stricmp(key, "hook-watch")) { + g_config.hook_watch = value[0] == '1'; + if (g_config.hook_watch) + DebugOutput("Config: Hook watch enabled.\n"); + } + else if (!stricmp(key, "monitor")) { + DWORD pid = (unsigned int)strtoul(value, NULL, 10); + if (!pid && !stricmp(value, "explorer")) + GetWindowThreadProcessId(GetShellWindow(), &pid); + if (pid) { + ProcessMessage(pid, 0); + DebugOutput("Config: Injected monitor into pid %d.\n", pid); + } + } else if (stricmp(key, "no-iat")) DebugOutput("Monitor config - unrecognised key %s.\n", key); @@ -1331,7 +1425,7 @@ void parse_config_line(char* line) } } -int read_config(void) +void read_config(void) { char buf[32768], config_fname[MAX_PATH]; FILE *fp; @@ -1344,6 +1438,7 @@ int read_config(void) #else g_config.hook_type = HOOK_HOTPATCH_JMP_INDIRECT; #endif + g_config.hook_restore = 1; g_config.protected_pids = 1; g_config.ntdll_protect = 1; g_config.ntdll_remap = 1; @@ -1357,13 +1452,10 @@ int read_config(void) g_config.api_rate_cap = 1; g_config.yarascan = 1; g_config.loaderlock_scans = 1; - g_config.amsidump = 1; g_config.syscall = 1; StepLimit = SINGLE_STEP_LIMIT; - strcpy(g_config.results, g_config.analyzer); - memset(g_config.str, 0, MAX_PATH); memset(g_config.pythonpath, 0, MAX_PATH); memset(g_config.w_results, 0, sizeof(WCHAR)*MAX_PATH); @@ -1376,6 +1468,8 @@ int read_config(void) PathRemoveFileSpec(g_config.analyzer); // remove filename sprintf(config_fname, "%s\\%u.ini", g_config.analyzer, GetCurrentProcessId()); + strcpy(g_config.results, g_config.analyzer); + fp = fopen(config_fname, "r"); // backward compatibility @@ -1385,17 +1479,14 @@ int read_config(void) fp = fopen(config_fname, "r"); } - // for debugging purposes if (fp == NULL) { memset(config_fname, 0, sizeof(config_fname)); sprintf(config_fname, "%s\\config.ini", g_config.analyzer); fp = fopen(config_fname, "r"); - if (fp == NULL) - return 0; } - memset(buf, 0, sizeof(buf)); if (fp) { + memset(buf, 0, sizeof(buf)); while (fgets(buf, sizeof(buf), fp) != NULL) { // cut off the newline char *p = strchr(buf, '\r'); @@ -1459,7 +1550,7 @@ int read_config(void) ImageBaseRemapped = TRUE; if (!our_process_name) - return 1; + return; if (!_stricmp(our_process_name, "explorer.exe") && g_config.interactive == 1) { @@ -1480,6 +1571,7 @@ int read_config(void) g_config.procmemdump = 0; g_config.yarascan = 0; g_config.ntdll_protect = 0; + g_config.hook_restore = 0; DebugOutput("Firefox-specific hook-set enabled.\n"); } if (!_stricmp(our_process_name, "iexplore.exe")) @@ -1490,6 +1582,7 @@ int read_config(void) g_config.ntdll_protect = 0; g_config.procmemdump = 0; g_config.yarascan = 0; + g_config.hook_restore = 0; DebugOutput("Internet Explorer-specific hook-set enabled.\n"); } @@ -1501,6 +1594,7 @@ int read_config(void) g_config.ntdll_protect = 0; g_config.yarascan = 0; g_config.procmemdump = 0; + g_config.hook_restore = 0; DebugOutput("Edge-specific hook-set enabled.\n"); } @@ -1512,6 +1606,7 @@ int read_config(void) g_config.ntdll_protect = 0; g_config.yarascan = 0; g_config.procmemdump = 0; + g_config.hook_restore = 0; DebugOutput("Chrome-specific hook-set enabled.\n"); } @@ -1523,6 +1618,7 @@ int read_config(void) g_config.procmemdump = 0; g_config.yarascan = 0; g_config.ntdll_protect = 0; + g_config.hook_restore = 0; DebugOutput("Microsoft Office settings enabled.\n"); } } @@ -1625,5 +1721,5 @@ int read_config(void) } } - return 1; + return; } diff --git a/config.h b/config.h index ba57aeb4..d4377910 100644 --- a/config.h +++ b/config.h @@ -23,6 +23,19 @@ along with this program. If not, see . #define BREAKPOINT_MAX 0x100 #define SYSBP_MAX 0x400 +// Anti-analysis related items so we can be consistent across various APIs +#define SPOOFED_DISK_SIZE 0x10000000000ull // 1TB +#define RECOVERY_PARTITION_SIZE 0x1f2af000 // Taken from random Win10 install + +#define SPOOFED_GPU_RAM 0x100000000l // 4GB +#define SPOOFED_GPU_NAME L"NVIDIA GTX 1650" + +#define SPOOFED_RAM 0x100000000l // 4GB +#define SPOOFED_RAM_RESERVED 0x410000 + +#define SPOOFED_CPU_CORE_NUM 4 + + struct _g_config { // name of the pipe to communicate with cuckoo wchar_t pipe_name[MAX_PATH]; @@ -104,6 +117,12 @@ struct _g_config { // Default hook type (may be overridden for specific functions) int hook_type; + // Hook trampoline allocated in low (<2GB) memory (64-bit) + int hook_low; + + // Attempt to restore modified hooks detected by unhook thread + int hook_restore; + // Disable hook content int disable_hook_content; @@ -125,6 +144,12 @@ struct _g_config { // ntdll write protection unsigned int ntdll_protect; + // ntdll unhook protection (NtReadFile-based) + unsigned int ntdll_unhook; + + // hook write protection + unsigned int hook_protect; + // ntdll remap protection unsigned int ntdll_remap; @@ -195,6 +220,9 @@ struct _g_config { // Enable debugger int debugger; + // Enable interactive debugger (CAPEsolo) + int idbg; + // Fake RDTSC int fake_rdtsc; @@ -216,12 +244,18 @@ struct _g_config { // AMSI dumps (Win10+) int amsidump; + // .NET JIT cache dumps + unsigned int jit_dumps; + // Minimal hook set int minhook; // Zero hook set int zerohook; + // Native hook set + int native; + // Microsoft Office hook set int office; @@ -246,6 +280,9 @@ struct _g_config { // Specify custom trace stepping behavior int stepmode; + // Enable Windows Loader snaps output + int snaps; + char *break_on_apiname; char *break_on_modname; char break_on_return[MAX_PATH]; @@ -283,10 +320,11 @@ struct _g_config { int base_on_caller; int trace_times; char *trace_into_api[EXCLUSION_MAX]; + int hook_watch; }; extern struct _g_config g_config; -int read_config(void); +void read_config(void); -#endif +#endif \ No newline at end of file diff --git a/hook_crypto.c b/hook_crypto.c index 57e287c0..9516494b 100644 --- a/hook_crypto.c +++ b/hook_crypto.c @@ -278,12 +278,12 @@ HOOKDEF(BOOL, WINAPI, CryptDeriveKey, } HOOKDEF(BOOL, WINAPI, CryptExportKey, - _In_ HCRYPTKEY hKey, - _In_ HCRYPTKEY hExpKey, - _In_ DWORD dwBlobType, - _In_ DWORD dwFlags, + _In_ HCRYPTKEY hKey, + _In_ HCRYPTKEY hExpKey, + _In_ DWORD dwBlobType, + _In_ DWORD dwFlags, _Out_ BYTE *pbData, - _Inout_ DWORD *pdwDataLen + _Inout_ DWORD *pdwDataLen ) { BOOL ret = Old_CryptExportKey(hKey, hExpKey, dwBlobType, dwFlags, pbData, pdwDataLen); if (pbData && pdwDataLen) @@ -291,6 +291,19 @@ HOOKDEF(BOOL, WINAPI, CryptExportKey, return ret; } +HOOKDEF(BOOL, WINAPI, CryptDuplicateKey, + _In_ HCRYPTKEY hKey, + _In_ DWORD* pdwReserved, + _In_ DWORD dwFlags, + _Out_ HCRYPTKEY* phKey +) { + BOOL ret = Old_CryptDuplicateKey(hKey, pdwReserved, dwFlags, phKey); + if (ret) { + LOQ_bool("crypto", "pph", "OldCryptKey", hKey, "NewCryptKey", *phKey, "Flags", dwFlags); + } + return ret; +} + HOOKDEF(BOOL, WINAPI, CryptDestroyKey, _In_ HCRYPTKEY hKey ) { @@ -346,6 +359,19 @@ HOOKDEF(HRESULT, WINAPI, HTTPSFinalProv, return ret; } +HOOKDEF(NTSTATUS, WINAPI, WTGetSignatureInfo, + _In_ LPWSTR pszFile, + _In_ HANDLE hFile, + _In_ DWORD sigInfoFlags, + _Out_ PVOID psiginfo, + _Out_ PVOID ppCertContext, + _Out_ PVOID phWVTStateData +) { + NTSTATUS ret = Old_WTGetSignatureInfo(pszFile, hFile, sigInfoFlags, psiginfo, ppCertContext, phWVTStateData); + LOQ_ntstatus("crypto", "uhh", "FileName", pszFile, "hFile", hFile, "InfoFlags", sigInfoFlags); + return ret; +} + HOOKDEF(BOOL, WINAPI, CryptDecodeObjectEx, _In_ DWORD dwCertEncodingType, _In_ LPCSTR lpszStructType, diff --git a/hook_file.c b/hook_file.c index 0f4257c0..b7e46e75 100644 --- a/hook_file.c +++ b/hook_file.c @@ -26,6 +26,7 @@ along with this program. If not, see . #include "misc.h" #include "ignore.h" #include "lookup.h" +#include "hook_file.h" #include "config.h" #define DUMP_FILE_MASK ((GENERIC_ALL | GENERIC_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | MAXIMUM_ALLOWED) & ~SYNCHRONIZE) @@ -507,6 +508,9 @@ HOOKDEF(NTSTATUS, WINAPI, NtReadFile, fname = calloc(32768, sizeof(wchar_t)); path_from_handle(FileHandle, fname, 32768); + if (!g_config.no_stealth && g_config.ntdll_unhook && InitialBufferLength) + prevent_module_unhooking(Buffer, fname); + if (read_count < 50) LOQ_ntstatus("filesystem", "pFbl", "FileHandle", FileHandle, "HandleName", fname, "Buffer", InitialBufferLength, InitialBuffer, "Length", AccumulatedLength); @@ -605,25 +609,293 @@ HOOKDEF(NTSTATUS, WINAPI, NtDeviceIoControlFile, __out PVOID OutputBuffer, __in ULONG OutputBufferLength ) { + lasterror_t lasterrors; + get_lasterrors(&lasterrors); ULONG_PTR length; - NTSTATUS ret = Old_NtDeviceIoControlFile(FileHandle, Event, - ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, - InputBuffer, InputBufferLength, OutputBuffer, - OutputBufferLength); + ULONG origbufferloglen = min((ULONG)buffer_log_max, InputBufferLength); + PCHAR origbuffer = malloc(origbufferloglen); + BOOLEAN hasorigbuffer = FALSE; + NTSTATUS ret; + + // Save off a copy of the buffer before calling the hook, as it may not be the same after the call + if (origbuffer) { + __try { + memcpy(origbuffer, InputBuffer, origbufferloglen); + hasorigbuffer = TRUE; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + hasorigbuffer = FALSE; + } + } + + + /* + * Check for AFD_SEND, we need to aggregate the buffer before calling the hook as calling the hook will clobber InputBuffer + * Simply do the logic to generate the buffer we want to log here, we'll log it later with other IOCTL logging. + */ + PCHAR aggregated_send_payload = NULL; + ULONG send_payload_size_to_log = 0; + if (IoControlCode == IOCTL_AFD_SEND) { + __try { + if (InputBuffer && InputBufferLength >= sizeof(AFD_SEND_INFO)) { + PAFD_SEND_INFO sendInfo = (PAFD_SEND_INFO)InputBuffer; + if (sendInfo->AfdBufferArray && sendInfo->AfdBufferCount > 0) { + ULONG total_send_size = 0; + for (ULONG i = 0; i < sendInfo->AfdBufferCount; i++) { + total_send_size += sendInfo->AfdBufferArray[i].len; + } + + if (total_send_size > 0) { + send_payload_size_to_log = min((ULONG)buffer_log_max, total_send_size); + aggregated_send_payload = (PCHAR)malloc(send_payload_size_to_log); + if (aggregated_send_payload) { + PCHAR current_pos = aggregated_send_payload; + ULONG bytes_copied = 0; + for (ULONG i = 0; i < sendInfo->AfdBufferCount && bytes_copied < send_payload_size_to_log; i++) { + PAFD_WSABUF current_source_buf = &sendInfo->AfdBufferArray[i]; + ULONG bytes_to_copy = min(current_source_buf->len, send_payload_size_to_log - bytes_copied); + if (bytes_to_copy > 0) { + memcpy(current_pos, current_source_buf->buf, bytes_to_copy); + current_pos += bytes_to_copy; + bytes_copied += bytes_to_copy; + } + } + } + } + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) { + // On exception, null out the buffer (if needed) so we can log generically later + if (aggregated_send_payload) { + free(aggregated_send_payload); + aggregated_send_payload = NULL; + } + } + } + set_lasterrors(&lasterrors); + + ret = Old_NtDeviceIoControlFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, + OutputBuffer, OutputBufferLength); if (NT_SUCCESS(ret)) length = IoStatusBlock->Information; else length = 0; - LOQ_ntstatus("device", "phbb", "FileHandle", FileHandle, - "IoControlCode", IoControlCode, - "InputBuffer", InputBufferLength, InputBuffer, - "OutputBuffer", length, OutputBuffer); + + get_lasterrors(&lasterrors); + + wchar_t* fname = NULL; + fname = calloc(32768, sizeof(wchar_t)); + if (fname) { + path_from_handle(FileHandle, fname, 32768); + } + + switch (IoControlCode) { + case IOCTL_AFD_BIND: + if (InputBufferLength >= sizeof(AFD_BindDataStruct) && hasorigbuffer) { + PAFD_BindDataStruct buf = (PAFD_BindDataStruct)origbuffer; + __try { + in_sockaddr* sockAddr = &buf->SockAddr; + char ipString[16]; + our_inet_ntop(AF_INET, &sockAddr->sin_addr, ipString, sizeof(ipString)); + unsigned short port = our_ntohs(sockAddr->sin_port); + LOQ_ntstatus( + "network", "pFhsi", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "ip", ipString, + "port", port + ); + break; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + // If we're here, just use generic logging for NtDeviceIoControlFile + goto generic_log; + } + } + else { + goto generic_log; + } + + case IOCTL_AFD_CONNECT: + if (InputBufferLength >= sizeof(AFD_ConnectDataStruct) && hasorigbuffer) { + AFD_ConnectDataStruct* buf = (AFD_ConnectDataStruct*)origbuffer; + __try { + in_sockaddr* sockAddr = &buf->SockAddr; + char ipString[16]; + our_inet_ntop(AF_INET, &sockAddr->sin_addr, ipString, sizeof(ipString)); + unsigned short port = our_ntohs(sockAddr->sin_port); + LOQ_ntstatus( + "network", "pFhsi", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "ip", ipString, + "port", port + ); + break; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + // If we're here, just use generic logging for NtDeviceIoControlFile + goto generic_log; + } + } + else { + // Unexpected InputBufferLength for this IOCTL, goto generic log + goto generic_log; + } + + case IOCTL_AFD_RECV: + __try { + DWORD wait_status = -1; // Init to -1 as WAIT_OBJECT_0 is 0 + if (InputBuffer && InputBufferLength >= sizeof(AFD_RECV_INFO)) { + if (ret == STATUS_PENDING && Event) { + /* + * We make some assumptions here; If we have an event it is an async call so we also assume: + * 1) The callee reset the event prior to calling this NtDeviceIoControlFile API + * 2) The callee will use NtWaitForSingleObject or similar, after calling NtDeviceIoControlFile + * + * NtWaitForSingleObject will wait on a handle, and if the callee is expecting to use NtWaitForSingleObject + * (as done in NTSockets) we'll end up deadlocking unless we also use SetEvent. This is why we use our own + * function to check for the event being signaled as it we do not call any APIs which interfere with the + * status of the event itself, so it is safe for the above assumptions as well as cases where the assumption + * may be wrong. + */ + wait_status = wait_for_event_to_be_signaled(Event, 5000); // Allow 5 seconds of time for the event to trigger + if (wait_status == WAIT_OBJECT_0) { + ret = IoStatusBlock->Status; + if (NT_SUCCESS(ret)) + length = IoStatusBlock->Information; + else + length = 0; + } + } + if (length == 0) { + /* + * This can happen if we have a NTSTATUS of 0x00000103 (STATUS_PENDING) + * Unideal, likely in the background kernel has provided the recv buffer to the API + */ + if (wait_status == WAIT_TIMEOUT) { + LOQ_ntstatus( + "network", "pFhs", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "Event", "Timeout waiting for recv, buffer will likely missing from API logs" + ); + } + else { + LOQ_ntstatus( + "network", "pFhs", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "Event", "Zero-byte receive" + ); + } + break; + } + + PAFD_RECV_INFO recvInfo = (PAFD_RECV_INFO)InputBuffer; + if (recvInfo->AfdBufferArray && recvInfo->AfdBufferCount > 0) { + ULONG total_payload_to_log = min((ULONG)buffer_log_max, (ULONG)length); + PCHAR aggregated_payload = (PCHAR)malloc(total_payload_to_log); + if (!aggregated_payload) { + // Sanity check + goto generic_log; + } + + PCHAR current_pos = aggregated_payload; + ULONG bytes_copied = 0; + ULONG bytes_remaining_from_total = (ULONG)length; + for (ULONG i = 0; i < recvInfo->AfdBufferCount && bytes_copied < total_payload_to_log; i++) { + PAFD_WSABUF current_source_buf = &recvInfo->AfdBufferArray[i]; + ULONG bytes_in_source = min(bytes_remaining_from_total, current_source_buf->len); + ULONG bytes_to_copy = min(bytes_in_source, total_payload_to_log - bytes_copied); + if (bytes_to_copy > 0) { + memcpy(current_pos, current_source_buf->buf, bytes_to_copy); + current_pos += bytes_to_copy; + bytes_copied += bytes_to_copy; + } + bytes_remaining_from_total -= bytes_in_source; + } + + LOQ_ntstatus( + "network", "pFhbi", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "buffer", (size_t)bytes_copied, aggregated_payload, + "length", length + ); + free(aggregated_payload); + break; + } + } + else { + // If the initial InputBuffer validation fails, go to the generic logger. + goto generic_log; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) { + // On exception, log generically + goto generic_log; + } + + case IOCTL_AFD_SEND: + // We parsed the send buffer prior to calling the hook, if the buffer is non-null, log it, otherwise log generically + if (aggregated_send_payload) { + LOQ_ntstatus( + "network", "pFhbi", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "buffer", (size_t)send_payload_size_to_log, aggregated_send_payload, + "length", (ULONG)length // Log the actual bytes sent from the result + ); + free(aggregated_send_payload); + break; + } + else { + goto generic_log; + } + default: + generic_log: + if (fname) { + LOQ_ntstatus( + "device", "pFhbb", + "FileHandle", FileHandle, + "HandleName", fname, + "IoControlCode", IoControlCode, + "InputBuffer", InputBufferLength, InputBuffer, + "OutputBuffer", length, OutputBuffer + ); + } + else { + LOQ_ntstatus( + "device", "phbb", + "FileHandle", FileHandle, + "IoControlCode", IoControlCode, + "InputBuffer", InputBufferLength, InputBuffer, + "OutputBuffer", length, OutputBuffer + ); + } + } if (!g_config.no_stealth && NT_SUCCESS(ret) && OutputBuffer) perform_device_fakery(OutputBuffer, (ULONG)length, IoControlCode); + if (origbuffer) + free(origbuffer); + + if (fname) + free(fname); + + set_lasterrors(&lasterrors); + return ret; } @@ -1351,10 +1623,8 @@ HOOKDEF(BOOL, WINAPI, GetDiskFreeSpaceExA, ) { BOOL ret = Old_GetDiskFreeSpaceExA(lpDirectoryName, lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); LOQ_bool("filesystem", "s", "DirectoryName", lpDirectoryName); - - /* Fake harddrive size to 256GB */ if (!g_config.no_stealth && ret && lpTotalNumberOfBytes) { - lpTotalNumberOfBytes->QuadPart = 256060514304L; + lpTotalNumberOfBytes->QuadPart = SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE; } return ret; @@ -1368,10 +1638,8 @@ HOOKDEF(BOOL, WINAPI, GetDiskFreeSpaceExW, ) { BOOL ret = Old_GetDiskFreeSpaceExW(lpDirectoryName, lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); LOQ_bool("filesystem", "u", "DirectoryName", lpDirectoryName); - - /* Fake harddrive size to 256GB */ if (!g_config.no_stealth && ret && lpTotalNumberOfBytes) { - lpTotalNumberOfBytes->QuadPart = 256060514304L; + lpTotalNumberOfBytes->QuadPart = SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE; } return ret; @@ -1386,12 +1654,10 @@ HOOKDEF(BOOL, WINAPI, GetDiskFreeSpaceA, ) { BOOL ret = Old_GetDiskFreeSpaceA(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters); LOQ_bool("filesystem", "s", "RootPathName", lpRootPathName); - - /* Fake harddrive size to 256GB */ if (!g_config.no_stealth) { __try { if (lpTotalNumberOfClusters && lpSectorsPerCluster && lpBytesPerSector && *lpSectorsPerCluster && *lpBytesPerSector) { - *lpTotalNumberOfClusters = (DWORD)(256060514304L / (*lpSectorsPerCluster * *lpBytesPerSector)); + *lpTotalNumberOfClusters = (DWORD)((SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE) / (*lpSectorsPerCluster * *lpBytesPerSector)); } } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1411,12 +1677,10 @@ HOOKDEF(BOOL, WINAPI, GetDiskFreeSpaceW, ) { BOOL ret = Old_GetDiskFreeSpaceW(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters); LOQ_bool("filesystem", "u", "RootPathName", lpRootPathName); - - /* Fake harddrive size to 256GB */ if (!g_config.no_stealth) { __try { if (lpTotalNumberOfClusters && lpSectorsPerCluster && lpBytesPerSector && *lpSectorsPerCluster && *lpBytesPerSector) { - *lpTotalNumberOfClusters = (DWORD)(256060514304L / (*lpSectorsPerCluster * *lpBytesPerSector)); + *lpTotalNumberOfClusters = (DWORD)((SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE) / (*lpSectorsPerCluster * *lpBytesPerSector)); } } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1638,4 +1902,4 @@ HOOKDEF(DWORD, WINAPI, RmStartSession, LOQ_ntstatus("filesystem", ""); return ret; -} +} \ No newline at end of file diff --git a/hook_file.h b/hook_file.h index a8dbb063..25d63f14 100644 --- a/hook_file.h +++ b/hook_file.h @@ -21,3 +21,9 @@ void file_init(); void file_close(HANDLE file_handle); void handle_duplicate(HANDLE old_handle, HANDLE new_handle); void remove_file_from_log_tracking(HANDLE fhandle); + +#define IOCTL_AFD_BIND 0x00012003 +#define IOCTL_AFD_CONNECT 0x00012007 +#define IOCTL_AFD_SEND 0x0001201f +#define IOCTL_AFD_RECV 0x00012017 +#define IOCTL_WMIQUERYALLDATA 0x00224000 diff --git a/hook_misc.c b/hook_misc.c index c26a93f0..2de97c84 100644 --- a/hook_misc.c +++ b/hook_misc.c @@ -26,6 +26,7 @@ along with this program. If not, see . #include "hook_sleep.h" #include "config.h" #include "ignore.h" +#include "powerbase.h" #include "CAPE\CAPE.h" #include "CAPE\Injection.h" #include "CAPE\Debugger.h" @@ -190,6 +191,21 @@ HOOKDEF(NTSTATUS, WINAPI, LdrGetDllHandle, return ret; } +HOOKDEF(NTSTATUS, WINAPI, LdrGetDllHandleEx, + __in ULONG Flags, + __in_opt PWSTR DllPath, + __in PULONG DllCharacteristics, + __in PUNICODE_STRING DllName, + __out_opt PVOID *DllHandle +) { + NTSTATUS ret = Old_LdrGetDllHandleEx(Flags, DllPath, DllCharacteristics, DllName, DllHandle); + if (DllHandle) + LOQ_ntstatus("system", "oP", "DllName", DllName, "DllHandle", DllHandle); + else + LOQ_ntstatus("system", "o", "DllName", DllName); + return ret; +} + HOOKDEF(NTSTATUS, WINAPI, LdrGetProcedureAddress, __in HMODULE ModuleHandle, __in_opt PANSI_STRING FunctionName, @@ -763,8 +779,8 @@ HOOKDEF(void, WINAPI, GetSystemInfo, Old_GetSystemInfo(lpSystemInfo); - if (!g_config.no_stealth && lpSystemInfo->dwNumberOfProcessors < 4) - lpSystemInfo->dwNumberOfProcessors = 4; + if (!g_config.no_stealth && lpSystemInfo->dwNumberOfProcessors < SPOOFED_CPU_CORE_NUM) + lpSystemInfo->dwNumberOfProcessors = SPOOFED_CPU_CORE_NUM; LOQ_void("misc", ""); @@ -817,7 +833,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtQuerySystemInformation, if (!g_config.no_stealth && SystemInformationClass == SystemBasicInformation && SystemInformationLength >= sizeof(SYSTEM_BASIC_INFORMATION) && NT_SUCCESS(ret)) { PSYSTEM_BASIC_INFORMATION p = (PSYSTEM_BASIC_INFORMATION)SystemInformation; - p->NumberOfProcessors = 4; + p->NumberOfProcessors = SPOOFED_CPU_CORE_NUM; } /* This is nearly arbitrary and simply designed to test whether the Upatre author(s) or others @@ -1165,8 +1181,8 @@ HOOKDEF(void, WINAPI, GlobalMemoryStatus, ) { BOOL ret = TRUE; Old_GlobalMemoryStatus(lpBuffer); - if (!g_config.no_stealth && lpBuffer->dwTotalPhys < 0x400000000) - lpBuffer->dwTotalPhys = (SIZE_T)0x400000000; + if (!g_config.no_stealth && lpBuffer->dwTotalPhys < SPOOFED_RAM) + lpBuffer->dwTotalPhys = (SIZE_T)SPOOFED_RAM; LOQ_void("misc", "ii", "MemoryLoad", lpBuffer->dwMemoryLoad, "TotalPhysicalMB", lpBuffer->dwTotalPhys / (1024 * 1024)); } @@ -1174,12 +1190,22 @@ HOOKDEF(BOOL, WINAPI, GlobalMemoryStatusEx, _Out_ LPMEMORYSTATUSEX lpBuffer ) { BOOL ret = Old_GlobalMemoryStatusEx(lpBuffer); - if (ret && !g_config.no_stealth && lpBuffer->ullTotalPhys < 0x400000000) - lpBuffer->ullTotalPhys = 0x400000000; + if (ret && !g_config.no_stealth && lpBuffer->ullTotalPhys < SPOOFED_RAM) + lpBuffer->ullTotalPhys = SPOOFED_RAM; LOQ_void("misc", "ii", "MemoryLoad", lpBuffer->dwMemoryLoad, "TotalPhysicalMB", lpBuffer->ullTotalPhys / (1024 * 1024)); return ret; } +HOOKDEF(BOOL, WINAPI, GetPhysicallyInstalledSystemMemory, + _Out_ PULONGLONG TotalMemoryInKilobytes +) { + BOOL ret = Old_GetPhysicallyInstalledSystemMemory(TotalMemoryInKilobytes); + if (ret && !g_config.no_stealth && (*TotalMemoryInKilobytes * 1024) < SPOOFED_RAM) + *TotalMemoryInKilobytes = SPOOFED_RAM / 1024; + LOQ_void("misc", "i", "TotalMemoryInKilobytes", *TotalMemoryInKilobytes); + return ret; +} + HOOKDEF(BOOL, WINAPI, SystemParametersInfoA, _In_ UINT uiAction, _In_ UINT uiParam, @@ -1937,4 +1963,69 @@ HOOKDEF(UINT, WINAPI, MsiInstallProductW, UINT ret = Old_MsiInstallProductW(szPackagePath, szCommandLine); LOQ_zero("misc", "uu", "PackagePath", szPackagePath, "CommandLine", szCommandLine); return ret; -} \ No newline at end of file +} + +HOOKDEF(ULONG, __fastcall, vDbgPrintExWithPrefixInternal, + __in PCH Prefix, + __in ULONG ComponentId, + __in ULONG Level, + __in PCHAR Format, + __in va_list arglist, + __in BOOLEAN HandleBreakpoint +) { + UCHAR Buffer[512]; + size_t cb = strlen(Prefix); + strcpy(Buffer, Prefix); + cb = _vsnprintf(Buffer + cb, sizeof(Buffer) - cb, Format, arglist) + cb; + + if (cb == -1) { + cb = sizeof(Buffer); + Buffer[sizeof(Buffer) - 1] = '\n'; + } + + DebugOutput("%s", Buffer); + + return Old_vDbgPrintExWithPrefixInternal(Prefix, ComponentId, Level, Format, arglist, HandleBreakpoint); +} + +HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA, + _In_ PCSTR Filename, + _Out_ PDWORD HeaderSum, + _Out_ PDWORD CheckSum +) { + DWORD ret = Old_MapFileAndCheckSumA(Filename, HeaderSum, CheckSum); + + if (HeaderSum && CheckSum) + *CheckSum = *HeaderSum; + + if (HeaderSum && CheckSum) + LOQ_zero("misc", "fhh", "Filename", Filename, "HeaderSum", *HeaderSum, "CheckSum", *CheckSum); + else + LOQ_zero("misc", "f", "Filename", Filename); + + return ret; +} + +HOOKDEF(NTSTATUS, WINAPI, NtPowerInformation, + __in POWER_INFORMATION_LEVEL InformationLevel, + __in_opt PVOID InputBuffer, + __in ULONG InputBufferLength, + __out_opt PVOID OutputBuffer, + __in ULONG OutputBufferLength +) { + NTSTATUS ret = Old_NtPowerInformation(InformationLevel, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength); + if (ret == 0 && OutputBuffer && InformationLevel == SystemPowerCapabilities && OutputBufferLength >= sizeof(SYSTEM_POWER_CAPABILITIES)) { + // Most VM systems does not support either S0 or S3 sleep, which can be used to detect the presence of a VM. + // S0, S4 and S5 being enabled is typical for a normal Modern Standby machine. + SYSTEM_POWER_CAPABILITIES* ptr = (SYSTEM_POWER_CAPABILITIES *)OutputBuffer; + ptr->AoAc = 1; + ptr->SystemS4 = 1; + ptr->SystemS5 = 1; + ptr->ThermalControl = 1; + } + LOQ_ntstatus("device", "ibb", + "InformationLevel", InformationLevel, + "InputBuffer", InputBufferLength, InputBuffer, + "OutputBuffer", OutputBufferLength, OutputBuffer); + return ret; +} diff --git a/hook_process.c b/hook_process.c index 6b342b5d..e563a782 100644 --- a/hook_process.c +++ b/hook_process.c @@ -42,12 +42,12 @@ extern void ProtectionHandler(PVOID BaseAddress, ULONG Protect, PULONG OldProtec extern void FreeHandler(PVOID BaseAddress), ProcessMessage(DWORD ProcessId, DWORD ThreadId); extern void ProcessTrackedRegion(), DebuggerShutdown(), DumpStrings(); extern LONG WINAPI mini_handler(__in struct _EXCEPTION_POINTERS *ExceptionInfo); - -extern HANDLE g_terminate_event_handle; +extern BOOL dll_is_hooked(const wchar_t *library); extern BOOL CAPEExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context); extern void file_handle_terminate(); extern int DoProcessDump(); extern PVOID GetHookCallerBase(); +extern HANDLE g_terminate_event_handle; extern BOOL ProcessDumped; static BOOL ntdll_protect_logged; @@ -127,6 +127,46 @@ HOOKDEF(BOOL, WINAPI, Module32FirstW, return ret; } +HOOKDEF(BOOL, WINAPI, WTSEnumerateProcessesW, + _In_ HANDLE hServer, + _In_ DWORD Reserved, + _In_ DWORD Version, + _Out_ PWTS_PROCESS_INFOW* ppProcessInfo, + _Out_ DWORD* pCount +) { + BOOL ret = Old_WTSEnumerateProcessesW(hServer, Reserved, Version, ppProcessInfo, pCount); + + LOQ_bool("process", ""); + + return ret; +} + +HOOKDEF(BOOL, WINAPI, WTSEnumerateProcessesExW, + _In_ HANDLE hServer, + _Inout_ DWORD* pLevel, + _In_ DWORD SessionId, + _Out_ LPWSTR* ppProcessInfo, + _Out_ DWORD* pCount +) { + BOOL ret = Old_WTSEnumerateProcessesExW(hServer, pLevel, SessionId, ppProcessInfo, pCount); + + LOQ_bool("process", ""); + + return ret; +} + +HOOKDEF(BOOL, WINAPI, K32EnumProcesses, + _Out_writes_bytes_(cb) DWORD* lpidProcess, + _In_ DWORD cb, + _Out_ LPDWORD lpcbNeeded +) { + BOOL ret = Old_K32EnumProcesses(lpidProcess, cb, lpcbNeeded); + + LOQ_bool("process", ""); + + return ret; +} + HOOKDEF(UINT, WINAPI, WinExec, __in LPCSTR lpCmdLine, __in UINT uCmdShow @@ -317,7 +357,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateUserProcess, DWORD pid = pid_from_process_handle(*ProcessHandle); - LOQ_ntstatus("process", "PPhhOOool", "ProcessHandle", ProcessHandle, + LOQ_ntstatus("process", "PPhhOOoool", "ProcessHandle", ProcessHandle, "ThreadHandle", ThreadHandle, "ProcessDesiredAccess", ProcessDesiredAccess, "ThreadDesiredAccess", ThreadDesiredAccess, @@ -325,6 +365,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateUserProcess, "ThreadName", ThreadObjectAttributes, "ImagePathName", &ProcessParameters->ImagePathName, "CommandLine", &ProcessParameters->CommandLine, + "DllPath", &ProcessParameters->DllPath, "ProcessId", pid); if (NT_SUCCESS(ret)) { @@ -362,9 +403,17 @@ HOOKDEF(NTSTATUS, WINAPI, RtlCreateUserProcess, ProcessParameters, ProcessSecurityDescriptor, ThreadSecurityDescriptor, ParentProcess, InheritHandles, DebugPort, ExceptionPort, ProcessInformation); + DWORD pid = pid_from_process_handle(ProcessInformation->ProcessHandle); - LOQ_ntstatus("process", "ohpl", "ImagePath", ImagePath, "ObjectAttributes", ObjectAttributes, - "ParentHandle", ParentProcess, "ProcessId", pid); + + LOQ_ntstatus("process", "ohpoool", "ImagePath", ImagePath, + "ObjectAttributes", ObjectAttributes, + "ParentHandle", ParentProcess, + "ImagePathName", &ProcessParameters->ImagePathName, + "CommandLine", &ProcessParameters->CommandLine, + "DllPath", &ProcessParameters->DllPath, + "ProcessId", pid); + if (NT_SUCCESS(ret)) { DWORD tid = tid_from_thread_handle(ProcessInformation->ThreadHandle); if (!g_config.single_process) { @@ -560,7 +609,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtResumeProcess, DWORD pid = pid_from_process_handle(ProcessHandle); if (g_config.injection) ResumeProcessHandler(ProcessHandle, pid); - pipe("RESUME:%d", pid); + pipe("RESUME:%d,0", pid); ret = Old_NtResumeProcess(ProcessHandle); LOQ_ntstatus("process", "pl", "ProcessHandle", ProcessHandle, "ProcessId", pid); return ret; @@ -713,21 +762,17 @@ HOOKDEF(NTSTATUS, WINAPI, NtMapViewOfSection, __in ULONG AllocationType, __in ULONG Win32Protect ) { - char *ModuleName = NULL; - unsigned int DllRVA; + NTSTATUS ret = Old_NtMapViewOfSection(SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, SectionOffset, ViewSize, InheritDisposition, AllocationType, Win32Protect); - NTSTATUS ret = Old_NtMapViewOfSection(SectionHandle, ProcessHandle, BaseAddress, ZeroBits, - CommitSize, SectionOffset, ViewSize, InheritDisposition, AllocationType, Win32Protect); DWORD pid = pid_from_process_handle(ProcessHandle); + UNICODE_STRING *module_name = get_module_name((ULONG_PTR)*BaseAddress); - ModuleName = convert_address_to_dll_name_and_offset((ULONG_PTR)*BaseAddress, &DllRVA); - - if (!ModuleName) + if (!module_name) LOQ_ntstatus("process", "ppPpPhs", "SectionHandle", SectionHandle,"ProcessHandle", ProcessHandle, "BaseAddress", BaseAddress, "SectionOffset", SectionOffset, "ViewSize", ViewSize, "Win32Protect", Win32Protect, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); else LOQ_ntstatus("process", "ppPspPhs", "SectionHandle", SectionHandle,"ProcessHandle", ProcessHandle, "BaseAddress", BaseAddress, - "ModuleName", ModuleName, "SectionOffset", SectionOffset, "ViewSize", ViewSize, "Win32Protect", Win32Protect, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); + "ModuleName", module_name, "SectionOffset", SectionOffset, "ViewSize", ViewSize, "Win32Protect", Win32Protect, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); if (NT_SUCCESS(ret)) { if (g_config.injection) @@ -736,14 +781,10 @@ HOOKDEF(NTSTATUS, WINAPI, NtMapViewOfSection, ProcessMessage(pid, 0); disable_sleep_skip(); } - else if (g_config.ntdll_remap && ret == STATUS_IMAGE_NOT_AT_BASE && Win32Protect == PAGE_READONLY) { + else if (g_config.ntdll_remap && ret == STATUS_IMAGE_NOT_AT_BASE && Win32Protect == PAGE_READONLY) prevent_module_reloading(BaseAddress); - } } - if (ModuleName) - free(ModuleName); - return ret; } @@ -898,8 +939,27 @@ HOOKDEF(NTSTATUS, WINAPI, NtReadVirtualMemory, ENSURE_SIZET(NumberOfBytesRead); ret = Old_NtReadVirtualMemory(ProcessHandle, BaseAddress, Buffer, NumberOfBytesToRead, NumberOfBytesRead); + DWORD pid = pid_from_process_handle(ProcessHandle); - LOQ_ntstatus("process", "pphB", "ProcessHandle", ProcessHandle, "BaseAddress", BaseAddress, "Size", NumberOfBytesToRead, "Buffer", NumberOfBytesRead, Buffer); + if (pid != GetCurrentProcessId()) { + LOQ_ntstatus( + "process", "piphB", + "ProcessHandle", ProcessHandle, + "ProcessId", pid, + "BaseAddress", BaseAddress, + "Size", NumberOfBytesToRead, + "Buffer", NumberOfBytesRead, Buffer + ); + } + else { + LOQ_ntstatus( + "process", "pphB", + "ProcessHandle", ProcessHandle, + "BaseAddress", BaseAddress, + "Size", NumberOfBytesToRead, + "Buffer", NumberOfBytesRead, Buffer + ); + } return ret; } @@ -915,8 +975,28 @@ HOOKDEF(BOOL, WINAPI, ReadProcessMemory, ENSURE_SIZET(lpNumberOfBytesRead); ret = Old_ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead); - - LOQ_bool("process", "pphB", "ProcessHandle", hProcess, "BaseAddress", lpBaseAddress, "Size", nSize, "Buffer", lpNumberOfBytesRead, lpBuffer); + DWORD pid = pid_from_process_handle(hProcess); + + if (pid != GetCurrentProcessId()) { + LOQ_bool( + "process", "pphB", + "ProcessHandle", hProcess, + "BaseAddress", lpBaseAddress, + "Size", nSize, + "Buffer", lpNumberOfBytesRead, lpBuffer, + "ProcessId", pid + ); + } + else { + LOQ_bool( + "process", "pphB", + "ProcessHandle", hProcess, + "BaseAddress", lpBaseAddress, + "Size", nSize, + "Buffer", lpNumberOfBytesRead, lpBuffer, + "ProcessId", pid + ); + } return ret; } @@ -937,12 +1017,27 @@ HOOKDEF(NTSTATUS, WINAPI, NtWriteVirtualMemory, pid = pid_from_process_handle(ProcessHandle); - LOQ_ntstatus("process", "ppBhs", - "ProcessHandle", ProcessHandle, - "BaseAddress", BaseAddress, - "Buffer", NumberOfBytesWritten, Buffer, - "BufferLength", is_valid_address_range((ULONG_PTR)NumberOfBytesWritten, 4) ? *NumberOfBytesWritten : 0, - "StackPivoted", is_stack_pivoted() ? "yes" : "no"); + if (pid != GetCurrentProcessId()) { + LOQ_ntstatus( + "process", "pipBhs", + "ProcessHandle", ProcessHandle, + "ProcessId", pid, + "BaseAddress", BaseAddress, + "Buffer", NumberOfBytesWritten, Buffer, + "BufferLength", is_valid_address_range((ULONG_PTR)NumberOfBytesWritten, 4) ? *NumberOfBytesWritten : 0, + "StackPivoted", is_stack_pivoted() ? "yes" : "no" + ); + } + else { + LOQ_ntstatus( + "process", "ppBhs", + "ProcessHandle", ProcessHandle, + "BaseAddress", BaseAddress, + "Buffer", NumberOfBytesWritten, Buffer, + "BufferLength", is_valid_address_range((ULONG_PTR)NumberOfBytesWritten, 4) ? *NumberOfBytesWritten : 0, + "StackPivoted", is_stack_pivoted() ? "yes" : "no" + ); + } if (pid != GetCurrentProcessId() && NT_SUCCESS(ret)) { if (g_config.injection) @@ -970,8 +1065,27 @@ HOOKDEF(BOOL, WINAPI, WriteProcessMemory, pid = pid_from_process_handle(hProcess); - LOQ_bool("process", "ppBhs", "ProcessHandle", hProcess, "BaseAddress", lpBaseAddress, - "Buffer", lpNumberOfBytesWritten, lpBuffer, "BufferLength", *lpNumberOfBytesWritten, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); + if (pid != GetCurrentProcessId()) { + LOQ_bool( + "process", "pipBhs", + "ProcessHandle", hProcess, + "ProcessId", pid, + "BaseAddress", lpBaseAddress, + "Buffer", lpNumberOfBytesWritten, lpBuffer, + "BufferLength", *lpNumberOfBytesWritten, + "StackPivoted", is_stack_pivoted() ? "yes" : "no" + ); + } + else { + LOQ_bool( + "process", "ppBhs", + "ProcessHandle", hProcess, + "BaseAddress", lpBaseAddress, + "Buffer", lpNumberOfBytesWritten, lpBuffer, + "BufferLength", *lpNumberOfBytesWritten, + "StackPivoted", is_stack_pivoted() ? "yes" : "no" + ); + } if (pid != GetCurrentProcessId() && ret) { if (g_config.injection) @@ -1048,18 +1162,21 @@ HOOKDEF(NTSTATUS, WINAPI, NtProtectVirtualMemory, NTSTATUS ret; MEMORY_BASIC_INFORMATION meminfo; DWORD OriginalNewAccessProtection = 0; - unsigned int DllRVA; - char *ModuleName = NULL; + UNICODE_STRING *module_name = NULL; if (BaseAddress) - ModuleName = convert_address_to_dll_name_and_offset((ULONG_PTR)*BaseAddress, &DllRVA); - - if (g_config.ntdll_protect && NewAccessProtection == PAGE_EXECUTE_READWRITE && BaseAddress && NumberOfBytesToProtect && - NtCurrentProcess() == ProcessHandle && is_in_dll_range((ULONG_PTR)*BaseAddress) && - ModuleName && !strcmp(ModuleName, "ntdll.dll")) { - // don't allow writes, this will cause memory access violations - // that we are going to handle in the RtlDispatchException hook + module_name = get_module_name((ULONG_PTR)*BaseAddress); + + if (module_name && g_config.ntdll_protect || g_config.hook_protect) { + if (NewAccessProtection == PAGE_EXECUTE_READWRITE && BaseAddress && NumberOfBytesToProtect && + NtCurrentProcess() == ProcessHandle && is_in_dll_range((ULONG_PTR)*BaseAddress)) { + if ((g_config.ntdll_protect && module_name->Length && !wcsncmp(module_name->Buffer, L"ntdll.dll", module_name->Length)) || + (g_config.hook_protect && dll_is_hooked(module_name->Buffer))) { + // don't allow writes, this will cause memory access violations that are handled in the RtlDispatchException hook OriginalNewAccessProtection = NewAccessProtection; NewAccessProtection = PAGE_EXECUTE_READ; + DebugOutput("NtProtectVirtualMemory: Protecting %wZ at 0x%p", module_name, *BaseAddress); + } + } } memset(&meminfo, 0, sizeof(meminfo)); @@ -1104,8 +1221,8 @@ HOOKDEF(NTSTATUS, WINAPI, NtProtectVirtualMemory, "NewAccessProtection", NewAccessProtection, "OldAccessProtection", OldAccessProtection, "StackPivoted", is_stack_pivoted() ? "yes" : "no", "IsStack", "yes"); } - else if (ModuleName) - LOQ_ntstatus("process", "pPsPhhHs", "ProcessHandle", ProcessHandle, "BaseAddress", BaseAddress, "ModuleName", ModuleName, + else if (module_name) + LOQ_ntstatus("process", "pPoPhhHs", "ProcessHandle", ProcessHandle, "BaseAddress", BaseAddress, "ModuleName", module_name, "NumberOfBytesProtected", NumberOfBytesToProtect, "MemoryType", meminfo.Type, "NewAccessProtection", NewAccessProtection, "OldAccessProtection", OldAccessProtection, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); else @@ -1113,9 +1230,6 @@ HOOKDEF(NTSTATUS, WINAPI, NtProtectVirtualMemory, "NumberOfBytesProtected", NumberOfBytesToProtect, "MemoryType", meminfo.Type, "NewAccessProtection", NewAccessProtection, "OldAccessProtection", OldAccessProtection, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); - if (ModuleName) - free(ModuleName); - return ret; } @@ -1129,17 +1243,19 @@ HOOKDEF(BOOL, WINAPI, VirtualProtectEx, BOOL ret; MEMORY_BASIC_INFORMATION meminfo; DWORD OriginalNewProtect = 0; - unsigned int DllRVA; - char *ModuleName = NULL; - ModuleName = convert_address_to_dll_name_and_offset((ULONG_PTR)lpAddress, &DllRVA); - - if (g_config.ntdll_protect && flNewProtect == PAGE_EXECUTE_READWRITE && lpAddress && dwSize && - NtCurrentProcess() == hProcess && is_in_dll_range((ULONG_PTR)lpAddress) && - ModuleName && !strcmp(ModuleName, "ntdll.dll")) { - // don't allow writes, this will cause memory access violations - // that we are going to handle in the RtlDispatchException hook + UNICODE_STRING *module_name = get_module_name((ULONG_PTR)lpAddress); + + if (module_name && g_config.ntdll_protect || g_config.hook_protect) { + if (flNewProtect == PAGE_EXECUTE_READWRITE && lpAddress && dwSize && + GetCurrentProcessId() == our_getprocessid(hProcess) && is_in_dll_range((ULONG_PTR)lpAddress)) { + if ((g_config.ntdll_protect && module_name->Length && !wcsncmp(module_name->Buffer, L"ntdll.dll", module_name->Length)) || + (g_config.hook_protect && dll_is_hooked(module_name->Buffer))) { + // don't allow writes, this will cause memory access violations that are handled in the RtlDispatchException hook OriginalNewProtect = flNewProtect; flNewProtect = PAGE_EXECUTE_READ; + DebugOutput("VirtualProtectEx: Protecting %wZ at 0x%p", module_name, lpAddress); + } + } } memset(&meminfo, 0, sizeof(meminfo)); @@ -1183,17 +1299,14 @@ HOOKDEF(BOOL, WINAPI, VirtualProtectEx, LOQ_bool("process", "ppphhHss", "ProcessHandle", hProcess, "Address", lpAddress, "Size", dwSize, "MemType", meminfo.Type, "Protection", flNewProtect, "OldProtection", lpflOldProtect, "StackPivoted", is_stack_pivoted() ? "yes" : "no", "IsStack", "yes"); } - else if (ModuleName) - LOQ_bool("process", "ppsphhHs", "ProcessHandle", hProcess, "Address", lpAddress, "ModuleName", ModuleName, + else if (module_name) + LOQ_bool("process", "ppophhHs", "ProcessHandle", hProcess, "Address", lpAddress, "ModuleName", module_name, "Size", dwSize, "MemType", meminfo.Type, "Protection", flNewProtect, "OldProtection", lpflOldProtect, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); else LOQ_bool("process", "ppphhHs", "ProcessHandle", hProcess, "Address", lpAddress, "Size", dwSize, "MemType", meminfo.Type, "Protection", flNewProtect, "OldProtection", lpflOldProtect, "StackPivoted", is_stack_pivoted() ? "yes" : "no"); - if (ModuleName) - free(ModuleName); - return ret; } @@ -1293,13 +1406,16 @@ HOOKDEF_NOTAIL(WINAPI, RtlDispatchException, if (ExceptionRecord && (ULONG_PTR)ExceptionRecord->ExceptionAddress >= g_our_dll_base && (ULONG_PTR)ExceptionRecord->ExceptionAddress < (g_our_dll_base + g_our_dll_size)) { if (!(g_config.debugger && ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)) { char buf[160]; - ULONG_PTR seh = 0; - DWORD *tebtmp = (DWORD *)NtCurrentTeb(); - if (tebtmp[0] != 0xffffffff) - seh = ((DWORD*)(DWORD_PTR)tebtmp[0])[1]; - if (seh < g_our_dll_base || seh >= (g_our_dll_base + g_our_dll_size)) { - _snprintf(buf, sizeof(buf), "Exception 0x%x reported at offset 0x%x in capemon itself while accessing 0x%p from hook %s", ExceptionRecord->ExceptionCode, (DWORD)((ULONG_PTR)ExceptionRecord->ExceptionAddress - g_our_dll_base), (PVOID)ExceptionRecord->ExceptionInformation[1], hook_info()->current_hook ? hook_info()->current_hook->funcname : "unknown"); - log_anomaly("capemon crash", buf); + PTEB Teb = NtCurrentTeb(); + if (Teb) { + PEXCEPTION_REGISTRATION_RECORD SehRecord = Teb->NtTib.ExceptionList; + if (SehRecord && SehRecord != EXCEPTION_CHAIN_END) { + ULONG_PTR HandlerAddress = (ULONG_PTR)SehRecord->Handler; + if (HandlerAddress < g_our_dll_base || HandlerAddress >= (g_our_dll_base + g_our_dll_size)) { + _snprintf(buf, sizeof(buf), "Exception 0x%x reported at offset 0x%x in capemon itself while accessing 0x%p from hook %s", ExceptionRecord->ExceptionCode, (DWORD)((ULONG_PTR)ExceptionRecord->ExceptionAddress - g_our_dll_base), (PVOID)ExceptionRecord->ExceptionInformation[1], hook_info()->current_hook ? hook_info()->current_hook->funcname : "unknown"); + log_anomaly("capemon crash", buf); + } + } } } } @@ -1320,21 +1436,21 @@ HOOKDEF_ALT(BOOL, WINAPI, RtlDispatchException, if (g_config.log_exceptions > 1 && ExceptionRecord && ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) mini_handler(&ExceptionInfo); - if (g_config.ntdll_protect) { + if (g_config.ntdll_protect || g_config.hook_protect) { if (ExceptionRecord && ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && ExceptionRecord->ExceptionFlags == 0 && ExceptionRecord->NumberParameters == 2 && ExceptionRecord->ExceptionInformation[0] == 1) { - unsigned int offset; - char *dllname = convert_address_to_dll_name_and_offset(ExceptionRecord->ExceptionInformation[1], &offset); - if (dllname && !strcmp(dllname, "ntdll.dll")) { - // if trying to write to ntdll.dll, then just skip the instruction + UNICODE_STRING *module_name = get_module_name((ULONG_PTR)ExceptionRecord->ExceptionInformation[1]); + if ((g_config.ntdll_protect && module_name->Length && !wcsncmp(module_name->Buffer, L"ntdll.dll", module_name->Length)) || + (g_config.hook_protect && module_name && dll_is_hooked(module_name->Buffer))) { + // if trying to write to protected module, skip the instruction if (!ntdll_protect_logged) { ntdll_protect_logged = TRUE; #ifdef _WIN64 - DebugOutput("RtlDispatchException: skipped instruction at 0x%p writing to ntdll (0x%p - 0x%p)\n", Context->Rip, ExceptionRecord->ExceptionInformation[1], offset); + DebugOutput("RtlDispatchException: skipped instruction at 0x%p writing to %wZ (0x%p)\n", Context->Rip, module_name, ExceptionRecord->ExceptionInformation[1]); } Context->Rip += lde((void*)Context->Rip); #else - DebugOutput("RtlDispatchException: skipped instruction at 0x%x writing to ntdll (0x%x - 0x%x)\n", Context->Eip, ExceptionRecord->ExceptionInformation[1], offset); + DebugOutput("RtlDispatchException: skipped instruction at 0x%x writing to %wZ (0x%x)\n", Context->Eip, module_name, ExceptionRecord->ExceptionInformation[1]); } Context->Eip += lde((void*)Context->Eip); #endif @@ -1343,8 +1459,6 @@ HOOKDEF_ALT(BOOL, WINAPI, RtlDispatchException, LOQ_void("system", "pppp", "ExceptionCode", ExceptionRecord->ExceptionCode, "ExceptionAddress", ExceptionRecord->ExceptionAddress, "ExceptionFlags", ExceptionRecord->ExceptionFlags, "ExceptionInformation", ExceptionRecord->ExceptionInformation[0]); return TRUE; } - if (dllname) - free(dllname); } } diff --git a/hook_sleep.c b/hook_sleep.c index a09f4e20..804c119b 100644 --- a/hook_sleep.c +++ b/hook_sleep.c @@ -49,7 +49,7 @@ static int num_wait_small = 0; void disable_sleep_skip() { if (sleep_skip_active && g_config.force_sleepskip < 1) { - pipe("INFO:Disabling sleep skipping."); + DebugOutput("Disabling sleep skipping."); sleep_skip_active = 0; } } @@ -80,7 +80,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtWaitForSingleObject, newint.QuadPart = Timeout->QuadPart; - if (newint.QuadPart > 0LL) { + if (sleep_skip_active && newint.QuadPart > 0LL) { /* convert absolute time to relative time */ GetSystemTimeAsFileTime(&ft); @@ -99,13 +99,13 @@ HOOKDEF(NTSTATUS, WINAPI, NtWaitForSingleObject, li.LowPart = ft.dwLowDateTime; /* clamp sleeps between 30 seconds and 1 hour down to 10 seconds as long as we didn't force off sleep skipping */ - if (milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { + if (sleep_skip_active && milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { newint.QuadPart = -(10000 * 10000); time_skipped.QuadPart += interval - (10000 * 10000); LOQ_ntstatus("system", "pis", "Handle", Handle, "Milliseconds", milli, "Status", "Skipped"); goto docall; } - else if (g_config.force_sleepskip > 0) { + else if (sleep_skip_active && g_config.force_sleepskip > 0) { time_skipped.QuadPart += interval; LOQ_ntstatus("system", "pis", "Handle", Handle, "Milliseconds", milli, "Status", "Skipped"); newint.QuadPart = 0; @@ -114,7 +114,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtWaitForSingleObject, else { disable_sleep_skip(); } - if (milli <= 10) { + if (sleep_skip_active && milli <= 10) { if (num_wait_small < 20) { LOQ_ntstatus("system", "pi", "Handle", Handle, "Milliseconds", milli); num_wait_small++; @@ -165,7 +165,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtDelayExecution, goto docall; } - if (newint.QuadPart > 0LL) { + if (sleep_skip_active && newint.QuadPart > 0LL) { /* convert absolute time to relative time */ if (Old_GetSystemTimeAsFileTime) Old_GetSystemTimeAsFileTime(&ft); @@ -190,7 +190,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtDelayExecution, li.LowPart = ft.dwLowDateTime; // check if we're still within the hardcoded limit - if(sleep_skip_active && (li.QuadPart < time_start.QuadPart + MAX_SLEEP_SKIP_DIFF * 10000)) { + if (sleep_skip_active && (li.QuadPart < time_start.QuadPart + MAX_SLEEP_SKIP_DIFF * 10000)) { time_skipped.QuadPart += interval; if (num_skipped < 20) { @@ -205,13 +205,13 @@ HOOKDEF(NTSTATUS, WINAPI, NtDelayExecution, goto skipcall; } /* clamp sleeps between 30 seconds and 1 hour down to 10 seconds as long as we didn't force off sleep skipping */ - else if (milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { + else if (sleep_skip_active && milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { newint.QuadPart = -(10000 * 10000); time_skipped.QuadPart += interval - (10000 * 10000); LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); goto docall; } - else if (g_config.force_sleepskip > 0) { + else if (sleep_skip_active && g_config.force_sleepskip > 0) { time_skipped.QuadPart += interval; LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); newint.QuadPart = 0; @@ -220,7 +220,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtDelayExecution, else { disable_sleep_skip(); } - if (milli <= 10) { + if (sleep_skip_active && milli <= 10) { if (num_small < 20) { LOQ_ntstatus("system", "i", "Milliseconds", milli); num_small++; @@ -259,13 +259,13 @@ HOOKDEF(DWORD, WINAPI, MsgWaitForMultipleObjectsEx, goto docall; /* clamp sleeps between 30 seconds and 1 hour down to 10 seconds as long as we didn't force off sleep skipping */ - else if (dwMilliseconds >= 30000 && dwMilliseconds <= 3600000 && g_config.force_sleepskip != 0) { + else if (sleep_skip_active && dwMilliseconds >= 30000 && dwMilliseconds <= 3600000 && g_config.force_sleepskip != 0) { time_skipped.QuadPart += (dwMilliseconds - 10000) * 10000; LOQ_msgwait("system", "is", "Milliseconds", dwMilliseconds, "Status", "Skipped"); dwMilliseconds = 10000; goto docall; } - else if (g_config.force_sleepskip > 0) { + else if (sleep_skip_active && g_config.force_sleepskip > 0) { LOQ_msgwait("system", "is", "Milliseconds", dwMilliseconds, "Status", "Skipped"); dwMilliseconds = 0; goto docall; @@ -274,7 +274,7 @@ HOOKDEF(DWORD, WINAPI, MsgWaitForMultipleObjectsEx, disable_sleep_skip(); } - if (dwMilliseconds <= 10) { + if (sleep_skip_active && dwMilliseconds <= 10) { if (num_msg_small < 20) { LOQ_msgwait("system", "i", "Milliseconds", dwMilliseconds); num_msg_small++; @@ -326,7 +326,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtSetTimer, goto docall; } - if (newint.QuadPart > 0LL) { + if (sleep_skip_active && newint.QuadPart > 0LL) { /* convert absolute time to relative time */ GetSystemTimeAsFileTime(&ft); @@ -341,13 +341,13 @@ HOOKDEF(NTSTATUS, WINAPI, NtSetTimer, milli = (unsigned long)(interval / 10000); /* clamp sleeps between 30 seconds and 1 hour down to 10 seconds as long as we didn't force off sleep skipping */ - if (milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { + if (sleep_skip_active && milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { newint.QuadPart = -(10000 * 10000); time_skipped.QuadPart += interval - (10000 * 10000); LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); goto docall; } - else if (g_config.force_sleepskip > 0) { + else if (sleep_skip_active && g_config.force_sleepskip > 0) { time_skipped.QuadPart += interval; LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); newint.QuadPart = 0; @@ -393,7 +393,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtSetTimerEx, goto docall; } - if (newint.QuadPart > 0LL) { + if (sleep_skip_active && newint.QuadPart > 0LL) { /* convert absolute time to relative time */ GetSystemTimeAsFileTime(&ft); @@ -410,14 +410,14 @@ HOOKDEF(NTSTATUS, WINAPI, NtSetTimerEx, milli = (unsigned long)(interval / 10000); /* clamp sleeps between 30 seconds and 1 hour down to 10 seconds as long as we didn't force off sleep skipping */ - if (milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { + if (sleep_skip_active && milli >= 30000 && milli <= 3600000 && g_config.force_sleepskip != 0) { timerinfo->DueTime.QuadPart = -(10000 * 10000); time_skipped.QuadPart += interval - (10000 * 10000); LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); modified_delay = TRUE; goto docall; } - else if (g_config.force_sleepskip > 0) { + else if (sleep_skip_active && g_config.force_sleepskip > 0) { time_skipped.QuadPart += interval; LOQ_ntstatus("system", "is", "Milliseconds", milli, "Status", "Skipped"); timerinfo->DueTime.QuadPart = 0; @@ -447,7 +447,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueryPerformanceCounter, ret = Old_NtQueryPerformanceCounter(PerformanceCounter, PerformanceFrequency); - if (NT_SUCCESS(ret)) { + if (NT_SUCCESS(ret) && sleep_skip_active) { if (!perf_multiplier.QuadPart) perf_multiplier.QuadPart = PerformanceFrequency->QuadPart / 1000; PerformanceCounter->QuadPart += (time_skipped.QuadPart / 10000) * perf_multiplier.QuadPart; @@ -468,13 +468,15 @@ HOOKDEF(void, WINAPI, GetLocalTime, get_lasterrors(&lasterror); - SystemTimeToFileTime(lpSystemTime, &ft); - li.HighPart = ft.dwHighDateTime; - li.LowPart = ft.dwLowDateTime; - li.QuadPart += time_skipped.QuadPart; - ft.dwHighDateTime = li.HighPart; - ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, lpSystemTime); + if (sleep_skip_active) { + SystemTimeToFileTime(lpSystemTime, &ft); + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + li.QuadPart += time_skipped.QuadPart; + ft.dwHighDateTime = li.HighPart; + ft.dwLowDateTime = li.LowPart; + FileTimeToSystemTime(&ft, lpSystemTime); + } LOQ_void("system", ""); @@ -492,13 +494,15 @@ HOOKDEF(void, WINAPI, GetSystemTime, get_lasterrors(&lasterror); - SystemTimeToFileTime(lpSystemTime, &ft); - li.HighPart = ft.dwHighDateTime; - li.LowPart = ft.dwLowDateTime; - li.QuadPart += time_skipped.QuadPart; - ft.dwHighDateTime = li.HighPart; - ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, lpSystemTime); + if (sleep_skip_active) { + SystemTimeToFileTime(lpSystemTime, &ft); + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + li.QuadPart += time_skipped.QuadPart; + ft.dwHighDateTime = li.HighPart; + ft.dwLowDateTime = li.LowPart; + FileTimeToSystemTime(&ft, lpSystemTime); + } LOQ_void("system", ""); @@ -534,7 +538,8 @@ HOOKDEF(DWORD, WINAPI, GetTickCount, ret = raw_gettickcount(); // add the time we've skipped - ret += (DWORD)(time_skipped.QuadPart / 10000); + if (sleep_skip_active) + ret += (DWORD)(time_skipped.QuadPart / 10000); return ret; } @@ -549,7 +554,8 @@ HOOKDEF(ULONGLONG, WINAPI, GetTickCount64, ret = raw_gettickcount64(); // add the time we've skipped - ret += (time_skipped.QuadPart / 10000); + if (sleep_skip_active) + ret += (time_skipped.QuadPart / 10000); return ret; } @@ -560,7 +566,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtQuerySystemTime, ) { NTSTATUS ret = Old_NtQuerySystemTime(SystemTime); LOQ_ntstatus("system", ""); - if(NT_SUCCESS(ret)) { + if (NT_SUCCESS(ret) && sleep_skip_active) { SystemTime->QuadPart += time_skipped.QuadPart; } return 0; @@ -576,7 +582,8 @@ HOOKDEF(DWORD, WINAPI, timeGetTime, ret = Old_timeGetTime(); // add the time we've skipped - ret += (DWORD)(time_skipped.QuadPart / 10000); + if (sleep_skip_active) + ret += (DWORD)(time_skipped.QuadPart / 10000); return ret; } @@ -590,11 +597,13 @@ HOOKDEF(void, WINAPI, GetSystemTimeAsFileTime, Old_GetSystemTimeAsFileTime(&ft); - li.HighPart = ft.dwHighDateTime; - li.LowPart = ft.dwLowDateTime; - li.QuadPart += time_skipped.QuadPart; - ft.dwHighDateTime = li.HighPart; - ft.dwLowDateTime = li.LowPart; + if (sleep_skip_active) { + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + li.QuadPart += time_skipped.QuadPart; + ft.dwHighDateTime = li.HighPart; + ft.dwLowDateTime = li.LowPart; + } memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft)); @@ -655,7 +664,7 @@ void init_sleep_skip(int first_process) time_start.LowPart = ft.dwLowDateTime; // we don't want to skip sleep calls in child processes - if(first_process == 0) { + if (first_process == 0) { disable_sleep_skip(); } } diff --git a/hook_special.c b/hook_special.c index b1fc35a1..f504ff27 100644 --- a/hook_special.c +++ b/hook_special.c @@ -69,21 +69,19 @@ HOOKDEF_NOTAIL(WINAPI, LdrLoadDll, if (g_config.tlsdump) { // lsass injected a second time - switch to 'normal' mode g_config.tlsdump = 0; - if (read_config()) { - log_init(g_config.debug || g_config.standalone); - set_hooks(); - notify_successful_load(); - } + read_config(); + log_init(g_config.debug || g_config.standalone); + set_hooks(); + notify_successful_load(); ret = 0; } if (g_config.interactive) { // explorer injected by malware - switch to 'normal' mode g_config.interactive = 2; g_config.minhook = 0; - if (read_config()) { - set_hooks(); - notify_successful_load(); - } + read_config(); + set_hooks(); + notify_successful_load(); ret = 0; } } @@ -205,6 +203,16 @@ HOOKDEF(BOOL, WINAPI, LdrpCallInitRoutine, return ret; } +HOOKDEF(int, __fastcall, FindFixAndRun, + struct cmdnode* cmdnode +) { + int ret = 0; + if (cmdnode && !our_isbadreadptr(cmdnode, sizeof(struct cmdnode)) && cmdnode->cmdline != NULL && cmdnode->type == 0) { + LOQ_zero("system", "uu", "Command", cmdnode->cmdline, "Arguments", cmdnode->argptr); + } + return Old_FindFixAndRun(cmdnode); +} + void end_transparent_hooks(){transparency_dummy--;} HOOKDEF(BOOL, WINAPI, CreateProcessInternalW, @@ -372,8 +380,6 @@ HOOKDEF(HRESULT, WINAPI, CoCreateInstance, inspect_clsid(&id1); } - disable_sleep_skip(); - set_lasterrors(&lasterror); memcpy(&saved_hookinfo, hook_info(), sizeof(saved_hookinfo)); @@ -424,8 +430,6 @@ HOOKDEF(HRESULT, WINAPI, CoCreateInstanceEx, inspect_clsid(&id1); } - disable_sleep_skip(); - set_lasterrors(&lasterror); memcpy(&saved_hookinfo, hook_info(), sizeof(saved_hookinfo)); @@ -484,6 +488,12 @@ HOOKDEF(HRESULT, WINAPI, CoGetClassObject, disable_sleep_skip(); + if (!called_by_hook()) { + inspect_clsid(&id1); + } + + disable_sleep_skip(); + set_lasterrors(&lasterror); memcpy(&saved_hookinfo, hook_info(), sizeof(saved_hookinfo)); diff --git a/hook_thread.c b/hook_thread.c index 8cc7c604..3be0314b 100644 --- a/hook_thread.c +++ b/hook_thread.c @@ -28,13 +28,10 @@ along with this program. If not, see . #include "lookup.h" #include "CAPE\CAPE.h" #include "CAPE\Debugger.h" +#include "CAPE\Injection.h" extern _RtlNtStatusToDosError pRtlNtStatusToDosError; extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); -extern void GetThreadContextHandler(DWORD Pid, LPCONTEXT Context); -extern void SetThreadContextHandler(DWORD Pid, const CONTEXT *Context); -extern void ResumeThreadHandler(DWORD Pid); -extern void CreateRemoteThreadHandler(DWORD Pid); extern void NtContinueHandler(PCONTEXT ThreadContext); extern void ProcessMessage(DWORD ProcessId, DWORD ThreadId); extern BOOL BreakpointsSet; @@ -72,7 +69,7 @@ void add_ignored_thread(DWORD tid) lasterror_t lasterror; get_lasterrors(&lasterror); - pipe("INFO:Adding ignored thread %d", tid); + DebugOutput("Adding ignored thread %d", tid); lookup_add(&g_ignored_threads, tid, 0); set_lasterrors(&lasterror); } @@ -86,7 +83,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueueApcThread, ) { DWORD pid = pid_from_thread_handle(ThreadHandle); DWORD tid = tid_from_thread_handle(ThreadHandle); - char *module_name = NULL; + char *module_name = NULL, *function_name = NULL; unsigned int offset; NTSTATUS ret; @@ -96,8 +93,11 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueueApcThread, ret = Old_NtQueueApcThread(ThreadHandle, ApcRoutine, ApcRoutineContext, ApcStatusBlock, ApcReserved); module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)ApcRoutine, &offset); + function_name = GetExportNameByAddress((PVOID)ApcRoutine); - if (module_name) + if (function_name && module_name) + LOQ_ntstatus("threading", "iippss", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine, "Module", module_name, "Name", function_name); + else if (module_name) LOQ_ntstatus("threading", "iipps", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine, "Module", module_name); else LOQ_ntstatus("threading", "iipp", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine); @@ -121,7 +121,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueueApcThreadEx, ) { DWORD pid = pid_from_thread_handle(ThreadHandle); DWORD tid = tid_from_thread_handle(ThreadHandle); - char *module_name = NULL; + char *module_name = NULL, *function_name = NULL; unsigned int offset; NTSTATUS ret; @@ -131,8 +131,11 @@ HOOKDEF(NTSTATUS, WINAPI, NtQueueApcThreadEx, ret = Old_NtQueueApcThreadEx(ThreadHandle, UserApcReserveHandle, ApcRoutine, ApcRoutineContext, ApcStatusBlock, ApcReserved); module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)ApcRoutine, &offset); + function_name = GetExportNameByAddress((PVOID)ApcRoutine); - if (module_name) + if (function_name && module_name) + LOQ_ntstatus("threading", "iippss", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine, "Module", module_name, "Name", function_name); + else if (module_name) LOQ_ntstatus("threading", "iipps", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine, "Module", module_name); else LOQ_ntstatus("threading", "iipp", "ProcessId", pid, "ThreadId", tid, "ThreadHandle", ThreadHandle, "ApcRoutine", ApcRoutine); @@ -211,7 +214,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateThreadEx, OUT PVOID lpBytesBuffer ) { DWORD pid = pid_from_process_handle(ProcessHandle); - char *module_name = NULL; + char *module_name = NULL, *function_name = NULL; unsigned int offset; disable_sleep_skip(); @@ -221,6 +224,7 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateThreadEx, lpBytesBuffer); module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)lpStartAddress, &offset); + function_name = GetExportNameByAddress((PVOID)lpStartAddress); if (NT_SUCCESS(ret)) { DWORD tid = tid_from_thread_handle(*hThread); @@ -245,7 +249,11 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateThreadEx, set_lasterrors(&lasterror); } - if (module_name) + if (function_name && module_name) + LOQ_ntstatus("threading", "Pppphiiss", "ThreadHandle", hThread, "ProcessHandle", ProcessHandle, + "StartAddress", lpStartAddress, "Parameter", lpParameter, "CreateFlags", CreateFlags, "ThreadId", tid, + "ProcessId", pid, "Module", module_name, "Name", function_name); + else if (module_name) LOQ_ntstatus("threading", "Pppphiis", "ThreadHandle", hThread, "ProcessHandle", ProcessHandle, "StartAddress", lpStartAddress, "Parameter", lpParameter, "CreateFlags", CreateFlags, "ThreadId", tid, "ProcessId", pid, "Module", module_name); @@ -255,7 +263,10 @@ HOOKDEF(NTSTATUS, WINAPI, NtCreateThreadEx, "ProcessId", pid); } else { - if (module_name) + if (function_name && module_name) + LOQ_ntstatus("threading", "Pppphss", "ThreadHandle", hThread, "ProcessHandle", ProcessHandle, + "StartAddress", lpStartAddress, "Parameter", lpParameter, "CreateFlags", CreateFlags, "Module", module_name, "Name", function_name); + else if (module_name) LOQ_ntstatus("threading", "Pppphs", "ThreadHandle", hThread, "ProcessHandle", ProcessHandle, "StartAddress", lpStartAddress, "Parameter", lpParameter, "CreateFlags", CreateFlags, "Module", module_name); else @@ -286,8 +297,8 @@ HOOKDEF(NTSTATUS, WINAPI, NtOpenThread, } if (ClientId) { - LOQ_ntstatus("threading", "Phiii", "ThreadHandle", ThreadHandle, "DesiredAccess", DesiredAccess, - "ProcessId", pid, "ThreadId", tid, "ProcessId", pid); + LOQ_ntstatus("threading", "Phii", "ThreadHandle", ThreadHandle, "DesiredAccess", DesiredAccess, + "ProcessId", pid, "ThreadId"); } else { LOQ_ntstatus("threading", "PhOi", "ThreadHandle", ThreadHandle, "DesiredAccess", DesiredAccess, "ObjectAttributes", ObjectAttributes, "ProcessId", pid); @@ -296,95 +307,296 @@ HOOKDEF(NTSTATUS, WINAPI, NtOpenThread, return ret; } +// This hook needs to be strictly matched to the NtSetContextThread hook in terms of stack frame size HOOKDEF(NTSTATUS, WINAPI, NtGetContextThread, __in HANDLE ThreadHandle, __inout LPCONTEXT Context ) { - ENSURE_HANDLE(ThreadHandle); - ENSURE_STRUCT(Context, CONTEXT); + DWORD pid = pid_from_thread_handle(ThreadHandle); DWORD tid = tid_from_thread_handle(ThreadHandle); NTSTATUS ret = Old_NtGetContextThread(ThreadHandle, Context); - DWORD pid = pid_from_thread_handle(ThreadHandle); - if (Context && Context->ContextFlags & CONTEXT_CONTROL) + + if (Context && (Context->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER)) == (CONTEXT_CONTROL | CONTEXT_INTEGER)) +#ifdef _WIN64 + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Rip, + "Rax", Context->Rax, + "Rbx", Context->Rbx, + "Rcx", Context->Rcx, + "Rdx", Context->Rdx, + "Rsp", Context->Rsp, + "ProcessId", pid, + "ThreadId", tid + ); +#else + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); +#endif + else if (Context && (Context->ContextFlags & CONTEXT_INTEGER)) { #ifdef _WIN64 - LOQ_ntstatus("threading", "pppi", "ThreadHandle", ThreadHandle, "HollowedInstructionPointer", - Context->Rcx, "CurrentInstructionPointer", Context->Rip, "ProcessId", pid); + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Rax", Context->Rax, + "Rbx", Context->Rbx, + "Rcx", Context->Rcx, + "Rdx", Context->Rdx, + "ProcessId", pid, + "ThreadId", tid + ); #else - LOQ_ntstatus("threading", "pppi", "ThreadHandle", ThreadHandle, "HollowedInstructionPointer", - Context->Eax, "CurrentInstructionPointer", Context->Eip, "ProcessId", pid); + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "ProcessId", pid, + "ThreadId", tid + ); #endif + } + else if (Context && (Context->ContextFlags & CONTEXT_CONTROL)) { +#ifdef _WIN64 + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Rip, + "Rsp", Context->Rsp, + "ProcessId", pid, + "ThreadId", tid + ); +#else + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); +#endif + } else - LOQ_ntstatus("threading", "pi", "ThreadHandle", ThreadHandle, "ProcessId", pid); + LOQ_ntstatus("threading", "pii", "ThreadHandle", ThreadHandle, "ProcessId", pid, "ThreadId", tid); - GetThreadContextHandler(pid, Context); + // This needs to be __declspec(noinline) to prevent inlining + GetThreadContextHandler(ThreadHandle, Context); + + return ret; +} - if (g_config.debugger) { - Context->Dr0 = 0; - Context->Dr1 = 0; - Context->Dr2 = 0; - Context->Dr3 = 0; - Context->Dr6 = 0; - Context->Dr7 = 0; +HOOKDEF(NTSTATUS, WINAPI, NtSetContextThread, + __in HANDLE ThreadHandle, + __in CONTEXT *Context +) { + DWORD pid = pid_from_thread_handle(ThreadHandle); + DWORD tid = tid_from_thread_handle(ThreadHandle); + + SetThreadContextHandler(ThreadHandle, Context); + + NTSTATUS ret = Old_NtSetContextThread(ThreadHandle, Context); + + if (Context && (Context->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER)) == (CONTEXT_CONTROL | CONTEXT_INTEGER)) +#ifdef _WIN64 + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Rip, + "Rax", Context->Rax, + "Rbx", Context->Rbx, + "Rcx", Context->Rcx, + "Rdx", Context->Rdx, + "Rsp", Context->Rsp, + "ProcessId", pid, + "ThreadId", tid + ); +#else + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); +#endif + else if (Context && (Context->ContextFlags & CONTEXT_INTEGER)) { +#ifdef _WIN64 + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Rax", Context->Rax, + "Rbx", Context->Rbx, + "Rcx", Context->Rcx, + "Rdx", Context->Rdx, + "ProcessId", pid, + "ThreadId", tid + ); +#else + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "ProcessId", pid, + "ThreadId", tid + ); +#endif + } + else if (Context && (Context->ContextFlags & CONTEXT_CONTROL)) { +#ifdef _WIN64 + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Rip, + "Rsp", Context->Rsp, + "ProcessId", pid, + "ThreadId", tid + ); +#else + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); +#endif } + else + + LOQ_ntstatus("threading", "pii", "ThreadHandle", ThreadHandle, "ProcessId", pid, "ThreadId", tid); return ret; } +#ifdef _WIN64 HOOKDEF(NTSTATUS, WINAPI, RtlWow64GetThreadContext, __in HANDLE ThreadHandle, __inout PWOW64_CONTEXT Context ) { - ENSURE_HANDLE(ThreadHandle); - ENSURE_STRUCT(Context, WOW64_CONTEXT); - DWORD tid = tid_from_thread_handle(ThreadHandle); DWORD pid = pid_from_thread_handle(ThreadHandle); + DWORD tid = tid_from_thread_handle(ThreadHandle); NTSTATUS ret = Old_RtlWow64GetThreadContext(ThreadHandle, Context); - LOQ_ntstatus("threading", "pi", "ThreadHandle", ThreadHandle, "ProcessId", pid); + if (Context && (Context->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER)) == (CONTEXT_CONTROL | CONTEXT_INTEGER)) + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); + else if (Context && (Context->ContextFlags & CONTEXT_INTEGER)) { + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "ProcessId", pid, + "ThreadId", tid + ); + } + else if (Context && (Context->ContextFlags & CONTEXT_CONTROL)) { + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); + } + else + LOQ_ntstatus("threading", "pii", "ThreadHandle", ThreadHandle, "ProcessId", pid, "ThreadId", tid); + + Wow64GetThreadContextHandler(ThreadHandle, Context); return ret; } -HOOKDEF(NTSTATUS, WINAPI, NtSetContextThread, - __in HANDLE ThreadHandle, - __in CONTEXT *Context +HOOKDEF(NTSTATUS, WINAPI, RtlWow64SetThreadContext, + __in HANDLE ThreadHandle, + __inout PWOW64_CONTEXT Context ) { - NTSTATUS ret; DWORD pid = pid_from_thread_handle(ThreadHandle); DWORD tid = tid_from_thread_handle(ThreadHandle); - if (pid == GetCurrentProcessId() && g_config.debugger && Context) { - PTHREADBREAKPOINTS ThreadBreakpoints = GetThreadBreakpoints(tid); - if (ThreadBreakpoints) - { - DebugOutput("NtSetContextThread: Protecting breakpoints for thread %d: 0x%p, 0x%p, 0x%p, 0x%p.\n", tid, ThreadBreakpoints->BreakpointInfo[0].Address, ThreadBreakpoints->BreakpointInfo[1].Address, ThreadBreakpoints->BreakpointInfo[2].Address, ThreadBreakpoints->BreakpointInfo[3].Address); - ContextSetThreadBreakpointsEx(Context, ThreadBreakpoints, TRUE); - } -#ifdef DEBUG_COMMENTS - else - DebugOutput("NtSetContextThread hook: No breakpoints to protect for thread %d.\n", tid); -#endif + Wow64SetThreadContextHandler(ThreadHandle, Context); + + NTSTATUS ret = Old_RtlWow64SetThreadContext(ThreadHandle, Context); + + if (Context && (Context->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER)) == (CONTEXT_CONTROL | CONTEXT_INTEGER)) + LOQ_ntstatus( + "threading", "pppppppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); + else if (Context && (Context->ContextFlags & CONTEXT_INTEGER)) { + LOQ_ntstatus( + "threading", "pppppii", + "ThreadHandle", ThreadHandle, + "Eax", Context->Eax, + "Ebx", Context->Ebx, + "Ecx", Context->Ecx, + "Edx", Context->Edx, + "ProcessId", pid, + "ThreadId", tid + ); + } + else if (Context && (Context->ContextFlags & CONTEXT_CONTROL)) { + LOQ_ntstatus( + "threading", "pppii", + "ThreadHandle", ThreadHandle, + "InstructionPointer", Context->Eip, + "Esp", Context->Esp, + "ProcessId", pid, + "ThreadId", tid + ); } - - ret = Old_NtSetContextThread(ThreadHandle, Context); - - if (Context && Context->ContextFlags & CONTEXT_CONTROL) -#ifdef _WIN64 - LOQ_ntstatus("threading", "pppp", "ThreadHandle", ThreadHandle, "HollowedInstructionPointer", Context->Rcx, "CurrentInstructionPointer", Context->Rip, "Flags", Context->ContextFlags); -#else - LOQ_ntstatus("threading", "pppp", "ThreadHandle", ThreadHandle, "HollowedInstructionPointer", Context->Eax, "CurrentInstructionPointer", Context->Eip, "Flags", Context->ContextFlags); -#endif else - LOQ_ntstatus("threading", "p", "ThreadHandle", ThreadHandle); - - SetThreadContextHandler(pid, Context); - if (pid != GetCurrentProcessId()) - ProcessMessage(pid, 0); + LOQ_ntstatus("threading", "pii", "ThreadHandle", ThreadHandle, "ProcessId", pid, "ThreadId", tid); return ret; } +#endif HOOKDEF(NTSTATUS, WINAPI, NtSuspendThread, __in HANDLE ThreadHandle, @@ -499,9 +711,10 @@ HOOKDEF(HANDLE, WINAPI, CreateThread, ENSURE_DWORD(lpThreadId); unsigned int DllRVA; - char *module_name = NULL; + char *module_name = NULL, *function_name = NULL; module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)lpStartAddress, &DllRVA); + function_name = GetExportNameByAddress((PVOID)*lpStartAddress); disable_sleep_skip(); ret = Old_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags | CREATE_SUSPENDED, lpThreadId); @@ -521,14 +734,20 @@ HOOKDEF(HANDLE, WINAPI, CreateThread, set_lasterrors(&lasterror); } - if (module_name) + if (function_name && module_name) + LOQ_nonnull("threading", "pssphI", "StartRoutine", lpStartAddress, "ModuleName", module_name, "Name", function_name, "Parameter", lpParameter, "CreationFlags", dwCreationFlags, "ThreadId", lpThreadId); + else if (module_name) LOQ_nonnull("threading", "psphI", "StartRoutine", lpStartAddress, "ModuleName", module_name, "Parameter", lpParameter, "CreationFlags", dwCreationFlags, "ThreadId", lpThreadId); else LOQ_nonnull("threading", "pphI", "StartRoutine", lpStartAddress, "Parameter", lpParameter, "CreationFlags", dwCreationFlags, "ThreadId", lpThreadId); } else - LOQ_nonnull("threading", "pph", "StartRoutine", lpStartAddress, "Parameter", lpParameter, - "CreationFlags", dwCreationFlags); + if (function_name && module_name) + LOQ_nonnull("threading", "pssph", "StartRoutine", lpStartAddress, "ModuleName", module_name, "Name", function_name, "Parameter", lpParameter, "CreationFlags", dwCreationFlags); + else if (module_name) + LOQ_nonnull("threading", "psph", "StartRoutine", lpStartAddress, "ModuleName", module_name, "Parameter", lpParameter, "CreationFlags", dwCreationFlags); + else + LOQ_nonnull("threading", "pph", "StartRoutine", lpStartAddress, "Parameter", lpParameter, "CreationFlags", dwCreationFlags); if (module_name) free(module_name); @@ -681,6 +900,15 @@ HOOKDEF(NTSTATUS, WINAPI, RtlCreateUserThread, return ret; } +HOOKDEF_NOTAIL(WINAPI, RtlUserThreadStart, + __in LPTHREAD_START_ROUTINE lpStartAddress, + __in LPVOID lpParameter +) { + NTSTATUS ret = 0; + LOQ_void("threading", "pp", "StartAddress", lpStartAddress, "Parameter", lpParameter); + return ret; +} + HOOKDEF(NTSTATUS, WINAPI, NtSetInformationThread, IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, @@ -778,3 +1006,36 @@ HOOKDEF(BOOL, WINAPI, NtTestAlert, ret = Old_NtTestAlert(); return ret; } + +HOOKDEF(BOOL, WINAPI, SetThreadStackGuarantee, + _Inout_ PULONG StackSizeInBytes +) { + ULONG inputSize = 0; + if (StackSizeInBytes != NULL) { + __try { + inputSize = *StackSizeInBytes; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + ; + } + } + + BOOL ret = Old_SetThreadStackGuarantee(StackSizeInBytes); + if (ret) { + LOQ_bool("threading", "ii", "InputSize", inputSize, "OutputSize", *StackSizeInBytes); + } + else { + LOQ_bool("threading", "i", "InputSize", inputSize); + } + + return ret; +} + +HOOKDEF(NTSTATUS, WINAPI, SetThreadDescription, + _In_ HANDLE hThread, + _In_ PCWSTR lpThreadDescription +) { + NTSTATUS ret = Old_SetThreadDescription(hThread, lpThreadDescription); + LOQ_ntstatus("threading", "pu", "ThreadHandle", hThread, "ThreadDescription", lpThreadDescription); + return ret; +} \ No newline at end of file diff --git a/hook_wmi.c b/hook_wmi.c new file mode 100644 index 00000000..f8b7eedf --- /dev/null +++ b/hook_wmi.c @@ -0,0 +1,99 @@ +#include "log.h" +#include "misc.h" + +HOOKDEF(HRESULT, WINAPI, WMI_Get, + PVOID _this, + LPCWSTR wszName, + LONG lFlags, + VARIANT* pVal, + LONG* pType, + LONG* plFlavor +) { + HRESULT ret; + ret = Old_WMI_Get(_this, wszName, lFlags, pVal, pType, plFlavor); + LOQ_hresult("system", "un", "Name", wszName, "Value", pVal); + return ret; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecQuery, + PVOID _this, + const BSTR strQueryLanguage, + const BSTR strQuery, + LONG lFlags, + PVOID pCtx, + PVOID* ppEnum +) { + HRESULT ret = 0; + LOQ_hresult("system", "u", "Query", strQuery); + return 0; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecQueryAsync, + PVOID _this, + const BSTR strQueryLanguage, + const BSTR strQuery, + long lFlags, + PVOID pCtx, + PVOID pResponseHandler +) { + HRESULT ret = 0; + LOQ_hresult("system", "u", "Query", strQuery); + return 0; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecMethod, + PVOID _this, + const BSTR strObjectPath, + const BSTR strMethodName, + long lFlags, + PVOID pCtx, + PVOID pInParams, + PVOID* ppOutParams, + PVOID* ppCallResult +) { + HRESULT ret = 0; + LOQ_hresult("system", "uu", "ObjectPath", strObjectPath, "MethodName", strMethodName); + return 0; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecMethodAsync, + PVOID _this, + const BSTR strObjectPath, + const BSTR strMethodName, + long lFlags, + PVOID pCtx, + PVOID pInParams, + PVOID pResponseHandler +) { + HRESULT ret = 0; + LOQ_hresult("system", "uu", "ObjectPath", strObjectPath, "MethodName", strMethodName); + return 0; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_GetObject, + PVOID _this, + const BSTR strObjectPath, + long lFlags, + PVOID pCtx, + PVOID* ppObject, + PVOID* ppCallResult +) { + HRESULT ret = 0; + if (strObjectPath && SysStringLen(strObjectPath) > 0) + LOQ_hresult("system", "u", "ObjectPath", strObjectPath); + else + LOQ_hresult("system", "u", "ObjectPath", L"[NULL or Empty]"); + return 0; +} + +HOOKDEF_NOTAIL(WINAPI, WMI_GetObjectAsync, + PVOID _this, + const BSTR strObjectPath, + long lFlags, + PVOID pCtx, + PVOID pResultHandler +) { + HRESULT ret = 0; + LOQ_hresult("system", "u", "ObjectPath", strObjectPath); + return 0; +} \ No newline at end of file diff --git a/hooking.c b/hooking.c index de093c8e..8267b468 100644 --- a/hooking.c +++ b/hooking.c @@ -278,6 +278,9 @@ void api_dispatch(hook_t *h, hook_info_t *hookinfo) else BreakpointOnReturn((PVOID)hookinfo->return_address); } + + if (g_config.hook_watch) + DebugOutput("api_dispatch: %s\n", h->funcname); } void add_force_hook_thread_func(const char* function) diff --git a/hooking.h b/hooking.h index 57c40e92..c04d7410 100644 --- a/hooking.h +++ b/hooking.h @@ -193,7 +193,9 @@ enum { #else enum { HOOK_NATIVE_JMP_INDIRECT, - HOOK_JMP_INDIRECT + HOOK_NATIVE_PUSH_RETN, + HOOK_JMP_INDIRECT, + HOOK_PUSH_RETN }; #endif diff --git a/hooking_32.c b/hooking_32.c index d4c24c21..6e1f67de 100644 --- a/hooking_32.c +++ b/hooking_32.c @@ -708,8 +708,8 @@ int hook_api(hook_t *h, int type) addr = (unsigned char *)GetProcAddress(hmod, h->funcname); if (exportaddr && addr && (PVOID)addr != exportaddr) { unsigned int offset; - char *module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)addr, &offset); - DebugOutput("hook_api: Warning - %s export address 0x%p differs from GetProcAddress -> 0x%p (%s::0x%x)\n", h->funcname, exportaddr, addr, module_name, offset); + UNICODE_STRING *module_name = get_module_name((ULONG_PTR)addr); + DebugOutput("hook_api: Warning - %s export address 0x%p differs from GetProcAddress -> 0x%p (%wZ::0x%x)\n", h->funcname, exportaddr, addr, module_name, offset); } else if (exportaddr && !addr) { addr = exportaddr; @@ -719,12 +719,14 @@ int hook_api(hook_t *h, int type) DebugOutput("hook_api: %s export address 0x%p obtained via GetFunctionAddress\n", h->funcname, addr); } } + } - if (addr == NULL && h->timestamp != 0 && h->rva != 0) { - DWORD timestamp = GetTimeStamp(hmod); - if (timestamp == h->timestamp) - addr = (unsigned char *)hmod + h->rva; - } + if (addr == NULL && h->timestamp != 0 && h->rva != 0) { + if (!hmod) + hmod = GetModuleHandleW(h->library); + DWORD timestamp = GetTimeStamp(hmod); + if (timestamp == h->timestamp) + addr = (unsigned char *)hmod + h->rva; } if (addr == NULL || addr == (unsigned char *)0xffbadd11) { diff --git a/hooking_64.c b/hooking_64.c index be189860..3749f2b7 100644 --- a/hooking_64.c +++ b/hooking_64.c @@ -170,22 +170,46 @@ static int addr_is_in_range(ULONG_PTR addr, const unsigned char *buf, DWORD size return 0; } -static void retarget_rip_relative_displacement(unsigned char **tramp, unsigned char **addr, _DInst *insn) +static void retarget_relative_displacement(unsigned char **tramp, unsigned char **addr, _DInst *insn) { unsigned short length = insn->size; - unsigned char offset = (unsigned char)(length - insn->imm_encoded_size - sizeof(int)); unsigned char *newtramp = *tramp; unsigned char *newaddr = *addr; - ULONG_PTR target; - int rel = *(int *)(newaddr + offset); - target = (ULONG_PTR)(newaddr + length + rel); - // copy the instruction directly to the trampoline - while (length-- != 0) { - *newtramp++ = *newaddr++; + + unsigned char offset = (unsigned char)(length - insn->imm_encoded_size - sizeof(int)); + ULONG_PTR target = (ULONG_PTR)(newaddr + length + *(int *)(newaddr + offset)); + int64_t rel = (int64_t)(target - (ULONG_PTR)(newtramp + length)); + + if (rel >= INT_MIN && rel <= INT_MAX) { + while (length-- != 0) + *newtramp++ = *newaddr++; + *(int *)(newtramp - insn->imm_encoded_size - sizeof(int)) = (int)rel; + } + else { + // mov r11, far target + *((WORD*)newtramp)++ = 0xBB49; + *((ULONG_PTR *)newtramp)++ = (ULONG_PTR)target; + if (*newaddr == 0xE8) { + // replace call near target with call far r11 + *((WORD*)newtramp)++ = 0xFF41; + *newtramp++ = 0xD3; + } + else if (*newaddr == 0xE9) { + // replace jmp near target with jmp far r11 + *((WORD*)newtramp)++ = 0xFF41; + *newtramp++ = 0xE3; + } + else if (insn->flags & FLAG_RIP_RELATIVE) { + // rewrite instruction to use rll + if ((*newaddr & 0xF0) == 0x40) + // modify REX prefix to use r11 + *newtramp++ = *newaddr++ | 0x41; + *newtramp++ = *newaddr++; + // modify ModR/M byte to use R11 + *newtramp++ = (*newaddr++ & 0xF8) | 3; + } + newaddr += 4; } - // now replace the displacement - rel = (int)(target - (ULONG_PTR)newtramp); - *(int *)(newtramp - insn->imm_encoded_size - sizeof(int)) = rel; *tramp = newtramp; *addr = newaddr; @@ -234,13 +258,11 @@ static int hook_create_trampoline(unsigned char *addr, int len, // addresses, otherwise we can simply copy the instruction to our // trampoline - if (addr[0] == 0xe8 || addr[0] == 0xe9 || (addr[0] == 0x0f && addr[1] >= 0x80 && addr[1] < 0x90) || - (insn->flags & FLAG_RIP_RELATIVE)) { - retarget_rip_relative_displacement(&tramp, &addr, insn); + if (addr[0] == 0xe8 || addr[0] == 0xe9 || (addr[0] == 0x0f && addr[1] >= 0x80 && addr[1] < 0x90) || (insn->flags & FLAG_RIP_RELATIVE)) { + retarget_relative_displacement(&tramp, &addr, insn); if (addr[0] == 0xe9 && len > 0) goto error; } - else if (addr[0] == 0xeb) { target = get_short_rel_target(addr); if (addr_is_in_range(target, origaddr, stoleninstrlen)) @@ -259,9 +281,8 @@ static int hook_create_trampoline(unsigned char *addr, int len, } // return instruction, indicates end of basic block as well, so we // have to check if we already have enough space for our hook.. - else if ((addr[0] == 0xc3 || addr[0] == 0xc2) && len > 0) { + else if ((addr[0] == 0xc3 || addr[0] == 0xc2) && len > 0) goto error; - } else { // copy the instruction directly to the trampoline while (length-- != 0) { @@ -272,9 +293,9 @@ static int hook_create_trampoline(unsigned char *addr, int len, } // append a jump from the trampoline to the original function - *tramp++ = 0xe9; - emit_rel(tramp, tramp, addr); - tramp += 4; + *((WORD*)tramp)++ = 0x25FF; + *((DWORD*)tramp)++ = 0; + *((uintptr_t*)tramp)++ = (uintptr_t)addr; // return the length of this trampoline return (int)(tramp - base); @@ -566,7 +587,7 @@ static void hook_create_pre_tramp_notail(hook_t *h) unsigned char pre_tramp2[] = { // test eax, eax 0x85, 0xc0, - // jnz 0x21 + // jnz 0x21 -> pre_tramp3_nostack 0x75, 0x21, // add rsp, 0x20 0x48, 0x83, 0xc4, 0x20, @@ -645,7 +666,7 @@ static void hook_create_pre_tramp_notail(hook_t *h) unsigned char pre_tramp4_nostack[] = { // test eax, eax 0x85, 0xc0, - // jnz 0x21 + // jnz 0x21 -> pre_tramp4_stack 0x75, 0x21, // add rsp, 0x20 (from pre_tramp12) 0x48, 0x83, 0xc4, 0x20, @@ -665,7 +686,7 @@ static void hook_create_pre_tramp_notail(hook_t *h) unsigned char pre_tramp4_stack[] = { // test eax, eax 0x85, 0xc0, - // jnz 0x3c + // jnz 0x3c -> pre_tramp5_nostack 0x75, 0x3c, // mov eax, numargs 0xb8, h->numargs, 0x00, 0x00, 0x00, @@ -833,6 +854,25 @@ static int hook_api_native_jmp_indirect(hook_t *h, unsigned char *from, return hook_api_jmp_indirect(h, from, to); } +static int hook_api_push_retn(hook_t *h, unsigned char *from, unsigned char *to) +{ + // push addr + *from++ = 0x68; + *(DWORD *) from = (DWORD)(DWORD_PTR)to; + + // retn + from[4] = 0xc3; + + memcpy(h->hookdata->hook_data, &to, sizeof(to)); + return 0; +} + +static int hook_api_native_push_retn(hook_t *h, unsigned char *from, unsigned char *to) +{ + from += 8; + return hook_api_push_retn(h, from, to); +} + hook_data_t *alloc_hookdata_near(void *addr) { PVOID BaseAddress; @@ -853,6 +893,23 @@ hook_data_t *alloc_hookdata_near(void *addr) return NULL; } +hook_data_t *alloc_hookdata_low() +{ + PCHAR BaseAddress = (PCHAR)0x10000; + int offset = 0x10000; + SIZE_T RegionSize = sizeof(hook_data_t); + LONG status; + + do { + status = pNtAllocateVirtualMemory(GetCurrentProcess(), (PVOID)&BaseAddress, 0, &RegionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (status >= 0) + return (hook_data_t *)BaseAddress; + BaseAddress += offset; + } while (status < 0); + + return NULL; +} + unsigned char *handle_stub(hook_t *h, unsigned char *addr) { unsigned char stack_offset = 0; @@ -915,7 +972,9 @@ int hook_api(hook_t *h, int type) int len; } hook_types[] = { /* HOOK_NATIVE_JMP_INDIRECT */{ &hook_api_native_jmp_indirect, 14 }, + /* HOOK_NATIVE_PUSH_RETN */{ &hook_api_native_push_retn, 14 }, /* HOOK_JMP_INDIRECT */{ &hook_api_jmp_indirect, 6 }, + /* HOOK_PUSH_RETN */{ &hook_api_push_retn, 6 }, }; // is this address already hooked? @@ -980,8 +1039,8 @@ int hook_api(hook_t *h, int type) addr = (unsigned char *)GetProcAddress(hmod, h->funcname); if (exportaddr && addr && (PVOID)addr != exportaddr) { unsigned int offset; - char *module_name = convert_address_to_dll_name_and_offset((ULONG_PTR)addr, &offset); - DebugOutput("hook_api: Warning - %s export address 0x%p differs from GetProcAddress -> 0x%p (%s::0x%x)\n", h->funcname, exportaddr, addr, module_name, offset); + UNICODE_STRING *module_name = get_module_name((ULONG_PTR)addr); + DebugOutput("hook_api: Warning - %s export address 0x%p differs from GetProcAddress -> 0x%p (%wZ::0x%x)\n", h->funcname, exportaddr, addr, module_name, offset); } else if (exportaddr && !addr) { addr = exportaddr; @@ -991,13 +1050,16 @@ int hook_api(hook_t *h, int type) DebugOutput("hook_api: %s export address 0x%p obtained via GetFunctionAddress\n", h->funcname, addr); } } + } - if (addr == NULL && h->timestamp != 0 && h->rva != 0) { - DWORD timestamp = GetTimeStamp(hmod); - if (timestamp == h->timestamp) - addr = (unsigned char *)hmod + h->rva; - } + if (addr == NULL && h->timestamp != 0 && h->rva != 0) { + if (!hmod) + hmod = GetModuleHandleW(h->library); + DWORD timestamp = GetTimeStamp(hmod); + if (timestamp == h->timestamp) + addr = (unsigned char *)hmod + h->rva; } + if (addr == NULL) { // function doesn't exist in this DLL, not a critical error return 0; @@ -1041,12 +1103,15 @@ int hook_api(hook_t *h, int type) addr = handle_stub(h, addr); - if (!wcscmp(h->library, L"ntdll") && !memcmp(addr, "\x4c\x8b\xd1\xb8", 4) && memcmp(addr+8, "\x0f\x05", 2)) { + if (h->library && !wcscmp(h->library, L"ntdll") && !memcmp(addr, "\x4c\x8b\xd1\xb8", 4) && memcmp(addr+8, "\x0f\x05", 2)) { // hooking a native API, leave in the mov eax, instruction // as some malware depends on this for direct syscalls // missing a few syscalls is better than crashing and getting no information // at all - type = HOOK_NATIVE_JMP_INDIRECT; + if (type == HOOK_PUSH_RETN) + type = HOOK_NATIVE_PUSH_RETN; + else + type = HOOK_NATIVE_JMP_INDIRECT; } // check if this is a valid hook type @@ -1061,10 +1126,12 @@ int hook_api(hook_t *h, int type) return 0; // make the address writable - if (VirtualProtect(addr, hook_types[type].len, PAGE_EXECUTE_READWRITE, - &old_protect)) { + if (VirtualProtect(addr, hook_types[type].len, PAGE_EXECUTE_READWRITE, &old_protect)) { - h->hookdata = alloc_hookdata_near(addr); + if (g_config.hook_low) + h->hookdata = alloc_hookdata_low(); + else + h->hookdata = alloc_hookdata_near(addr); if (h->hookdata && hook_create_trampoline(addr, hook_types[type].len, h->hookdata->tramp)) { //hook_store_exception_info(h); @@ -1095,17 +1162,15 @@ int hook_api(hook_t *h, int type) add_dll_range((ULONG_PTR)hmod, (ULONG_PTR)hmod + GetAllocationSize(hmod)); } } - else { + else pipe("WARNING:Unable to place hook on %z", h->funcname); - } // restore the old protection VirtualProtect(addr, hook_types[type].len, old_protect, &old_protect); } - else { + else pipe("WARNING:Unable to change protection for hook on %z", h->funcname); - } return ret; } @@ -1159,6 +1224,8 @@ static int our_stackwalk(ULONG_PTR _rip, ULONG_PTR sp, PVOID *backtrace, unsigne runfunc = RtlLookupFunctionEntry(ctx.Rip, &imgbase, NULL); // needs LdrpInvertedFunctionTableSRWLock on Win10 memset(&nvctx, 0, sizeof(nvctx)); if (runfunc == NULL) { + if (our_isbadreadptr((PVOID)ctx.Rsp, sizeof(PVOID))) + break; ctx.Rip = (ULONG_PTR)(*(ULONG_PTR *)ctx.Rsp); ctx.Rsp += 8; } diff --git a/hooks.c b/hooks.c index 811454b6..67f2ec6c 100644 --- a/hooks.c +++ b/hooks.c @@ -19,8 +19,17 @@ along with this program. If not, see . #include "misc.h" #include "hooking.h" #include "hooks.h" +#include "CAPE\CAPE.h" +typedef struct +{ + PCHAR FunctionName; + PVOID Address; +} NameByAddress; + +extern NameByAddress* GetAddressesByYara(HMODULE ModuleBase, PCHAR FunctionNames[], SIZE_T FunctionCount, SIZE_T* OutFoundCount); extern VOID CALLBACK New_DllLoadNotification(ULONG NotificationReason, const PLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context); +extern PVOID GetAddressByYara(HMODULE ModuleBase, PCHAR FunctionName); extern void DebugOutput(_In_ LPCTSTR lpOutputString, ...); extern void ErrorOutput(_In_ LPCTSTR lpOutputString, ...); extern DWORD GetTimeStamp(LPVOID Address); @@ -38,6 +47,9 @@ void disable_tail_call_optimization(void) #define HOOK(library, funcname) {L###library, #funcname, NULL, NULL, \ &New_##funcname, (void **) &Old_##funcname, NULL, FALSE, FALSE, 0, FALSE} +#define HOOK_WITHNAME(library, funcname, mangled) {L###library, mangled, NULL, NULL, \ + &New_##funcname, (void **) &Old_##funcname, NULL, FALSE, FALSE, 0, FALSE} + #define HOOK_SPECIAL(library, funcname) {L###library, #funcname, NULL, NULL, \ &New_##funcname, (void **) &Old_##funcname, NULL, TRUE, FALSE, 0, FALSE} @@ -53,15 +65,22 @@ void disable_tail_call_optimization(void) #define HOOK_FUNCRVA(library, funcname, timestamp, rva) {L###library, #funcname, NULL, NULL, \ &New_##funcname, (void **) &Old_##funcname, NULL, FALSE, FALSE, 0, FALSE, timestamp, rva} -hook_t full_hooks[] = { +#define HOOK_EXE(funcname) {NULL, #funcname, NULL, NULL, \ + &New_##funcname, (void **) &Old_##funcname, NULL, FALSE, FALSE, 0, FALSE} + +#define HOOK_EXERVA(funcname, timestamp, rva) {NULL, #funcname, NULL, NULL, \ + &New_##funcname, (void **) &Old_##funcname, NULL, FALSE, FALSE, 0, FALSE, timestamp, rva} +hook_t full_hooks[] = { // Process Hooks HOOK_NOTAIL_ALT(ntdll, RtlDispatchException, 2), HOOK_NOTAIL(ntdll, NtRaiseException, 3), + HOOK_NOTAIL_ALT(ntdll, LdrLoadDll, 4), HOOK_NOTAIL(ntdll, LdrUnloadDll, 1), HOOK_SPECIAL(ntdll, NtCreateUserProcess), HOOK_SPECIAL(kernel32, CreateProcessInternalW), + HOOK(ntdll, LdrpCallInitRoutine), HOOK(ntdll, NtAllocateVirtualMemory), HOOK(ntdll, NtAllocateVirtualMemoryEx), @@ -101,6 +120,9 @@ hook_t full_hooks[] = { HOOK(kernel32, Process32NextW), HOOK(kernel32, Module32FirstW), HOOK(kernel32, Module32NextW), + HOOK(kernelbase, K32EnumProcesses), + HOOK(wtsapi32, WTSEnumerateProcessesW), + HOOK(wtsapi32, WTSEnumerateProcessesExW), HOOK(kernel32, CreateProcessA), HOOK(kernel32, CreateProcessW), HOOK(kernel32, WinExec), @@ -109,6 +131,7 @@ hook_t full_hooks[] = { // all variants of ShellExecute end up in ShellExecuteExW HOOK(shell32, ShellExecuteExW), HOOK(msvcrt, system), + HOOK(ntdll, vDbgPrintExWithPrefixInternal), // Thread Hooks HOOK_SPECIAL(ntdll, NtCreateThread), @@ -118,8 +141,12 @@ hook_t full_hooks[] = { HOOK(ntdll, NtQueueApcThreadEx), HOOK(ntdll, NtOpenThread), HOOK(ntdll, NtGetContextThread), - HOOK(ntdll, RtlWow64GetThreadContext), HOOK(ntdll, NtSetContextThread), + HOOK_NOTAIL(ntdll, RtlUserThreadStart, 2), +#ifdef _WIN64 + HOOK(ntdll, RtlWow64GetThreadContext), + HOOK(ntdll, RtlWow64SetThreadContext), +#endif HOOK(ntdll, NtSuspendThread), HOOK(ntdll, NtResumeThread), HOOK(ntdll, NtAlertResumeThread), @@ -130,6 +157,8 @@ hook_t full_hooks[] = { HOOK(ntdll, NtContinue), HOOK(ntdll, NtContinueEx), HOOK(ntdll, NtTestAlert), + HOOK(kernelbase, SetThreadStackGuarantee), + HOOK(kernelbase, SetThreadDescription), HOOK(kernel32, CreateThread), HOOK(kernel32, CreateRemoteThread), HOOK(kernel32, CreateRemoteThreadEx), @@ -155,6 +184,19 @@ hook_t full_hooks[] = { HOOK_SPECIAL(combase, CoGetClassObject), HOOK_SPECIAL(combase, CoGetObject), + // WMI Hooks +#ifdef _WIN64 + HOOK_WITHNAME(fastprox, WMI_Get, "?Get@CWbemObject@@UEAAJPEBGJPEAUtagVARIANT@@PEAJ2@Z"), +#else + HOOK_WITHNAME(fastprox, WMI_Get, "?Get@CWbemObject@@UAGJPBGJPAUtagVARIANT@@PAJ2@Z"), +#endif + HOOK_NOTAIL(fastprox, WMI_ExecQuery, 6), + HOOK_NOTAIL(fastprox, WMI_ExecMethod, 8), + HOOK_NOTAIL(fastprox, WMI_ExecQueryAsync, 6), + HOOK_NOTAIL(fastprox, WMI_ExecMethodAsync, 7), + HOOK_NOTAIL(fastprox, WMI_GetObject, 6), + HOOK_NOTAIL(fastprox, WMI_GetObjectAsync, 5), + // File Hooks HOOK(ntdll, NtQueryAttributesFile), HOOK(ntdll, NtQueryFullAttributesFile), @@ -194,6 +236,7 @@ hook_t full_hooks[] = { HOOK(kernel32, GetDiskFreeSpaceW), HOOK(kernel32, GetVolumeNameForVolumeMountPointW), HOOK(kernel32, GetVolumeInformationByHandleW), + HOOK(kernel32, SetFileInformationByHandle), HOOK(shell32, SHGetFolderPathW), HOOK(shell32, SHGetKnownFolderPath), HOOK(shell32, SHGetFileInfoW), @@ -209,7 +252,7 @@ hook_t full_hooks[] = { HOOK(rstrtmgr, RmStartSession), // Registry Hooks - // Note: Most, if not all, of the Registry API go natively from both the 'A' as well as + // Note: Most, if not all, of the Registry API go natively from both the 'A' as well as // the 'W' versions. So we have to hook all the ascii *and* unicode APIs of those functions. HOOK(advapi32, RegOpenKeyExA), HOOK(advapi32, RegOpenKeyExW), @@ -342,6 +385,7 @@ hook_t full_hooks[] = { HOOK(ntdll, RtlAddVectoredExceptionHandler), HOOK(kernel32, SetErrorMode), HOOK(ntdll, LdrGetDllHandle), + HOOK(ntdll, LdrGetDllHandleEx), HOOK(ntdll, LdrGetProcedureAddress), HOOK(ntdll, LdrGetProcedureAddressForCaller), HOOK(kernel32, DeviceIoControl), @@ -391,6 +435,7 @@ hook_t full_hooks[] = { //HOOK(ole32, OleConvertOLESTREAMToIStorage), HOOK(kernel32, GlobalMemoryStatus), HOOK(kernel32, GlobalMemoryStatusEx), + HOOK(kernel32, GetPhysicallyInstalledSystemMemory), HOOK(user32, SystemParametersInfoA), HOOK(user32, SystemParametersInfoW), HOOK(pstorec, PStoreCreateInstance), @@ -408,6 +453,9 @@ hook_t full_hooks[] = { HOOK(shlwapi, UrlCanonicalizeW), HOOK_NOTAIL(vbe7, rtcCreateObject2, 3), #endif + HOOK(ntdll, NtPowerInformation), + + HOOK(cmd, FindFixAndRun), // Language related hooks HOOK(ntdll, NtQueryDefaultUILanguage), @@ -599,6 +647,7 @@ hook_t full_hooks[] = { HOOK(advapi32, CryptImportKey), HOOK(wintrust, HTTPSCertificateTrust), HOOK(wintrust, HTTPSFinalProv), + HOOK(wintrust, WTGetSignatureInfo), HOOK(crypt32, CryptDecodeObjectEx), HOOK(crypt32, CryptImportPublicKeyInfo), HOOK(ncrypt, NCryptImportKey), @@ -745,28 +794,162 @@ hook_t full_hooks[] = { HOOK_SPECIAL(vbscript, VbsPrint), }; -// This hook set is intended to include only hooks which are necessary -// to follow the execution chain with base functionality +hook_t native_hooks[] = { -hook_t min_hooks[] = { + HOOK_NOTAIL_ALT(ntdll, RtlDispatchException, 2), + HOOK_NOTAIL(ntdll, NtRaiseException, 3), HOOK_NOTAIL_ALT(ntdll, LdrLoadDll, 4), HOOK_NOTAIL(ntdll, LdrUnloadDll, 1), HOOK_SPECIAL(ntdll, NtCreateUserProcess), - HOOK_SPECIAL(kernel32, CreateProcessInternalW), - HOOK_SPECIAL(clrjit, compileMethod), - HOOK_SPECIAL(ole32, CoCreateInstance), - HOOK_SPECIAL(ole32, CoCreateInstanceEx), - HOOK_SPECIAL(ole32, CoGetClassObject), - HOOK_SPECIAL(ole32, CoGetObject), - HOOK_SPECIAL(combase, CoCreateInstance), - HOOK_SPECIAL(combase, CoCreateInstanceEx), - HOOK_SPECIAL(combase, CoGetClassObject), - HOOK_SPECIAL(combase, CoGetObject), + // File Hooks + HOOK(ntdll, NtQueryAttributesFile), + HOOK(ntdll, NtQueryFullAttributesFile), + HOOK(ntdll, NtCreateFile), + HOOK(ntdll, NtOpenFile), + HOOK(ntdll, NtReadFile), + HOOK(ntdll, NtWriteFile), + HOOK(ntdll, NtDeleteFile), + HOOK(ntdll, NtDeviceIoControlFile), + HOOK(ntdll, NtQueryDirectoryFile), + HOOK(ntdll, NtQueryInformationFile), + HOOK(ntdll, NtSetInformationFile), + HOOK(ntdll, NtOpenDirectoryObject), + HOOK(ntdll, NtCreateDirectoryObject), + HOOK(ntdll, NtQueryDirectoryObject), + // Native Registry Hooks + HOOK(ntdll, NtCreateKey), + HOOK(ntdll, NtOpenKey), + HOOK(ntdll, NtOpenKeyEx), + HOOK(ntdll, NtRenameKey), + HOOK(ntdll, NtReplaceKey), + HOOK(ntdll, NtEnumerateKey), + HOOK(ntdll, NtEnumerateValueKey), + HOOK(ntdll, NtSetValueKey), + HOOK(ntdll, NtQueryValueKey), + HOOK(ntdll, NtQueryMultipleValueKey), + HOOK(ntdll, NtDeleteKey), + HOOK(ntdll, NtDeleteValueKey), + HOOK(ntdll, NtLoadKey), + HOOK(ntdll, NtLoadKey2), + HOOK(ntdll, NtLoadKeyEx), + HOOK(ntdll, NtQueryKey), + HOOK(ntdll, NtSaveKey), + HOOK(ntdll, NtSaveKeyEx), + + // Sync Hooks + HOOK(ntdll, NtCreateMutant), + HOOK(ntdll, NtOpenMutant), + HOOK(ntdll, NtReleaseMutant), + HOOK(ntdll, NtCreateEvent), + HOOK(ntdll, NtOpenEvent), + HOOK(ntdll, NtCreateNamedPipeFile), + HOOK(ntdll, NtAddAtom), + HOOK(ntdll, NtAddAtomEx), + HOOK(ntdll, NtFindAtom), + HOOK(ntdll, NtDeleteAtom), + HOOK(ntdll, NtQueryInformationAtom), + + // Process Hooks + HOOK(ntdll, NtAllocateVirtualMemory), + HOOK(ntdll, NtAllocateVirtualMemoryEx), + HOOK(ntdll, NtReadVirtualMemory), + HOOK(ntdll, NtWriteVirtualMemory), + HOOK(ntdll, NtWow64WriteVirtualMemory64), + HOOK(ntdll, NtWow64ReadVirtualMemory64), + HOOK(ntdll, NtProtectVirtualMemory), + HOOK(ntdll, NtFreeVirtualMemory), + HOOK(ntdll, NtCreateProcess), + HOOK(ntdll, NtCreateProcessEx), + HOOK(ntdll, RtlCreateUserProcess), + HOOK(ntdll, NtOpenProcess), + HOOK(ntdll, NtTerminateProcess), + HOOK(ntdll, RtlReportSilentProcessExit), + HOOK(ntdll, NtResumeProcess), + HOOK(ntdll, NtCreateSection), + HOOK(ntdll, NtDuplicateObject), + HOOK(ntdll, NtMakeTemporaryObject), + HOOK(ntdll, NtMakePermanentObject), + HOOK(ntdll, NtOpenSection), + HOOK(ntdll, NtMapViewOfSection), + HOOK(ntdll, NtMapViewOfSectionEx), + HOOK(ntdll, NtUnmapViewOfSection), + HOOK(ntdll, NtUnmapViewOfSectionEx), + HOOK(ntdll, NtOpenProcessToken), + HOOK(ntdll, NtQueryInformationToken), + HOOK(ntdll, DbgUiWaitStateChange), + + // Thread Hooks + HOOK(ntdll, NtCreateThread), + HOOK(ntdll, NtCreateThreadEx), + HOOK(ntdll, NtTerminateThread), + HOOK(ntdll, NtQueueApcThread), + HOOK(ntdll, NtQueueApcThreadEx), + HOOK(ntdll, NtOpenThread), + HOOK(ntdll, NtGetContextThread), +#ifdef _WIN64 + HOOK(ntdll, RtlWow64GetThreadContext), + HOOK(ntdll, RtlWow64SetThreadContext), +#endif + HOOK(ntdll, NtSetContextThread), + HOOK(ntdll, NtSuspendThread), + HOOK(ntdll, NtResumeThread), + HOOK(ntdll, RtlCreateUserThread), + HOOK(ntdll, NtSetInformationThread), + HOOK(ntdll, NtQueryInformationThread), + HOOK(ntdll, NtYieldExecution), + HOOK(ntdll, NtContinue), + + // Misc Hooks + //HOOK(ntdll, RtlMoveMemory), + HOOK(ntdll, RtlAddVectoredExceptionHandler), + HOOK(ntdll, LdrGetDllHandle), + HOOK(ntdll, LdrGetProcedureAddress), + HOOK(ntdll, LdrGetProcedureAddressForCaller), + HOOK_NOTAIL(ntdll, NtShutdownSystem, 1), + HOOK_NOTAIL(ntdll, NtSetSystemPowerState, 3), + HOOK_NOTAIL(ntdll, NtRaiseHardError, 6), + HOOK(ntdll, NtClose), + HOOK(ntdll, NtLoadDriver), + HOOK(ntdll, NtSetInformationProcess), + //HOOK(ntdll, NtQueryInformationProcess), + HOOK(ntdll, RtlDecompressBuffer), + HOOK(ntdll, RtlCompressBuffer), + HOOK(ntdll, NtQuerySystemInformation), +#ifndef _WIN64 + HOOK(ntdll, RtlDosPathNameToNtPathName_U), + HOOK(ntdll, NtQueryLicenseValue), +#endif + + // transaction functions (for process doppelganging) + HOOK(ntdll, NtCreateTransaction), + HOOK(ntdll, NtOpenTransaction), + HOOK(ntdll, NtRollbackTransaction), + HOOK(ntdll, NtCommitTransaction), + HOOK(ntdll, RtlSetCurrentTransaction), + + // Sleep Hooks + HOOK(ntdll, NtQueryPerformanceCounter), + HOOK(ntdll, NtDelayExecution), + HOOK(ntdll, NtWaitForSingleObject), + HOOK_SPECIAL(ntdll, NtQuerySystemTime), + HOOK(ntdll, NtSetTimer), + HOOK(ntdll, NtSetTimerEx), +}; + +// This hook set is intended to include only hooks which are necessary +// to follow the execution chain with base functionality + +hook_t min_hooks[] = { HOOK_NOTAIL_ALT(ntdll, RtlDispatchException, 2), HOOK_NOTAIL(ntdll, NtRaiseException, 3), + HOOK_NOTAIL_ALT(ntdll, LdrLoadDll, 4), + HOOK_NOTAIL(ntdll, LdrUnloadDll, 1), + HOOK_SPECIAL(ntdll, NtCreateUserProcess), + HOOK_SPECIAL(kernel32, CreateProcessInternalW), + HOOK(ntdll, NtCreateProcess), HOOK(ntdll, NtCreateProcessEx), HOOK(ntdll, RtlCreateUserProcess), @@ -793,13 +976,23 @@ hook_t min_hooks[] = { HOOK(ntdll, RtlCreateUserThread), HOOK(kernel32, CreateRemoteThread), HOOK(kernel32, CreateRemoteThreadEx), + + HOOK_SPECIAL(clrjit, compileMethod), + HOOK_SPECIAL(ole32, CoCreateInstance), + HOOK_SPECIAL(ole32, CoCreateInstanceEx), + HOOK_SPECIAL(ole32, CoGetClassObject), + HOOK_SPECIAL(ole32, CoGetObject), + HOOK_SPECIAL(combase, CoCreateInstance), + HOOK_SPECIAL(combase, CoCreateInstanceEx), + HOOK_SPECIAL(combase, CoGetClassObject), + HOOK_SPECIAL(combase, CoGetObject), + HOOK(user32, SendNotifyMessageA), HOOK(user32, SendNotifyMessageW), HOOK(user32, SetWindowLongA), HOOK(user32, SetWindowLongW), HOOK(user32, SetWindowLongPtrA), HOOK(user32, SetWindowLongPtrW), - HOOK(user32, SetWindowsHookExA), HOOK(user32, SetWindowsHookExW), @@ -1175,6 +1368,7 @@ hook_t office_hooks[] = { HOOK(oleaut32, VarBstrCat), HOOK_NOTAIL(usp10, ScriptIsComplex, 3), HOOK_NOTAIL(inseng,DownloadFile,3), + HOOK(imagehlp, MapFileAndCheckSumA), #ifndef _WIN64 HOOK(ntdll, RtlDosPathNameToNtPathName_U), HOOK(ntdll, NtQueryLicenseValue), @@ -1462,6 +1656,10 @@ hook_t test_hooks[] = { HOOK_SPECIAL(ntdll, NtContinue), }; +hook_t exe_hooks[] = { + HOOK_EXE(FindFixAndRun), +}; + BOOL inside_hook(LPVOID Address) { for (unsigned int i = 0; i < hooks_arraysize; i++) { @@ -1487,6 +1685,64 @@ BOOL set_hooks_dll(const wchar_t *library) return ret; } +void set_hooks_exe(void) +{ + LDR_MODULE* mod; + PEB* peb = (PEB*)get_peb(); + mod = (LDR_MODULE*)peb->LoaderData->InLoadOrderModuleList.Flink; + HMODULE ullExeBase = (HMODULE)(mod->BaseAddress); + + int hook_count = sizeof(exe_hooks) / sizeof(exe_hooks[0]); + char* func_names[sizeof(exe_hooks) / sizeof(exe_hooks[0])]; + + for (int i = 0; i < hook_count; i++) + func_names[i] = (char*)exe_hooks[i].funcname; + + SIZE_T found_count = 0; + NameByAddress* results = GetAddressesByYara(ullExeBase, func_names, hook_count, &found_count); + + if (!results || found_count == 0) { + if (results) free(results); + return; + } + + for (int i = 0; i < hook_count; i++) { + if (exe_hooks[i].timestamp && exe_hooks[i].rva) { + hook_t* hook = &exe_hooks[i]; + if (hook_api(hook, g_config.hook_type) < 0) + DebugOutput("set_hooks_exe: Failed to hook %s at RVA 0x%x", hook->funcname, hook->rva); + else + DebugOutput("set_hooks_exe: Hooked %s at RVA 0x%x", hook->funcname, hook->rva); + } + else for (SIZE_T j = 0; j < found_count; j++) { + if (results[j].FunctionName && results[j].Address && !strcmp(results[j].FunctionName, exe_hooks[i].funcname)) { + hook_t* hook = &exe_hooks[i]; + hook->addr = results[j].Address; + + if (hook_api(hook, g_config.hook_type) < 0) + DebugOutput("set_hooks_exe: Failed to hook %s at 0x%p", hook->funcname, hook->addr); + else + DebugOutput("set_hooks_exe: Hooked %s at 0x%p", hook->funcname, hook->addr); + } + } + } + + free(results); + +} + +BOOL dll_is_hooked(const wchar_t *library) +{ + if (!library) + return FALSE; + BOOL ret = FALSE; + for (unsigned int i = 0; i < hooks_arraysize; i++) { + if (!wcsicmp((hooks+i)->library, library)) + ret = TRUE; + } + return ret; +} + void set_hooks_by_export_directory(const wchar_t *exportdirectory, const wchar_t *library) { unsigned int Hooked = 0; @@ -1595,6 +1851,11 @@ void set_hooks() hooks_size = sizeof(browser_hooks); hooks_arraysize = ARRAYSIZE(browser_hooks); } + else if (g_config.native) { + hooks = native_hooks; + hooks_size = sizeof(native_hooks); + hooks_arraysize = ARRAYSIZE(native_hooks); + } else { hooks = full_hooks; hooks_size = sizeof(full_hooks); @@ -1655,5 +1916,7 @@ void set_hooks() DebugOutput("Hooked %d out of %d functions\n", Hooked, hooks_arraysize); + set_hooks_exe(); + hook_enable(); } diff --git a/hooks.h b/hooks.h index 5a57d0da..a301ee2c 100644 --- a/hooks.h +++ b/hooks.h @@ -1030,6 +1030,28 @@ HOOKDEF(BOOL, WINAPI, Module32NextW, __out LPMODULEENTRY32W lpme ); +HOOKDEF(BOOL, WINAPI, K32EnumProcesses, + _Out_writes_bytes_(cb) DWORD* lpidProcess, + _In_ DWORD cb, + _Out_ LPDWORD lpcbNeeded +); + +HOOKDEF(BOOL, WINAPI, WTSEnumerateProcessesW, + _In_ HANDLE hServer, + _In_ DWORD Reserved, + _In_ DWORD Version, + _Out_ PWTS_PROCESS_INFOW* ppProcessInfo, + _Out_ DWORD* pCount +); + +HOOKDEF(BOOL, WINAPI, WTSEnumerateProcessesExW, + _In_ HANDLE hServer, + _Inout_ DWORD* pLevel, + _In_ DWORD SessionId, + _Out_ LPWSTR* ppProcessInfo, + _Out_ DWORD* pCount +); + HOOKDEF(UINT, WINAPI, WinExec, __in LPCSTR lpCmdLine, __in UINT uCmdShow @@ -1261,6 +1283,74 @@ HOOKDEF(HRESULT, WINAPI, CoGetObject, _Out_ LPVOID *ppv ); +// WMI Hooks +HOOKDEF(HRESULT, WINAPI, WMI_Get, + PVOID _this, + LPCWSTR wszName, + LONG lFlags, + VARIANT* pVal, + LONG* pType, + LONG* plFlavor +); + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecQuery, + PVOID _this, + const BSTR strQueryLanguage, + const BSTR strQuery, + LONG lFlags, + PVOID pCtx, + PVOID* ppEnum +); + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecQueryAsync, + PVOID _this, + const BSTR strQueryLanguage, + const BSTR strQuery, + LONG lFlags, + PVOID pCtx, + PVOID pResponseHandler +); + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecMethod, + PVOID _this, + const BSTR strObjectPath, + const BSTR strMethodName, + long lFlags, + PVOID pCtx, + PVOID pInParams, + PVOID* ppOutParams, + PVOID* ppCallResult +); + +HOOKDEF_NOTAIL(WINAPI, WMI_ExecMethodAsync, + PVOID _this, + const BSTR strObjectPath, + const BSTR strMethodName, + long lFlags, + PVOID pCtx, + PVOID pInParams, + PVOID pResponseHandler +); + +HOOKDEF_NOTAIL(WINAPI, WMI_GetObject, + PVOID _this, + const BSTR strObjectPath, + LONG lFlags, + PVOID pCtx, + PVOID* ppObject, + PVOID* ppCallResult +); + +HOOKDEF_NOTAIL(WINAPI, WMI_GetObjectAsync, + PVOID _this, + const BSTR strObjectPath, + LONG lFlags, + PVOID pCtx, + PVOID pResultHandler +); + +// End of WMI Hooks + HOOKDEF(NTSTATUS, WINAPI, NtMapViewOfSection, __in HANDLE SectionHandle, __in HANDLE ProcessHandle, @@ -1504,15 +1594,22 @@ HOOKDEF(NTSTATUS, WINAPI, NtGetContextThread, __inout LPCONTEXT Context ); +HOOKDEF(NTSTATUS, WINAPI, NtSetContextThread, + __in HANDLE ThreadHandle, + __in CONTEXT *Context +); + +#ifdef _WIN64 HOOKDEF(NTSTATUS, WINAPI, RtlWow64GetThreadContext, __in HANDLE ThreadHandle, __inout PWOW64_CONTEXT Context ); -HOOKDEF(NTSTATUS, WINAPI, NtSetContextThread, - __in HANDLE ThreadHandle, - __in CONTEXT *Context +HOOKDEF(NTSTATUS, WINAPI, RtlWow64SetThreadContext, + __in HANDLE ThreadHandle, + __inout PWOW64_CONTEXT Context ); +#endif HOOKDEF(NTSTATUS, WINAPI, NtSuspendThread, __in HANDLE ThreadHandle, @@ -1564,6 +1661,11 @@ HOOKDEF(HANDLE, WINAPI, CreateRemoteThreadEx, __out_opt LPDWORD lpThreadId ); +HOOKDEF_NOTAIL(WINAPI, RtlUserThreadStart, + __in LPTHREAD_START_ROUTINE lpStartAddress, + __in LPVOID lpParameter +); + HOOKDEF(BOOL, WINAPI, TerminateThread, __inout HANDLE hThread, __in DWORD dwExitCode @@ -1586,6 +1688,15 @@ HOOKDEF(BOOL, WINAPI, NtTestAlert, VOID ); +HOOKDEF(BOOL, WINAPI, SetThreadStackGuarantee, + _Inout_ PULONG StackSizeInBytes +); + +HOOKDEF(NTSTATUS, WINAPI, SetThreadDescription, + _In_ HANDLE hThread, + _In_ PCWSTR lpThreadDescription +); + // // Misc Hooks // @@ -1680,6 +1791,14 @@ HOOKDEF(NTSTATUS, WINAPI, LdrGetDllHandle, __out PHANDLE pHModule ); +HOOKDEF(NTSTATUS, WINAPI, LdrGetDllHandleEx, + __in ULONG Flags, + __in_opt PWSTR DllPath, + __in PULONG DllCharacteristics, + __in PUNICODE_STRING DllName, + __out_opt PVOID *DllHandle +); + HOOKDEF(NTSTATUS, WINAPI, LdrGetProcedureAddress, __in HMODULE ModuleHandle, __in_opt PANSI_STRING FunctionName, @@ -1958,6 +2077,10 @@ HOOKDEF(BOOL, WINAPI, GlobalMemoryStatusEx, _Out_ LPMEMORYSTATUSEX lpBuffer ); +HOOKDEF(BOOL, WINAPI, GetPhysicallyInstalledSystemMemory, + _Out_ PULONGLONG TotalMemoryInKilobytes +); + HOOKDEF(BOOL, WINAPI, SystemParametersInfoA, _In_ UINT uiAction, _In_ UINT uiParam, @@ -1982,6 +2105,7 @@ HOOKDEF(HRESULT, WINAPI, PStoreCreateInstance, // // Network Hooks // + HOOKDEF(DWORD, WINAPI, InternetConfirmZoneCrossingA, _In_ HWND hWnd, _In_ LPTSTR szUrlPrev, @@ -2974,6 +3098,13 @@ HOOKDEF(BOOL, WINAPI, CryptExportKey, _Inout_ DWORD *pdwDataLen ); +HOOKDEF(BOOL, WINAPI, CryptDuplicateKey, + _In_ HCRYPTKEY hKey, + _In_ DWORD* pdwReserved, + _In_ DWORD dwFlags, + _Out_ HCRYPTKEY* phKey +); + HOOKDEF(BOOL, WINAPI, CryptDestroyKey, _In_ HCRYPTKEY hKey ); @@ -3024,6 +3155,15 @@ HOOKDEF(HRESULT, WINAPI, HTTPSFinalProv, PVOID data // PCRYPT_PROVIDER_DATA ); +HOOKDEF(NTSTATUS, WINAPI, WTGetSignatureInfo, + _In_ LPWSTR pszFile, + _In_ HANDLE hFile, + _In_ DWORD sigInfoFlags, + _Out_ PVOID psiginfo, + _Out_ PVOID ppCertContext, + _Out_ PVOID phWVTStateData +); + HOOKDEF(BOOL, WINAPI, CryptDecodeObjectEx, _In_ DWORD dwCertEncodingType, _In_ LPCSTR lpszStructType, @@ -3653,4 +3793,31 @@ HOOKDEF(UINT, WINAPI, MsiInstallProductW, _In_ LPCWSTR szCommandLine ); +HOOKDEF(ULONG, __fastcall, vDbgPrintExWithPrefixInternal, + __in PCH Prefix, + __in ULONG ComponentId, + __in ULONG Level, + __in PCHAR Format, + __in va_list arglist, + __in BOOLEAN HandleBreakpoint +); + +HOOKDEF(NTSTATUS, WINAPI, NtPowerInformation, + __in POWER_INFORMATION_LEVEL InformationLevel, + __in_opt PVOID InputBuffer, + __in ULONG InputBufferLength, + __out_opt PVOID OutputBuffer, + __in ULONG OutputBufferLength +); + +HOOKDEF(int, __fastcall, FindFixAndRun, + struct cmdnode *cmdnode +); + +HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA, + _In_ PCSTR Filename, + _Out_ PDWORD HeaderSum, + _Out_ PDWORD CheckSum +); + #include "hook_vbscript.h" diff --git a/loader/loader/Loader.c b/loader/loader/Loader.c index 18f28f95..a4cd4d1b 100644 --- a/loader/loader/Loader.c +++ b/loader/loader/Loader.c @@ -30,7 +30,7 @@ along with this program.If not, see . SYSTEM_INFO SystemInfo; char PipeOutput[MAX_PATH], LogPipe[MAX_PATH]; -BOOL DisableIATPatching, FirstProcess; +BOOL DisableIATPatching, FirstProcess, LoaderSnaps; void pipe(char* Buffer, SIZE_T Length); @@ -114,7 +114,7 @@ int ScanForNonZero(LPVOID Buffer, SIZE_T Size) } __except(EXCEPTION_EXECUTE_HANDLER) { - DebugOutput("ScanForNonZero: Exception occured reading memory address 0x%x\n", (char*)Buffer+p); + DebugOutput("ScanForNonZero: Exception occurred reading memory address 0x%x\n", (char*)Buffer+p); return 0; } @@ -236,7 +236,7 @@ int ReadConfig(DWORD ProcessId, char *DllName) DebugOutput("Loader: Successfully loaded pipe name %s.\n", LogPipe); #endif } - if (!strcmp(key, "no-iat")) + else if (!strcmp(key, "no-iat")) { DisableIATPatching = Value[0] == '1'; if (DisableIATPatching) @@ -244,6 +244,8 @@ int ReadConfig(DWORD ProcessId, char *DllName) else DebugOutput("Loader: IAT patching enabled.\n"); } + else if (!strcmp(key, "snaps")) + LoaderSnaps = Value[0] == '1'; else if (!strcmp(key, "first-process")) FirstProcess = Value[0] == '1'; } @@ -255,7 +257,18 @@ int ReadConfig(DWORD ProcessId, char *DllName) return 1; } -BOOL GetProcessPeb(HANDLE ProcessHandle, PPEB Peb) +DWORD GetNtGlobalFlagsOffset() +{ + _RtlGetNtGlobalFlags pRtlGetNtGlobalFlags = (_RtlGetNtGlobalFlags)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetNtGlobalFlags"); + +#ifdef _WIN64 + return (DWORD)*((PBYTE)pRtlGetNtGlobalFlags + 11); +#else + return (DWORD)*((PBYTE)pRtlGetNtGlobalFlags + 8); +#endif +} + +PVOID GetProcessPeb(HANDLE ProcessHandle, PPEB Peb) { _NtQueryInformationProcess pNtQueryInformationProcess; PROCESS_BASIC_INFORMATION ProcessBasicInformation; @@ -267,10 +280,10 @@ BOOL GetProcessPeb(HANDLE ProcessHandle, PPEB Peb) memset(&ProcessBasicInformation, 0, sizeof(ProcessBasicInformation)); if (pNtQueryInformationProcess(ProcessHandle, 0, &ProcessBasicInformation, sizeof(ProcessBasicInformation), &ulSize) >= 0 && ulSize == sizeof(ProcessBasicInformation)) - if (ReadProcessMemory(ProcessHandle, ProcessBasicInformation.PebBaseAddress, Peb, sizeof(PEB), &dwBytesRead)) - return TRUE; + if (!Peb || (Peb && ReadProcessMemory(ProcessHandle, ProcessBasicInformation.PebBaseAddress, Peb, sizeof(PEB), &dwBytesRead))) + return ProcessBasicInformation.PebBaseAddress; - return FALSE; + return NULL; } DWORD GetProcessInitialThreadId(HANDLE ProcessHandle) @@ -326,6 +339,32 @@ DWORD GetProcessInitialThreadId(HANDLE ProcessHandle) return 0; } +static BOOL EnableLoaderSnaps(HANDLE ProcessHandle, PPEB Peb) +{ + ULONG gflags = 0; + SIZE_T dwBytesRead, dwBytesWritten; + + PVOID pNtGlobalFlag = (PVOID)((PBYTE)Peb + GetNtGlobalFlagsOffset()); + + if (!ReadProcessMemory(ProcessHandle, pNtGlobalFlag, &gflags, sizeof(gflags), &dwBytesRead)) + { + ErrorOutput("Loader: ReadProcessMemory failed (NtGlobalFlag)"); + return FALSE; + } + + gflags |= 0x2; + + if (!WriteProcessMemory(ProcessHandle, pNtGlobalFlag, &gflags, sizeof(gflags), &dwBytesWritten)) + { + ErrorOutput("Loader: WriteProcessMemory failed (NtGlobalFlag)"); + return FALSE; + } + + DebugOutput("Loader: Snaps enabled.\n"); + + return TRUE; +} + static int GrantDebugPrivileges(void) { HANDLE Token = NULL; @@ -1100,7 +1139,7 @@ static int InjectDllViaIAT(HANDLE ProcessHandle, HANDLE ThreadHandle, const char } // Now set the import table directory entry to point to the new table - NtHeader.IMPORT_DIRECTORY.Size = NewSizeOfImportDescriptors; + NtHeader.IMPORT_DIRECTORY.Size = NewImportDirectorySize; NtHeader.IMPORT_DIRECTORY.VirtualAddress = NewImportsRVA; // Set bound imports values to zero to prevent them overriding our new import table @@ -1192,9 +1231,13 @@ static int InjectDll(int ProcessId, int ThreadId, const char *DllPath) goto out; } - if (!GetProcessPeb(ProcessHandle, &Peb)) + PPEB pPeb = GetProcessPeb(ProcessHandle, &Peb); + if (!pPeb) DebugOutput("InjectDll: GetProcessPeb failure.\n"); + if (LoaderSnaps) + EnableLoaderSnaps(ProcessHandle, pPeb); + // If no thread id supplied, we fetch the initial thread id from the initial TEB if (!ThreadId && Peb.ImageBaseAddress && !Peb.Ldr) { @@ -1314,15 +1357,15 @@ int CreateMonitorPipe(char* Name, char* Dll) char *p; if ((p = strchr(buf, ','))) { *p = '\0'; - ProcessId = atoi(&buf[10]); // skipping the '0:' or '1:' suspended flag - ThreadId = atoi(p + 1); // (soon to be deprecated) + ProcessId = atoi(&buf[8]); + ThreadId = atoi(p + 1); } else { ProcessId = atoi(&buf[10]); } if (ProcessId && ThreadId && ProcessId != LastPid) { - DebugOutput("About to call InjectDll on process %d, thread 5%d.\n", ProcessId, ThreadId); + DebugOutput("About to call InjectDll on process %d, thread %d.\n", ProcessId, ThreadId); if (InjectDll(ProcessId, ThreadId, Dll)) LastPid = ProcessId; } @@ -1334,6 +1377,28 @@ int CreateMonitorPipe(char* Name, char* Dll) } +void HandleDebugOutputString(const DEBUG_EVENT dbgEvent, HANDLE hProcess) +{ + char buffer[4096]; + SIZE_T read, size = min(sizeof(buffer) - 2, (SIZE_T)dbgEvent.u.DebugString.nDebugStringLength); + + ReadProcessMemory(hProcess, dbgEvent.u.DebugString.lpDebugStringData, buffer, size, &read); + + size = (int)(min(read, size)); + buffer[size] = 0; + buffer[size + 1] = 0; + + if (dbgEvent.u.DebugString.fUnicode) + DebugOutput("%ws", buffer); + else + { + wchar_t wbuffer[4096]; + int ret = MultiByteToWideChar(CP_ACP, 0, (char*)buffer, (int)size, wbuffer, ARRAYSIZE(wbuffer)); + if (ret != 0) + DebugOutput("%ws", wbuffer); + } +} + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DebugOutput("CAPE loader.\n"); @@ -1425,7 +1490,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine return ret; } - else if (!strcmp(__argv[1], "load")) + else if (!strcmp(__argv[1], "load") || !strcmp(__argv[1], "snaps")) { // usage: loader.exe load DWORD ExplorerPid = 0, ProcessId = 0; @@ -1490,7 +1555,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine sie.lpAttributeList = pAttributeList; - if (!CreateProcess(__argv[3], szCommand, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &sie.StartupInfo, &pi)) + DWORD CreationFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT; + + if (!strcmp(__argv[1], "snaps")) + CreationFlags = CreationFlags | DEBUG_ONLY_THIS_PROCESS; + + if (!CreateProcess(__argv[3], szCommand, NULL, NULL, FALSE, CreationFlags, NULL, NULL, &sie.StartupInfo, &pi)) { ErrorOutput("Loader: CreateProcess error"); return 0; @@ -1512,6 +1582,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine else DebugOutput("Loader: Loaded config for process %d.\n", pi.dwProcessId); #endif + + if (!strcmp(__argv[1], "snaps")) + { + PPEB Peb = GetProcessPeb(pi.hProcess, NULL); + if (Peb) + EnableLoaderSnaps(pi.hProcess, Peb); + } + ret = InjectDll(pi.dwProcessId, pi.dwThreadId, __argv[2]); if (ret) @@ -1525,6 +1603,46 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { ResumeThread(ThreadHandle); CloseHandle(ThreadHandle); + if (!strcmp(__argv[1], "snaps")) + { + while (TRUE) + { + DEBUG_EVENT dbgEvent; + WaitForDebugEvent(&dbgEvent, INFINITE); + switch (dbgEvent.dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + DebugOutput("Exception event"); + break; + case CREATE_THREAD_DEBUG_EVENT: + DebugOutput("Thread created"); + break; + case CREATE_PROCESS_DEBUG_EVENT: + DebugOutput("Process created"); + break; + case EXIT_THREAD_DEBUG_EVENT: + DebugOutput("Thread exited"); + break; + case EXIT_PROCESS_DEBUG_EVENT: + DebugOutput("Process exited"); + break; + case LOAD_DLL_DEBUG_EVENT: + DebugOutput("Dll loaded"); + break; + case UNLOAD_DLL_DEBUG_EVENT: + DebugOutput("Dll unloaded"); + break; + case OUTPUT_DEBUG_STRING_EVENT: + HandleDebugOutputString(dbgEvent, pi.hProcess); + break; + case RIP_EVENT: + DebugOutput("RIP event"); + break; + } + + ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); + } + } } else DebugOutput("There was a problem resuming the new process %s.\n", __argv[3]); diff --git a/loader/loader/Loader.h b/loader/loader/Loader.h index bd99167a..f52b301f 100644 --- a/loader/loader/Loader.h +++ b/loader/loader/Loader.h @@ -160,6 +160,8 @@ typedef ULONG(WINAPI * _RtlNtStatusToDosError) __in NTSTATUS Status ); +typedef ULONG (WINAPI *_RtlGetNtGlobalFlags)(void); + typedef HRESULT (WINAPI *PDLLREGRSRV)(void); typedef void (cdecl *PSHELLCODE)(void); diff --git a/log.c b/log.c index 3f328cc4..1cfbbdc2 100644 --- a/log.c +++ b/log.c @@ -290,6 +290,132 @@ static void log_wstring(const wchar_t *str, int length) free(utf8s); } +static void log_variant(VARIANT* var) { + char log_msg[32]; + __try { + switch (var->vt) { + case VT_NULL: + log_string("NULL", -1); + break; + case 74: + // Undocumented, likely internal Variant Type in vbscript engine + // Observed with: + // Return value (arg1) with VbsStrReverse + // Function argument (arg3) with VbsExecute + log_variant((VARIANT*)var->pvRecord); + break; + case 130: + log_wstring(var->bstrVal, -1); + break; + case VT_BSTR: + log_wstring(var->bstrVal, -1); + break; + case VT_BSTR | VT_BYREF: + log_wstring(var->pbstrVal ? *var->pbstrVal : NULL, -1); + break; + case VT_BOOL: + if (var->boolVal) + log_string("TRUE", 4); + else + log_string("FALSE", 5); + break; + case VT_BOOL | VT_BYREF: + if (*var->pboolVal) + log_string("TRUE", 4); + else + log_string("FALSE", 5); + break; + case VT_INT: + log_int32(var->intVal); + break; + case VT_INT | VT_BYREF: + log_int32(*var->pintVal); + break; + case VT_UINT: + log_int32(var->uintVal); + break; + case VT_UINT | VT_BYREF: + log_int32(*var->puintVal); + break; + case VT_I8: + log_int64(var->llVal); + break; + case VT_I8 | VT_BYREF: + log_int64(*var->pllVal); + break; + case VT_UI8: + log_int64(var->ullVal); + break; + case VT_UI8 | VT_BYREF: + log_int64(*var->pullVal); + break; + case VT_I4: + log_int32(var->lVal); + break; + case VT_I4 | VT_BYREF: + log_int32(*var->plVal); + break; + case VT_UI4: + log_int32(var->ulVal); + break; + case VT_UI4 | VT_BYREF: + log_int32(*var->pulVal); + break; + case VT_I2: + log_int32(var->iVal); + break; + case VT_I2 | VT_BYREF: + log_int32(*var->piVal); + break; + case VT_UI2: + log_int32(var->uiVal); + break; + case VT_UI2 | VT_BYREF: + log_int32(*var->puiVal); + break; + case VT_I1: + log_int32(var->cVal); + break; + case VT_I1 | VT_BYREF: + log_int32(*var->pcVal); + break; + case VT_UI1: + log_int32(var->bVal); + break; + case VT_UI1 | VT_BYREF: + log_int32(*var->pbVal); + break; + case VT_VARIANT: + log_variant(var->pvarVal); + break; + case VT_VARIANT | VT_BYREF: + log_variant(var->pvarVal); + break; + case VT_DATE: + // Note: Maybe convert to a datestamp? + log_int64((int64_t)var->date); + break; + case VT_DATE | VT_BYREF: + // Note: Maybe convert to a datestamp string? + log_int64((int64_t)*var->pdate); + break; + case VT_R8: + log_int64((int64_t)var->dblVal); + break; + case VT_R8 | VT_BYREF: + log_int64((int64_t)*var->pdblVal); + break; + default: + snprintf(log_msg, 32, "Unhandled VARIANT Type: %hu", var->vt); + //log_string((const char*)var->vt, -1); + break; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) { + log_string("", 0); + } +} + static void log_argv(int argc, const char ** argv) { int i; @@ -482,6 +608,9 @@ void loq(int index, const char *category, const char *name, else if (key == 'l' || key == 'L') { (void)va_arg(args, ULONG_PTR); } + else if (key == 'n') { + (void)va_arg(args, VARIANT *); + } else if (key == 'p' || key == 'P') { (void)va_arg(args, void *); } @@ -664,6 +793,10 @@ void loq(int index, const char *category, const char *name, } log_ptr(theptr); } + else if (key == 'n') { + VARIANT* s = va_arg(args, VARIANT*); + log_variant(s); + } else if (key == 'x') { LARGE_INTEGER value = va_arg(args, LARGE_INTEGER); log_int64(value.QuadPart); diff --git a/log.h b/log.h index 4f4cc936..98c5bf63 100644 --- a/log.h +++ b/log.h @@ -33,6 +33,7 @@ along with this program. If not, see . // i -> (int) -> integer // l -> (long) -> long integer // L -> (long *) -> pointer to a long integer +// n -> (VARIANT *) -> logs the value appropiately based on the variant type // p -> (void *) -> pointer (alias for l) // P -> (void **) -> pointer to a handle (alias for L) // o -> (UNICODE_STRING *) -> unicode string diff --git a/misc.c b/misc.c index df4d9963..ca80210e 100644 --- a/misc.c +++ b/misc.c @@ -34,7 +34,6 @@ along with this program. If not, see . extern char *our_process_name; static _NtQueryInformationProcess pNtQueryInformationProcess; -static _NtQueryInformationThread pNtQueryInformationThread; static _RtlGenRandom pRtlGenRandom; static _NtQueryAttributesFile pNtQueryAttributesFile; static _NtQueryObject pNtQueryObject; @@ -43,6 +42,7 @@ static _NtDelayExecution pNtDelayExecution; static _NtQuerySystemInformation pNtQuerySystemInformation; static _RtlEqualUnicodeString pRtlEqualUnicodeString; static _RtlInitUnicodeString pRtlInitUnicodeString; +_NtQueryInformationThread pNtQueryInformationThread; _NtMapViewOfSection pNtMapViewOfSection; _NtUnmapViewOfSection pNtUnmapViewOfSection; _NtAllocateVirtualMemory pNtAllocateVirtualMemory; @@ -51,6 +51,8 @@ _NtFreeVirtualMemory pNtFreeVirtualMemory; _LdrRegisterDllNotification pLdrRegisterDllNotification; _RtlNtStatusToDosError pRtlNtStatusToDosError; _RtlCompareMemory pRtlCompareMemory; +_NtQueryEvent pNtQueryEvent; +_NtQueryVirtualMemory pNtQueryVirtualMemory; void resolve_runtime_apis(void) { @@ -79,6 +81,7 @@ void resolve_runtime_apis(void) *(FARPROC *)&pRtlAdjustPrivilege = GetProcAddress(ntdllbase, "RtlAdjustPrivilege"); *(FARPROC *)&pRtlNtStatusToDosError = GetProcAddress(ntdllbase, "RtlNtStatusToDosError"); *(FARPROC *)&pRtlCompareMemory = GetProcAddress(ntdllbase, "RtlCompareMemory"); + *(FARPROC*)&pNtQueryVirtualMemory = GetProcAddress(ntdllbase, "NtQueryVirtualMemory"); } ULONG_PTR g_our_dll_base; @@ -632,6 +635,41 @@ BOOLEAN is_valid_address_range(ULONG_PTR start, DWORD len) return TRUE; } +BOOLEAN our_isbadreadptr(const void* addr, ULONG len) +{ + SIZE_T reslen; + PUCHAR startaddr = (PUCHAR)addr; + PUCHAR endaddr = startaddr + len; + PUCHAR p; + MEMORY_BASIC_INFORMATION meminfo; + lasterror_t lasterror; + BOOLEAN ret = FALSE; + + /* check for overflow */ + if ((ULONG_PTR)endaddr < (ULONG_PTR)startaddr) + return TRUE; + + get_lasterrors(&lasterror); + for (p = startaddr; p < endaddr; p = (PUCHAR)meminfo.BaseAddress + meminfo.RegionSize) { + memset(&meminfo, 0, sizeof(meminfo)); + if (pNtQueryVirtualMemory(NtCurrentProcess(), p, MemoryBasicInformation, &meminfo, sizeof(meminfo), &reslen)) { + ret = TRUE; + break; + } + if (!(meminfo.State & MEM_COMMIT) || !(meminfo.Type & (MEM_IMAGE | MEM_MAPPED | MEM_PRIVATE))) { + ret = TRUE; + break; + } + if ((meminfo.Protect & (PAGE_GUARD | PAGE_NOACCESS)) && (meminfo.Type != MEM_IMAGE || meminfo.Protect != PAGE_NOACCESS)) { + ret = TRUE; + break; + } + } + + set_lasterrors(&lasterror); + return ret; +} + DWORD parent_process_id() // By Napalm @ NetCore2K (rohitab.com) { PROCESS_BASIC_INFORMATION pbi; @@ -993,6 +1031,26 @@ char *convert_address_to_dll_name_and_offset(ULONG_PTR addr, unsigned int *offse return NULL; } +UNICODE_STRING* get_module_name(ULONG_PTR addr) +{ + PLDR_DATA_TABLE_ENTRY mod; + PLIST_ENTRY pHeadEntry; + PLIST_ENTRY pListEntry; + PEB *peb = (PEB *)get_peb(); + + pHeadEntry = &peb->LoaderData->InLoadOrderModuleList; + for(pListEntry = pHeadEntry->Flink; + pListEntry != pHeadEntry; + pListEntry = pListEntry->Flink) + { + mod = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + if (addr < (ULONG_PTR)mod->BaseAddress || addr >= ((ULONG_PTR)mod->BaseAddress + mod->SizeOfImage)) + continue; + return &mod->BaseDllName; + } + return NULL; +} + // hide our module from PEB // http://www.openrce.org/blog/view/844/How_to_hide_dll @@ -1087,20 +1145,24 @@ uint32_t path_from_handle(HANDLE handle, return length; } -uint32_t path_from_object_attributes(const OBJECT_ATTRIBUTES *obj, - wchar_t *path, uint32_t buffer_length) +uint32_t path_from_object_attributes(const OBJECT_ATTRIBUTES *obj, wchar_t *path, uint32_t buffer_length) { uint32_t copylen, obj_length, length; - if (obj->ObjectName == NULL || obj->ObjectName->Buffer == NULL) { - return path_from_handle(obj->RootDirectory, path, buffer_length);; - } + if (obj == NULL) + return 0; + + if (obj->ObjectName == NULL || obj->ObjectName->Buffer == NULL) + return path_from_handle(obj->RootDirectory, path, buffer_length); // ObjectName->Length is actually the size in bytes. obj_length = obj->ObjectName->Length / sizeof(wchar_t); copylen = min(obj_length, buffer_length - 1); + if (our_isbadreadptr(obj->ObjectName->Buffer, copylen * sizeof(wchar_t))) + return 0; + if (obj->RootDirectory == NULL) { memcpy(path, obj->ObjectName->Buffer, copylen * sizeof(wchar_t)); path[copylen] = L'\0'; @@ -1768,57 +1830,6 @@ DWORD get_pid_by_tid(DWORD tid) return ret; } -BOOLEAN is_suspended(DWORD pid, DWORD tid) -{ - ULONG length; - PSYSTEM_PROCESS_INFORMATION pspi = NULL, proc; - ULONG requestedlen = 16384; - lasterror_t lasterror; - BOOLEAN ret = FALSE; - - get_lasterrors(&lasterror); - - pspi = malloc(requestedlen); - if (pspi == NULL) - goto out; - - while (pNtQuerySystemInformation(SystemProcessInformation, pspi, requestedlen, &length) == STATUS_INFO_LENGTH_MISMATCH) { - free(pspi); - requestedlen <<= 1; - pspi = malloc(requestedlen); - if (pspi == NULL) - goto out; - } - // now we have a valid list of process information - proc = pspi; - while (1) { - ULONG i; - - if ((DWORD)(ULONG_PTR)proc->UniqueProcessId != pid) - goto next; - for (i = 0; i < proc->NumberOfThreads; i++) { - PSYSTEM_THREAD thread = &proc->Threads[i]; - if (tid && (DWORD)(ULONG_PTR)thread->ClientId.UniqueThread != tid) - continue; - if (thread->WaitReason != Suspended) - goto out; - } - break; -next: - if (!proc->NextEntryOffset) - break; - proc = (PSYSTEM_PROCESS_INFORMATION)((PCHAR)proc + proc->NextEntryOffset); - } - ret = TRUE; -out: - if (pspi) - free(pspi); - - set_lasterrors(&lasterror); - - return ret; -} - static PUCHAR get_rel_target(PUCHAR buf) { return buf + 5 + *(int *)&buf[1]; @@ -1931,6 +1942,29 @@ static BOOL get_section_bounds(HMODULE mod, const char * sectionname, PUCHAR *st return FALSE; } +static BOOL get_section_file_bounds(HMODULE mod, const char * sectionname, PUCHAR *start, PUCHAR *end) +{ + PUCHAR buf = (PUCHAR)mod; + PIMAGE_DOS_HEADER doshdr; + PIMAGE_NT_HEADERS nthdr; + PIMAGE_SECTION_HEADER sechdr; + unsigned int numsecs, i; + + doshdr = (PIMAGE_DOS_HEADER)buf; + nthdr = (PIMAGE_NT_HEADERS)(buf + doshdr->e_lfanew); + sechdr = (PIMAGE_SECTION_HEADER)((PUCHAR)&nthdr->OptionalHeader + nthdr->FileHeader.SizeOfOptionalHeader); + numsecs = nthdr->FileHeader.NumberOfSections; + + for (i = 0; i < numsecs; i++) { + if (memcmp(sechdr[i].Name, sectionname, strlen(sectionname))) + continue; + *start = buf + sechdr[i].PointerToRawData; + *end = *start + sechdr[i].SizeOfRawData; + return TRUE; + } + return FALSE; +} + ULONG_PTR get_connectex_addr(HMODULE mod) { PUCHAR start, end; @@ -2409,3 +2443,143 @@ void prevent_module_reloading(PVOID *BaseAddress) { free(absolutepath); } + +void prevent_module_unhooking(PVOID buffer, wchar_t *filename) +{ + PUCHAR file_start = NULL, file_end = NULL, mem_start = NULL, mem_end = NULL; + + wchar_t *whitelist[] = { +#ifdef _WIN64 + L"\\Device\\HarddiskVolume2\\Windows\\System32\\ntdll.dll", +#else + L"\\Device\\HarddiskVolume2\\Windows\\SysWOW64\\ntdll.dll", +#endif + NULL + }; + + for (int i = 0; whitelist[i]; i++) { + if (!wcsicmp(whitelist[i], filename)) { + get_section_file_bounds(buffer, ".text", &file_start, &file_end); + break; + } + } + + if (!file_start) + return; + + if (!get_section_bounds((HMODULE)ntdll_base, ".text", &mem_start, &mem_end)) + return; + + memcpy(file_start, mem_start, (unsigned int)(file_end - file_start)); +} + +static size_t append_octet(char** p, size_t* remaining, unsigned char octet) { + char* start = *p; + size_t written_chars = 0; + + // A temporary buffer to hold the characters of the octet (max 3 chars for 0-255) + char temp_buffer[3]; + int i = 0; + + // Handle 0 + if (octet == 0) { + temp_buffer[i++] = '0'; + } + else { + // Extract digits in reverse order + unsigned char val = octet; + while (val > 0) { + temp_buffer[i++] = (val % 10) + '0'; + val /= 10; + } + } + + // Write the digits to the destination buffer in the correct order + written_chars = i; + if (*remaining <= written_chars) { // Check if there's enough space (including null terminator) + return 0; + } + + while (i > 0) { + *(*p)++ = temp_buffer[--i]; + } + + *remaining -= written_chars; + return written_chars; +} + +const char* our_inet_ntop(int af, const void* src, char* dst, size_t size) { + if (src == NULL || dst == NULL) { + return NULL; + } + + if (af != AF_INET) { + return NULL; + } + + if (size < OUR_INET_ADDRSTRLEN) { + return NULL; + } + + // Cast the source to a pointer to raw bytes (unsigned char). + const unsigned char* p_addr = (const unsigned char*)src; + char* p = dst; + size_t remaining = size; + + for (int i = 0; i < 4; ++i) { + // Read the i-th byte directly from memory. This avoids all endianness problems. + unsigned char octet = p_addr[i]; + if (append_octet(&p, &remaining, octet) == 0) { + return NULL; + } + + if (i < 3) { + if (remaining <= 1) { + return NULL; + } + *p++ = '.'; + remaining--; + } + } + + *p = '\0'; + return dst; +} + +unsigned short our_ntohs(unsigned short netshort) { + return (netshort >> 8) | (netshort << 8); +} + +DWORD wait_for_event_to_be_signaled(HANDLE hEvent, DWORD dwTimeout) { + ULONGLONG startTime = raw_gettickcount(); + ULONGLONG currentTime; + NTSTATUS status; + EVENT_BASIC_INFORMATION eventInfo; + ULONG returnLength; + + while (TRUE) { + status = pNtQueryEvent(hEvent, EventBasicInformation, &eventInfo, sizeof(eventInfo), &returnLength); + if (status == STATUS_SUCCESS) { + // Check the state. 1 means signaled. + if (eventInfo.EventState == 1) { + return WAIT_OBJECT_0; + } + } + + // Check for timeout. + currentTime = raw_gettickcount(); + if ((currentTime - startTime) > dwTimeout) { + return WAIT_TIMEOUT; + } + + raw_sleep(250); + } +} + +void* gettib() { +#ifdef _WIN64 + return (void *)__readgsqword(0); +#else + return (void *)__readfsdword(0); +#endif +} diff --git a/misc.h b/misc.h index c8c3c313..65c82b5d 100644 --- a/misc.h +++ b/misc.h @@ -126,6 +126,41 @@ typedef SIZE_T (WINAPI *_RtlCompareMemory)( _In_ SIZE_T Length ); +typedef enum _EVENT_INFORMATION_CLASS { + EventBasicInformation +} EVENT_INFORMATION_CLASS; + +typedef enum _EVENT_TYPE { + NotificationEvent, // manual-reset event + SynchronizationEvent // auto-reset event +} EVENT_TYPE; + +typedef struct _EVENT_BASIC_INFORMATION { + EVENT_TYPE EventType; + LONG EventState; +} EVENT_BASIC_INFORMATION, * PEVENT_BASIC_INFORMATION; + +typedef NTSTATUS(NTAPI* _NtQueryEvent)( + _In_ HANDLE EventHandle, + _In_ EVENT_INFORMATION_CLASS EventInformationClass, + _Out_writes_bytes_(EventInformationLength) PVOID EventInformation, + _In_ ULONG EventInformationLength, + _Out_opt_ PULONG ReturnLength + ); +typedef enum { + MemoryBasicInformation = 0, + MemorySectionName = 2, +} MEMORY_INFORMATION_CLASS; + +typedef NTSTATUS(WINAPI* _NtQueryVirtualMemory)( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, + _Out_ PVOID Buffer, + _In_ SIZE_T Length, + _Out_opt_ PSIZE_T ResultLength +); + _NtSetInformationProcess pNtSetInformationProcess; _NtMapViewOfSection pNtMapViewOfSection; _NtUnmapViewOfSection pNtUnmapViewOfSection; @@ -146,8 +181,8 @@ void raw_sleep(int msecs); DWORD randint(DWORD min, DWORD max); BOOL is_directory_objattr(const OBJECT_ATTRIBUTES *obj); BOOL file_exists(const OBJECT_ATTRIBUTES *obj); +UNICODE_STRING* get_module_name(ULONG_PTR addr); void hide_module_from_peb(HMODULE module_handle); -BOOLEAN is_suspended(DWORD pid, DWORD tid); int path_is_system(const wchar_t *path_w); int path_is_program_files(const wchar_t *path_w); BOOLEAN parent_has_path(char* path); @@ -262,6 +297,7 @@ LONG WINAPI capemon_exception_handler(__in struct _EXCEPTION_POINTERS *Exception BOOLEAN prevent_module_unloading(PVOID BaseAddress); void prevent_module_reloading(PVOID *BaseAddress); +void prevent_module_unhooking(PVOID buffer, wchar_t *filename); struct envstruct { ULONG k; @@ -270,3 +306,87 @@ struct envstruct { }; const char* GetLanguageName(LANGID langID); + +#define OUR_INET_ADDRSTRLEN 16 + +typedef struct _in_sockaddr { + short sin_family; // e.g. AF_INET + unsigned short sin_port; // e.g. htons(3490) + unsigned long sin_addr; // see struct in_addr, below + char sin_zero[8]; // zero this if you want to +} in_sockaddr; + +typedef struct _AFD_ConnectDataStruct +{ + DWORD dwUnknown1; + DWORD dwUnknown2; + DWORD dwUnknown3; + in_sockaddr SockAddr; +} AFD_ConnectDataStruct; + +typedef struct _AFD_BindDataStruct +{ + DWORD dwUnknown1; + in_sockaddr SockAddr; +} AFD_BindDataStruct, * PAFD_BindDataStruct; + +typedef struct _AFD_WSABUF { + ULONG len; + PCHAR buf; +} AFD_WSABUF, * PAFD_WSABUF; + +typedef struct _AFD_RECV_INFO { + PAFD_WSABUF AfdBufferArray; + ULONG AfdBufferCount; + ULONG AfdFlags; + ULONG TdiFlags; +} AFD_RECV_INFO, * PAFD_RECV_INFO; + +typedef struct _AFD_SEND_INFO { + PAFD_WSABUF AfdBufferArray; + ULONG AfdBufferCount; + ULONG TdiFlags; +} AFD_SEND_INFO, * PAFD_SEND_INFO; + +const char* our_inet_ntop(int af, const void* src, char* dst, size_t size); +unsigned short our_ntohs(unsigned short netshort); +DWORD wait_for_event_to_be_signaled(HANDLE hEvent, DWORD dwTimeout); +BOOLEAN our_isbadreadptr(const void* addr, ULONG len); + +typedef HANDLE CRTHANDLE; + +/* +* Structure defs for FindFixAndSave hook in cmd.exe +* From https://github.com/KingKDot/Exorcism/blob/54a44302469160aa7b93f4b72e93206d06a786ac/cmdtest/cmdtest/dllmain.cpp#L70 +*/ +struct savtype { + TCHAR* saveptrs[12]; +}; + +struct relem { + CRTHANDLE rdhndl; // handle to be redirected + TCHAR* fname; // filename (or &n) + CRTHANDLE svhndl; // where orig handle is saved + int flag; // Append flag + TCHAR rdop; // Type ('>' | '<') + struct relem* nxt; // Next structure +}; + +struct node { // Used for operators + int type; // Type of operator + struct savtype save; // FOR processor saves orig strings here + struct relem* rio; // M022 - Linked redirection list + struct node* lhs; // Ptr to left hand side of the operator + struct node* rhs; // Ptr to right hand side of the operator + INT_PTR extra[4]; // M022 - Padding now needed +}; + +struct cmdnode { + int type; // Type of command + struct savtype save; // FOR processor saves orig strings here + struct relem* rio; // M022 - Linked redirection list + PTCHAR cmdline; // Ptr to command line + PTCHAR argptr; // Ptr to type of command + int flag; // M022 - Valid for cond and goto types + int cmdarg; // M022 - Argument to STRTYP routine +}; diff --git a/ntapi.h b/ntapi.h index 69eed2ae..bd39c452 100644 --- a/ntapi.h +++ b/ntapi.h @@ -30,6 +30,8 @@ along with this program. If not, see . #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define EXCEPTION_CHAIN_END ((PEXCEPTION_REGISTRATION_RECORD)-1) + #ifndef _MSC_VER #define __out #define __in @@ -524,6 +526,22 @@ typedef struct _PROC_THREAD_ATTRIBUTE_LIST typedef void *PVOID, **PPVOID; +typedef struct _LDR_MODULE { + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID BaseAddress; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} LDR_MODULE, * PLDR_MODULE; + typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; @@ -604,7 +622,7 @@ typedef struct _PEB { PVOID PostProcessInitRoutine; BYTE Reserved4[136]; ULONG SessionId; -} PEB; +} PEB, *PPEB; #else typedef struct _PEB { BOOLEAN InheritedAddressSpace; @@ -664,6 +682,18 @@ typedef struct _PEB { } PEB, *PPEB; #endif +typedef struct _TEB +{ + NT_TIB NtTib; + PVOID EnvironmentPointer; + CLIENT_ID ClientId; + PVOID ActiveRpcHandle; + PVOID ThreadLocalStoragePointer; + PPEB ProcessEnvironmentBlock; + ULONG LastErrorValue; +// truncated +} TEB, *PTEB; + typedef enum _DBG_STATE { DbgIdle, @@ -1003,6 +1033,13 @@ typedef struct _TIMER_SET_COALESCABLE_TIMER_INFO { PBOOLEAN PreviousState; } TIMER_SET_COALESCABLE_TIMER_INFO, *PTIMER_SET_COALESCABLE_TIMER_INFO; +typedef struct _WTS_PROCESS_INFOW { + DWORD SessionId; + DWORD ProcessId; + LPWSTR pProcessName; + PSID pUserSid; +} WTS_PROCESS_INFOW, *PWTS_PROCESS_INFOW; + typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); typedef BOOL (WINAPI *PDLL_INIT_ROUTINE)( diff --git a/pipe.c b/pipe.c index 3fbbcdc4..ddfeccab 100644 --- a/pipe.c +++ b/pipe.c @@ -224,17 +224,23 @@ int pipe2(void *out, int *outlen, const char *fmt, ...) va_list args; int len; int ret = -1; - va_start(args, fmt); - len = _pipe_sprintf(NULL, fmt, args); - if(len > 0) { - char *buf = calloc(1, len + 1); - _pipe_sprintf(buf, fmt, args); - va_end(args); - if(CallNamedPipeW(g_config.pipe_name, buf, len, out, *outlen, - (DWORD *) outlen, NMPWAIT_WAIT_FOREVER) != 0) - ret = 0; - free(buf); + if (g_config.standalone) { + *outlen = 0; + } + else { + va_start(args, fmt); + len = _pipe_sprintf(NULL, fmt, args); + if(len > 0) { + char *buf = calloc(1, len + 1); + _pipe_sprintf(buf, fmt, args); + va_end(args); + + if(CallNamedPipeW(g_config.pipe_name, buf, len, out, *outlen, + (DWORD *) outlen, NMPWAIT_WAIT_FOREVER) != 0) + ret = 0; + free(buf); + } } return ret; } diff --git a/unhook.c b/unhook.c index dc53fbef..f35b3cf1 100644 --- a/unhook.c +++ b/unhook.c @@ -135,8 +135,8 @@ void invalidate_regions_for_hook(const hook_t *hook) void remove_hook(const char *funcname) { for (uint32_t idx = 0; idx < g_index; idx++) { - DWORD old_protect; if (g_addr[idx] && !stricmp(g_unhook_hooks[idx]->funcname, funcname)) { + DWORD old_protect; if (!VirtualProtect(g_addr[idx], g_length[idx], PAGE_EXECUTE_READWRITE, &old_protect)) return; memcpy(g_addr[idx], g_orig[idx], g_length[idx]); @@ -157,10 +157,40 @@ void restore_hooks_on_range(ULONG_PTR start, ULONG_PTR end) __try { for (idx = 0; idx < g_index; idx++) { + DWORD old_protect; if ((ULONG_PTR)g_addr[idx] < start || ((ULONG_PTR)g_addr[idx] + g_length[idx]) > end) continue; if (!memcmp(g_orig[idx], g_addr[idx], g_length[idx])) { + if (!VirtualProtect(g_addr[idx], g_length[idx], PAGE_EXECUTE_READWRITE, &old_protect)) + return; memcpy(g_addr[idx], g_our[idx], g_length[idx]); + VirtualProtect(g_addr[idx], g_length[idx], old_protect, &old_protect); + log_hook_restoration(g_unhook_hooks[idx]); + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) { + ; + } + + set_lasterrors(&lasterror); +} + + +void restore_hook(uint32_t idx) +{ + lasterror_t lasterror; + + get_lasterrors(&lasterror); + + __try { + for (uint32_t i = 0; i < g_index; i++) { + if (i == idx) { + DWORD old_protect; + if (!VirtualProtect(g_addr[idx], g_length[idx], PAGE_EXECUTE_READWRITE, &old_protect)) + return; + memcpy(g_addr[idx], g_our[idx], g_length[idx]); + VirtualProtect(g_addr[idx], g_length[idx], old_protect, &old_protect); log_hook_restoration(g_unhook_hooks[idx]); } } @@ -195,14 +225,19 @@ static DWORD WINAPI _unhook_detect_thread(LPVOID param) for (idx = 0; idx < g_index; idx++) { if (g_unhook_hooks[idx]->is_hooked && g_hook_reported[idx] == 0) { char *tmpbuf = NULL; - if (!is_valid_address_range((ULONG_PTR)g_addr[idx], g_length[idx])) { + if (!is_valid_address_range((ULONG_PTR)g_addr[idx], g_length[idx])) continue; - } __try { int is_modification = 1; // Check whether this memory region still equals what we made it. - if (!memcmp(g_addr[idx], g_our[idx], g_length[idx])) { + if (!memcmp(g_addr[idx], g_our[idx], g_length[idx])) continue; + + // Attempt restoration + if (g_config.hook_restore) { + restore_hook(idx); + if (!memcmp(g_addr[idx], g_our[idx], g_length[idx])) + continue; } // If the memory region matches the original contents, then it