280 lines
7.4 KiB
C++
280 lines
7.4 KiB
C++
#include "shell.h"
|
|
#include "parser.h"
|
|
#include "commands.h"
|
|
#include "errors.h"
|
|
#include "util.h"
|
|
|
|
#include <fxos/function.h>
|
|
#include <fxos/vspace.h>
|
|
#include <fxos/util/Timer.h>
|
|
#include <fxos/util/log.h>
|
|
#include <fmt/core.h>
|
|
#include <endian.h>
|
|
|
|
// TODO: fxos: Proper definition of function analysis version
|
|
#define FXOS_FUNCTION_ANALYSIS_VERSION 1
|
|
|
|
//---
|
|
// af
|
|
//---
|
|
|
|
struct _af_args
|
|
{
|
|
bool update = false;
|
|
bool force = false;
|
|
std::string name = "";
|
|
std::vector<u32> addresses;
|
|
};
|
|
|
|
static _af_args parse_af(Session &session, Parser &parser)
|
|
{
|
|
_af_args args;
|
|
parser.option("-u", [&args](Parser &) { args.update = true; });
|
|
parser.option("--force", [&args](Parser &) { args.force = true; });
|
|
parser.option("-n", [&args](Parser &p) { args.name = p.symbol(""); });
|
|
parser.accept_options();
|
|
|
|
do {
|
|
args.addresses.push_back(parser.expr(session.currentBinary()));
|
|
}
|
|
while(!parser.at_end());
|
|
|
|
parser.end();
|
|
return args;
|
|
}
|
|
|
|
static void af_analyze(Binary &binary, _af_args const &args)
|
|
{
|
|
int successes = 0, skipped = 0, errors = 0;
|
|
Timer timer;
|
|
timer.start();
|
|
|
|
auto const &addresses = args.addresses;
|
|
int const FAV = FXOS_FUNCTION_ANALYSIS_VERSION;
|
|
|
|
for(int i = 0; i < (int)addresses.size(); i++) {
|
|
u32 entry = addresses[i];
|
|
printr("[%d/%zu] Analyzing 0x%08x...", i + 1, addresses.size(), entry);
|
|
|
|
/* Check if there is already a function defined here */
|
|
Function *existing = binary.functionAt(entry);
|
|
|
|
if(!existing || existing->analysisVersion() < FAV) {
|
|
auto f = std::make_unique<Function>(binary, entry);
|
|
if(f->exploreFunctionAt(entry)) {
|
|
f->updateFunctionSize();
|
|
f->setAnalysisVersion(FAV);
|
|
binary.addObject(std::move(f));
|
|
successes++;
|
|
}
|
|
else {
|
|
FxOS_log(ERR, "... while analyzing 0x%08x", entry);
|
|
errors++;
|
|
}
|
|
}
|
|
else {
|
|
skipped++;
|
|
}
|
|
|
|
/* TODO: Queue subfunctions for recursive analysis */
|
|
}
|
|
timer.stop();
|
|
printf("\nAnalyzed %d functions (+%d skipped, +%d errors) in %s\n",
|
|
successes, skipped, errors, timer.format_time().c_str());
|
|
|
|
/* TODO: Check for overlapping functions etc */
|
|
}
|
|
|
|
void _af(Session &session, _af_args const &args)
|
|
{
|
|
Binary *b = session.currentBinary();
|
|
if(!b)
|
|
return FxOS_log(ERR, "No current binary!\n");
|
|
af_analyze(*b, args);
|
|
}
|
|
|
|
//--
|
|
// afs
|
|
//---
|
|
|
|
static _af_args parse_afs(Session &, Parser &parser)
|
|
{
|
|
_af_args args;
|
|
parser.option("-u", [&args](Parser &) { args.update = true; });
|
|
parser.option("--force", [&args](Parser &) { args.force = true; });
|
|
parser.accept_options();
|
|
parser.end();
|
|
return args;
|
|
}
|
|
|
|
void _afs(Session &session, _af_args &args)
|
|
{
|
|
Binary *b = session.currentBinary();
|
|
if(!b)
|
|
return FxOS_log(ERR, "No current binary!\n");
|
|
|
|
OS *os = b->OSAnalysis();
|
|
if(!os)
|
|
return FxOS_log(ERR, "afs: No OS analysis, cannot enumerate syscalls");
|
|
|
|
// TODO: afs: Use syscall info
|
|
|
|
for(int i = 0; i < os->syscall_count(); i++)
|
|
args.addresses.push_back(os->syscall(i));
|
|
|
|
af_analyze(*b, args);
|
|
}
|
|
|
|
//---
|
|
// am
|
|
//---
|
|
|
|
static void _am_cg_main_menu_function(Binary &b)
|
|
{
|
|
VirtualSpace &vspace = b.vspace();
|
|
|
|
OS *os = b.OSAnalysis();
|
|
if(!os) {
|
|
FxOS_log(ERR, "no OS analysis");
|
|
return;
|
|
}
|
|
|
|
if(os->syscall_count() < 0x1e58) {
|
|
FxOS_log(ERR, "less than 0x1e58 syscalls");
|
|
return;
|
|
}
|
|
|
|
uint32_t sc_addr = os->syscall(0x1e58);
|
|
fmt::print("syscall %%1e58 found at 0x{:08x}\n", sc_addr);
|
|
|
|
/* Check up to 150 instructions to find the call to the internal function
|
|
SaveAndOpenMainMenu(). This call is in a widget of the shape
|
|
|
|
mov.l GetkeyToMainFunctionReturnFlag, rX
|
|
mov #3, rY
|
|
bsr SaveAndOpenMainMenu
|
|
mov.b rY, @rX
|
|
bra <start of widget>
|
|
nop */
|
|
for(int i = 0; i < 150; i++) {
|
|
uint16_t i0 = vspace.read_u16(sc_addr + 2 * (i + 0));
|
|
uint16_t i1 = vspace.read_u16(sc_addr + 2 * (i + 1));
|
|
uint16_t i2 = vspace.read_u16(sc_addr + 2 * (i + 2));
|
|
uint16_t i3 = vspace.read_u16(sc_addr + 2 * (i + 3));
|
|
uint16_t i4 = vspace.read_u16(sc_addr + 2 * (i + 4));
|
|
uint16_t i5 = vspace.read_u16(sc_addr + 2 * (i + 5));
|
|
|
|
/* Match: mov.l @(disp, pc), rX */
|
|
if((i0 & 0xf000) != 0xd000)
|
|
continue;
|
|
int rX = (i0 >> 8) & 0x0f;
|
|
|
|
/* Match: mov #3, rY */
|
|
if((i1 & 0xf0ff) != 0xe003)
|
|
continue;
|
|
int rY = (i1 >> 8) & 0x0f;
|
|
|
|
/* Match: bsr @(disp, pc) */
|
|
if((i2 & 0xf000) != 0xb000)
|
|
continue;
|
|
int disp = (i2 & 0x0fff);
|
|
|
|
/* Match: mov.b rX, @rY */
|
|
if((i3 != 0x2000 + (rX << 8) + (rY << 4)))
|
|
continue;
|
|
|
|
/* Match: bra @(_, pc) */
|
|
if((i4 & 0xf000) != 0xa000)
|
|
continue;
|
|
|
|
/* Match: nop */
|
|
if(i5 != 0x0009)
|
|
continue;
|
|
|
|
/* Return the target of the bsr instruction */
|
|
uint32_t fun_addr = sc_addr + 2 * (i + 2) + 4 + disp * 2;
|
|
|
|
fmt::print("found widget at 0x{:08x}\n", sc_addr + 2 * i);
|
|
fmt::print("rX = r{}, rY = r{}, disp = {}\n", rX, rY, disp);
|
|
fmt::print("main menu function address: 0x{:08x}\n", fun_addr);
|
|
}
|
|
}
|
|
|
|
static std::string parse_am(Session &session, Parser &parser)
|
|
{
|
|
if(!session.currentBinary())
|
|
return "";
|
|
|
|
std::string name = parser.symbol();
|
|
parser.end();
|
|
|
|
return name;
|
|
}
|
|
|
|
void _am(Session &session, std::string name)
|
|
{
|
|
if(!session.currentBinary()) {
|
|
FxOS_log(ERR, "am: no current binary");
|
|
return;
|
|
}
|
|
|
|
if(name == "cg_main_menu_function")
|
|
_am_cg_main_menu_function(*session.currentBinary());
|
|
else
|
|
FxOS_log(ERR, "am: unknown misc. command '%s'", name);
|
|
}
|
|
|
|
//---
|
|
// Command definitions
|
|
//---
|
|
|
|
static ShellCommand _af_cmd(
|
|
"af",
|
|
[](Session &s, Parser &p) {
|
|
auto args = parse_af(s, p);
|
|
_af(s, args);
|
|
},
|
|
[](Session &s, Parser &p) { parse_af(s, p); }, "Analysis: Functions", R"(
|
|
af [-u|--force] [-n <name>] [<addresses>...]
|
|
|
|
Explore and disassemble functions starting at the specified addresses. For each
|
|
explored function, a binary object of Function type is created, and the
|
|
function is statically analyzed.
|
|
|
|
By default, addresses where functions already exist are not reanalyzed.
|
|
Specifying -u (update) causes all functions to be re-processed, while keeping
|
|
user-specified information (name, prototype, etc). Specifying --force causes
|
|
all functions to be reanalyzed from scratch without keeping user-specified
|
|
information.
|
|
|
|
When a single address is given, -n can specify the name of the function object
|
|
to be created.
|
|
)");
|
|
|
|
static ShellCommand _afs_cmd(
|
|
"afs",
|
|
[](Session &s, Parser &p) {
|
|
auto args = parse_afs(s, p);
|
|
_afs(s, args);
|
|
},
|
|
[](Session &s, Parser &p) { parse_afs(s, p); },
|
|
"Analysis: Functions (Syscalls)", R"(
|
|
afs [-u|--force]
|
|
|
|
Explore and disassemble syscalls. Like af, but automatically pulls function
|
|
names and prototypes out of the predefined syscall table, when there is one.
|
|
)");
|
|
|
|
static ShellCommand _am_cmd(
|
|
"am",
|
|
[](Session &s, Parser &p) {
|
|
auto name = parse_am(s, p);
|
|
_am(s, name);
|
|
},
|
|
[](Session &s, Parser &p) { parse_am(s, p); }, "Analysis: Misc functions",
|
|
R"(
|
|
am <name>
|
|
|
|
Runs miscellaneous analysis functions; commonly used for prototyping.
|
|
)");
|