Giter Site home page Giter Site logo

ljd's Introduction

LuaJIT raw-bytecode decompiler (LJD)

The original name was ljwthgnd as in LuaJIT 'What The Hell is Going On' Decompiler named under the LuaJIT C sources variable naming convention.

WARNING! This code is nor finished, nor tested yet! There is no even slightest warranty that resulting code is even near to the original. Use it at your own risk of the wasted time.

SECOND WARNING! And, BTW, this all is a one huge prototype. Because the "release" version should be written into lua itself. Because it's cool to decompile the decompiler - a great test too!

Requirements

Python 3.0+ from python.org

How to use it

There is no argument parsing right now, so comment out things in the main.py script and launch it as in main.py path/to/file.luac

TODO

There is a lot of work to do, in the order of priority

  1. Logical subexpressions in while statements:

    	while x < (xi and 2 or 3) do
    		print ("Hello crazy world!")
    	end

    Logical subexpressions (the subexpressions used as operands in ariphmetic or comparison operations inside other exrpressions) are currently supported only for ifs. To support them for whiles and repeat untils an expression unwarping logic should be moved at the very beginning. But it won't work without all fixes being done in a loop unwarping logic. So we need to split that and move the fixes before expressions before loops before ifs. That's not that easy...

  2. AST Mutations:

    1. Use the line information (or common sense if there is no line information) to squash similar expressions into a single expression.
  3. Formatting improvements

    1. Use the line information (or common sense) to preserve empty lines and break long statements like in the original code.

      This is mostly done, but only in the "common sense" part.

    2. Use method-style calls and definitions for tables.

  4. Features not supported:

    1. GOTO statement (from lua 5.2). All the required functionality is now in place, but that's rather a low-priority task right now.

    2. Local sub-blocks:

    do
    	...
    end
    These subblocks are not reflected anyhow directly in the bytecode.
    The only way to guess them is to watch local variable scopes, which
    is simple enough in case of non-stripped bytecode and a bit
    harder otherwise.
    

ljd's People

Contributors

nightnord avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ljd's Issues

X-Rebirth 3.0 menu_trading_offers.xpl - assert src is None and assert false is not None

Hi NightNord,

I am having a little trouble decompiling a file from the current 3.0 release of Egosofts X-Rebirth (/ui/addons/ego_detailmonitor/menu_trading_offers.xpl). The decompiling process fails with the following error.

File "ljd-master\ljd\ast\unwarper.py", line 722, in _get_operator
    assert src is None
AssertionError

When I do some debug ouput at this location it tells me that it is looking at a function call.

<ljd.ast.nodes.FunctionCall object at 0x00000000030B9710>.

I have no idea if this is the right thing to do or if it will have any side effects but adding another elseif block with a check for a function call fixes the issue and the process continues.

elif isinstance(src, nodes.FunctionCall ):
    is_true = True

However a little later it runs in another error in the same file which I cannot figure out. I think it is trying to find the end of an expression and fails.

File "ljd-master\ljd\ast\unwarper.py", line 1055, in _search_expression_end
    assert false is not None
AssertionError

I am really stuck at this point. Do you have any idea (regarding any of the two topics)?

Thanks a lot for any input.

Decompiling LuaJit Scripts from Farming Simulator 2015

Hello.

If tried to decompile bytecode files from FS15. But just getting any KeyError. Do you have any suggestion?

Hopefully Blue

Text from Commandpromptscreen:

Traceback (most recent call last):
File "main.py", line 123, in
retval = main()
File "main.py", line 85, in main
ast = ljd.ast.builder.build(prototype)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 28, in build
return _build_function_definition(prototype)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 49, in _build_function_definition
node.statements.contents = _build_function_blocks(state, instructions)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 71, in _build_function_blocks
_establish_warps(state, instructions)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 175, in _establish_warps
block.warp, shift = _build_warp(state, block.last_address, warp)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 198, in _build_warp
return _build_numeric_loop_warp(state, last_addr, last)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 318, in _build_numeric_loop_warp
warp.body = state._warp_in_block(destination)
File "D:\Users\Bluetiger\Downloads\dataS\scripts\ljd-master\ljd\ast\builder.py
", line 22, in _warp_in_block
block = self.block_starts[addr]
KeyError: -31379

a*b => a * b, a/b => a / b, etc...

------------------------------ ljd/lua/writer.py ------------------------------
index dede627..2416648 100644
@@ -287,19 +287,19 @@ class Visitor(traverse.Visitor):
            self._write(" + ")
        elif node.type == nodes.BinaryOperator.T_SUBTRACT:
            self._write(" - ")

        elif node.type == nodes.BinaryOperator.T_DIVISION:
-           self._write("/")
+           self._write(" / ")
        elif node.type == nodes.BinaryOperator.T_MULTIPLY:
-           self._write("*")
+           self._write(" * ")
        elif node.type == nodes.BinaryOperator.T_MOD:
-           self._write("%")
+           self._write(" % ")

        else:
            assert node.type == nodes.BinaryOperator.T_POW
-           self._write("^")
+           self._write(" ^ ")

        if right_parentheses:
            self._write("(")

        self._visit(node.right)
@@ -601,11 +601,11 @@ class Visitor(traverse.Visitor):
    def visit_numeric_loop_warp(self, node):
        self._write("for ")

        self._visit(node.index)

-       self._write("=")
+       self._write(" = ")

        self._visit(node.controls)

The method call's first (table) argument is named slot*

        self.cameraSpot = Vector.new(slot1)
        self.parentSpot = Vector.new(slot1)
        self.parentRotation = Rotation.new(slot1)
        self.rotateRay = Vector.new(slot1)
        self.rotationAdd = Rotation.new(slot1)
        self.matrix = Matrix.new(slot1)

Probably it should be the "Vector", "Rotation" and "Matrix" instead, as per the disassembler:

55     [ 49]   MOV       2             1       ; slot2 = Vector
56     [ 49]   TGETS     1     1       33      ; slot1 = Vector.new
57     [ 49]   CALL      1     2       2       ; slot1 = Vector.new(Vector)
58     [ 49]   TSETS     1     0       29      ; self.rotateRay = slot1
59     [ 50]   GGET      1             30      ; slot1 = _env["Rotation"]
60     [ 50]   MOV       2             1       ; slot2 = Rotation
61     [ 50]   TGETS     1     1       33      ; slot1 = Rotation.new
62     [ 50]   CALL      1     2       2       ; slot1 = Rotation.new(Rotation)
63     [ 50]   TSETS     1     0       28      ; self.rotationAdd = slot1
64     [ 51]   GGET      1             26      ; slot1 = _env["Matrix"]
65     [ 51]   MOV       2             1       ; slot2 = Matrix
66     [ 51]   TGETS     1     1       33      ; slot1 = Matrix.new
67     [ 51]   CALL      1     2       2       ; slot1 = Matrix.new(Matrix)

It's also possible that the issue a bit more broad as MOV'ing slots isn't fully (clearly) supported right now.

ASCII codec error

$ python3 main.py foo.luac
Traceback (most recent call last):
  File "main.py", line 123, in <module>
    retval = main()
  File "main.py", line 77, in main
    header, prototype = ljd.rawdump.parser.parse(file_in)
  File "/Users/agladysh/projects/ljd/ljd/rawdump/parser.py", line 34, in parse
    r = r and _read_prototypes(parser, parser.prototypes)
  File "/Users/agladysh/projects/ljd/ljd/rawdump/parser.py", line 72, in _read_prototypes
    if not ljd.rawdump.prototype.read(state, prototype):
  File "/Users/agladysh/projects/ljd/ljd/rawdump/prototype.py", line 52, in read
    r = r and _read_constants(parser, prototype)
  File "/Users/agladysh/projects/ljd/ljd/rawdump/prototype.py", line 140, in _read_constants
    return ljd.rawdump.constants.read(parser, prototype.constants)
  File "/Users/agladysh/projects/ljd/ljd/rawdump/constants.py", line 31, in read
    r = r and _read_complex_constants(parser, constants.complex_constants)
  File "/Users/agladysh/projects/ljd/ljd/rawdump/constants.py", line 59, in _read_complex_constants
    complex_constants.append(string.decode("ascii"))
UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 22: ordinal not in range(128)

Unfortunately I can not share foo.luac, but it is a valid LuaJIT 2.0.2 bytecode. Is there another way I can help you to debug this issue?

экранирование кавычек

не хочу городить пулл-реквест, покажу тут. патч для ljd/lua/writer.py

@@ -682,6 +682,7 @@ class Visitor(traverse.Visitor):
            text = node.value

            text = text.replace("\\", "\\\\")
            text = text.replace("\t", "\\t")
            text = text.replace("\n", "\\n")
+           text = text.replace("\"", "\\\"")

            self._write('"' + text + '"')

Function arguments are not counted as local usage

function drawTableCells(myTable, tableelement, firstRow, lastRow, curRow)
...
                if firstRow <= tableelement.numFixedRows then
                        local firstRow = tableelement.numFixedRows + 1
                end

The firstRow assignment shouldn't be marked local.

Complex table constructors are dissected into several assignments if the table variable is a local

local L = {}

L["Smart Defense"] = ffi.string(C.GetLocalizedText(1001, 5600, "Smart Defense"))
L["Activates all drones' passive abilities"] = ffi.string(C.GetLocalizedText(20209, 1002, "Activates all drones' passive abilities"))
local private = {
        hidden = true,
        firstrun = false,
        skip = false,
        currentbuttonnum = 0
}

private.buttonList = {}

It should look like the following instead

local L = {
        ["Smart Defense"] = ffi.string(C.GetLocalizedText(1001, 5600, "Smart Defense"))
        ["Activates all drones' passive abilities"] = ffi.string(C.GetLocalizedText(20209, 1002, "Activates all drones' passive abilities"))
}

local private = {
        hidden = true,
        firstrun = false,
        skip = false,
        currentbuttonnum = 0,
        buttonList = {}
}

IndexError: list index out of range

@zzwlpx改过的版本反编译
PS F:\ctf\njupt\ljd-master> py -3 .\main.py .\rua_decode.lua
Traceback (most recent call last):
File ".\main.py", line 126, in
retval = main()
File ".\main.py", line 86, in main
ast = ljd.ast.builder.build(prototype)
File "F:\ctf\njupt\ljd-master\ljd\ast\builder.py", line 30, in build
return _build_function_definition(prototype)
File "F:\ctf\njupt\ljd-master\ljd\ast\builder.py", line 52, in _build_function_definition
node.statements.contents = _build_function_blocks(state, instructions)
File "F:\ctf\njupt\ljd-master\ljd\ast\builder.py", line 74, in _build_function_blocks
_establish_warps(state, instructions)
File "F:\ctf\njupt\ljd-master\ljd\ast\builder.py", line 179, in _establish_warps
block.warp, shift = _build_warp(state, block.last_address, warp)
File "F:\ctf\njupt\ljd-master\ljd\ast\builder.py", line 192, in _build_warp
last = instructions[-1]
IndexError: list index out of range

打了一下log:block的first_address和last_address分别是1和-32756,于是传进去的instructions是空的
请问是什么问题呢_(:з」∠)_该怎么改?
样本:https://pan.baidu.com/s/16bvmqNFnFUqVDy78hH5W5A

AttributeError: 'FunctionCall' object has no attribute 'expressions'

The call stack:

  File "main.py"
    ljd.ast.unwarper.unwarp(ast)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 35, in unwarp
    _run_step(_unwarp_expressions, node)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 43, in _run_step
    statements.contents = step(statements.contents, **kargs)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 103, in _unwarp_expressions
    expressions = _find_expressions(start, body, end)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 305, in _find_expressions
    slot, slot_type = _get_simple_local_assignment_slot(start, body, end)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 463, in _get_simple_local_assignment_slot
    true, _false, body = _get_terminators(body)
  File "/Users/yi/Developer/source/ljd/ljd/ast/unwarper.py", line 861, in _get_terminators
    src = prev.contents[0].expressions.contents[0]
AttributeError: 'FunctionCall' object has no attribute 'expressions'

And the code:

print(type(prev.contents[0]))
src = prev.contents[0].expressions.contents[0]

mostly the type is <class 'ljd.ast.nodes.Assignment'>, which is right.
But when the type is <class 'ljd.ast.nodes.FunctionCall'>, the error occuse

Sometimes table constructors may contain references to the table itself

An example from widget_fullscreen.xpl:

    tableelement.scrollBar = {
        sliderState = {
            mouseOver = false,
            mouseClick = false
        },
        element = clone(private.master.table.scrollBar, "table" .. tableindex .. "scrollbar"),
        sliderElement = getElement("slider", tableelement.scrollBar.element)
    }

This causing an error as there is no "tableelement.scrollBar.element" at the access time.

Output method calls using the "table:method" notation

Things like

vector = Vector.new(Vector)

or

local a, b, c = table.foo(table, 3)

should actually look like

vector = Vector:new()
local a, b, c = table:foo(3)

It should be fairly easy to detect - if the function is a table element and the first argument is the table itself, then that's a method call.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.