mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Track import access
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user