#include #include #include #include #include #include namespace FxOS { OS::OS(VirtualSpace &space): type {UNKNOWN}, m_space {space} { if(!space.covers(0x80000000, (256 << 10))) { FxOS_log(ERR, "OS analysis failed: space doesn't have at least 256 kB " "of ROM to analyze from"); return; } /* Detect OS type by heuristic */ if(space.read_str(0x80010000, 8).value == "CASIOWIN") this->type = FX; else if(space.read_str(0x80020000, 8).value == "CASIOWIN") this->type = CG; else { FxOS_log(ERR, "OS analysis failed: cannot determine OS type"); return; } parse_header(); parse_syscall_table(); parse_footer(); } void OS::parse_header() { VirtualSpace &s = m_space; if(this->type == FX) { /* Bootcode timestamp at the very end of the bootcode */ this->bootcode_timestamp = s.read_str(0x8000ffb0, 14); this->bootcode_checksum = s.read_u32(0x8000fffc); this->version = s.read_str(0x80010020, 10); this->serial_number = s.read_str(0x8000ffd0, 8); } else if(this->type == CG) { this->bootcode_timestamp = s.read_str(0x8001ffb0, 14); this->bootcode_checksum = s.read_u32(0x8001fffc); this->version = s.read_str(0x80020020, 10); this->serial_number = s.read_str(0x8001ffd0, 8); } /* Version has format MM.mm.pppp */ version_major = std::stoi(this->version.value.substr(0, 2)); version_minor = std::stoi(this->version.value.substr(3, 2)); version_patch = std::stoi(this->version.value.substr(6, 4)); } bool OS::version_lt(int major, int minor, int patch) const noexcept { if(this->version_major < major) return true; if(this->version_major > major) return false; if(this->version_minor < minor) return true; if(this->version_minor > minor) return false; return (this->version_patch < patch); } bool OS::version_le(int major, int minor, int patch) const noexcept { if(this->version_major < major) return true; if(this->version_major > major) return false; if(this->version_minor < minor) return true; if(this->version_minor > minor) return false; return (this->version_patch <= patch); } bool OS::version_gt(int major, int minor, int patch) const noexcept { if(this->version_major < major) return false; if(this->version_major > major) return true; if(this->version_minor < minor) return false; if(this->version_minor > minor) return true; return (this->version_patch > patch); } bool OS::version_ge(int major, int minor, int patch) const noexcept { if(this->version_major < major) return false; if(this->version_major > major) return true; if(this->version_minor < minor) return false; if(this->version_minor > minor) return true; return (this->version_patch >= patch); } //--- // Syscall resolution //--- int OS::syscall_count() const noexcept { return m_syscall_table.size(); } uint32_t OS::syscall(int id) const { return m_syscall_table[id]; } int OS::find_syscall(uint32_t entry) const noexcept { try { return m_syscall_addresses.at(entry); } catch(std::out_of_range &e) { return -1; } } uint32_t OS::syscall_table_address() const noexcept { uint32_t address = (this->type == FX) ? 0x8001007c : 0x8002007c; return m_space.read_u32(address); } void OS::parse_syscall_table() { /* Traverse the syscall table */ uint32_t syscall_table = syscall_table_address(); int id = 0; while(1) { uint32_t entry = m_space.read_u32(syscall_table + 4 * id); MemoryRegion const *r = MemoryRegion::region_for(entry); if(!r) break; m_syscall_table.push_back(entry); m_syscall_addresses[entry] = id; id++; } } //--- // Footer search //--- void OS::parse_footer() { VirtualSpace &s = m_space; /* Find the footer address (occurrence of "CASIOABSLangdata") */ uint32_t start = MemoryRegion::ROM.start; uint32_t end = MemoryRegion::ROM.end; this->footer = s.search(start, end, "CASIOABSLangdata", 16); if(this->footer == end) { this->footer = -1; this->timestamp = std::string(""); this->langdata = 0; this->computed_checksum = -1; return; } uint32_t addr = this->footer + 8; this->langdata = 0; while(1) { void const *entry = s.translate(addr, 8); if(!entry || memcmp(entry, "Langdata", 8) != 0) break; this->langdata++; addr += 0x30; } this->timestamp = s.read_str(addr, 14); this->checksum = s.read_u32(addr + 0x18); this->computed_checksum = this->compute_checksum(); } //--- // Checksum //--- static uint32_t accumulate_range( VirtualSpace &m_space, uint32_t start, uint32_t end) { uint32_t sum = 0; /* Read from dynamically-sized bindings; read_u8() would be too slow */ while(start < end) { int size; uint8_t *buf = (uint8_t *)m_space.translate_dynamic(start, &size); if(!buf || size <= 0) return -1; if(start + size > end) size = end - start; for(int i = 0; i < size; i++) sum += buf[i]; start += size; } return sum; } uint32_t OS::compute_checksum() const { if(this->type == FX) { return accumulate_range(m_space, 0x80010000, this->checksum.address); } else if(this->type == CG) { if(this->version_lt(2, 2, 0)) { return accumulate_range(m_space, 0x80020000, 0x80b5feb0) + accumulate_range(m_space, 0x80b5fef0, 0x80b5fff8); } else { return accumulate_range(m_space, 0x80020000, 0x80b20000) + accumulate_range( m_space, this->footer + 8, this->checksum.address); } } return -1; } } /* namespace FxOS */