HyperDbg/hyperdbg/libhyperdbg/code/debugger/misc/pci-id.cpp

386 lines
11 KiB
C++
Raw Permalink Blame History

/**
* @file pci-id.cpp
* @author Bj<42>rn Ruytenberg (bjorn@bjornweb.nl)
* @brief Provides runtime access to PCI ID database
* @details
* @version 0.12
* @date 2024-12-04
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
static CHAR * PciIdDatabaseBuffer = NULL;
/**
* @brief Trims whitespaces in passed string
*
* @param Str
* @param MaxLen
* @return CHAR*
*/
CHAR *
TrimWhitespace(CHAR * Str, UINT8 MaxLen)
{
CHAR * End;
while (*Str == ' ')
Str++; // Trim leading space
if (*Str == '\0')
return Str;
End = Str + strnlen_s(Str, MaxLen) - 1;
while (End > Str && (*End == ' ' || *End == '\n' || *End == '\r'))
End--;
*(End + 1) = '\0';
return Str;
}
/**
* @brief Converts passed string to lowercase
*
* @param Str
* @return CHAR*
*/
CHAR *
ToLower(CHAR * Str)
{
UINT8 StrLength = (UINT8)strnlen_s(Str, PCI_ID_AS_STR_LENGTH);
CHAR * CurrentChar = Str;
while (CurrentChar < Str + StrLength)
{
*CurrentChar = tolower(*CurrentChar);
CurrentChar++;
}
return Str;
}
/**
* @brief Read line from string. Treats SrcBuffer as a stream (similar to fgets and friends), i.e. updates SrcBuffer by number of characters read.
*
* @param DestBuffer
* @param CharLimit
* @param SrcBuffer
* @return CHAR*
*/
CHAR *
ReadLine(CHAR * DestBuffer, UINT64 CharLimit, CHAR ** SrcBuffer)
{
CHAR * Line = strchr(*SrcBuffer, '\n');
if (!Line)
{
return NULL;
}
else
{
strncpy_s(DestBuffer, CharLimit, *SrcBuffer, (Line - *SrcBuffer));
*SrcBuffer += (Line - *SrcBuffer + 1);
return *SrcBuffer;
}
}
/**
* @brief Get Vendor by PCI ID, encoded in ASCII. Do not call directly - use GetVendorById() instead.
*
* @param Filename
* @param VendorId
* @return Vendor *
*/
Vendor *
GetVendorByIdStr(const CHAR * Filename, const CHAR * VendorId)
{
Vendor * MatchedVendor = NULL;
BOOLEAN FoundVendorId = FALSE;
Device * LastDevice = NULL;
SubDevice * LastSubDevice = NULL;
CHAR * PciIdDbBufPtr = NULL;
CHAR Line[1024] = {'\0'};
if (!PciIdDatabaseBuffer)
{
FILE * f = fopen(Filename, "rb");
SIZE_T Length = 0;
if (f == NULL)
{
ShowMessages("Error: Cannot open file '%s': error %d\n", Filename, errno);
return NULL;
}
fseek(f, 0, SEEK_END);
Length = ftell(f);
PciIdDatabaseBuffer = (CHAR *)malloc(Length);
if (!PciIdDatabaseBuffer)
{
return NULL;
}
fseek(f, 0, SEEK_SET);
fread(PciIdDatabaseBuffer, 1, Length, f);
fclose(f);
}
PciIdDbBufPtr = PciIdDatabaseBuffer;
while (ReadLine(Line, sizeof(Line), &PciIdDbBufPtr) != NULL)
{
CHAR FormatStr[24];
// Skip comments and empty lines
if (Line[0] == '#' || Line[0] == '\0')
{
continue;
}
// Find vendor
// We assume PCI ID database comprises unique entries only, i.e. we return the first matching entry
if (Line[0] != '\t' && FoundVendorId == FALSE)
{
CHAR VendorBuf[PCI_ID_AS_STR_LENGTH + 1], VendorNameBuf[PCI_NAME_STR_LENGTH + 1];
snprintf(FormatStr, sizeof(FormatStr), "%%4s %%%d[^\n]", PCI_NAME_STR_LENGTH); // FormatStr = "%4s %PCI_NAME_STR_LENGTH[^\n]"
if (sscanf(Line, FormatStr, VendorBuf, VendorNameBuf) == 2)
{
if (strncmp(VendorBuf, VendorId, sizeof(VendorId)) == 0)
{
MatchedVendor = (Vendor *)malloc(sizeof(Vendor));
if (!MatchedVendor)
{
return NULL;
}
INT Result = sscanf(VendorBuf, "%hx", &(MatchedVendor->VendorId));
if (Result != 1)
{
return NULL;
}
strncpy_s(MatchedVendor->VendorName, sizeof(MatchedVendor->VendorName), TrimWhitespace(VendorNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
FoundVendorId = TRUE;
}
}
}
// Get all devices for vendor
else if (Line[0] == '\t' && Line[1] != '\t' && FoundVendorId == TRUE)
{
CHAR DeviceBuf[PCI_ID_AS_STR_LENGTH + 1], DeviceNameBuf[PCI_NAME_STR_LENGTH + 1];
snprintf(FormatStr, sizeof(FormatStr), "%%4s %%%d[^\n]", PCI_NAME_STR_LENGTH); // FormatStr = "%4s %PCI_NAME_STR_LENGTH[^\n]"
if (sscanf(Line + 1, FormatStr, DeviceBuf, DeviceNameBuf) == 2)
{
Device * NewDevice = (Device *)malloc(sizeof(Device));
if (!NewDevice)
{
FreeVendor(MatchedVendor);
return NULL;
}
int Result = sscanf(DeviceBuf, "%hx", &(NewDevice->DeviceId));
if (Result != 1)
{
FreeVendor(MatchedVendor);
return NULL;
}
strncpy_s(NewDevice->DeviceName, sizeof(NewDevice->DeviceName), TrimWhitespace(DeviceNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
NewDevice->SubDevices = NULL;
NewDevice->Next = NULL;
if (LastDevice)
{
LastDevice->Next = NewDevice;
}
else
{
MatchedVendor->Devices = NewDevice; // First device
}
LastDevice = NewDevice;
LastSubDevice = NULL;
}
}
// Get all subdevices for device
else if (Line[0] == '\t' && Line[1] == '\t' && FoundVendorId == TRUE && LastDevice)
{
CHAR SubVendorBuf[PCI_ID_AS_STR_LENGTH + 1], SubDeviceBuf[PCI_ID_AS_STR_LENGTH + 1], SubsystemNameBuf[PCI_NAME_STR_LENGTH + 1];
snprintf(FormatStr, sizeof(FormatStr), "%%4s %%4s %%%d[^\n]", PCI_NAME_STR_LENGTH); // FormatStr = "%4s %4s %PCI_NAME_STR_LENGTH[^\n]"
if (sscanf(Line + 2, FormatStr, SubVendorBuf, SubDeviceBuf, SubsystemNameBuf) == 3)
{
SubDevice * NewSubDevice = (SubDevice *)malloc(sizeof(SubDevice));
if (!NewSubDevice)
{
FreeVendor(MatchedVendor);
return NULL;
}
int Result = sscanf(SubVendorBuf, "%hx", &NewSubDevice->SubVendorId);
if (Result != 1)
{
FreeVendor(MatchedVendor);
return NULL;
}
Result = sscanf(SubDeviceBuf, "%hx", &NewSubDevice->SubDeviceId);
if (Result != 1)
{
FreeVendor(MatchedVendor);
return NULL;
}
strncpy_s(NewSubDevice->SubSystemName, sizeof(NewSubDevice->SubSystemName), TrimWhitespace(SubsystemNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
NewSubDevice->Next = NULL;
if (LastSubDevice)
{
LastSubDevice->Next = NewSubDevice;
}
else
{
LastDevice->SubDevices = NewSubDevice; // First subdevice
}
LastSubDevice = NewSubDevice;
}
}
else if (Line[0] != '\t' && FoundVendorId == TRUE) // We hit the next vendor entry, so we're done parsing
{
break;
}
}
return MatchedVendor;
}
/**
* @brief Frees Vendor and all of its members
*
* @param VendorToFree
* @return VOID
*/
VOID
FreeVendor(Vendor * VendorToFree)
{
if (VendorToFree == NULL)
return;
Device * CurrentDevice = VendorToFree->Devices;
while (CurrentDevice)
{
SubDevice * CurrentSubDevice = CurrentDevice->SubDevices;
while (CurrentSubDevice)
{
SubDevice * NextSubDevice = CurrentSubDevice->Next;
free(CurrentSubDevice);
CurrentSubDevice = NextSubDevice;
}
Device * NextDevice = CurrentDevice->Next;
free(CurrentDevice);
CurrentDevice = NextDevice;
}
}
/**
* @brief Frees PciIdDatabaseBuffer
* @return VOID
*/
VOID
FreePciIdDatabase()
{
if (PciIdDatabaseBuffer != NULL)
{
free(PciIdDatabaseBuffer);
PciIdDatabaseBuffer = NULL;
}
}
/**
* @brief Returns Vendor entry, including corresponding devices and subdevices
* @details Use FreeVendor() on returned Vendor pointer after usage. First call will initialize database - call FreeDatabase() once done querying.
*
* @param VendorId
* @return Vendor
*/
Vendor *
GetVendorById(UINT16 VendorId)
{
CHAR VendorIdAsStr[5];
CHAR ExecutablePath[MAX_PATH];
HMODULE hModule = GetModuleHandle(NULL);
snprintf(VendorIdAsStr, sizeof(VendorIdAsStr), "%04X", VendorId);
GetModuleFileName(hModule, ExecutablePath, sizeof(ExecutablePath));
// Extract executable name
CHAR * ExecutableName = strrchr(ExecutablePath, '\\');
if (ExecutableName != NULL)
{
ExecutableName++;
}
else
{
ExecutableName = ExecutablePath;
}
// Swap executable name for PCI_ID_DATABASE_PATH
strncpy(ExecutableName, PCI_ID_DATABASE_PATH, sizeof(PCI_ID_DATABASE_PATH));
return GetVendorByIdStr(ExecutablePath, ToLower(VendorIdAsStr));
}
/**
* @brief Returns Device entry corresponding to DeviceId
*
* @param VendorToUse
* @param DeviceId
* @return Device
*/
Device *
GetDeviceFromVendor(Vendor * VendorToUse, UINT16 DeviceId)
{
Device * CurrentDevice = NULL;
if (!VendorToUse)
{
return NULL;
}
CurrentDevice = VendorToUse->Devices;
while (CurrentDevice != NULL)
{
if (CurrentDevice->DeviceId == DeviceId)
{
return CurrentDevice;
}
CurrentDevice = CurrentDevice->Next;
}
return NULL;
}
/**
* @brief Returns SubDevice entry corresponding to SubVendorId and DeviceId
*
* @param DeviceToUse
* @param SubVendorId
* @param SubDeviceId
* @return SubDevice
*/
SubDevice *
GetSubDeviceFromDevice(Device * DeviceToUse, UINT16 SubVendorId, UINT16 SubDeviceId)
{
SubDevice * CurrentSubDevice = NULL;
if (!DeviceToUse)
{
return NULL;
}
CurrentSubDevice = DeviceToUse->SubDevices;
while (CurrentSubDevice != NULL)
{
if (CurrentSubDevice->SubVendorId == SubVendorId && CurrentSubDevice->SubDeviceId == SubDeviceId)
{
return CurrentSubDevice;
}
CurrentSubDevice = CurrentSubDevice->Next;
}
return NULL;
}