Track import access

This commit is contained in:
momo5502
2025-08-09 17:07:33 +02:00
parent 09ad463027
commit eb6d352a81
4 changed files with 195 additions and 4 deletions

View File

@@ -124,7 +124,7 @@ namespace
}
template <typename CharType = char>
void print_arg_as_string(windows_emulator& win_emu, size_t index)
void print_arg_as_string(windows_emulator& win_emu, const size_t index)
{
const auto var_ptr = get_function_argument(win_emu.emu(), index);
if (var_ptr)
@@ -152,9 +152,60 @@ namespace
}
}
bool is_thread_alive(const analysis_context& c, const uint32_t thread_id)
{
for (const auto& t : c.win_emu->process.threads | std::views::values)
{
if (t.id == thread_id)
{
return true;
}
}
return false;
}
void update_import_access(analysis_context& c, const uint64_t address)
{
if (c.accessed_imports.empty())
{
return;
}
auto entry = c.accessed_imports.find(address);
if (entry != c.accessed_imports.end())
{
c.accessed_imports.erase(entry);
}
const auto& t = c.win_emu->current_thread();
for (entry = c.accessed_imports.begin(); entry != c.accessed_imports.end();)
{
auto& a = entry->second;
constexpr auto inst_delay = 100u;
const auto is_same_thread = t.id == a.thread_id;
const auto execution_delay_reached =
is_same_thread && a.access_inst_count + inst_delay <= t.executed_instructions;
if (!execution_delay_reached && is_thread_alive(c, a.thread_id))
{
++entry;
continue;
}
c.win_emu->log.print(color::green, "Import read access without execution: %s (%s) at 0x%" PRIx64 " (%s)\n",
a.import_name.c_str(), a.import_module.c_str(), a.access_rip,
a.accessor_module.c_str());
entry = c.accessed_imports.erase(entry);
}
}
void handle_instruction(analysis_context& c, const uint64_t address)
{
auto& win_emu = *c.win_emu;
update_import_access(c, address);
#ifdef OS_EMSCRIPTEN
if ((win_emu.get_executed_instructions() % 0x20000) == 0)
@@ -293,6 +344,65 @@ namespace
c.win_emu->log.info("%.*s%s", static_cast<int>(data.size()), data.data(), data.ends_with("\n") ? "" : "\n");
}
}
void watch_import_table(analysis_context& c)
{
c.win_emu->setup_process_if_necessary();
const auto& import_list = c.win_emu->mod_manager.executable->imports;
if (import_list.empty())
{
return;
}
auto min = std::numeric_limits<uint64_t>::max();
auto max = std::numeric_limits<uint64_t>::min();
for (const auto& imports : import_list | std::views::values)
{
for (const auto& import : imports)
{
min = std::min(import.address, min);
max = std::max(import.address, max);
}
}
c.win_emu->emu().hook_memory_read(min, max - min, [&c](const uint64_t address, const void*, size_t) {
const auto& import_list = c.win_emu->mod_manager.executable->imports;
const auto rip = c.win_emu->emu().read_instruction_pointer();
if (!c.win_emu->mod_manager.executable->is_within(rip))
{
return;
}
for (const auto& [module_name, imports] : import_list)
{
for (const auto& import : imports)
{
if (address != import.address)
{
continue;
}
const auto function_address = c.win_emu->emu().read_memory<uint64_t>(address);
auto& access = c.accessed_imports[function_address];
access.access_rip = c.win_emu->emu().read_instruction_pointer();
access.accessor_module = c.win_emu->mod_manager.find_name(access.access_rip);
access.import_name = import.name;
access.import_module = module_name;
const auto& t = c.win_emu->current_thread();
access.thread_id = t.id;
access.access_inst_count = t.executed_instructions;
return;
}
}
});
}
}
void register_analysis_callbacks(analysis_context& c)
@@ -317,9 +427,11 @@ void register_analysis_callbacks(analysis_context& c)
cb.on_generic_access = make_callback(c, handle_generic_access);
cb.on_generic_activity = make_callback(c, handle_generic_activity);
cb.on_suspicious_activity = make_callback(c, handle_suspicious_activity);
watch_import_table(c);
}
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address)
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, const uint64_t address)
{
if (manager.executable->is_within(address))
{
@@ -338,4 +450,4 @@ mapped_module* get_module_if_interesting(module_manager& manager, const string_s
}
return nullptr;
}
}

View File

@@ -20,6 +20,16 @@ struct analysis_settings
string_set ignored_functions{};
};
struct accessed_import
{
uint32_t thread_id{};
uint64_t access_rip{};
uint64_t access_inst_count{};
std::string accessor_module{};
std::string import_name{};
std::string import_module{};
};
struct analysis_context
{
const analysis_settings* settings{};
@@ -27,7 +37,9 @@ struct analysis_context
std::string output{};
bool has_reached_main{false};
std::map<uint64_t, accessed_import> accessed_imports{};
};
void register_analysis_callbacks(analysis_context& c);
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address);
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address);

View File

@@ -9,7 +9,14 @@ struct exported_symbol
uint64_t address{};
};
struct imported_symbol
{
std::string name{};
uint64_t address{};
};
using exported_symbols = std::vector<exported_symbol>;
using imported_symbols = std::map<std::string, std::vector<imported_symbol>>;
using address_name_mapping = std::map<uint64_t, std::string>;
struct mapped_section
@@ -28,6 +35,7 @@ struct mapped_module
uint64_t entry_point{};
exported_symbols exports{};
imported_symbols imports{};
address_name_mapping address_names{};
std::vector<mapped_section> sections{};

View File

@@ -29,6 +29,64 @@ namespace
return mem;
}
void collect_imports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
const auto& import_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (import_directory_entry.VirtualAddress == 0 || import_directory_entry.Size == 0)
{
return;
}
const auto import_descriptors = buffer.as<IMAGE_IMPORT_DESCRIPTOR>(import_directory_entry.VirtualAddress);
for (size_t i = 0;; ++i)
{
const auto descriptor = import_descriptors.get(i);
if (!descriptor.Name)
{
break;
}
const auto module_name = buffer.as_string(descriptor.Name);
auto& imports = binary.imports[module_name];
auto original_thunk_data = buffer.as<IMAGE_THUNK_DATA>(descriptor.FirstThunk);
if (descriptor.OriginalFirstThunk)
{
original_thunk_data = buffer.as<IMAGE_THUNK_DATA>(descriptor.OriginalFirstThunk);
}
for (size_t j = 0;; ++j)
{
const auto original_thunk = original_thunk_data.get(j);
if (!original_thunk.u1.AddressOfData)
{
break;
}
imported_symbol sym{};
const auto thunk_rva = descriptor.FirstThunk //
+ sizeof(IMAGE_THUNK_DATA) * j //
+ offsetof(IMAGE_THUNK_DATA, u1.Function);
sym.address = thunk_rva + binary.image_base;
if (IMAGE_SNAP_BY_ORDINAL(original_thunk.u1.Ordinal))
{
sym.name = "#" + std::to_string(original_thunk.u1.Ordinal);
}
else
{
sym.name = buffer.as_string(original_thunk.u1.AddressOfData + offsetof(IMAGE_IMPORT_BY_NAME, Name));
}
imports.push_back(std::move(sym));
}
}
}
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
@@ -248,6 +306,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
apply_relocations(binary, mapped_buffer, optional_header);
collect_exports(binary, mapped_buffer, optional_header);
collect_imports(binary, mapped_buffer, optional_header);
memory.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());