├── .gitattributes ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── src ├── heuristics.h ├── lua_util.h ├── main.c ├── py_util.h ├── read_lua.h ├── read_py.h └── util.h └── test ├── bigger.py ├── double_local.test ├── hello.luac ├── helloworld.py ├── local.luac └── medium.test /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md diff 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # debug and bin directories 32 | debug/ 33 | bin/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Veit Heller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | STD=c11 2 | override CFLAGS+=-Werror -Wall -g -fPIC -O2 -DNDEBUG -ftrapv -Wfloat-equal -Wundef -Wwrite-strings -Wconversion -Wuninitialized -pedantic -std=$(STD) 3 | PREFIX=/usr/bin/ 4 | BUILDDIR=bin/ 5 | DEBUGDIR=debug/ 6 | LIBS=-D_GNU_SOURCE 7 | 8 | CC=cc 9 | 10 | TARGET=decode 11 | SOURCES=$(wildcard src/*.c) 12 | 13 | #Makes everything 14 | all: 15 | mkdir -p $(BUILDDIR) 2> /dev/null 16 | $(CC) $(CFLAGS) $(LIBS) $(SOURCES) -o $(BUILDDIR)$(TARGET) 17 | 18 | #Uses picky extensions and makes everything(Extensions may break compiling) 19 | dev: 20 | make all CFLAGS+="-Wshadow -Wunreachable-code -Wswitch-enum -Wswitch-default -Wcast-align -Winit-self -Wpointer-arith" 21 | 22 | #Run the preprocessor only 23 | pp: 24 | mkdir -p $(DEBUGDIR) 2> /dev/null 25 | $(CC) -E $(LIBS) $(SOURCES) > $(DEBUGDIR)preprocessed.i 26 | 27 | #Run the preprocessor and asm generator 28 | asm: 29 | mkdir -p $(DEBUGDIR) 2> /dev/null 30 | $(CC) -S $(LIBS) $(SOURCES) 31 | mv *.s $(DEBUGDIR) 32 | 33 | #Create object code 34 | obj: 35 | mkdir -p $(DEBUGDIR) 2> /dev/null 36 | $(CC) -c $(LIBS) $(SOURCES) 37 | mv *.o $(DEBUGDIR) 38 | 39 | #Make all diagnostics files 40 | diagnostics: pp asm obj 41 | 42 | #Cleans directory(no uninstall!) 43 | clean: 44 | rm -rf $(BUILDDIR) $(DEBUGDIR) 45 | 46 | #Installs into specified(or default) directory 47 | install: 48 | install -d $(PREFIX)$(TARGET) 49 | install $(BUILDDIR)$(TARGET) $(PREFIX)$(TARGET) 50 | 51 | #Uninstalls from specified(or default)directory 52 | uninstall: 53 | rm -rf $(PREFIX)$(TARGET) 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # decode 2 | 3 | A Lua/Python Bytecode Disassembler. It is in the very early stages of 4 | development. The Lua Disassembler could be called functional, 5 | a typical listing it provides you with would look like this: 6 | 7 | ``` 8 | $ bin/decode test/hello.luac 9 | File has 'luac' ending, assuming lua bytecode. 10 | 11 | ; Disassembly of file: test/hello.luac 12 | ; Endianness: little endian 13 | ; Int size: 4 14 | ; Size_t size: 8 15 | ; Instruction size: 4 16 | ; lua_Number size: 8 17 | 18 | ; Source file name: hello.lua 19 | ; Line defined: 0 20 | ; Last Line defined: 0 21 | ; Number of Upvalues: 0 22 | ; Number of Parameters: 0 23 | ; Vararg flag: 2 24 | ; Maximum stack size: 2 25 | 26 | ; Constants: (Size 2) 27 | ; Line Type Value 28 | ; ------------------- 29 | 0000 STRING: print 30 | 0001 STRING: hello world and lua 31 | 32 | ; Function prototypes list(Size 0): { 33 | ; } 34 | 35 | ; Opcodes (Size 4): 36 | ; Line Opcode A | B | C 37 | ; ------------------------------------------------- 38 | 0001 GETGLOBAL 0 | 0 | 39 | 0001 LOADK 1 | 1 | 40 | 0001 CALL 0 | 2 | 1 41 | 0001 RETURN 0 | 1 | 42 | 43 | 44 | ; End of binary information. 45 | ``` 46 | 47 | The code looks like it just came out of a cows' behind, so beware. 48 | 49 | ## Why another disassembler? 50 | 51 | This disassembler is written purely in C, so it may well be faster than lad, 52 | ChunkySpy, dis et al. Also, **it does not depend on any external libraries**. 53 | You can use it even without having Lua or Python installed. Yay, right? 54 | 55 | ## TODO 56 | 57 | * Parse Python (only header is parsed until now, then the output is mostly garbage) 58 | -------------------------------------------------------------------------------- /src/heuristics.h: -------------------------------------------------------------------------------- 1 | #ifndef HEURISTICS_H 2 | #define HEURISTICS_H 3 | 4 | #include "read_lua.h" 5 | #include "read_py.h" 6 | 7 | static inline void heuristics(char* file){ 8 | char* content = NULL; 9 | FILE* f = fopen(file, "r"); 10 | size_t unused; 11 | 12 | if(!f) 13 | die("Could not open file."); 14 | 15 | content = (char*) malloc(10 * sizeof(char)); 16 | 17 | if(!content) 18 | die("Could not allocate enough memory."); 19 | 20 | unused = fread(content, 9, 1, f); 21 | if(unused != 1) fprintf(stderr, "Reading the file in may have failed.\n"); 22 | fclose(f); 23 | content[9] = 0; 24 | 25 | if(starts_with(content, LUA_SIG)) 26 | lua(file); 27 | else if(starts_with(content, PY_SIG)) 28 | py(file); 29 | else 30 | puts("Could not match magic numbers. decode only support Lua 5.1/Python 3.4."); 31 | 32 | free(content); 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/lua_util.h: -------------------------------------------------------------------------------- 1 | #ifndef LUA_UTIL_H 2 | #define LUA_UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "util.h" 8 | 9 | #define LUA "^.*\\.luac$" 10 | #define LUA_SIG "\x1B\x4C\x75\x61" // = \27Lua 11 | #define LUA_VERSION 0x51 12 | #define LUA_HEADER_SIZE 12 13 | 14 | #define RETRIEVE_LUA_OPCODE(N) (N & 0x3F) 15 | #define RETRIEVE_LUA_FIELD_A(N) ((N & 0x3FC0) >> 6) 16 | #define RETRIEVE_LUA_FIELD_B(N) ((N & 0xFF800000) >> 23) 17 | #define RETRIEVE_LUA_FIELD_BX(N) ((N & 0xFFFFC000) >> 14) 18 | #define RETRIEVE_LUA_FIELD_SBX(N) (signed) RETRIEVE_LUA_FIELD_BX(N) 19 | #define RETRIEVE_LUA_FIELD_C(N) ((N & 0x7FC000) >> 14) 20 | 21 | enum LUA_FIELD_USAGE{ 22 | AB, 23 | ABX, 24 | ABC, 25 | SBX, 26 | AC, 27 | ASBX, 28 | A 29 | }; 30 | 31 | static const char* const LUA_OPCODE[] = { "MOVE", "LOADK", "LOADBOOL", "LOADNIL", 32 | "GETUPVAL", "GETGLOBAL", "GETTABLE", 33 | "SETGLOBAL", "SETUPVAL", "SETTABLE", 34 | "NEWTABLE", "SELF", "ADD", "SUB", "MUL", 35 | "DIV", "MOD", "POW", "UNM", "NOT", "LEN", 36 | "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", 37 | "TESTSET", "CALL", "TAILCALL", "RETURN", 38 | "FORLOOP", "FORPREP", "TFORLOOP", "SETLIST", 39 | "CLOSE", "CLOSURE", "VARARG" }; 40 | 41 | static const char LUA_OPCODE_FIELDS[] = { AB, ABX, ABC, AB, AB, ABX, ABC, ABX, AB, ABC, 42 | ABC, ABC, ABC, ABC, ABC, ABC, ABC, ABC, AB, AB, 43 | AB, ABC, SBX, ABC, ABC, ABC, AC, ABC, ABC, ABC, 44 | AB, ASBX, ASBX, AC, ABC, A, ABX, AB }; 45 | 46 | static char lua_int = 0; 47 | static char lua_size_t = 0; 48 | static char lua_instruction = 0; 49 | 50 | typedef struct lua_code { 51 | char* code; 52 | int old_size; 53 | int decoded_size; 54 | char** decoded; 55 | int* lines; 56 | } lua_code; 57 | 58 | static inline lua_code* lua_code_new(char* code){ 59 | lua_code* lua_c = (lua_code*) malloc(sizeof(lua_code)); 60 | 61 | lua_c->code = code; 62 | lua_c->decoded_size = -1; 63 | 64 | return lua_c; 65 | } 66 | 67 | static inline char lua_code_allocated(lua_code* code){ 68 | return code->decoded_size != -1; 69 | } 70 | 71 | static inline void lua_code_delete(lua_code* code){ 72 | if(lua_code_allocated(code)){ 73 | free(code->decoded); 74 | free(code->lines); 75 | } 76 | 77 | free(code); 78 | } 79 | 80 | static inline void lua_code_allocate(lua_code* code, int size){ 81 | if(lua_code_allocated(code)){ 82 | char** temp_decoded; 83 | int* temp_lines; 84 | 85 | code->old_size = code->decoded_size; 86 | code->decoded_size += size; 87 | 88 | temp_decoded = (char**) calloc((long unsigned int)code->decoded_size * sizeof(char*), 1); 89 | if(!temp_decoded){ 90 | lua_code_delete(code); 91 | die("Could not reallocate: out of memory"); 92 | } 93 | code->decoded = temp_decoded; 94 | temp_decoded = NULL; 95 | 96 | temp_lines = (int*) calloc((long unsigned int)code->decoded_size * sizeof(int), 1); 97 | if(!temp_lines){ 98 | lua_code_delete(code); 99 | die("Could not reallocate: out of memory"); 100 | } 101 | code->lines = temp_lines; 102 | temp_lines = NULL; 103 | 104 | return; 105 | } 106 | code->decoded = (char**) malloc((long unsigned int)size * sizeof(char*)); 107 | code->lines = (int*) calloc((long unsigned int)size * sizeof(int), 1); 108 | code->decoded_size = size; 109 | code->old_size = 0; 110 | } 111 | 112 | static inline void lua_code_add_decoded(lua_code* code, char* decoded, int size, int position){ 113 | code->decoded[position+code->old_size] = strndup(decoded, (size_t) size); 114 | } 115 | 116 | static inline void lua_code_print(lua_code* code){ 117 | register int i; 118 | 119 | if(!code->code || !lua_code_allocated(code) || !code->decoded || !code->lines) 120 | fprintf(stderr, "Tried to print empty code object."); 121 | 122 | printf("%s Opcodes (Size %d): \n", COMMENT, code->decoded_size); 123 | printf("%s Line\tOpcode\t\t%8s\t|\t%8s\t|\t%8s\n", COMMENT, "A", "B", "C"); 124 | printf("%s ----------------------------------------------------------------------------------\n", COMMENT); 125 | 126 | for(i = 0; i < code->decoded_size; i++) 127 | if (code->decoded[i]) printf(" %04d\t%s", code->lines[i], code->decoded[i]); 128 | } 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "heuristics.h" 6 | 7 | static inline char * parse_args(int argc, char** argv){ 8 | register unsigned int i; 9 | 10 | for(i = 1; i < argc; i++){ 11 | if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) 12 | print_usage(0); 13 | else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) 14 | print_version(0); 15 | else 16 | return argv[i]; 17 | } 18 | 19 | die("No input file specified."); 20 | 21 | return NULL; 22 | } 23 | 24 | static inline int check_ending(char* file, const char* ending){ 25 | regex_t regex; 26 | if(regcomp(®ex, ending, 0)) 27 | die("Could not compile regex."); 28 | 29 | return regexec(®ex, file, 0, NULL, 0) == 0; 30 | } 31 | 32 | int main(int argc, char** argv){ 33 | char *file = parse_args(argc, argv); 34 | 35 | if(check_ending(file, LUA)){ 36 | puts("File has 'luac' ending, assuming lua bytecode.\n"); 37 | lua(file); 38 | } 39 | else if(check_ending(file, PY)){ 40 | puts("File has 'pyc' ending, assuming python bytecode.\n"); 41 | py(file); 42 | } 43 | else{ 44 | heuristics(file); 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/py_util.h: -------------------------------------------------------------------------------- 1 | #ifndef PY_UTIL_H 2 | #define PY_UTIL_H 3 | 4 | #define PY "^.*\\.pyc$" 5 | #define PY_SIG "\x16\r\r\n" 6 | #define PY_HEADER_SIZE 12 7 | #define HAVE_ARGUMENT 90 8 | #define HIGHEST_PY_OP 148 9 | 10 | #define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) 11 | 12 | /* All further definitions are taken from CPython, latest revision */ 13 | 14 | const char* PY_OPCODES[258]; 15 | 16 | static void initialize_py_ops() { 17 | PY_OPCODES[ 1] = "POP_TOP"; 18 | PY_OPCODES[ 2] = "ROT_TWO"; 19 | PY_OPCODES[ 3] = "ROT_THREE"; 20 | PY_OPCODES[ 4] = "DUP_TOP"; 21 | PY_OPCODES[ 5] = "DUP_TOP_TWO"; 22 | PY_OPCODES[ 9] = "NOP"; 23 | PY_OPCODES[ 10] = "UNARY_POSITIVE"; 24 | PY_OPCODES[ 11] = "UNARY_NEGATIVE"; 25 | PY_OPCODES[ 12] = "UNARY_NOT"; 26 | PY_OPCODES[ 15] = "UNARY_INVERT"; 27 | PY_OPCODES[ 16] = "BINARY_MATRIX_MULTIPLY"; 28 | PY_OPCODES[ 17] = "INPLACE_MATRIX_MULTIPLY"; 29 | PY_OPCODES[ 19] = "BINARY_POWER"; 30 | PY_OPCODES[ 20] = "BINARY_MULTIPLY"; 31 | PY_OPCODES[ 22] = "BINARY_MODULO"; 32 | PY_OPCODES[ 23] = "BINARY_ADD"; 33 | PY_OPCODES[ 24] = "BINARY_SUBTRACT"; 34 | PY_OPCODES[ 25] = "BINARY_SUBSCR"; 35 | PY_OPCODES[ 26] = "BINARY_FLOOR_DIVIDE"; 36 | PY_OPCODES[ 27] = "BINARY_TRUE_DIVIDE"; 37 | PY_OPCODES[ 28] = "INPLACE_FLOOR_DIVIDE"; 38 | PY_OPCODES[ 29] = "INPLACE_TRUE_DIVIDE"; 39 | PY_OPCODES[ 50] = "GET_AITER"; 40 | PY_OPCODES[ 51] = "GET_ANEXT"; 41 | PY_OPCODES[ 52] = "BEFORE_ASYNC_WITH"; 42 | PY_OPCODES[ 55] = "INPLACE_ADD"; 43 | PY_OPCODES[ 56] = "INPLACE_SUBTRACT"; 44 | PY_OPCODES[ 57] = "INPLACE_MULTIPLY"; 45 | PY_OPCODES[ 59] = "INPLACE_MODULO"; 46 | PY_OPCODES[ 60] = "STORE_SUBSCR"; 47 | PY_OPCODES[ 61] = "DELETE_SUBSCR"; 48 | PY_OPCODES[ 62] = "BINARY_LSHIFT"; 49 | PY_OPCODES[ 63] = "BINARY_RSHIFT"; 50 | PY_OPCODES[ 64] = "BINARY_AND"; 51 | PY_OPCODES[ 65] = "BINARY_XOR"; 52 | PY_OPCODES[ 66] = "BINARY_OR"; 53 | PY_OPCODES[ 67] = "INPLACE_POWER"; 54 | PY_OPCODES[ 68] = "GET_ITER"; 55 | PY_OPCODES[ 69] = "GET_YIELD_FROM_ITER"; 56 | PY_OPCODES[ 70] = "PRINT_EXPR"; 57 | PY_OPCODES[ 71] = "LOAD_BUILD_CLASS"; 58 | PY_OPCODES[ 72] = "YIELD_FROM"; 59 | PY_OPCODES[ 73] = "GET_AWAITABLE"; 60 | PY_OPCODES[ 75] = "INPLACE_LSHIFT"; 61 | PY_OPCODES[ 76] = "INPLACE_RSHIFT"; 62 | PY_OPCODES[ 77] = "INPLACE_AND"; 63 | PY_OPCODES[ 78] = "INPLACE_XOR"; 64 | PY_OPCODES[ 79] = "INPLACE_OR"; 65 | PY_OPCODES[ 80] = "BREAK_LOOP"; 66 | PY_OPCODES[ 81] = "WITH_CLEANUP_START"; 67 | PY_OPCODES[ 82] = "WITH_CLEANUP_FINISH"; 68 | PY_OPCODES[ 83] = "RETURN_VALUE"; 69 | PY_OPCODES[ 84] = "IMPORT_STAR"; 70 | PY_OPCODES[ 86] = "YIELD_VALUE"; 71 | PY_OPCODES[ 87] = "POP_BLOCK"; 72 | PY_OPCODES[ 88] = "END_FINALLY"; 73 | PY_OPCODES[ 89] = "POP_EXCEPT"; 74 | PY_OPCODES[ 90] = "STORE_NAME"; 75 | PY_OPCODES[ 91] = "DELETE_NAME"; 76 | PY_OPCODES[ 92] = "UNPACK_SEQUENCE"; 77 | PY_OPCODES[ 93] = "FOR_ITER"; 78 | PY_OPCODES[ 94] = "UNPACK_EX"; 79 | PY_OPCODES[ 95] = "STORE_ATTR"; 80 | PY_OPCODES[ 96] = "DELETE_ATTR"; 81 | PY_OPCODES[ 97] = "STORE_GLOBAL"; 82 | PY_OPCODES[ 98] = "DELETE_GLOBAL"; 83 | PY_OPCODES[100] = "LOAD_CONST"; 84 | PY_OPCODES[101] = "LOAD_NAME"; 85 | PY_OPCODES[102] = "BUILD_TUPLE"; 86 | PY_OPCODES[103] = "BUILD_LIST"; 87 | PY_OPCODES[104] = "BUILD_SET"; 88 | PY_OPCODES[105] = "BUILD_MAP"; 89 | PY_OPCODES[106] = "LOAD_ATTR"; 90 | PY_OPCODES[107] = "COMPARE_OP"; 91 | PY_OPCODES[108] = "IMPORT_NAME"; 92 | PY_OPCODES[109] = "IMPORT_FROM"; 93 | PY_OPCODES[110] = "JUMP_FORWARD"; 94 | PY_OPCODES[111] = "JUMP_IF_FALSE_OR_POP"; 95 | PY_OPCODES[112] = "JUMP_IF_TRUE_OR_POP"; 96 | PY_OPCODES[113] = "JUMP_ABSOLUTE"; 97 | PY_OPCODES[114] = "POP_JUMP_IF_FALSE"; 98 | PY_OPCODES[115] = "POP_JUMP_IF_TRUE"; 99 | PY_OPCODES[116] = "LOAD_GLOBAL"; 100 | PY_OPCODES[119] = "CONTINUE_LOOP"; 101 | PY_OPCODES[120] = "SETUP_LOOP"; 102 | PY_OPCODES[121] = "SETUP_EXCEPT"; 103 | PY_OPCODES[122] = "SETUP_FINALLY"; 104 | PY_OPCODES[124] = "LOAD_FAST"; 105 | PY_OPCODES[125] = "STORE_FAST"; 106 | PY_OPCODES[126] = "DELETE_FAST"; 107 | PY_OPCODES[130] = "RAISE_VARARGS"; 108 | PY_OPCODES[131] = "CALL_FUNCTION"; 109 | PY_OPCODES[132] = "MAKE_FUNCTION"; 110 | PY_OPCODES[133] = "BUILD_SLICE"; 111 | PY_OPCODES[134] = "MAKE_CLOSURE"; 112 | PY_OPCODES[135] = "LOAD_CLOSURE"; 113 | PY_OPCODES[136] = "LOAD_DEREF"; 114 | PY_OPCODES[137] = "STORE_DEREF"; 115 | PY_OPCODES[138] = "DELETE_DEREF"; 116 | PY_OPCODES[140] = "CALL_FUNCTION_VAR"; 117 | PY_OPCODES[141] = "CALL_FUNCTION_KW"; 118 | PY_OPCODES[142] = "CALL_FUNCTION_VAR_KW"; 119 | PY_OPCODES[143] = "SETUP_WITH"; 120 | PY_OPCODES[144] = "EXTENDED_ARG"; 121 | PY_OPCODES[145] = "LIST_APPEND"; 122 | PY_OPCODES[146] = "SET_ADD"; 123 | PY_OPCODES[147] = "MAP_ADD"; 124 | PY_OPCODES[148] = "LOAD_CLASSDEREF"; 125 | PY_OPCODES[149] = "BUILD_LIST_UNPACK"; 126 | PY_OPCODES[150] = "BUILD_MAP_UNPACK"; 127 | PY_OPCODES[151] = "BUILD_MAP_UNPACK_WITH_CALL"; 128 | PY_OPCODES[152] = "BUILD_TUPLE_UNPACK"; 129 | PY_OPCODES[153] = "BUILD_SET_UNPACK"; 130 | PY_OPCODES[154] = "SETUP_ASYNC_WITH"; 131 | PY_OPCODES[155] = "FORMAT_VALUE"; 132 | PY_OPCODES[257] = "EXCEPT_HANDLER"; 133 | } 134 | 135 | enum PY_TYPES { 136 | TYPE_NULL = '0', 137 | TYPE_NONE = 'N', 138 | TYPE_FALSE = 'F', 139 | TYPE_TRUE = 'T', 140 | TYPE_STOPITER = 'S', 141 | TYPE_ELLIPSIS = '.', 142 | TYPE_INT = 'i', 143 | TYPE_FLOAT = 'f', 144 | TYPE_BINARY_FLOAT = 'g', 145 | TYPE_COMPLEX = 'x', 146 | TYPE_BINARY_COMPLEX = 'y', 147 | TYPE_LONG = 'l', 148 | TYPE_STRING = 's', 149 | TYPE_INTERNED = 't', 150 | TYPE_REF = 'r', 151 | TYPE_TUPLE = '(', 152 | TYPE_LIST = '[', 153 | TYPE_DICT = '{', 154 | TYPE_CODE = 'c', 155 | TYPE_UNICODE = 'u', 156 | TYPE_UNKNOWN = '?', 157 | TYPE_SET = '<', 158 | TYPE_FROZENSET = '>', 159 | FLAG_REF = '\x80', 160 | TYPE_ASCII = 'a', 161 | TYPE_ASCII_INTERNED = 'A', 162 | TYPE_SMALL_TUPLE = ')', 163 | TYPE_SHORT_ASCII = 'z', 164 | TYPE_SHORT_ASCII_INTERNED = 'Z', 165 | }; 166 | #endif 167 | -------------------------------------------------------------------------------- /src/read_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_LUA_H 2 | #define READ_LUA_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "lua_util.h" 9 | #include "util.h" 10 | 11 | static inline char* print_lua_string(const char* prepend, char* stripped, unsigned long offset){ 12 | unsigned long long c; 13 | unsigned long long i; 14 | 15 | stripped = copy(&c, stripped, lua_size_t); 16 | 17 | if(prepend) printf("%s %s ", COMMENT, prepend); 18 | for(i = offset; i < c; i++) putchar(*(stripped+i)); 19 | if(!c) puts("(none)"); 20 | else putchar('\n'); 21 | 22 | stripped += c; 23 | 24 | return stripped; 25 | } 26 | 27 | static inline lua_code* get_lua_opcodes(lua_code* stripped){ 28 | uint32_t ins; 29 | int p, o; 30 | register int i; 31 | unsigned char c; 32 | const int size = 100; 33 | char buffer[size]; 34 | 35 | stripped->code = copy(&p, stripped->code, lua_int); 36 | 37 | if(!p) return stripped; 38 | 39 | lua_code_allocate(stripped, p); 40 | 41 | for(i = 0; i < p; i++){ 42 | ins = 0; 43 | stripped->code = copy(&ins, stripped->code, lua_instruction); 44 | c = (unsigned char) RETRIEVE_LUA_OPCODE(ins); 45 | if(c > 37){ 46 | fprintf(stderr, "Opcode %d does not exist. Corrupted?\n", c); 47 | print_backtrace(); 48 | continue; 49 | } 50 | o = snprintf(buffer, (size_t) size, "%-8s\t", LUA_OPCODE[c]); 51 | 52 | switch(LUA_OPCODE_FIELDS[c]){ 53 | case A: 54 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8s\t|\n", RETRIEVE_LUA_FIELD_A(ins), ""); 55 | break; 56 | case SBX: 57 | o += snprintf(buffer+o, (size_t)(size-o), "%8s\t|\t%8d\n", "", RETRIEVE_LUA_FIELD_SBX(ins)); 58 | break; 59 | case AB: 60 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8d\t|\n", RETRIEVE_LUA_FIELD_A(ins), 61 | RETRIEVE_LUA_FIELD_B(ins)); 62 | break; 63 | case AC: 64 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8s\t|\t%8d\n", RETRIEVE_LUA_FIELD_A(ins), "", 65 | RETRIEVE_LUA_FIELD_C(ins)); 66 | break; 67 | case ASBX: 68 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8d\t\n", RETRIEVE_LUA_FIELD_A(ins), 69 | RETRIEVE_LUA_FIELD_SBX(ins)); 70 | break; 71 | case ABC: 72 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8d\t|\t%8d\n", RETRIEVE_LUA_FIELD_A(ins), 73 | RETRIEVE_LUA_FIELD_B(ins), 74 | RETRIEVE_LUA_FIELD_C(ins)); 75 | break; 76 | case ABX: 77 | o += snprintf(buffer+o, (size_t)(size-o), "%8d\t|\t%8d\n", RETRIEVE_LUA_FIELD_A(ins), 78 | RETRIEVE_LUA_FIELD_BX(ins)); 79 | break; 80 | default: 81 | fprintf(stderr, "Opcode argument encoding wrong: Corrupted?\n"); 82 | } 83 | lua_code_add_decoded(stripped, buffer, o, i); 84 | } 85 | 86 | return stripped; 87 | } 88 | 89 | static inline lua_code* print_lua_constants(lua_code* stripped){ 90 | int p; 91 | int64_t t; 92 | register unsigned int i; 93 | 94 | stripped->code = copy(&p, stripped->code, lua_int); 95 | 96 | printf("%s Constants: (Size %u)\n", COMMENT, p); 97 | printf("%s Line\tType\tValue\n", COMMENT); 98 | printf("%s -------------------\n", COMMENT); 99 | 100 | for(i = 0; i < p; i++){ 101 | printf("%04u\t", i); 102 | switch(*(stripped->code)++){ 103 | case 0: 104 | puts("NIL"); 105 | break; 106 | case 1: 107 | printf("BOOL: %s\n", *(stripped->code)++ ? "TRUE" : "FALSE"); 108 | break; 109 | case 3: 110 | printf("NUMBER: "); 111 | stripped->code = copy(&t, stripped->code, 8); 112 | printf("%lld\n", t); 113 | break; 114 | case 4: 115 | printf("STRING: "); 116 | stripped->code = print_lua_string(NULL, stripped->code, 0); 117 | break; 118 | default: 119 | printf("%i\t", (*(stripped->code--))); 120 | die("UNKNOWN CONSTANT TYPE: CORRUPTED?"); 121 | } 122 | } 123 | 124 | return stripped; 125 | } 126 | 127 | static inline lua_code* get_lua_source_line_positions(lua_code* stripped){ 128 | register int i; 129 | int c, p; 130 | 131 | stripped->code = copy(&c, stripped->code, lua_int); 132 | 133 | if(c == 0) return stripped; 134 | 135 | if(stripped->decoded_size != c) fprintf(stderr, 136 | "INFO: Source Lines List (Size %d) do not map to instructions (Size %d).\n", 137 | c, 138 | stripped->decoded_size); 139 | 140 | for(i = 0; i < c; i++){ 141 | stripped->code = copy(&p, stripped->code, lua_int); 142 | 143 | stripped->lines[i] = p; 144 | } 145 | 146 | return stripped; 147 | } 148 | 149 | static inline lua_code* print_lua_locals(lua_code* stripped){ 150 | register unsigned int i; 151 | unsigned int c, number; 152 | 153 | stripped->code = copy(&c, stripped->code, lua_int); 154 | 155 | if(c == 0) return stripped; 156 | 157 | printf("%s Constant list(size %u): \n", COMMENT, c); 158 | 159 | for(i = 0; i < c; i++){ 160 | stripped->code = print_lua_string("Name:\t", stripped->code, 0); 161 | 162 | stripped->code = copy(&number, stripped->code, lua_int); 163 | 164 | printf("%s Start of scope: %u\t", COMMENT, number); 165 | 166 | stripped->code = copy(&number, stripped->code, lua_int); 167 | 168 | printf("End of scope: %u\n", number); 169 | } 170 | 171 | return stripped; 172 | } 173 | 174 | static inline lua_code* print_lua_upvalues(lua_code* stripped){ 175 | register unsigned int i; 176 | unsigned int c; 177 | 178 | stripped->code = copy(&c, stripped->code, lua_int); 179 | 180 | if(c == 0) return stripped; 181 | 182 | printf("%s Upvalue list (size %u):\n", COMMENT, c); 183 | for(i = 0; i < c; i++) stripped->code = print_lua_string(NULL, stripped->code, 0); 184 | 185 | return stripped; 186 | } 187 | 188 | static inline char* lua_check(const char* file){ 189 | size_t unused; 190 | char* content = NULL; 191 | size_t file_size = 0; 192 | FILE* lua_file = fopen(file, "r"); 193 | 194 | if(!lua_file) die("Could not open file."); 195 | 196 | fseek(lua_file, 0, SEEK_END); 197 | file_size = (size_t) ftell(lua_file); 198 | fseek(lua_file, 0, SEEK_SET); 199 | 200 | if(!file_size || file_size < LUA_HEADER_SIZE) die("File is empty/not big enough to fit lua header."); 201 | 202 | content = (char*) malloc(file_size + 1); 203 | 204 | if(!content) die("Could not allocate enough memory."); 205 | 206 | unused = fread(content, file_size, 1, lua_file); 207 | if(unused != 1) fprintf(stderr, "Reading the file in may have failed.\n"); 208 | fclose(lua_file); 209 | content[file_size] = 0; 210 | 211 | if(starts_with(content, LUA_SIG) == 0) die("Lua header not correct. Signature verification failed."); 212 | 213 | if(content[4] != LUA_VERSION) die("This disassembler only works with Lua 5.1."); 214 | 215 | lua_int = content[7]; 216 | lua_size_t = content[8]; 217 | lua_instruction = content[9]; 218 | 219 | printf("%s Disassembly of file: %s\n", COMMENT, file); 220 | printf("%s Endianness: %s endian\n", COMMENT, content[6] == 0 ? "big" : "little"); 221 | printf("%s Int size: %i\n", COMMENT, lua_int); 222 | printf("%s Size_t size: %i\n", COMMENT, lua_size_t); 223 | printf("%s Instruction size: %i\n", COMMENT, lua_instruction); 224 | printf("%s lua_Number size: %i\n", COMMENT, content[10]); 225 | 226 | if(sizeof(size_t) < lua_size_t) puts("The lua binary size_t datatype is bigger than the size_t of this machine. This might lead to strange behaviour in the source line positions section. Please report any issues on Github(https://github.com/hellerve/decode/issues) or to veit@veitheller.de"); 227 | if(sizeof(unsigned int) < lua_int) puts("The lua binary int datatype is bigger than the int of this machine. This might lead to strange behaviour in the source line positions section. Please report any issues on Github(https://github.com/hellerve/decode/issues) or to veit@veitheller.de"); 228 | 229 | putchar('\n'); 230 | 231 | return content; 232 | } 233 | 234 | static inline lua_code* print_lua_general_info(lua_code* stripped){ 235 | unsigned int p; 236 | 237 | stripped->code = copy(&p, stripped->code, lua_int); 238 | printf("%s Line defined: %u\n", COMMENT, p); 239 | 240 | stripped->code = copy(&p, stripped->code, lua_int); 241 | printf("%s Last Line defined: %u\n", COMMENT, p); 242 | 243 | printf("%s Number of Upvalues: %i\n", COMMENT, *(stripped->code)++); 244 | printf("%s Number of Parameters: %i\n", COMMENT, *(stripped->code)++); 245 | printf("%s Vararg flag: %i\n", COMMENT, *(stripped->code)++); 246 | printf("%s Maximum stack size: %i\n", COMMENT, *(stripped->code)++); 247 | 248 | return stripped; 249 | } 250 | 251 | static inline lua_code* print_lua_function_prototypes(lua_code* stripped); 252 | 253 | static inline lua_code* print_lua_function(lua_code* stripped){ 254 | stripped->code = print_lua_string("Source file name:", stripped->code, 0); 255 | 256 | stripped = print_lua_general_info(stripped); 257 | putchar('\n'); 258 | stripped = get_lua_opcodes(stripped); 259 | stripped = print_lua_constants(stripped); 260 | putchar('\n'); 261 | stripped = print_lua_function_prototypes(stripped); 262 | putchar('\n'); 263 | 264 | if(stripped) stripped = get_lua_source_line_positions(stripped); 265 | 266 | lua_code_print(stripped); 267 | 268 | if(stripped){ 269 | putchar('\n'); 270 | stripped = print_lua_locals(stripped); 271 | putchar('\n'); 272 | stripped = print_lua_upvalues(stripped); 273 | } 274 | 275 | return stripped; 276 | } 277 | 278 | static inline lua_code* print_lua_function_prototypes(lua_code* stripped){ 279 | unsigned int c; 280 | register unsigned int i; 281 | 282 | stripped->code = copy(&c, stripped->code, lua_int); 283 | 284 | printf("%s Function prototypes list(Size %u): {\n", COMMENT, c); 285 | 286 | for(i = 0; i < c; i++) stripped = print_lua_function(stripped); 287 | 288 | printf("%s }\n", COMMENT); 289 | 290 | return stripped; 291 | } 292 | 293 | void lua(char* file){ 294 | char* file_contents = lua_check(file); 295 | lua_code* stripped = lua_code_new(file_contents+LUA_HEADER_SIZE); 296 | 297 | stripped = print_lua_function(stripped); 298 | 299 | puts("; End of binary information."); 300 | 301 | free((char*)file_contents); 302 | lua_code_delete(stripped); 303 | } 304 | 305 | 306 | #endif 307 | -------------------------------------------------------------------------------- /src/read_py.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_PY_H 2 | #define READ_PY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "py_util.h" 9 | #include "util.h" 10 | 11 | static inline char* py_check(const char* file, size_t* file_size){ 12 | char* content = NULL; 13 | char* copy_p = NULL; 14 | time_t timestamp; 15 | int size; 16 | size_t unused; 17 | FILE* py_file = fopen(file, "r"); 18 | 19 | if(!py_file) 20 | die("Could not open file."); 21 | 22 | fseek(py_file, 0, SEEK_END); 23 | *file_size = (size_t) ftell(py_file); 24 | fseek(py_file, 0, SEEK_SET); 25 | 26 | if(!(*file_size) || *file_size < 12) 27 | die("File is empty/not big enough to fit python header."); 28 | 29 | content = (char*) malloc((*file_size) + 1); 30 | 31 | if(!content) 32 | die("Could not allocate enough memory."); 33 | 34 | unused = fread(content, *file_size, 1, py_file); 35 | if(unused != 1) fprintf(stderr, "Reading the file in may have failed.\n"); 36 | fclose(py_file); 37 | content[*file_size] = 0; 38 | 39 | if(starts_with(content, PY_SIG) == 0) 40 | die("Python not right. Only Python3.4 bytecode is supported."); 41 | 42 | copy_p = content+4; 43 | 44 | copy_p = copy(×tamp, copy_p, 4); 45 | copy_p = copy(&size, copy_p, 4); 46 | *file_size = (size_t)size; 47 | 48 | printf("%s Disassembly of file: %s\n", COMMENT, file); 49 | printf("%s Compilation Timestamp: %s", COMMENT, ctime(×tamp)); 50 | printf("%s Size: %i\n", COMMENT, size); 51 | 52 | return content; 53 | } 54 | 55 | static inline char* print_py_opcodes(char* content, size_t len){ 56 | /* All further info is from CPython: Python/marshal.c */ 57 | size_t i; 58 | unsigned char x; 59 | uint16_t arg; 60 | for(i = 35; i <= len; i++) { 61 | x = (unsigned char) content[i]; 62 | printf("[%zu] %s(%d) ", i-35, PY_OPCODES[x], x); 63 | if(HAS_ARG(x)) { 64 | arg = (uint16_t) ((content[i+1]<<8) + content[i+2]); 65 | printf("%d\n", arg); 66 | i += 2; 67 | } 68 | else puts(""); 69 | } 70 | return content; 71 | } 72 | 73 | static inline void py(char* file){ 74 | size_t len; 75 | initialize_py_ops(); 76 | 77 | char* file_contents = py_check(file, &len); 78 | char* stripped = file_contents+PY_HEADER_SIZE; 79 | 80 | putchar('\n'); 81 | stripped = print_py_opcodes(stripped, len); 82 | putchar('\n'); 83 | 84 | puts("; End of binary information."); 85 | 86 | free(file_contents); 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define VERSION "0.1" 10 | #define COMMENT ";" 11 | 12 | static inline void print_usage(int returncode){ 13 | printf("decode, the interpreter disassembler\nVersion %s\n\nUsage: decode \n", VERSION); 14 | exit(returncode); 15 | } 16 | 17 | static inline void print_version(int returncode){ 18 | printf("decode, the interpreter disassembler\nVersion %s\n", VERSION); 19 | exit(returncode); 20 | } 21 | 22 | static inline void die(const char* message){ 23 | fprintf(stderr, "%s\n", message); 24 | exit(1); 25 | } 26 | 27 | static inline int starts_with(const char* a, const char* b){ 28 | if(strncmp(a, b, strlen(b)) == 0) return 1; 29 | return 0; 30 | } 31 | 32 | static inline char* copy(void* dest, char* src, char s){ 33 | if(!memcpy(dest, src, (size_t)s)) 34 | die("Could not copy memory"); 35 | 36 | return src + s; 37 | } 38 | 39 | static inline void print_backtrace() { 40 | void* trace[10]; 41 | int c, i; 42 | char** names; 43 | 44 | c = backtrace(trace, 10); 45 | names = backtrace_symbols(trace, c); 46 | 47 | for(i = 0; i < c; i++) printf("; %s\n", names[i]); 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /test/bigger.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | return 16 3 | 4 | def x(): 5 | return foo(24) 6 | 7 | def bar(): 8 | x = 1 9 | y = 2 10 | z = 3 11 | return x() + foo() 12 | -------------------------------------------------------------------------------- /test/double_local.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/port-zero/decode/d6495f0209b38034053db479c25c961b7658d0fc/test/double_local.test -------------------------------------------------------------------------------- /test/hello.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/port-zero/decode/d6495f0209b38034053db479c25c961b7658d0fc/test/hello.luac -------------------------------------------------------------------------------- /test/helloworld.py: -------------------------------------------------------------------------------- 1 | def hello(): 2 | print("Hello world!") 3 | 4 | hello() 5 | -------------------------------------------------------------------------------- /test/local.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/port-zero/decode/d6495f0209b38034053db479c25c961b7658d0fc/test/local.luac -------------------------------------------------------------------------------- /test/medium.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/port-zero/decode/d6495f0209b38034053db479c25c961b7658d0fc/test/medium.test --------------------------------------------------------------------------------