__checkReturn NT_TIB32* GetWow64Teb( __in PETHREAD thread ) { if(PsGetProcessWow64Process(IoThreadToProcess(thread))) { NT_TIB* teb = reinterpret_cast<NT_TIB*>(PsGetThreadTeb(thread)); if (teb) { NT_TIB32* teb32 = reinterpret_cast<NT_TIB32*>(teb->ExceptionList); if (teb32 && ((ULONG_PTR)teb32->Self == (ULONG_PTR)teb32)) return teb32; } } return NULL; }
/// <summary> /// Get exported function address /// </summary> /// <param name="pBase">Module base</param> /// <param name="name_ord">Function name or ordinal</param> /// <param name="pProcess">Target process for user module</param> /// <param name="baseName">Dll name for api schema</param> /// <returns>Found address, NULL if not found</returns> PVOID BBGetModuleExport( IN PVOID pBase, IN PCCHAR name_ord, IN PEPROCESS pProcess, IN PUNICODE_STRING baseName ) { PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)pBase; PIMAGE_NT_HEADERS32 pNtHdr32 = NULL; PIMAGE_NT_HEADERS64 pNtHdr64 = NULL; PIMAGE_EXPORT_DIRECTORY pExport = NULL; ULONG expSize = 0; ULONG_PTR pAddress = 0; ASSERT( pBase != NULL ); if (pBase == NULL) return NULL; // Protect from UserMode AV __try { // Not a PE file if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE) return NULL; pNtHdr32 = (PIMAGE_NT_HEADERS32)((PUCHAR)pBase + pDosHdr->e_lfanew); pNtHdr64 = (PIMAGE_NT_HEADERS64)((PUCHAR)pBase + pDosHdr->e_lfanew); // Not a PE file if (pNtHdr32->Signature != IMAGE_NT_SIGNATURE) return NULL; // 64 bit image if (pNtHdr32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { pExport = (PIMAGE_EXPORT_DIRECTORY)(pNtHdr64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)pBase); expSize = pNtHdr64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; } // 32 bit image else { pExport = (PIMAGE_EXPORT_DIRECTORY)(pNtHdr32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)pBase); expSize = pNtHdr32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; } PUSHORT pAddressOfOrds = (PUSHORT)(pExport->AddressOfNameOrdinals + (ULONG_PTR)pBase); PULONG pAddressOfNames = (PULONG)(pExport->AddressOfNames + (ULONG_PTR)pBase); PULONG pAddressOfFuncs = (PULONG)(pExport->AddressOfFunctions + (ULONG_PTR)pBase); for (ULONG i = 0; i < pExport->NumberOfFunctions; ++i) { USHORT OrdIndex = 0xFFFF; PCHAR pName = NULL; // Find by index if ((ULONG_PTR)name_ord <= 0xFFFF) { OrdIndex = (USHORT)i; } // Find by name else if ((ULONG_PTR)name_ord > 0xFFFF && i < pExport->NumberOfNames) { pName = (PCHAR)(pAddressOfNames[i] + (ULONG_PTR)pBase); OrdIndex = pAddressOfOrds[i]; } // Weird params else return NULL; if (((ULONG_PTR)name_ord <= 0xFFFF && (USHORT)((ULONG_PTR)name_ord) == OrdIndex + pExport->Base) || ((ULONG_PTR)name_ord > 0xFFFF && strcmp( pName, name_ord ) == 0)) { pAddress = pAddressOfFuncs[OrdIndex] + (ULONG_PTR)pBase; // Check forwarded export if (pAddress >= (ULONG_PTR)pExport && pAddress <= (ULONG_PTR)pExport + expSize) { WCHAR strbuf[256] = { 0 }; ANSI_STRING forwarder = { 0 }; ANSI_STRING import = { 0 }; UNICODE_STRING uForwarder = { 0 }; ULONG delimIdx = 0; PVOID forwardBase = NULL; PVOID result = NULL; // System image, not supported if (pProcess == NULL) return NULL; RtlInitAnsiString( &forwarder, (PCSZ)pAddress ); RtlInitEmptyUnicodeString( &uForwarder, strbuf, sizeof( strbuf ) ); RtlAnsiStringToUnicodeString( &uForwarder, &forwarder, FALSE ); for (ULONG i = 0; i < uForwarder.Length / sizeof( WCHAR ); i++) { if (uForwarder.Buffer[i] == L'.') { uForwarder.Length = (USHORT)(i * sizeof( WCHAR )); uForwarder.Buffer[i] = L'\0'; delimIdx = i; break; } } // Get forward function name/ordinal RtlInitAnsiString( &import, forwarder.Buffer + delimIdx + 1 ); RtlAppendUnicodeToString( &uForwarder, L".dll" ); // // Check forwarded module // UNICODE_STRING resolved = { 0 }; UNICODE_STRING resolvedName = { 0 }; BBResolveImagePath( NULL, pProcess, KApiShemaOnly, &uForwarder, baseName, &resolved ); BBStripPath( &resolved, &resolvedName ); forwardBase = BBGetUserModule( pProcess, &resolvedName, PsGetProcessWow64Process( pProcess ) != NULL ); result = BBGetModuleExport( forwardBase, import.Buffer, pProcess, &resolvedName ); RtlFreeUnicodeString( &resolved ); return result; } break; } } } __except (EXCEPTION_EXECUTE_HANDLER) { DPRINT( "BlackBone: %s: Exception\n", __FUNCTION__ ); } return (PVOID)pAddress; }
/// <summary> /// Unlink user-mode module from Loader lists /// </summary> /// <param name="pProcess">Target process</param> /// <param name="pBase">Module base</param> /// <param name="isWow64">If TRUE - unlink from PEB32 Loader, otherwise use PEB64</param> /// <returns>Status code</returns> NTSTATUS BBUnlinkFromLoader( IN PEPROCESS pProcess, IN PVOID pBase, IN BOOLEAN isWow64 ) { NTSTATUS status = STATUS_SUCCESS; ASSERT( pProcess != NULL ); if (pProcess == NULL) return STATUS_INVALID_PARAMETER; // Protect from UserMode AV __try { // Wow64 process if (isWow64) { PPEB32 pPeb32 = (PPEB32)PsGetProcessWow64Process( pProcess ); if (pPeb32 == NULL) { DPRINT( "BlackBone: %s: No PEB present. Aborting\n", __FUNCTION__ ); return STATUS_NOT_FOUND; } // Search in InLoadOrderModuleList for (PLIST_ENTRY32 pListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)pPeb32->Ldr)->InLoadOrderModuleList.Flink; pListEntry != &((PPEB_LDR_DATA32)pPeb32->Ldr)->InLoadOrderModuleList; pListEntry = (PLIST_ENTRY32)pListEntry->Flink) { PLDR_DATA_TABLE_ENTRY32 pEntry = CONTAINING_RECORD( pListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks ); // Unlink if ((PVOID)pEntry->DllBase == pBase) { RemoveEntryList32( &pEntry->InLoadOrderLinks ); RemoveEntryList32( &pEntry->InInitializationOrderLinks ); RemoveEntryList32( &pEntry->InMemoryOrderLinks ); RemoveEntryList32( &pEntry->HashLinks ); break; } } } // Native process else { PPEB pPeb = PsGetProcessPeb( pProcess ); if (!pPeb) { DPRINT( "BlackBone: %s: No PEB present. Aborting\n", __FUNCTION__ ); return STATUS_NOT_FOUND; } // Search in InLoadOrderModuleList for (PLIST_ENTRY pListEntry = pPeb->Ldr->InLoadOrderModuleList.Flink; pListEntry != &pPeb->Ldr->InLoadOrderModuleList; pListEntry = pListEntry->Flink) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD( pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); // Unlink if (pEntry->DllBase == pBase) { RemoveEntryList( &pEntry->InLoadOrderLinks ); RemoveEntryList( &pEntry->InInitializationOrderLinks ); RemoveEntryList( &pEntry->InMemoryOrderLinks ); RemoveEntryList( &pEntry->HashLinks ); break; } } } } __except (EXCEPTION_EXECUTE_HANDLER) { DPRINT( "BlackBone: %s: Exception, Code: 0x%X\n", __FUNCTION__, GetExceptionCode() ); } return status; }
/// <summary> /// Get module base address by name /// </summary> /// <param name="pProcess">Target process</param> /// <param name="ModuleName">Nodule name to search for</param> /// <param name="isWow64">If TRUE - search in 32-bit PEB</param> /// <returns>Found address, NULL if not found</returns> PVOID BBGetUserModule( IN PEPROCESS pProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN isWow64 ) { ASSERT( pProcess != NULL ); if (pProcess == NULL) return NULL; // Protect from UserMode AV __try { LARGE_INTEGER time = { 0 }; time.QuadPart = -250ll * 10 * 1000; // 250 msec. // Wow64 process if (isWow64) { PPEB32 pPeb32 = (PPEB32)PsGetProcessWow64Process( pProcess ); if (pPeb32 == NULL) { DPRINT( "BlackBone: %s: No PEB present. Aborting\n", __FUNCTION__ ); return NULL; } // Wait for loader a bit for (INT i = 0; !pPeb32->Ldr && i < 10; i++) { DPRINT( "BlackBone: %s: Loader not intialiezd, waiting\n", __FUNCTION__ ); KeDelayExecutionThread( KernelMode, TRUE, &time ); } // Still no loader if (!pPeb32->Ldr) { DPRINT( "BlackBone: %s: Loader was not intialiezd in time. Aborting\n", __FUNCTION__ ); return NULL; } // Search in InLoadOrderModuleList for (PLIST_ENTRY32 pListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)pPeb32->Ldr)->InLoadOrderModuleList.Flink; pListEntry != &((PPEB_LDR_DATA32)pPeb32->Ldr)->InLoadOrderModuleList; pListEntry = (PLIST_ENTRY32)pListEntry->Flink) { UNICODE_STRING ustr; PLDR_DATA_TABLE_ENTRY32 pEntry = CONTAINING_RECORD( pListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks ); RtlUnicodeStringInit( &ustr, (PWCH)pEntry->BaseDllName.Buffer ); if (RtlCompareUnicodeString( &ustr, ModuleName, TRUE ) == 0) return (PVOID)pEntry->DllBase; } } // Native process else { PPEB pPeb = PsGetProcessPeb( pProcess ); if (!pPeb) { DPRINT( "BlackBone: %s: No PEB present. Aborting\n", __FUNCTION__ ); return NULL; } // Wait for loader a bit for (INT i = 0; !pPeb->Ldr && i < 10; i++) { DPRINT( "BlackBone: %s: Loader not intialiezd, waiting\n", __FUNCTION__ ); KeDelayExecutionThread( KernelMode, TRUE, &time ); } // Still no loader if (!pPeb->Ldr) { DPRINT( "BlackBone: %s: Loader was not intialiezd in time. Aborting\n", __FUNCTION__ ); return NULL; } // Search in InLoadOrderModuleList for (PLIST_ENTRY pListEntry = pPeb->Ldr->InLoadOrderModuleList.Flink; pListEntry != &pPeb->Ldr->InLoadOrderModuleList; pListEntry = pListEntry->Flink) { PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD( pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); if (RtlCompareUnicodeString( &pEntry->BaseDllName, ModuleName, TRUE ) == 0) return pEntry->DllBase; } } } __except (EXCEPTION_EXECUTE_HANDLER) { DPRINT( "BlackBone: %s: Exception, Code: 0x%X\n", __FUNCTION__, GetExceptionCode() ); } return NULL; }