fxos/shell/a.cpp

273 lines
6.8 KiB
C++

#include "shell.h"
#include "parser.h"
#include "commands.h"
#include "errors.h"
#include "util.h"
#include <fxos/disassembly.h>
#include <fxos/vspace.h>
#include <fxos/util/Timer.h>
#include <fxos/util/log.h>
#include <fxos/passes/cfg.h>
#include <fxos/passes/pcrel.h>
#include <fxos/passes/syscall.h>
#include <fmt/core.h>
#include <endian.h>
//---
// ad
//---
static void ad_disassemble_all(
VirtualSpace &space, std::vector<uint32_t> const &addresses, bool force)
{
int successes = 0, errors = 0;
Timer timer;
/* Analyze the CFGs of all functions */
timer.start();
CfgPass cfg_pass(space.disasm);
/* We collect subfunction addresses while running the pass */
for(int i = 0; i < (int)addresses.size(); i++) {
uint32_t entry = addresses[i];
printr("[cfg %d/%zu] Disassembling 0x%08x...", i + 1, addresses.size(),
entry);
if(!cfg_pass.exploreFunction(entry)) {
FxOS_log(ERR, "while processing 0x%08x", entry);
errors++;
if(!force)
return;
}
else {
for(Claim const &c: cfg_pass.resultClaims())
space.disasm.addExclusiveClaim(c);
successes++;
}
}
timer.stop();
printf("\n");
FxOS_log(LOG, "Finished pass <cfg> in %s", timer.format_time());
printr("[syscall] Finding syscall references...");
timer.restart();
OS *os = space.os_analysis();
if(os) {
SyscallPass syscall_pass(space.disasm, os);
if(!syscall_pass.analyzeAllInstructions()) {
errors++;
if(!force)
return;
}
}
timer.stop();
printf("\n");
FxOS_log(LOG, "Finished pass <syscall> in %s", timer.format_time());
printf(
"Successfully analyzed %d functions (%d errors)\n", successes, errors);
/* TODO: Get subfunction addresses by abstract interpretation and keep
going recursively */
}
static std::vector<uint32_t> parse_ad(Session &session, Parser &parser)
{
if(!session.current_space)
return std::vector<uint32_t>();
std::vector<uint32_t> addresses;
do {
addresses.push_back(parser.expr(session.current_space));
}
while(!parser.at_end());
parser.end();
return addresses;
}
void _ad(Session &session, std::vector<uint32_t> const &addresses)
{
if(!session.current_space)
return;
VirtualSpace &space = *session.current_space;
ad_disassemble_all(space, addresses, false);
}
//--
// ads
//---
static void parse_ads(Session &session, Parser &parser)
{
if(!session.current_space)
return;
parser.end();
}
void _ads(Session &session)
{
if(!session.current_space)
return;
VirtualSpace &space = *session.current_space;
OS *os = space.os_analysis();
if(!os) {
printf("ads: OS analysis failed, cannot enumerate syscalls");
return;
}
std::vector<uint32_t> addresses;
for(int i = 0; i < os->syscall_count(); i++)
addresses.push_back(os->syscall(i));
ad_disassemble_all(space, addresses, true);
}
//---
// am
//---
static void _am_cg_main_menu_function(VirtualSpace &vspace)
{
OS *os = vspace.os_analysis();
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.current_space)
return "";
std::string name = parser.symbol();
parser.end();
return name;
}
void _am(Session &session, std::string name)
{
if(!session.current_space) {
FxOS_log(ERR, "am: no virtual space");
return;
}
if(name == "cg_main_menu_function")
_am_cg_main_menu_function(*session.current_space);
else
FxOS_log(ERR, "am: unknown misc. command '%s'", name);
}
//---
// Command definitions
//---
static ShellCommand _ad_cmd(
"ad",
[](Session &s, Parser &p) {
auto addresses = parse_ad(s, p);
_ad(s, addresses);
},
[](Session &s, Parser &p) { parse_ad(s, p); }, "Analysis: Disassemble", R"(
ad [<addresses>...]
Disassemble the given set of addresses into the current virtual space's main
disassembly. The main disassembly is used for OS-wide features like cross-
reference search or call graphs.
)");
static ShellCommand _ads_cmd(
"ads",
[](Session &s, Parser &p) {
parse_ads(s, p);
_ads(s);
},
[](Session &s, Parser &p) { parse_ads(s, p); },
"Analysis: Disassemble all Syscalls", R"(
ads
Disassembles all syscalls entries using ad, which stores the results in the
current virtual space's main disassembly. Unlike ad, this commands continues
even if some syscalls fail to disassemble.
)");
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.
)");