mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-08 02:18:53 +00:00
173 lines
5.4 KiB
C++
173 lines
5.4 KiB
C++
/*
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#include <osquery/core.h>
|
|
#include <osquery/filesystem.h>
|
|
#include <osquery/logger.h>
|
|
#include <osquery/tables.h>
|
|
|
|
#include "osquery/core/conversions.h"
|
|
#include "osquery/tables/system/linux/smbios_utils.h"
|
|
|
|
namespace osquery {
|
|
namespace tables {
|
|
|
|
#define kLinuxSMBIOSRawAddress_ 0xF0000
|
|
#define kLinuxSMBIOSRawLength_ 0x10000
|
|
|
|
const std::string kLinuxEFISystabPath = "/sys/firmware/efi/systab";
|
|
const std::string kLinuxDMISysfsPath = "/sys/firmware/dmi/tables/DMI";
|
|
|
|
void LinuxSMBIOSParser::readFromAddress(size_t address, size_t length) {
|
|
auto status = osquery::readRawMem(address, length, (void**)&data_);
|
|
if (!status.ok() || data_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Search for the SMBIOS/DMI tables magic header string.
|
|
size_t offset;
|
|
for (offset = 0; offset <= (length - sizeof(DMIEntryPoint)); offset += 16) {
|
|
// Could look for "_SM_" for the SMBIOS header, but the DMI header exists
|
|
// in both SMBIOS and the legacy DMI spec.
|
|
if (memcmp(data_ + offset, "_DMI_", 5) == 0) {
|
|
auto dmi_data = (DMIEntryPoint*)(data_ + offset);
|
|
if (discoverTables(dmi_data->tableAddress, dmi_data->tableLength)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LinuxSMBIOSParser::readFromSystab(const std::string& systab) {
|
|
std::string content;
|
|
if (!readFile(kLinuxEFISystabPath, content).ok()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto& line : osquery::split(content, "\n")) {
|
|
if (line.find("SMBIOS") == 0) {
|
|
auto details = osquery::split(line, "=");
|
|
if (details.size() == 2 && details[1].size() > 2) {
|
|
long long int address;
|
|
safeStrtoll(details[1], 16, address);
|
|
|
|
// Be sure not to read past the 0x000F0000 - 0x00100000 range.
|
|
// Otherwise strict /dev/mem access will generate a log line.
|
|
size_t size = 0x100;
|
|
if (address < 0x100000 && (address + size) > 0x100000) {
|
|
// If the address is within the 1M strict /dev/mem boundary, and is
|
|
// within 226 bytes of that boundary, reduce the read size.
|
|
size = 0x100000 - address;
|
|
}
|
|
readFromAddress(address, size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LinuxSMBIOSParser::readFromSysfs(const std::string& sysfs_dmi) {
|
|
std::string content;
|
|
readFile(sysfs_dmi, content);
|
|
table_data_ = (uint8_t*)malloc(content.size());
|
|
memcpy(table_data_, content.data(), content.size());
|
|
table_size_ = content.size();
|
|
}
|
|
|
|
bool LinuxSMBIOSParser::discoverTables(size_t address, size_t length) {
|
|
// Linux will expose the SMBIOS/DMI entry point structures, which contain
|
|
// a member variable with the DMI tables start address and size.
|
|
// This applies to both the EFI-variable and physical memory search.
|
|
auto status = osquery::readRawMem(address, length, (void**)&table_data_);
|
|
if (!status.ok() || table_data_ == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// The read was successful, save the size and wait for requests to parse.
|
|
table_size_ = length;
|
|
return true;
|
|
}
|
|
|
|
bool LinuxSMBIOSParser::discover() {
|
|
if (osquery::isReadable(kLinuxDMISysfsPath)) {
|
|
VLOG(1) << "Reading SMBIOS from sysfs DMI node";
|
|
readFromSysfs(kLinuxDMISysfsPath);
|
|
} else if (osquery::isReadable(kLinuxEFISystabPath)) {
|
|
VLOG(1) << "Reading SMBIOS from EFI provided memory location";
|
|
readFromSystab(kLinuxEFISystabPath);
|
|
} else {
|
|
readFromAddress(kLinuxSMBIOSRawAddress_, kLinuxSMBIOSRawLength_);
|
|
}
|
|
return valid();
|
|
}
|
|
|
|
QueryData genSMBIOSTables(QueryContext& context) {
|
|
LinuxSMBIOSParser parser;
|
|
if (!parser.discover()) {
|
|
VLOG(1) << "Could not read SMBIOS memory";
|
|
return {};
|
|
}
|
|
|
|
QueryData results;
|
|
parser.tables(([&results](
|
|
size_t index, const SMBStructHeader* hdr, uint8_t* address, size_t size) {
|
|
genSMBIOSTable(index, hdr, address, size, results);
|
|
}));
|
|
|
|
return results;
|
|
}
|
|
|
|
QueryData genPlatformInfo(QueryContext& context) {
|
|
LinuxSMBIOSParser parser;
|
|
if (!parser.discover()) {
|
|
VLOG(1) << "Could not read SMBIOS memory";
|
|
return {};
|
|
}
|
|
|
|
QueryData results;
|
|
parser.tables(([&results](
|
|
size_t index, const SMBStructHeader* hdr, uint8_t* address, size_t size) {
|
|
if (hdr->type != kSMBIOSTypeBIOS || size < 0x12) {
|
|
return;
|
|
}
|
|
|
|
Row r;
|
|
// The DMI string data uses offsets (indexes) into a data section that
|
|
// trails the header and structure offsets.
|
|
uint8_t* data = address + hdr->length;
|
|
r["vendor"] = dmiString(data, address, 0x04);
|
|
r["version"] = dmiString(data, address, 0x05);
|
|
r["date"] = dmiString(data, address, 0x08);
|
|
|
|
// Firmware load address as a WORD.
|
|
size_t firmware_address = (address[0x07] << 8) + address[0x06];
|
|
std::stringstream hex_id;
|
|
hex_id << std::hex << std::setw(4) << std::setfill('0') << firmware_address;
|
|
r["address"] = "0x" + hex_id.str();
|
|
|
|
// Firmware size as a count of 64k blocks.
|
|
size_t firmware_size = (address[0x09] + 1) << 6;
|
|
r["size"] = std::to_string(firmware_size * 1024);
|
|
|
|
// Minor and major BIOS revisions.
|
|
r["revision"] = std::to_string((size_t)address[0x14]) + "." +
|
|
std::to_string((size_t)address[0x15]);
|
|
r["volume_size"] = "0";
|
|
r["extra"] = "";
|
|
results.push_back(r);
|
|
}));
|
|
|
|
return results;
|
|
}
|
|
}
|
|
}
|