Skip to content

Commit

Permalink
Add findcall plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Fasano committed Oct 30, 2023
1 parent 2acdb22 commit d11cf30
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
1 change: 1 addition & 0 deletions panda/plugins/config.panda
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ coverage
dynamic_symbols
edge_coverage
filereadmon
findcall
forcedexec
gdb
hooks
Expand Down
3 changes: 3 additions & 0 deletions panda/plugins/findcall/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CFLAGS+= -std=c++17
$(PLUGIN_TARGET_DIR)/panda_$(PLUGIN_NAME).so: \
$(PLUGIN_OBJ_DIR)/$(PLUGIN_NAME).o
23 changes: 23 additions & 0 deletions panda/plugins/findcall/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Plugin: FindCall
===========

Summary
-------

At every function call, check if the argument is one or more specified strings. Track both module+offsets and absolute addresses for calls where this occurs. Report the module+offsets and absolute addresses where ALL strings are observed.

Arguments
---------

`output_file`: Optional, path to store results at. Default is `findcall.txt` in the current directory

Dependencies
------------
`callwitharg` must be loaded and configured with the target strings you're looking for
`callstack_instr`

APIs and Callbacks
------------------

Example
-------
157 changes: 157 additions & 0 deletions panda/plugins/findcall/findcall.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include "panda/plugin.h"
#include "panda/plugin_plugin.h"
#include <fstream>
#include <thread>
#include <atomic>
#include <iostream>
#include <filesystem>
#include <sys/types.h>
#include <set>
#include <map>

#include "callstack_instr/callstack_instr.h"
#include "callwitharg/callwitharg.h"
#include "osi/osi_types.h"

extern "C" {
//#include "callwitharg/callwitharg_int_fns.h"
#include "callwitharg/callwitharg_ext.h"
#include "osi/osi_ext.h"
bool init_plugin(void *);
void uninit_plugin(void *);
}

//size_t target_str_len;
//char *target_str;
std::ofstream outfile;

// We want to store a mapping from unique strigs -> set of (module+offset (as a tuple))
std::map<std::string, std::set<std::tuple<std::string, target_ulong>>> matches;

// Also we store unique strings -> absolute addresses
std::map<std::string, std::set<target_ulong>> matches_abs;

void on_match(CPUState* cpu, target_ulong func_addr, target_ulong *args, char* value, uint matching_idx, uint args_read) {
assert(args_read >= 1);

// A match happend! Use OSI to get our current module+offst
OsiProc *current = get_current_process(cpu);

OsiModule *m = get_mapping_by_addr(cpu, current, func_addr);
if (m) {
// If matches doesn't have a key for this string, add it
if (matches.find(value) == matches.end()) {
matches[value] = std::set<std::tuple<std::string, target_ulong>>();
}

if (matches_abs.find(value) == matches_abs.end()) {
matches_abs[value] = std::set<target_ulong>();
}

// Now check the matches[value] set for this module+offset tuple - if it's not there, add it
std::tuple<std::string, target_ulong> t(m->name, func_addr - m->base);
if (matches[value].find(t) == matches[value].end()) {
matches[value].insert(t);
printf("Match of %s at func_addr " TARGET_FMT_lx " which is in module %s + " TARGET_FMT_lx "\n", value, func_addr, m->name, func_addr - m->base);
}

if (matches_abs[value].find(func_addr) == matches_abs[value].end()) {
matches_abs[value].insert(func_addr);
printf("Match of %s at func_addr " TARGET_FMT_lx "\n", value, func_addr);
}

// cleanup
free_osimodule(m);
}else {
printf("Match of %s at unknown\n", value);
}
}

// logfile default is cwd/findcall.txt
std::filesystem::path logfile = std::filesystem::current_path() / "findcall.txt";

bool init_plugin(void *self) {
init_callwitharg_api();
init_osi_api();

std::unique_ptr<panda_arg_list, void(*)(panda_arg_list*)> args(
panda_get_args("findcall"), panda_free_args);

const char* logfile_arg = panda_parse_string_opt(args.get(), "output_file",
NULL, "Output file to record compared values into");
if (logfile_arg) logfile = std::string(logfile_arg);

/*
target_str = strdup(panda_parse_string_req(args.get(), "target_str", "String to match"));
target_str_len = strlen(target_str);
if (target_str_len <= 0) {
printf("targetcmp error: invalid target_str argument\n");
return false;
}
*/

#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_X86_64)
outfile.open(logfile.string(), std::ios_base::out | std::ios_base::trunc); // Empty file to start

// Tell callwitharg to call on_match when it finds a match of our string
//add_target_string(target_str);

// Register on_call_match with callwitharg's on_call_match_str PPP callback
PPP_REG_CB("callwitharg", on_call_match_str, on_match);
return true;
#endif
printf("ERROR: Unsupported architecture for targetcmp\n");
return false;
}

void uninit_plugin(void *self) {
// Find all unique module+offset values in matches, then check if all keys ever have each value - if so print
std::map<std::tuple<std::string, target_ulong>, std::set<std::string>> modoff_to_keys;
for (auto const& [key, val] : matches) {
for (auto const& [mod, off] : val) {
modoff_to_keys[std::make_tuple(mod, off)].insert(key);
}
}
// Now we have a map from module+offset to set of keys that have that module+offset - print all that have all keys
for (auto const& [modoff, keys] : modoff_to_keys) {
if (keys.size() == matches.size()) {

// Write module, offset to outfile
outfile << std::get<0>(modoff) << "," << std::get<1>(modoff) << std::endl;

// Print the common module+offset then the two keys
printf("%s + " TARGET_FMT_lx "\n", std::get<0>(modoff).c_str(), std::get<1>(modoff));
for (auto const& key : keys) {
printf(" %s\n", key.c_str());
}
}
}

// Now write down all absolute addresses that are in every key
std::map<target_ulong, std::set<std::string>> abs_to_keys;
for (auto const& [key, val] : matches_abs) {
for (auto const& abs : val) {
abs_to_keys[abs].insert(key);
}
}
// Now we have a map from absolute address to set of keys that have that absolute address - print all that have all keys
for (auto const& [abs, keys] : abs_to_keys) {
if (keys.size() == matches_abs.size()) {

// Write absolute address to outfile: just raw address
outfile << abs << std::endl;

// Print the common absolute address then the two keys
printf(TARGET_FMT_lx "\n", abs);
for (auto const& key : keys) {
printf(" %s\n", key.c_str());
}
}
}

if (outfile.is_open()) {
outfile.close();
}

}

0 comments on commit d11cf30

Please sign in to comment.