MulanRevive / bounty

重金悬赏,重现被说成 “Python 套壳”、“换皮”的木兰编程语言的所有功能
20 stars 0 forks source link

【悬赏】建立构建工具链 #7

Open nobodxbodon opened 4 years ago

nobodxbodon commented 4 years ago

从源码生成可执行文件,使用原方案的 pyinstaller 或其他类似技术。 不需一开始就基于逆向工程,可以先新建一个最简原型(比如repl),进行技术验证。

nobodxbodon commented 4 years ago

用逆向工程完成了构建,但测试未能完全通过。 详见:

nobodxbodon commented 4 years ago

开始研究为何测试未过。先用--dump-python看对应python代码:

>..\原始资料\可执行文件\ulang-0.2.2.exe --dump-python range.mulan
import sys
from math import *
ARGV = sys.argv[1:]
for n in range(0, 2 + 1):
  print(n)
for n in range(1, 4):
  print(n)
for n in range((-1), 4 + 1, 2):
  print(n)

>__main__.exe --dump-python range.mulan
import sys
from math import *
ARGV = sys.argv[1:]
for n in range(0, 2 + 1):
  print(n)
for n in range(1, 4):
  print(n)
for n in range(len(1), 4 + 1, 2):
  print(n)

可见-1错变成了len(1)。

ast信息比较长,可用--dump-ast查看。对应差异部分(需要好的diff工具,因为是一整行,现在靠硬看对比)如下:

UnaryOp(op=USub(), operand=Num(n=1, lineno=8, col_offset=20), lineno=8, col_offset=19)

Call(func=Name(id='len', ctx=Load(), lineno=8, col_offset=19)

奇怪的多余return去掉后,此测试用例通过。

nobodxbodon commented 4 years ago

继续:

>__main__.exe --dump-python if_elif.mulan
import sys
from math import *
ARGV = sys.argv[1:]
if False:
  if True:
    pass

>..\原始资料\可执行文件\ulang-0.2.2.exe --dump-python if_elif.mulan
import sys
from math import *
ARGV = sys.argv[1:]
if False:
  if True:
    pass
elif True:
  print(1)>__main__.exe --dump-python if_elif.mulan
import sys
from math import *
ARGV = sys.argv[1:]
if False:
  if True:
    pass

D:\study\mulan\bounty\测试代码>..\原始资料\可执行文件\ulang-0.2.2.exe --dump-python if_elif.mulan
import sys
from math import *
ARGV = sys.argv[1:]
if False:
  if True:
    pass
elif True:
  print(1)

暂且不管为何要多个if true(优化问题?)。好像elif语句完全被跳过了。再比ast:

orelse=[], lineno=1, col_offset=1

orelse=[If(test=NameConstant(value=True, lineno=3,
col_offset=8), body=[Expr(value=Call(func=Name(id='print', ctx=Load(), lineno=4, col_offset=5), args=[Num(n=1, lineno=4, col_offset=11)], keywords=[], lineno=4, col_offset=5), lineno=4, col_offset=5)], orelse=[], lineno=3, col_offset=3)], lineno=1, col_offset=1

orelse部分哪去了??貌似这段中,orelse的处理有问题:

    @pg_.production('if_stmt : IF expr block elif_stmt')
    @pg_.production('if_stmt : IF expr block ELSE block')
    @pg_.production('elif_stmt : ')
    @pg_.production('elif_stmt : ELIF expr block elif_stmt')
    @pg_.production('elif_stmt : ELIF expr block ELSE block')
    def if_stmt(self, p):
        if len(p) == 0:
            return []
        elif len(p) == 5:
            return ast.If(test=(p[1]),
              body=(p[2]),
              orelse=(p[(-1)]),
              lineno=(self.getlineno(p)),
              col_offset=(self.getcolno(p)))
        else:
            if not isinstance(p[(-1)], list):
                p[-1] = []

        return ast.If(test=(p[1]),
          body=(p[2]),
          orelse=(p[(-1)]),
          lineno=(self.getlineno(p)),
          col_offset=(self.getcolno(p)))

3月18日更:貌似修复了

nobodxbodon commented 4 years ago

先看个貌似简单些的:

>__main__.exe --dump-python for_in.mulan
import sys
from math import *
ARGV = sys.argv[1:]
sum = 0
for n in range(5):
  sum **= n
print(sum)

>..\原始资料\可执行文件\ulang-0.2.2.exe --dump-python for_in.mulan
import sys
from math import *
ARGV = sys.argv[1:]
sum = 0
for n in range(5):
  sum += n
print(sum)

+=处理错误的样子。果然ast认错了:

op=Add()

op=Pow()

这个错误难道是反编译导致的吗?看起来倒是很“人造”。hmm希望所有错误都是这么“简单”。

nm 刚反应过来,这个绝对不可能是反编译的锅吧!哪个反编译能”智能“到把^改成**的??

nobodxbodon commented 4 years ago

下面这个lambda就比较巧了:

>__main__.exe --dump-ast lambda.mulan
> 路径略去\ulang\parser\core.py(845)bin_expr()
(Pdb)

直接定位到问题所在:

        else:
            breakpoint()
        return ast.BinOp((p[0]),

为何会跑到bp呢?竟然又是个低级错误。难道真是反编译的锅?

nobodxbodon commented 4 years ago

type 错误也是神奇。比较 ast 后,发现是type_body中只有第一块的内容。结果定位到这个缩进错误

nobodxbodon commented 4 years ago

接下去,打算进行实现的摸索。首先完成一个最小的“交互环境”原型,使用最简单的 rply 规则,并生成 exe。