fingerprints

This commit is contained in:
Sean 2020-02-18 17:03:29 -07:00
parent 9d903712bf
commit 54b1b4b8ce
No known key found for this signature in database
GPG Key ID: B111C910D99B42B8
12 changed files with 535 additions and 31 deletions

View File

@ -1,12 +1,16 @@
OBJS = main.o omf.o handle.o map.o
OBJS = main.o omf.o handle.o map.o disasm.o api.o
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11
LDFLAGS = -largp
CXXFLAGS = -Wall -std=c++11
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
LDFLAGS = -largp
endif
all: regs
regs: $(OBJS)
$(CXX) $(CXXFLAGS) $(LIBS) -o $@ $^
$(CXX) $(CXXFLAGS) $(LIBS) -o $@ $(LDFLAGS) $^
%.o: %.cc
$(CXX) -c $(CXXFLAGS) -o $@ $<

201
src/api.cc Normal file
View File

@ -0,0 +1,201 @@
/** @copyright 2020 Sean Kasun */
#include <iostream>
#include "api.h"
Fingerprints::Fingerprints() {
root = std::make_shared<Fingerprint>();
}
void Fingerprints::add(const std::vector<uint8_t> &keys, std::string name,
uint8_t numDB) {
auto node = root;
for (auto key : keys) {
if (node->map.find(key) == node->map.end()) {
node->map.insert(std::pair<uint8_t, std::shared_ptr<Fingerprint>>
(key, std::make_shared<Fingerprint>()));
}
node = node->map[key];
}
node->name = name;
node->numDB = numDB;
}
API::API(unsigned char *dat, unsigned int len) {
auto h = TheHandle::createFromArray(std::vector<uint8_t>(dat, dat + len));
auto numSymbols = h->r32();
for (uint32_t i = 0; i < numSymbols; i++) {
auto id = h->r32();
auto kind = h->r8();
auto name = h->rs();
ids.insert(std::pair<uint32_t, std::string>(id, name));
std::shared_ptr<symbol::Symbol> s = nullptr;
switch (kind) {
case symbol::isIntrinsic:
s = std::make_shared<symbol::Intrinsic>();
s->kind = symbol::isIntrinsic;
break;
case symbol::isEnum:
s = std::make_shared<symbol::Enum>();
s->kind = symbol::isEnum;
break;
case symbol::isAlias:
s = std::make_shared<symbol::Ref>();
s->kind = symbol::isAlias;
break;
case symbol::isStruct:
s = std::make_shared<symbol::Struct>();
s->kind = symbol::isStruct;
break;
case symbol::isUnion:
s = std::make_shared<symbol::Struct>();
s->kind = symbol::isUnion;
break;
case symbol::isRef:
std::cerr << "isRef on root!" << std::endl;
break;
case symbol::isFunction:
s = std::make_shared<symbol::Function>();
s->kind = symbol::isFunction;
break;
default:
std::cerr << "Unknown type" << std::endl;
break;
}
s->name = name;
symbols.insert(std::pair<std::string, symbol::Symbol>(name, s));
}
for (uint32_t i = 0; i < numSymbols; i++) {
auto id = h->r32();
auto s = symbols[ids[id]];
s->size = h->r32();
switch (s->kind) {
case symbol::isIntrinsic:
setIntrinsic(h, s);
break;
case symbol::isEnum:
setEnum(h, s);
break;
case symbol::isAlias:
setRef(h, s);
break;
case symbol::isStruct:
setStruct(h, s);
break;
case symbol::isUnion:
setStruct(h, s);
break;
case symbol::isRef:
std::cerr << "base level ref" << std::endl;
break;
case symbol::isFunction:
setFunction(h, s);
break;
}
}
}
std::shared_ptr<symbol::Symbol> API::lookup(uint32_t id) {
return symbols[ids[id]];
}
void API::setIntrinsic(Handle h, std::shared_ptr<symbol::Symbol> s) {
auto i = std::static_pointer_cast<symbol::Intrinsic>(s);
auto type = h->r8();
switch (type) {
case symbol::Intrinsic::U8:
i->type = symbol::Intrinsic::U8;
break;
case symbol::Intrinsic::U16:
i->type = symbol::Intrinsic::U16;
break;
case symbol::Intrinsic::U32:
i->type = symbol::Intrinsic::U32;
break;
case symbol::Intrinsic::S8:
i->type = symbol::Intrinsic::S8;
break;
case symbol::Intrinsic::S16:
i->type = symbol::Intrinsic::S16;
break;
case symbol::Intrinsic::S32:
i->type = symbol::Intrinsic::S32;
break;
}
}
void API::setEnum(Handle h, std::shared_ptr<symbol::Symbol> s) {
auto e = std::static_pointer_cast<symbol::Enum>(s);
e->type = lookup(h->r32());
auto num = h->r32();
for (uint32_t i = 0; i < num; i++) {
auto value = h->r32();
e->entries[h->rs()] = value;
}
}
void API::setRef(Handle h, std::shared_ptr<symbol::Symbol> s) {
auto r = std::static_pointer_cast<symbol::Ref>(s);
auto id = h->r32();
if (id == 0) {
r->symbol = nullptr;
} else {
r->symbol = lookup(id);
r->pointer = h->r32();
r->array = static_cast<int32_t>(h->r32());
r->reg = h->rs();
}
}
void API::setStruct(Handle h, std::shared_ptr<symbol::Symbol> s) {
auto str = std::static_pointer_cast<symbol::Struct>(s);
auto numFields = h->r32();
for (uint32_t i = 0; i < numFields; i++) {
auto key = h->rs();
auto kind = h->r8();
auto size = h->r32();
std::shared_ptr<symbol::Symbol> sym = nullptr;
switch (kind) {
case symbol::isRef:
sym = std::make_shared<symbol::Ref>();
sym->kind = symbol::isRef;
sym->size = size;
setRef(h, sym);
break;
case symbol::isStruct:
sym = std::make_shared<symbol::Struct>();
sym->kind = symbol::isStruct;
sym->size = size;
setStruct(h, sym);
break;
case symbol::isUnion:
sym = std::make_shared<symbol::Struct>();
sym->kind = symbol::isUnion;
sym->size = size;
setStruct(h, sym);
break;
default:
std::cerr << "Unknown field type" << std::endl;
break;
}
str->fields.push_back({key, sym});
}
}
void API::setFunction(Handle h, std::shared_ptr<symbol::Symbol> s) {
auto f = std::static_pointer_cast<symbol::Function>(s);
auto numArgs = h->r32();
for (uint32_t i = 0; i < numArgs; i++) {
auto key = h->rs();
auto ref = std::make_shared<symbol::Ref>();
setRef(h, ref);
f->arguments.push_back({key, ref});
}
f->returnType = std::make_shared<symbol::Ref>();
setRef(h, f->returnType);
auto numSig = h->r32();
for (uint32_t i = 0; i < numSig; i++) {
f->signature.push_back(h->r32());
}
}

86
src/api.h Normal file
View File

@ -0,0 +1,86 @@
/** @copyright 2020 Sean Kasun */
#pragma once
#include <cstdint>
#include <map>
#include "handle.h"
struct Fingerprint {
std::map<uint8_t, std::shared_ptr<Fingerprint>> map;
uint8_t numDB;
std::string name;
};
class Fingerprints {
public:
Fingerprints();
void add(const std::vector<uint8_t> &keys, std::string name,
uint8_t numDB = 0);
std::shared_ptr<Fingerprint> root;
};
namespace symbol {
enum Kind {
isIntrinsic = 0, isEnum, isAlias, isStruct, isUnion, isRef, isFunction,
};
struct Symbol {
std::string name;
uint32_t size;
Kind kind;
};
struct Ref : public Symbol {
std::shared_ptr<Symbol> symbol;
uint32_t pointer;
int32_t array;
std::string reg;
};
struct Argument {
std::string key;
std::shared_ptr<Ref> ref;
};
struct Function : public Symbol {
std::vector<Argument> arguments;
std::shared_ptr<Ref> returnType;
std::vector<uint32_t> signature;
};
struct Field {
std::string key;
std::shared_ptr<Symbol> value;
};
struct Struct : public Symbol {
std::vector<Field> fields;
};
struct Intrinsic : public Symbol {
enum uint8_t { U8, U16, U23, S8, S16, S32 } type;
};
struct Enum : public Symbol {
std::shared_ptr<Symbol> type;
std::map<std::string, uint32_t> entries;
};
}
class API {
public:
API(unsigned char *dat, unsigned int len);
std::map<std::string, std::shared_ptr<symbol::Symbol>> symbols;
private:
std::shared_ptr<symbol::Symbol> lookup(uint32_t id);
void setIntrinsic(Handle h, std::shared_ptr<symbol::Symbol> s);
void setEnum(Handle h, std::shared_ptr<symbol::Symbol> s);
void setRef(Handle h, std::shared_ptr<symbol::Symbol> s);
void setStruct(Handle h, std::shared_ptr<symbol::Symbol> s);
void setFunction(Handle h, std::shared_ptr<symbol::Symbol> s);
std::map<uint32_t, std::string> ids;
};

127
src/disasm.cc Normal file
View File

@ -0,0 +1,127 @@
/** @copyright 2020 Sean Kasun */
#include "disasm.h"
#include <fstream>
#include <iostream>
Disassembler::Disassembler(std::shared_ptr<Fingerprints> prints) : fingerprints(prints) { }
bool Disassembler::disassemble(std::vector<Segment> segments,
std::vector<Entry> entries) {
this->segments = segments;
// trace all entry points
for (auto &entry : entries) {
if (!trace(entry)) {
std::cerr << "Failed to trace execution flow" << std::endl;
return false;
}
}
// build the basic blocks
if (!basicBlocks()) {
std::cerr << "Failed to calculate basic blocks" << std::endl;
return false;
}
// disassemble each segment
for (auto &segment : segments) {
std::string fname = "seg" + std::to_string(segment.segnum);
std::ofstream f(fname, std::ios::out | std::ios::binary | std::ios::trunc);
if (!f.is_open()) {
std::cerr << "Failed to open '" << fname << "' for writing" << std::endl;
return false;
}
f << "Section $" << std::ios::hex << segment.segnum << " "
<< segment.name << std::endl;
if (!decode(segment.mapped, segment.mapped + segment.length)) {
std::cerr << "Disassembly failed" << std::endl;
return false;
}
f.close();
}
return true;
}
bool Disassembler::trace(const Entry &start) {
std::stack<Entry> workList;
workList.push(start);
labels.insert(std::pair<uint32_t, uint32_t>(start.org, start.org));
while (!workList.empty()) {
auto state = workList.top();
workList.pop();
std::shared_ptr<Inst> inst = nullptr;
do {
auto ptr = getAddress(state.org);
if (ptr == nullptr) {
return false;
}
auto addr = state.org;
inst = nullptr;
int16_t numDB = 0;
if (fingerprints) { // scan for fingerprints
auto node = fingerprints->root;
int8_t len = 0;
auto fstart = ptr->tell();
do {
node = node->map.value(ptr->r8(), nullptr);
len++;
if (node != nullptr && !node->name.isEmpty()) {
if (inst == nullptr) {
inst = std::make_shared<Inst>();
inst->type = Special;
}
inst->name = node->name;
inst->length = len;
numDB = node->numDB;
}
} while (node != nullptr && !ptr->eof());
if (inst) {
fstart += inst->length;
state.org += inst->length;
}
ptr->seek(fstart);
}
if (numDB > 0 && inst) {
inst->name += " {";
for (int i = 0; i < numDB; i++) {
if (i) {
inst->name += ", ";
}
inst->name += hex2(ptr->r8());
}
inst->name += "}";
inst->length += numDB;
state.org += numDB;
}
if (!inst) {
inst = decodeInst(ptr, &state);
}
map[addr] = inst;
if (inst->type == Jump || inst->type == Branch || inst->type == Call) {
auto target = target(inst, resolver);
if (target > 0 && !labels.contains(target)) {
workList.push({state.flags, target});
labels.insert(target, target);
}
}
if (inst->type == Jump || inst->type == Branch ||
inst->type == Return) {
branches.insert(state.org, addr);
}
if (inst->type == Invalid) {
branches.insert(addr, addr);
}
} while (inst->type != Return && inst->type != Jump &&
inst->type != Invalid);
}
return true;
}
Handle Disassembler::getAddress(uint32_t address) {
for (auto &s : segments) {
if (address >= s.mapped && address < s.mapped + s.length) {
s.data->seek(address - s.mapped);
return s.data;
}
}
return nullptr;
}

View File

@ -2,6 +2,9 @@
#pragma once
#include <cstdint>
#include <map>
#include "omf.h"
#include "api.h"
enum {
IsX8 = 0x10,
@ -12,3 +15,34 @@ enum {
IsM8Changed = 0x400,
IsEmuChanged = 0x2000,
};
enum InsType : uint16_t {
Normal = 0x00,
Call = 0x01,
Jump = 0x02,
Return = 0x03,
Branch = 0x04,
Special = 0x05, // fingerprint
Invalid = 0xff,
};
struct Inst {
std::string name;
InsType type;
uint16_t length;
};
class Disassembler {
public:
Disassembler(std::shared_ptr<Fingerprints> prints);
bool disassemble(std::vector<Segment> segments, std::vector<Entry> entries);
private:
bool trace(const Entry &start);
Handle getAddress(uint32_t address);
std::map<uint32_t, uint32_t> labels;
std::map<uint32_t, uint32_t> branches;
std::vector<Segment> segments;
std::shared_ptr<Fingerprints> fingerprints;
};

View File

@ -68,6 +68,15 @@ uint8_t TheHandle::r8() {
return *pos++;
}
std::string TheHandle::rs() {
std::string r;
uint8_t len = *pos++;
for (auto i = 0; i < len; i++) {
r += static_cast<char>(*pos++);
}
return r;
}
std::string TheHandle::read(int32_t len) {
std::string r;
for (auto i = 0; i < len; i++) {

View File

@ -19,6 +19,7 @@ class TheHandle {
uint32_t r24();
uint16_t r16();
uint8_t r8();
std::string rs();
void seek(int64_t pos);
void skip(int64_t length);
std::string read(int32_t length);

View File

@ -4,7 +4,8 @@
#include <iostream>
#include "disasm.h"
#include "omf.h"
#include "api.h"
#include "../iigs.c"
const char *argp_program_version = "regs 0.2";
const char *argp_program_bug_address = "sean@seancode.com";
@ -109,11 +110,54 @@ int main(int argc, char **argv) {
argp_parse(&argp, argc, argv, 0, 0, &arguments);
// load map if it exists
Map map(arguments.filename, arguments.org, arguments.flags);
OMF omf(map);
if (!omf.load(arguments.filename)) {
Map map(arguments.filename);
OMF omf;
if (!omf.load(arguments.filename, arguments.org)) {
std::cerr << "Failed to load " << arguments.filename << std::endl;
return -1;
}
auto segments = omf.get();
if (map.needsEntry()) {
for (auto &s : segments) {
if ((s.kind & 0x1f) == 0) { // code
map.addEntry(s.mapped + s.entry, arguments.flags);
break;
}
}
}
API api(iigs_dat, iigs_dat_len);
auto prints = std::make_shared<Fingerprints>();
for (auto s : api.symbols) {
if (s->kind == symbol::isFunction) {
auto f = std::static_pointer_cast<symbol::Function>(s);
if (f->signature.size() >= 2) {
if (f->signature[0] >= 0) { // tool
// ldx tool, jsl e1/0000
std::vector<uint8_t> sig = { 0xa2, f->signature[0], f->signature[1],
0x22, 0x00, 0x00, 0xe1 };
prints->add(sig, f->name);
} else if (f->signature[0] == -1) { // p16/gsos
// jsl e1/00a8
std::vector<uint8_t> sig = { 0x22, 0xa8, 0x00, 0xe1,
f->signature[2] & 0xff, f->signature[2] >> 8 };
prints->add(sig, f->name, f->signature[1] & 0xff);
} else if (f->signature[0] == -2) { // p8
// jsr bf00
std::vector<uint8_t> sig = { 0x20, 0x00, 0xbf, f->signature[2] };
prints->add(sig, f->name, f->signature[1] & 0xff);
} else if (f->signature[0] == -3) { // smartport
// jsr c50d
std::vector<uint8_t> sig5 = { 0x20, 0x0d, 0xc5, f->signature[2] };
std::vector<uint8_t> sig7 = { 0x20, 0x0d, 0xc7, f->signature[2] };
prints->add(sig5, f->name, f->signature[1]);
prints->add(sig7, f->name, f->signature[1]);
}
}
}
}
Disassembler d(prints);
d.disassemble(segments, map.getEntries());
}

View File

@ -3,16 +3,11 @@
#include <string>
#include <fstream>
Map::Map(const char *filename, uint32_t org, uint32_t flags) {
Map::Map(const char *filename) {
std::string mapname = filename;
mapname += ".regs";
File file(mapname);
if (!file.is_open()) {
Entry entry;
entry.org = org;
this->org = org;
entry.flags = flags;
entryPoints.push_back(entry);
return;
}
@ -54,10 +49,18 @@ Map::Map(const char *filename, uint32_t org, uint32_t flags) {
}
}
void Map::addEntry(uint32_t entry) {
bool Map::needsEntry() {
return entryPoints.size() == 0;
}
std::vector<Entry> Map::getEntries() {
return entryPoints;
}
void Map::addEntry(uint32_t entry, uint32_t flags) {
Entry e;
e.org = entry;
e.flags = 0;
e.flags = flags;
entryPoints.push_back(e);
}

View File

@ -31,8 +31,10 @@ struct Entry {
class Map {
public:
Map(const char *filename, uint32_t org, uint32_t flags);
void addEntry(uint32_t entry);
Map(const char *filename);
bool needsEntry();
std::vector<Entry> getEntries();
void addEntry(uint32_t entry, uint32_t flags);
uint32_t org;
private:

View File

@ -13,21 +13,22 @@ enum SegOp {
SUPER = 0xf7,
};
OMF::OMF(const Map &map) : map(map) {
OMF::OMF() {
}
static bool compareSegments(const Segment &a, const Segment &b) {
return a.mapped < b.mapped;
}
bool OMF::load(const char *filename) {
bool OMF::load(const char *filename, uint32_t org) {
handle = TheHandle::createFromFile(filename);
if (!isOMF()) {
Segment seg;
seg.bytecnt = handle->length;
seg.kind = 0; // code
seg.mapped = map.org;
seg.entry = 0;
seg.mapped = org;
seg.data = handle;
segments.push_back(seg);
} else {
@ -40,13 +41,6 @@ bool OMF::load(const char *filename) {
if (!relocSegments()) {
return false;
}
// add the first entry point
for (auto &s : segments) {
if ((s.kind & 0x1f) == 0) { // code
map.addEntry(s.mapped + s.entry);
break;
}
}
}
std::sort(segments.begin(), segments.end(), compareSegments);
return true;

View File

@ -26,8 +26,8 @@ struct Segment {
class OMF {
public:
explicit OMF(const Map &map);
bool load(const char *filename);
explicit OMF();
bool load(const char *filename, uint32_t org);
std::vector<Segment> get() const;
private:
@ -39,6 +39,5 @@ class OMF {
uint32_t value);
Handle handle;
const Map &map;
std::vector<Segment> segments;
};