273 lines
6.8 KiB
C++
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.
|
|
)");
|