//----------------------------------------------------------------------------- // // Copyright (C) 2015-2017 David Hill // // See COPYING for license information. // //----------------------------------------------------------------------------- // // Module class bytecode reading. // //----------------------------------------------------------------------------- #include "Module.hpp" #include "Array.hpp" #include "BinaryIO.hpp" #include "Environment.hpp" #include "Error.hpp" #include "Function.hpp" #include "Init.hpp" #include "Jump.hpp" #include "Script.hpp" #include //----------------------------------------------------------------------------| // Extern Functions | // namespace ACSVM { // // Module::chunkIterACSE // bool Module::chunkIterACSE(Byte const *data, std::size_t size, bool (Module::*chunker)(Byte const *, std::size_t, Word)) { std::size_t iter = 0; while(iter != size) { // Need space for header. if(size - iter < 8) throw ReadError(); // Read header. Word chunkName = ReadLE4(data + iter + 0); Word chunkSize = ReadLE4(data + iter + 4); // Consume header. iter += 8; // Need space for payload. if(size - iter < chunkSize) throw ReadError(); // Read payload. if((this->*chunker)(data + iter, chunkSize, chunkName)) return true; // Consume payload. iter += chunkSize; } return false; } // // Module::chunkStrTabACSE // void Module::chunkStrTabACSE(Vector &strV, Byte const *data, std::size_t size, bool junk) { std::size_t iter = 0; if(junk) { if(size < 12) throw ReadError(); /*junk = ReadLE4(data + iter);*/ iter += 4; strV.alloc(ReadLE4(data + iter)); iter += 4; /*junk = ReadLE4(data + iter);*/ iter += 4; } else { if(size < 4) throw ReadError(); strV.alloc(ReadLE4(data + iter)); iter += 4; } if(size - iter < strV.size() * 4) throw ReadError(); for(String *&str : strV) { str = readStringACS0(data, size, ReadLE4(data + iter)); iter += 4; } } // // Module::chunkerACSE_AIMP // bool Module::chunkerACSE_AIMP(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("AIMP")) return false; if(size < 4) throw ReadError(); // Chunk starts with a number of entries. However, that is redundant with // just checking for the end of the chunk as in MIMP, so do that. // Determine highest index. Word arrC = 0; for(std::size_t iter = 4; iter != size;) { if(size - iter < 8) throw ReadError(); Word idx = ReadLE4(data + iter); iter += 4; /* len = LeadLE4(data + iter);*/ iter += 4; arrC = std::max(arrC, idx + 1); Byte const *next; std::tie(std::ignore, next, std::ignore) = ScanStringACS0(data, size, iter); iter = next - data + 1; } // Read imports. arrImpV.alloc(arrC); for(std::size_t iter = 4; iter != size;) { Word idx = ReadLE4(data + iter); iter += 4; /* len = LeadLE4(data + iter);*/ iter += 4; Byte const *next; std::size_t len; std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); std::unique_ptr str = ParseStringACS0(data + iter, next, len); arrImpV[idx] = env->getString(str.get(), len); iter = next - data + 1; } return true; } // // Module::chunkerACSE_AINI // bool Module::chunkerACSE_AINI(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("AINI")) return false; if(size < 4 || size % 4) throw ReadError(); Word idx = ReadLE4(data); // Silently ignore out of bounds initializers. if(idx >= arrInitV.size()) return false; auto &init = arrInitV[idx]; for(std::size_t iter = 4; iter != size; iter += 4) init.setVal(iter / 4 - 1, ReadLE4(data + iter)); return false; } // // Module::chunkerACSE_ARAY // bool Module::chunkerACSE_ARAY(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("ARAY")) return false; if(size % 8) throw ReadError(); Word arrC = 0; // Determine highest index. for(std::size_t iter = 0; iter != size; iter += 8) arrC = std::max(arrC, ReadLE4(data + iter) + 1); arrNameV.alloc(arrC); arrInitV.alloc(arrC); arrSizeV.alloc(arrC); for(std::size_t iter = 0; iter != size;) { Word idx = ReadLE4(data + iter); iter += 4; Word len = ReadLE4(data + iter); iter += 4; arrInitV[idx].reserve(len); arrSizeV[idx] = len; // Use names from MEXP. if(idx < regNameV.size()) { arrNameV[idx] = regNameV[idx]; regNameV[idx] = nullptr; } } return true; } // // Module::chunkerACSE_ASTR // bool Module::chunkerACSE_ASTR(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("ASTR")) return false; if(size % 4) throw ReadError(); for(std::size_t iter = 0; iter != size;) { Word idx = ReadLE4(data + iter); iter += 4; // Silently ignore out of bounds initializers. if(idx >= arrInitV.size()) continue; auto &init = arrInitV[idx]; for(Word i = 0, e = arrSizeV[idx]; i != e; ++i) init.setTag(i, InitTag::String); } return false; } // // Module::chunkerACSE_ATAG // bool Module::chunkerACSE_ATAG(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("ATAG")) return false; if(size < 5 || data[0]) throw ReadError(); Word idx = ReadLE4(data + 1); // Silently ignore out of bounds initializers. if(idx >= arrInitV.size()) return false; auto &init = arrInitV[idx]; for(std::size_t iter = 5; iter != size; ++iter) { switch(data[iter]) { case 0: init.setTag(iter - 5, InitTag::Integer); break; case 1: init.setTag(iter - 5, InitTag::String); break; case 2: init.setTag(iter - 5, InitTag::Function); break; } } return false; } // // Module::chunkerACSE_FARY // bool Module::chunkerACSE_FARY(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("FARY")) return false; if(size < 2 || (size - 2) % 4) throw ReadError(); Word idx = ReadLE2(data); std::size_t arrC = (size - 2) / 4; if(idx < functionV.size() && functionV[idx]) functionV[idx]->locArrC = arrC; return false; } // // Module::chunkerACSE_FNAM // bool Module::chunkerACSE_FNAM(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("FNAM")) return false; chunkStrTabACSE(funcNameV, data, size, false); return true; } // // Module::chunkerACSE_FUNC // bool Module::chunkerACSE_FUNC(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("FUNC")) return false; if(size % 8) throw ReadError(); // Read functions. functionV.alloc(size / 8); std::size_t iter = 0; for(Function *&func : functionV) { Word idx = iter / 8; Word argC = ReadLE1(data + iter); iter += 1; Word locRegC = ReadLE1(data + iter); iter += 1; Word flags = ReadLE2(data + iter); iter += 2; Word codeIdx = ReadLE4(data + iter); iter += 4; // Ignore undefined functions for now. if(!codeIdx) continue; String *funcName = idx < funcNameV.size() ? funcNameV[idx] : nullptr; Function *function = env->getFunction(this, funcName); function->argC = argC; function->locRegC = locRegC; function->flagRet = flags & 0x0001; function->codeIdx = codeIdx; func = function; } return true; } // // Module::chunkerACSE_JUMP // bool Module::chunkerACSE_JUMP(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("JUMP")) return false; if(size % 4) throw ReadError(); // Read jumps. jumpV.alloc(size / 4); std::size_t iter = 0; for(Jump &jump : jumpV) { jump.codeIdx = ReadLE4(data + iter); iter += 4; } return true; } // // Module::chunkerACSE_LOAD // bool Module::chunkerACSE_LOAD(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("LOAD")) return false; // Count imports. std::size_t importC = 0; for(Byte const *iter = data, *end = data + size; iter != end; ++ iter) if(!*iter) ++importC; importV.alloc(importC); for(std::size_t iter = 0, i = 0; iter != size;) { Byte const *next; std::size_t len; std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); std::unique_ptr str = ParseStringACS0(data + iter, next, len); auto loadName = env->getModuleName(str.get(), len); if(loadName != name) importV[i++] = env->getModule(std::move(loadName)); else importV[i++] = this; iter = next - data + 1; } return true; } // // Module::chunkerACSE_MEXP // bool Module::chunkerACSE_MEXP(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("MEXP")) return false; chunkStrTabACSE(regNameV, data, size, false); return true; } // // Module::chunkerACSE_MIMP // bool Module::chunkerACSE_MIMP(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("MIMP")) return false; // Determine highest index. Word regC = 0; for(std::size_t iter = 0; iter != size;) { if(size - iter < 4) throw ReadError(); Word idx = ReadLE4(data + iter); iter += 4; regC = std::max(regC, idx + 1); Byte const *next; std::tie(std::ignore, next, std::ignore) = ScanStringACS0(data, size, iter); iter = next - data + 1; } // Read imports. regImpV.alloc(regC); for(std::size_t iter = 0; iter != size;) { Word idx = ReadLE4(data + iter); iter += 4; Byte const *next; std::size_t len; std::tie(std::ignore, next, len) = ScanStringACS0(data, size, iter); std::unique_ptr str = ParseStringACS0(data + iter, next, len); regImpV[idx] = env->getString(str.get(), len); iter = next - data + 1; } return true; } // // Module::chunkerACSE_MINI // bool Module::chunkerACSE_MINI(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("MINI")) return false; if(size % 4 || size < 4) throw ReadError("bad MINI size"); Word idx = ReadLE4(data); Word regC = idx + size / 4 - 1; if(regC > regInitV.size()) regInitV.realloc(regC); for(std::size_t iter = 4; iter != size; iter += 4) regInitV[idx++] = ReadLE4(data + iter); return true; } // // Module::chunkerACSE_MSTR // bool Module::chunkerACSE_MSTR(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("MSTR")) return false; if(size % 4) throw ReadError(); for(std::size_t iter = 0; iter != size;) { Word idx = ReadLE4(data + iter); iter += 4; // Silently ignore out of bounds initializers. if(idx < regInitV.size()) regInitV[idx].tag = InitTag::String; } return false; } // // Module::chunkerACSE_SARY // bool Module::chunkerACSE_SARY(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SARY")) return false; if(size < 2 || (size - 2) % 4) throw ReadError(); Word nameInt = ReadLE2(data); std::size_t arrC = (size - 2) / 4; if(nameInt & 0x8000) nameInt |= 0xFFFF0000; for(Script &scr : scriptV) if(scr.name.i == nameInt) scr.locArrC = arrC; return false; } // // Module::chunkerACSE_SFLG // bool Module::chunkerACSE_SFLG(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SFLG")) return false; if(size % 4) throw ReadError(); for(std::size_t iter = 0; iter != size;) { Word nameInt = ReadLE2(data + iter); iter += 2; Word flags = ReadLE2(data + iter); iter += 2; bool flagNet = !!(flags & 0x0001); bool flagClient = !!(flags & 0x0002); if(nameInt & 0x8000) nameInt |= 0xFFFF0000; for(Script &scr : scriptV) { if(scr.name.i == nameInt) { scr.flagClient = flagClient; scr.flagNet = flagNet; } } } return false; } // // Module::chunkerACSE_SNAM // bool Module::chunkerACSE_SNAM(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SNAM")) return false; chunkStrTabACSE(scrNameV, data, size, false); return true; } // // Module::chunkerACSE_SPTR8 // // Reads 8-byte SPTR chunk. // bool Module::chunkerACSE_SPTR8(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SPTR")) return false; if(size % 8) throw ReadError(); // Read scripts. scriptV.alloc(size / 8, this); std::size_t iter = 0; for(Script &scr : scriptV) { Word nameInt = ReadLE2(data + iter); iter += 2; Word type = ReadLE1(data + iter); iter += 1; scr.argC = ReadLE1(data + iter); iter += 1; scr.codeIdx = ReadLE4(data + iter); iter += 4; if(nameInt & 0x8000) nameInt |= 0xFFFF0000; setScriptNameTypeACSE(&scr, nameInt, type); } return true; } // // Module::chunkerACSE_SPTR12 // // Reads 12-byte SPTR chunk. // bool Module::chunkerACSE_SPTR12(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SPTR")) return false; if(size % 12) throw ReadError(); // Read scripts. scriptV.alloc(size / 12, this); std::size_t iter = 0; for(Script &scr : scriptV) { Word nameInt = ReadLE2(data + iter); iter += 2; Word type = ReadLE2(data + iter); iter += 2; scr.codeIdx = ReadLE4(data + iter); iter += 4; scr.argC = ReadLE4(data + iter); iter += 4; if(nameInt & 0x8000) nameInt |= 0xFFFF0000; setScriptNameTypeACSE(&scr, nameInt, type); } return true; } // // Module::chunkerACSE_STRE // bool Module::chunkerACSE_STRE(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("STRE")) return false; std::size_t iter = 0; if(size < 12) throw ReadError(); /*junk = ReadLE4(data + iter);*/ iter += 4; stringV.alloc(ReadLE4(data + iter)); iter += 4; /*junk = ReadLE4(data + iter);*/ iter += 4; if(size - iter < stringV.size() * 4) throw ReadError(); for(String *&str : stringV) { std::size_t offset = ReadLE4(data + iter); iter += 4; // Decrypt string. std::unique_ptr buf; std::size_t len; std::tie(buf, len) = DecryptStringACSE(data, size, offset); // Scan string. Byte const *bufEnd; std::tie(std::ignore, bufEnd, len) = ScanStringACS0(buf.get(), len, 0); // Parse string. str = env->getString(ParseStringACS0(buf.get(), bufEnd, len).get(), len); } return true; } // // Module::chunkerACSE_STRL // bool Module::chunkerACSE_STRL(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("STRL")) return false; chunkStrTabACSE(stringV, data, size, true); return true; } // // Module::chunkerACSE_SVCT // bool Module::chunkerACSE_SVCT(Byte const *data, std::size_t size, Word chunkName) { if(chunkName != MakeID("SVCT")) return false; if(size % 4) throw ReadError(); for(std::size_t iter = 0; iter != size;) { Word nameInt = ReadLE2(data + iter); iter += 2; Word regC = ReadLE2(data + iter); iter += 2; if(nameInt & 0x8000) nameInt |= 0xFFFF0000; for(Script &scr : scriptV) { if(scr.name.i == nameInt) scr.locRegC = regC; } } return false; } // // Module::readBytecodeACSE // void Module::readBytecodeACSE(Byte const *data, std::size_t size, bool compressed, std::size_t offset) { std::size_t iter = offset; // Find table start. if(iter > size || size - iter < 4) throw ReadError(); iter = ReadLE4(data + iter); if(iter > size) throw ReadError(); // Read chunks. if(offset == 4) { readChunksACSE(data + iter, size - iter, false); } else { if(iter <= offset) readChunksACSE(data + iter, offset - iter, true); else readChunksACSE(data + iter, size - iter, true); } // Read code. readCodeACS0(data, size, compressed); loaded = true; } // // Module::readChunksACSE // void Module::readChunksACSE(Byte const *data, std::size_t size, bool fakeACS0) { // MEXP - Module Variable/Array Export chunkIterACSE(data, size, &Module::chunkerACSE_MEXP); // ARAY - Module Arrays chunkIterACSE(data, size, &Module::chunkerACSE_ARAY); // AINI - Module Array Init chunkIterACSE(data, size, &Module::chunkerACSE_AINI); // FNAM - Function Names chunkIterACSE(data, size, &Module::chunkerACSE_FNAM); // FUNC - Functions chunkIterACSE(data, size, &Module::chunkerACSE_FUNC); // FARY - Function Arrays chunkIterACSE(data, size, &Module::chunkerACSE_FARY); // JUMP - Dynamic Jump Targets chunkIterACSE(data, size, &Module::chunkerACSE_JUMP); // MINI - Module Variable Init chunkIterACSE(data, size, &Module::chunkerACSE_MINI); // SNAM - Script Names chunkIterACSE(data, size, &Module::chunkerACSE_SNAM); // SPTR - Script Pointers if(fakeACS0) chunkIterACSE(data, size, &Module::chunkerACSE_SPTR8); else chunkIterACSE(data, size, &Module::chunkerACSE_SPTR12); // SARY - Script Arrays chunkIterACSE(data, size, &Module::chunkerACSE_SARY); // SFLG - Script Flags chunkIterACSE(data, size, &Module::chunkerACSE_SFLG); // SVCT - Script Variable Count chunkIterACSE(data, size, &Module::chunkerACSE_SVCT); // STRE - Encrypted String Literals if(!chunkIterACSE(data, size, &Module::chunkerACSE_STRE)) { // STRL - String Literals chunkIterACSE(data, size, &Module::chunkerACSE_STRL); } // LOAD - Library Loading chunkIterACSE(data, size, &Module::chunkerACSE_LOAD); // Process function imports. for(auto &func : functionV) { if(func) continue; std::size_t idx = &func - functionV.data(); if(idx >= funcNameV.size()) continue; auto &funcName = funcNameV[idx]; if(!funcName) continue; for(auto &import : importV) { for(auto &funcImp : import->functionV) { if(funcImp && funcImp->name == funcName) { func = funcImp; goto func_found; } } } func_found:; } // AIMP - Module Array Import chunkIterACSE(data, size, &Module::chunkerACSE_AIMP); // MIMP - Module Variable Import chunkIterACSE(data, size, &Module::chunkerACSE_MIMP); // ASTR - Module Array Strings chunkIterACSE(data, size, &Module::chunkerACSE_ASTR); // ATAG - Module Array Tagging chunkIterACSE(data, size, &Module::chunkerACSE_ATAG); // MSTR - Module Variable Strings chunkIterACSE(data, size, &Module::chunkerACSE_MSTR); for(auto &init : arrInitV) init.finish(); } // // Module::setScriptNameTypeACSE // void Module::setScriptNameTypeACSE(Script *scr, Word nameInt, Word type) { // If high bit is set, script is named. if((scr->name.i = nameInt) & 0x80000000) { // Fetch name. Word nameIdx = ~scr->name.i; if(nameIdx < scrNameV.size()) scr->name.s = scrNameV[nameIdx]; } scr->type = env->getScriptTypeACSE(type); } // // Module::DecryptStringACSE // std::pair< std::unique_ptr /*data*/, std::size_t /*size*/> Module::DecryptStringACSE(Byte const *data, std::size_t size, std::size_t iter) { Word const key = iter * 157135; // Calculate length. Start at 1 for null terminator. std::size_t len = 1; for(std::size_t i = iter, n = 0;; ++i, ++n, ++len) { if(i == size) throw ReadError(); Byte c = static_cast(data[i] ^ (n / 2 + key)); if(!c) break; } // Decrypt data. std::unique_ptr buf{new Byte[len]}; for(std::size_t i = iter, n = 0;; ++i, ++n) { Byte c = static_cast(data[i] ^ (n / 2 + key)); if(!(buf[n] = c)) break; } return {std::move(buf), len}; } } // EOF