468 lines
16 KiB
C++
468 lines
16 KiB
C++
#include "usbFilter.h"
|
||
|
||
|
||
using json = nlohmann::json;
|
||
|
||
usbFilter::usbFilter(/* args */)
|
||
{
|
||
}
|
||
|
||
usbFilter::~usbFilter()
|
||
{
|
||
}
|
||
|
||
string usbFilter::EnumDriversAndDevices()
|
||
{
|
||
json list = json::array();
|
||
|
||
// 1. 获取USB设备(libudev)
|
||
{
|
||
udev* udev_ctx = udev_new();
|
||
if (udev_ctx) {
|
||
udev_enumerate* enumerate = udev_enumerate_new(udev_ctx);
|
||
udev_enumerate_add_match_subsystem(enumerate, "usb");
|
||
udev_enumerate_scan_devices(enumerate);
|
||
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
|
||
|
||
udev_list_entry* dev_list_entry;
|
||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||
const char* path = udev_list_entry_get_name(dev_list_entry);
|
||
udev_device* dev = udev_device_new_from_syspath(udev_ctx, path);
|
||
|
||
const char* vendor = udev_device_get_sysattr_value(dev, "idVendor");
|
||
const char* product = udev_device_get_sysattr_value(dev, "idProduct");
|
||
const char* devpath = udev_device_get_syspath(dev);
|
||
const char* devname = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
|
||
if (!devname) devname = udev_device_get_property_value(dev, "ID_MODEL");
|
||
if (!devname) devname = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE");
|
||
if (!devname) devname = devpath ? devpath : "";
|
||
if (vendor && product) {
|
||
json item;
|
||
item["VendorId"] = vendor;
|
||
item["ProductId"] = product;
|
||
item["DeviceName"] = devname ? devname : "";
|
||
item["DevPath"] = devpath ? devpath : "";
|
||
item["Subsystem"] = "USB";
|
||
list.push_back(item);
|
||
}
|
||
udev_device_unref(dev);
|
||
}
|
||
udev_enumerate_unref(enumerate);
|
||
udev_unref(udev_ctx);
|
||
}
|
||
}
|
||
|
||
// 2. 获取PCI/PCIe设备(libudev)
|
||
{
|
||
udev* udev_ctx = udev_new();
|
||
if (udev_ctx) {
|
||
udev_enumerate* enumerate = udev_enumerate_new(udev_ctx);
|
||
udev_enumerate_add_match_subsystem(enumerate, "pci");
|
||
udev_enumerate_scan_devices(enumerate);
|
||
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
|
||
|
||
udev_list_entry* dev_list_entry;
|
||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||
const char* path = udev_list_entry_get_name(dev_list_entry);
|
||
udev_device* dev = udev_device_new_from_syspath(udev_ctx, path);
|
||
|
||
const char* vendor = udev_device_get_sysattr_value(dev, "vendor");
|
||
const char* product = udev_device_get_sysattr_value(dev, "device");
|
||
// 尝试多种方式获取设备名
|
||
const char* devpath = udev_device_get_syspath(dev);
|
||
const char* devname = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
|
||
if (!devname) devname = udev_device_get_property_value(dev, "ID_MODEL");
|
||
if (!devname) devname = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE");
|
||
if (!devname) devname = devpath ? devpath : "";
|
||
|
||
if (vendor && product) {
|
||
// vendor和device通常是0x开头的16进制,去掉0x
|
||
string vendor_id = vendor;
|
||
string product_id = product;
|
||
if (vendor_id.find("0x") == 0) vendor_id = vendor_id.substr(2);
|
||
if (product_id.find("0x") == 0) product_id = product_id.substr(2);
|
||
|
||
json item;
|
||
item["VendorId"] = vendor_id;
|
||
item["ProductId"] = product_id;
|
||
item["DeviceName"] = devname ? devname : "";
|
||
item["DevPath"] = devpath ? devpath : "";
|
||
item["Subsystem"] = "PCI";
|
||
list.push_back(item);
|
||
}
|
||
udev_device_unref(dev);
|
||
}
|
||
udev_enumerate_unref(enumerate);
|
||
udev_unref(udev_ctx);
|
||
}
|
||
}
|
||
|
||
return list.dump();
|
||
}
|
||
|
||
bool usbFilter::open(const string &devPath)
|
||
{
|
||
// 检查设备路径是否为空
|
||
if (devPath.empty()) {
|
||
return false;
|
||
}
|
||
// 检查设备路径是否存在
|
||
string devPathTrans = sysfsToDevPath(devPath);
|
||
if (devPathTrans.empty()) {
|
||
LOG_ERROR("Device path does not exist" );
|
||
return false;
|
||
}else{
|
||
LOG_DEBUG("Opening device: %s", devPathTrans.c_str());
|
||
}
|
||
|
||
int pid = getDeviceUserPID(devPathTrans);
|
||
if (pid < 0) {
|
||
LOG_ERROR("No process is using the device: %s", devPathTrans.c_str());
|
||
return false;
|
||
} else {
|
||
LOG_DEBUG("Device %s is being used by process with PID: %d", devPathTrans.c_str(), pid);
|
||
}
|
||
//获取当前目录
|
||
string currendDir = CURRENTDIR;
|
||
// string scriptPath = currentDir + "/scripts";
|
||
string scriptPath = currendDir + "/testScripts";
|
||
|
||
|
||
LOG_INFO("Ready to run frida scripts. The scripts are located in %s.",scriptPath.c_str());
|
||
|
||
// 构造命令行
|
||
// string cmd = "frida -p " + to_string(pid) + " -l " + scriptPath + "/frida_hook_libusb_basic_Version2.js &";
|
||
string cmd = "frida -p " + to_string(pid) + " -l " + scriptPath + "/frida_hook_libusb_basic_Version2.js &";
|
||
|
||
LOG_DEBUG("Running command: %s", cmd.c_str());
|
||
// 执行命令
|
||
int result = system(cmd.c_str());
|
||
if (result != 0) {
|
||
LOG_ERROR("Failed to run command: %s", cmd.c_str());
|
||
return false;
|
||
}
|
||
LOG_INFO("Command executed successfully.");
|
||
return true;
|
||
|
||
}
|
||
|
||
string usbFilter::sysfsToDevPath(const string& sysfs_path) {
|
||
struct stat st;
|
||
|
||
// Check if the sysfs path exists
|
||
if (stat(sysfs_path.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||
return ""; // Path doesn't exist or is not a directory
|
||
}
|
||
|
||
// Check if this is a USB device path
|
||
if (sysfs_path.find("/usb") != string::npos) {
|
||
return convertUsbSysfsPath(sysfs_path);
|
||
}
|
||
|
||
// Check if this is a PCI/PCIe device path
|
||
if (sysfs_path.find("/pci") != string::npos) {
|
||
return convertPciSysfsPath(sysfs_path);
|
||
}
|
||
|
||
// Unknown device type
|
||
return "";
|
||
}
|
||
|
||
// Helper function to convert USB sysfs path
|
||
string usbFilter::convertUsbSysfsPath(const string& sysfs_path) {
|
||
// Construct paths to busnum and devnum files
|
||
string busnum_path = sysfs_path + "/busnum";
|
||
string devnum_path = sysfs_path + "/devnum";
|
||
|
||
// Read bus number
|
||
ifstream bus_file(busnum_path);
|
||
if (!bus_file.is_open()) {
|
||
return "";
|
||
}
|
||
|
||
int busnum = 0;
|
||
bus_file >> busnum;
|
||
bus_file.close();
|
||
|
||
if (busnum <= 0) {
|
||
return "";
|
||
}
|
||
|
||
// Read device number
|
||
ifstream dev_file(devnum_path);
|
||
if (!dev_file.is_open()) {
|
||
return "";
|
||
}
|
||
|
||
int devnum = 0;
|
||
dev_file >> devnum;
|
||
dev_file.close();
|
||
|
||
if (devnum <= 0) {
|
||
return "";
|
||
}
|
||
|
||
// Construct the /dev path with proper zero-padding
|
||
ostringstream dev_path;
|
||
dev_path << "/dev/bus/usb/"
|
||
<< setfill('0') << setw(3) << busnum
|
||
<< "/"
|
||
<< setfill('0') << setw(3) << devnum;
|
||
|
||
string result = dev_path.str();
|
||
|
||
// Check if the /dev node actually exists
|
||
if (access(result.c_str(), F_OK) != 0) {
|
||
return ""; // Device node doesn't exist
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// Helper function to convert PCI/PCIe sysfs path
|
||
string usbFilter::convertPciSysfsPath(const string& sysfs_path) {
|
||
// First check if there are any special device nodes (e.g., for graphics cards, network cards)
|
||
|
||
// 1. Check for graphics card
|
||
string device_path = sysfs_path + "/drm";
|
||
DIR* dir = opendir(device_path.c_str());
|
||
if (dir) {
|
||
// Found DRM device (likely a graphics card)
|
||
closedir(dir);
|
||
|
||
// Look for card* entries in /dev/dri/
|
||
dir = opendir("/dev/dri");
|
||
if (dir) {
|
||
struct dirent *entry;
|
||
regex card_regex("card[0-9]+");
|
||
|
||
while ((entry = readdir(dir)) != NULL) {
|
||
string name = entry->d_name;
|
||
if (regex_match(name, card_regex)) {
|
||
// For each card, check if it corresponds to our PCI device
|
||
string cardpath = "/dev/dri/" + name;
|
||
|
||
// Need to check if this card corresponds to our PCI device
|
||
// One way is to check the device node major/minor numbers against sys/dev entries
|
||
struct stat card_stat;
|
||
if (stat(cardpath.c_str(), &card_stat) == 0) {
|
||
ostringstream dev_path;
|
||
dev_path << sysfs_path << "/drm/" << name << "/dev";
|
||
|
||
ifstream dev_file(dev_path.str());
|
||
if (dev_file.is_open()) {
|
||
string dev_numbers;
|
||
getline(dev_file, dev_numbers);
|
||
dev_file.close();
|
||
|
||
// Format is major:minor
|
||
size_t colon_pos = dev_numbers.find(':');
|
||
if (colon_pos != string::npos) {
|
||
int major = stoi(dev_numbers.substr(0, colon_pos));
|
||
int minor = stoi(dev_numbers.substr(colon_pos + 1));
|
||
|
||
if (major == major(card_stat.st_rdev) &&
|
||
minor == minor(card_stat.st_rdev)) {
|
||
closedir(dir);
|
||
return cardpath;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
closedir(dir);
|
||
}
|
||
}
|
||
|
||
// 2. Check for network interfaces
|
||
device_path = sysfs_path + "/net";
|
||
dir = opendir(device_path.c_str());
|
||
if (dir) {
|
||
// Found network interface
|
||
struct dirent *entry;
|
||
while ((entry = readdir(dir)) != NULL) {
|
||
if (entry->d_type == DT_DIR &&
|
||
strcmp(entry->d_name, ".") != 0 &&
|
||
strcmp(entry->d_name, "..") != 0) {
|
||
|
||
closedir(dir);
|
||
// Network interfaces don't have /dev entries, but we can return the interface name
|
||
return "/sys/class/net/" + string(entry->d_name);
|
||
}
|
||
}
|
||
closedir(dir);
|
||
}
|
||
|
||
// 3. Check for NVMe devices
|
||
if (sysfs_path.find("nvme") != string::npos) {
|
||
// Extract the NVMe controller number (e.g., nvme0)
|
||
regex nvme_regex("nvme([0-9]+)");
|
||
smatch match;
|
||
if (regex_search(sysfs_path, match, nvme_regex) && match.size() > 1) {
|
||
string nvme_num = match[1].str();
|
||
string nvme_dev = "/dev/nvme" + nvme_num;
|
||
|
||
if (access(nvme_dev.c_str(), F_OK) == 0) {
|
||
return nvme_dev;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. Check for SCSI/SATA devices that might be connected via PCIe
|
||
string block_path = sysfs_path + "/block";
|
||
dir = opendir(block_path.c_str());
|
||
if (dir) {
|
||
struct dirent *entry;
|
||
while ((entry = readdir(dir)) != NULL) {
|
||
if (entry->d_type == DT_DIR &&
|
||
strcmp(entry->d_name, ".") != 0 &&
|
||
strcmp(entry->d_name, "..") != 0) {
|
||
|
||
string dev_name = entry->d_name;
|
||
string block_dev = "/dev/" + dev_name;
|
||
|
||
if (access(block_dev.c_str(), F_OK) == 0) {
|
||
closedir(dir);
|
||
return block_dev;
|
||
}
|
||
}
|
||
}
|
||
closedir(dir);
|
||
}
|
||
|
||
// 5. Generic way to find PCI device - try to use the device's resource file
|
||
ifstream resource_file(sysfs_path + "/resource");
|
||
if (resource_file.is_open()) {
|
||
// This is more complex and would require parsing the resource file
|
||
// and checking if it maps to any character device
|
||
resource_file.close();
|
||
}
|
||
|
||
// 6. Check for udev links in /dev/char/ directory
|
||
string dev_content;
|
||
ifstream dev_file(sysfs_path + "/dev");
|
||
if (dev_file.is_open()) {
|
||
getline(dev_file, dev_content); // Format is "major:minor"
|
||
dev_file.close();
|
||
|
||
if (!dev_content.empty()) {
|
||
// Check in /dev/char/ directory
|
||
string char_dev_path = "/dev/char/" + dev_content;
|
||
if (access(char_dev_path.c_str(), F_OK) == 0) {
|
||
// This is a character device
|
||
return char_dev_path;
|
||
}
|
||
|
||
// Check in /dev/block/ directory
|
||
string block_dev_path = "/dev/block/" + dev_content;
|
||
if (access(block_dev_path.c_str(), F_OK) == 0) {
|
||
// This is a block device
|
||
return block_dev_path;
|
||
}
|
||
}
|
||
}
|
||
|
||
// If we reach here, couldn't find a corresponding /dev node
|
||
return "";
|
||
}
|
||
|
||
|
||
|
||
// 修复getDeviceUserPID函数中的错误和警告
|
||
int usbFilter::getDeviceUserPID(const string& devpath) {
|
||
// First check if the device path exists
|
||
struct stat st;
|
||
if (stat(devpath.c_str(), &st) != 0) {
|
||
return -1; // Device path doesn't exist
|
||
}
|
||
|
||
// Use lsof command to find processes using this device
|
||
string cmd = "lsof -t " + devpath + " 2>/dev/null";
|
||
|
||
array<char, 128> buffer;
|
||
string result;
|
||
unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
|
||
|
||
if (!pipe) {
|
||
return -1; // Failed to run command
|
||
}
|
||
|
||
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
|
||
result += buffer.data();
|
||
}
|
||
|
||
// Trim whitespace and newlines
|
||
result.erase(result.find_last_not_of(" \n\r\t") + 1);
|
||
|
||
// If result is empty, no process is using the device
|
||
if (result.empty()) {
|
||
return -1;
|
||
}
|
||
|
||
// Parse the PID from the result (may have multiple lines if multiple processes)
|
||
regex pid_regex("^(\\d+)$", regex::multiline);
|
||
smatch match;
|
||
|
||
if (regex_search(result, match, pid_regex) && match.size() > 1) {
|
||
try {
|
||
return stoi(match[1].str());
|
||
} catch (const invalid_argument& e) {
|
||
return -1;
|
||
} catch (const out_of_range& e) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
// Alternative approach using /proc filesystem if lsof fails or isn't available
|
||
if (access("/proc", F_OK) == 0) {
|
||
// Iterate through all processes
|
||
string proc_dir = "/proc";
|
||
array<char, 256> cmd;
|
||
|
||
// 修复这行错误
|
||
FILE* fp = popen("ls -d /proc/[0-9]*", "r");
|
||
if (fp) {
|
||
while (fgets(cmd.data(), cmd.size(), fp) != nullptr) {
|
||
string pid_dir = cmd.data();
|
||
pid_dir.erase(pid_dir.find_last_not_of("\n\r") + 1);
|
||
|
||
// Check process fd directory
|
||
string fd_dir = pid_dir + "/fd";
|
||
FILE* fd_fp = popen(("ls -l " + fd_dir + " 2>/dev/null").c_str(), "r");
|
||
if (fd_fp) {
|
||
array<char, 512> fd_info;
|
||
bool found = false;
|
||
|
||
while (fgets(fd_info.data(), fd_info.size(), fd_fp) != nullptr) {
|
||
string line = fd_info.data();
|
||
|
||
// Check if this fd points to our device
|
||
if (line.find(devpath) != string::npos) {
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
pclose(fd_fp);
|
||
|
||
if (found) {
|
||
pclose(fp);
|
||
// Extract PID from path /proc/[0-9]*
|
||
regex proc_regex("/proc/(\\d+)");
|
||
if (regex_search(pid_dir, match, proc_regex) && match.size() > 1) {
|
||
try {
|
||
return stoi(match[1].str());
|
||
} catch (...) {
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
pclose(fp);
|
||
}
|
||
}
|
||
|
||
return -1; // No process found using the device
|
||
} |