Skip to content

Commit

Permalink
Merge pull request #45 from binarly-io/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
yeggor authored Apr 28, 2022
2 parents 4dc9fe0 + 4268093 commit d2cdee8
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 74 deletions.
150 changes: 79 additions & 71 deletions efiXplorer/efiAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2245,87 +2245,89 @@ bool EfiAnalysis::EfiAnalyzer::findSmmGetVariableOveflow() {

bool EfiAnalysis::EfiAnalyzer::analyzeNvramVariables() {
msg("[%s] Get NVRAM variables information\n", plugin_name);
std::vector<ea_t> var_services;
std::string getVariableStr("GetVariable");
std::string setVariableStr("SetVariable");
for (auto j_service : allServices) {
json service = j_service;
std::string service_name = static_cast<std::string>(service["service_name"]);
ea_t addr = static_cast<ea_t>(service["address"]);
if (!service_name.compare(getVariableStr) ||
!service_name.compare(setVariableStr)) {
var_services.push_back(addr);
}
}
sort(var_services.begin(), var_services.end());
for (auto ea : var_services) {
msg("[%s] GetVariable/SetVariable call: 0x%016llX\n", plugin_name,
static_cast<uint64_t>(ea));
json item;
item["addr"] = ea;
insn_t insn;
auto addr = ea;
bool name_found = false;
bool guid_found = false;
func_t *f = get_func(ea);
if (f == nullptr) {
continue;
std::vector<std::string> nvram_services = {"GetVariable", "SetVariable"};
for (auto service_str : nvram_services) {
std::vector<ea_t> var_services;
for (auto j_service : allServices) {
json service = j_service;
std::string service_name = static_cast<std::string>(service["service_name"]);
ea_t addr = static_cast<ea_t>(service["address"]);
if (!service_name.compare(service_str)) {
var_services.push_back(addr);
}
}
for (auto i = 0; i < 16; i++) {
addr = prev_head(addr, 0);
decode_insn(&insn, addr);
if (!name_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RCX && insn.ops[1].type == o_mem) {
msg("[%s] VariableName address: 0x%016llX\n", plugin_name,
static_cast<uint64_t>(insn.ops[1].addr));
std::string var_name = getWideString(insn.ops[1].addr);
msg("[%s] VariableName: %s\n", plugin_name, var_name.c_str());
item["VariableName"] = var_name;
name_found = true;
}
// If GUID is global variable
if (!guid_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RDX && insn.ops[1].type == o_mem) {
msg("[%s] VendorGuid address (global): 0x%016llX\n", plugin_name,
static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getGlobalGuid(insn.ops[1].addr);
msg("[%s] GUID: %s\n", plugin_name, guid.to_string().c_str());
item["VendorGuid"] = guid.to_string();
guid_found = true;
}
// If GUID is local variable
if (!guid_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RDX && insn.ops[1].type == o_displ) {
switch (insn.ops[1].reg) {
case REG_RBP: {
msg("[%s] VendorGuid address (regarding to RBP): 0x%016llX\n",
plugin_name, static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getStackGuid(f, insn.ops[1].addr);
msg("[%s] GUID: %s\n", plugin_name, guid.to_string().c_str());
item["VendorGuid"] = guid.to_string();
guid_found = true;
continue;
sort(var_services.begin(), var_services.end());
for (auto ea : var_services) {
msg("[%s] GetVariable/SetVariable call: 0x%016llX\n", plugin_name,
static_cast<uint64_t>(ea));
json item;
item["addr"] = ea;
insn_t insn;
auto addr = ea;
bool name_found = false;
bool guid_found = false;
func_t *f = get_func(ea);
if (f == nullptr) {
continue;
}
for (auto i = 0; i < 16; i++) {
addr = prev_head(addr, 0);
decode_insn(&insn, addr);
if (!name_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RCX && insn.ops[1].type == o_mem) {
msg("[%s] VariableName address: 0x%016llX\n", plugin_name,
static_cast<uint64_t>(insn.ops[1].addr));
std::string var_name = getWideString(insn.ops[1].addr);
msg("[%s] VariableName: %s\n", plugin_name, var_name.c_str());
item["VariableName"] = var_name;
name_found = true;
}
case REG_RSP: {
msg("[%s] VendorGuid address (regarding to RSP): 0x%016llX\n",
plugin_name, static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getStackGuid(f, insn.ops[1].addr);
// If GUID is global variable
if (!guid_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RDX && insn.ops[1].type == o_mem) {
msg("[%s] VendorGuid address (global): 0x%016llX\n", plugin_name,
static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getGlobalGuid(insn.ops[1].addr);
msg("[%s] GUID: %s\n", plugin_name, guid.to_string().c_str());
item["VendorGuid"] = guid.to_string();
guid_found = true;
continue;
}
default:
continue;
// If GUID is local variable
if (!guid_found && insn.itype == NN_lea && insn.ops[0].type == o_reg &&
insn.ops[0].reg == REG_RDX && insn.ops[1].type == o_displ) {
switch (insn.ops[1].reg) {
case REG_RBP: {
msg("[%s] VendorGuid address (regarding to RBP): 0x%016llX\n",
plugin_name, static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getStackGuid(f, insn.ops[1].addr);
msg("[%s] GUID: %s\n", plugin_name, guid.to_string().c_str());
item["VendorGuid"] = guid.to_string();
guid_found = true;
continue;
}
case REG_RSP: {
msg("[%s] VendorGuid address (regarding to RSP): 0x%016llX\n",
plugin_name, static_cast<uint64_t>(insn.ops[1].addr));
EfiGuid guid = getStackGuid(f, insn.ops[1].addr);
msg("[%s] GUID: %s\n", plugin_name, guid.to_string().c_str());
item["VendorGuid"] = guid.to_string();
guid_found = true;
continue;
}
default:
continue;
}
}
if (name_found && guid_found) {
break;
}
}
if (name_found && guid_found) {
break;
if (name_found && guid_found) { // if only name or only GUID found, it will
// now saved (check the logs)
item["service"] = service_str;
nvramVariables.push_back(item);
}
}
if (name_found || guid_found) {
nvramVariables.push_back(item);
}
}
return true;
}
Expand Down Expand Up @@ -2437,6 +2439,12 @@ void showAllChoosers(EfiAnalysis::EfiAnalyzer analyzer) {
guids_show(analyzer.allGuids, title);
}

// open window with NVRAM variables
if (analyzer.allGuids.size()) {
qstring title = "efiXplorer: NVRAM";
nvram_show(analyzer.nvramVariables, title);
}

// open window with vulnerabilities
if (calloutAddrs.size() + peiGetVariableOverflow.size() + getVariableOverflow.size() +
smmGetVariableOverflow.size()) {
Expand Down
3 changes: 1 addition & 2 deletions efiXplorer/efiAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class EfiAnalyzer {
std::vector<json> allProtocols;
std::vector<json> allPPIs;
std::vector<json> allServices;
std::vector<json> nvramVariables;
std::vector<func_t *> smiHandlers;
uint8_t arch;

Expand Down Expand Up @@ -94,8 +95,6 @@ class EfiAnalyzer {
json smmServices;
json smmServicesAll;
json dbProtocols;
std::vector<json>
nvramVariables; // [{"addr": ..., "VariableName": ..., "VendorGuid": ...}, ...]
std::map<json, std::string> dbProtocolsMap; // a map to look up a GUID name by value
std::vector<ea_t> markedInterfaces;

Expand Down
54 changes: 53 additions & 1 deletion efiXplorer/efiUi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,51 @@ const int s_chooser_t::widths_s[] = {
32, // Table name
};

// services column widths
// services column headers
const char *const s_chooser_t::header_s[] = {
"Address", // 0
"Service name", // 1
"Table name" // 2
};

// services column widths
const int nvram_chooser_t::widths_nvram[] = {
16, // Address
32, // Variable name
32, // Variable GUID
32, // Service
};

// NVRAMs column headers
const char *const nvram_chooser_t::header_nvram[] = {
"Address", // 0
"Variable name", // 1
"Variable GUID", // 2
"Service" // 3
};

inline nvram_chooser_t::nvram_chooser_t(const char *title_, bool ok,
std::vector<json> nvram)
: chooser_t(0, qnumber(widths_nvram), widths_nvram, header_nvram, title_), list() {
CASSERT(qnumber(widths_nvram) == qnumber(header_nvram));
build_list(ok, nvram);
}

void idaapi nvram_chooser_t::get_row(qstrvec_t *cols_, int *, chooser_item_attrs_t *,
size_t n) const {
ea_t ea = list[n];
qstrvec_t &cols = *cols_;
json item = chooser_nvram[n];
std::string name = static_cast<std::string>(item["VariableName"]);
std::string guid = static_cast<std::string>(item["VendorGuid"]);
std::string service = static_cast<std::string>(item["service"]);
cols[0].sprnt("%016llX", static_cast<uint64_t>(ea));
cols[1].sprnt("%s", name.c_str());
cols[2].sprnt("%s", guid.c_str());
cols[3].sprnt("%s", service.c_str());
CASSERT(qnumber(header_nvram) == 4);
}

inline vulns_chooser_t::vulns_chooser_t(const char *title_, bool ok,
std::vector<json> vulns)
: chooser_t(0, qnumber(widths_vulns), widths_vulns, header_vulns, title_), list() {
Expand Down Expand Up @@ -167,6 +205,15 @@ void idaapi s_chooser_t::get_row(qstrvec_t *cols_, int *, chooser_item_attrs_t *
CASSERT(qnumber(header_s) == 3);
}

bool nvram_show(std::vector<json> nvram, qstring title) {
bool ok;
// open the window
nvram_chooser_t *ch = new nvram_chooser_t(title.c_str(), ok, nvram);
// default cursor position is 0 (first row)
ch->choose();
return true;
}

bool vulns_show(std::vector<json> vulns, qstring title) {
bool ok;
// open the window
Expand Down Expand Up @@ -334,6 +381,11 @@ struct action_handler_loadreport_t : public action_handler_t {
title = "efiXplorer: GUIDs";
guids_show(guids, title);
}
auto nvram = reportData["nvramVariables"];
if (!nvram.is_null()) { // show NVRAM
title = "efiXplorer: NVRAM";
nvram_show(nvram, title);
}
auto vulns = reportData["vulns"];
if (!vulns.is_null()) { // show vulns
std::vector<json> vulnsRes;
Expand Down
49 changes: 49 additions & 0 deletions efiXplorer/efiUi.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,57 @@ class s_chooser_t : public chooser_t {
};
};

//-------------------------------------------------------------------------
// NVRAM chooser
class nvram_chooser_t : public chooser_t {
protected:
static const int widths_nvram[];
static const char *const header_nvram[];

public:
eavec_t list;
json chooser_nvram;

// this object must be allocated using `new`
nvram_chooser_t(const char *title, bool ok, std::vector<json> nvrams);

// function that is used to decide whether a new chooser should be opened or
// we can use the existing one. The contents of the window are completely
// determined by its title
virtual const void *get_obj_id(size_t *len) const {
*len = strlen(title);
return title;
}

// function that returns number of lines in the list
virtual size_t idaapi get_count() const { return list.size(); }

// function that generates the list line
virtual void idaapi get_row(qstrvec_t *cols, int *icon_, chooser_item_attrs_t *attrs,
size_t n) const;

// function that is called when the user hits `Enter`
virtual cbret_t idaapi enter(size_t n) {
if (n < list.size())
jumpto(list[n]);
return cbret_t();
}

protected:
void build_list(bool ok, std::vector<json> nvrams) {
size_t n = 0;
for (auto nvram : nvrams) {
list.push_back(nvram["addr"]);
chooser_nvram[n] = nvram;
n++;
}
ok = true;
};
};

extern action_desc_t action_load_report;

bool nvram_show(std::vector<json> nvram, qstring title);
bool vulns_show(std::vector<json> vulns, qstring title);
bool guids_show(std::vector<json> guid, qstring title);
bool protocols_show(std::vector<json> protocols, qstring title);
Expand Down

0 comments on commit d2cdee8

Please sign in to comment.