忘记密码?

一键登录

草根吧

【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译

查看: 468|回复: 8

【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译

[复制链接]

76

主题

153

热度

178

贡献

中级草根

Rank: 3Rank: 3

发表于 2018-2-8 11:09:31 | 显示全部楼层 |阅读模式
风格插件简介

资源来源::应用中心

适用版本::discuzX3.2 discuzX3.1 discuzX3.0 微社区应用 discuzF1.0 discuzX3.3 discuzL1.0 discuzX3.4 

资源类型:功能插件

应用中心地址::https://www.52pojie.cn/thread-696901-1-1.html

免责声明:本源码来源于网络,仅供学习交流之用,所有权归属原创作者所有,所有下载者表示默认接受并同意签订草根吧免责声明协议,草根吧仅提供交流学习平台,请下载24小时内删除,切勿用于商业用途。如有侵权,请联系我们删除。

本帖最后由吾爱开源 Ganlv 分享于 2018-2-8 04:56 编辑


上一篇文章 :【原创】PHP加密中的“VMProtect”——魔方加密反编译分析过程 https://www.caogen8.co/t-32248-1-1.html
楼主并没有学过编译原理、数据结构、汇编语言等等的专业课程,本文纯属自己想出来的东西,并不一定是最优的解决方案,欢迎大家回帖交流
上回书说道,我们已经分析出来了代码的一些功能了,也有部分半自动化的反编译成果了。
总而言之,我们的目标是可行的,我们可以继续做下去。
自动反汇编
首先我们根据上回的分析手动整理出了一套指令集,整理指令集这个过程谁也帮不了你。我用了 4 天时间开源了这个加密方式,想想编写这套加密方式的人说不定可能要花上一个多月呢,想想 4 天也不算太多哈。
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 105855qyzny040t0kriti0
看看左侧那一堆 .1 .2 .3 你就知道我尝试了多少种方法来编写这堆东西。
反汇编器分析
可以参考一下附件的文件,我这里只讲一部分,其他请自行分析。
这个函数是我统一用来记录反编译之后结果的。注意 $eip - 6,上回分析过,1 字节秘钥,4 字节指令,1 字节指令长度,指令长度都是 6,同时我们要以指令开始处作为引用的基准,所以统一减去 6。
  1. protected function dasmLine($eip, $asm, $args = [])
  2. {
  3.     $this->asm[$eip - 6] = [
  4.         'asm' => $asm,
  5.         'args' => $args,
  6.     ];
  7. }
复制代码
由于这套指令集大部分都是无操作数的指令,部分是以下 1 字节为立即数的指令,部分是以下 2 字节为立即数的指令,另外还有少量其他类型立即数的指令,我这里使用了 php 的魔术方法 __call 就是对部分函数统一处理
  1. public function __call($name, $arguments)
  2. {
  3.     if (in_array($name, ['func7', 'func8', 'func10', 'func20', 'func33', 'func35', 'func41', 'func44', 'func56'])) {
  4.         // 1 字节立即数
  5.         $this->dasmLine($arguments[0], $this->asmMap[$name], [$this->getInt($arguments[0], 1)]);
  6.         return 1;
  7.     } elseif (in_array($name, ['func1', 'func16'])) {
  8.         // 2 字节立即数
  9.         $this->dasmLine($arguments[0], $this->asmMap[$name], [$this->getInt($arguments[0], 2)]);
  10.         return 2;
  11.     } elseif (method_exists($this, '_' . $name)) {
  12.         // 特殊指令
  13.         $result = $this->{'_' . $name}($arguments[0], $this->asmMap[$name]);
  14.         $this->dasmLine($arguments[0], $this->asmMap[$name], $result[1]);
  15.         return $result[0];
  16.     } elseif (isset($this->asmMap[$name])) {
  17.         // 无操作数指令
  18.         $this->dasmLine($arguments[0], $this->asmMap[$name]);
  19.         return 0;
  20.     } else {
  21.         throw new \Exception('Call undefined function ' . $name);
  22.     }
  23. }
复制代码
然后就是一些通用的函数了,注意这个 getFunc 就是获取函数名(或者说获取下一条指令)的函数,就是把原本文件的算法抄过来了,然后加上函数名映射。
  1. protected function getFunc($eip)
  2. {
  3.     $key = $this->getMemory($eip, 1);
  4.     $func = $this->getMemory($eip + 1, 2) . $this->getMemory($eip + 4, 2);
  5.     $func = str_repeat($key, 4) ^ $func;
  6.     $func = base64_decode('zb+8') . $func;
  7.     if (isset($this->fnm[$func])) { // function name map
  8.         $func = $this->fnm[$func];
  9.     } else {
  10.         throw new \Exception('Function not exists: $eip=' . $eip . ' func=' . $func);
  11.     }
  12.     if (ord($key ^ $this->getMemory($eip + 3, 1)) != 6) {
  13.         throw new \Exception('Instruction length is not 6');
  14.     }
  15.     return $func;
  16. }
复制代码
其他就是对每一个特殊指令进行单独操作,这个过程也很漫长。
然后我们就会得到反汇编的文件。
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110006csgbgbg9gugg4gg4
为了方便阅读,我有写了一个输出为文本文件的函数,我们之后分析起来比较方便。
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110028bndqn1dnuwqkkluk
然后想了想,我又加了一个功能,就是跳转列表,这个功能在我们分析的过程中还是非常重要的,因为程序不可能都是线性的,条件分支会给反编译带来麻烦,不规则的跳转更麻烦,我们之后要想办法消除这些跳转。
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110049oiki8tib8bfz3nfa
你要是自己实现反编译了,你就知道为什么不推荐使用 goto 语句了。
至此,我们的反编译器实现完成了。我们只用到了那个导出的 bin,完全脱离了原本的虚拟机。
反编译之后的指令,已经没有 1/2/4/6/12 位立即数的说法了,数字已经完全解码出来了,不用再考虑位数的问题了,可以说把指令集简化了。
代码分块
之前说到了跳转很烦人,我们要想办法消除这些跳转,就是代码分块。
IDA 也有这个功能,这个功能好像还非常强大,只不过我还没想过开源过什么东西,还没学 IDA。
本来我是没有分块这一步骤的,直接反编译,但是后来发现跟踪跳转太麻烦了,所以就单独出来,先分块,然后再反编译。
分块的基础就是根据跳转,这套指令集只有两个跳转,jmp 和 jnz,这两个一个是 if 语句,一个是 if 的 stmts 块结束到 else 块结束出的跳转,一个是跳出 while 的条件,一个是继续循环的跳回指令。
这里说的 stmts 与 else
  1. if (cond)
  2.     stmts-block
  3. else
  4.     else-block
复制代码
有两种方法,一种是顺序搜索,另一种是跟踪搜索。
最开始我就是想的顺序搜索,遇到 jnz 和 jmp 都不跳转,只是记录他们的位置和相关信息,之后统一处理,顺序分析代码,之后再区分 stmts 块和 else 块。后来发现代码中有一些难点不太好实现。比如两个位置同时跳往同一处,到这里时,我需要同时组装两个 if 块,有时候两个 if 的代码块并不容易区分。
后来我就改用跟踪搜索了,先说 jnz,遇到 jnz 的话,代码分两支执行,遇到 jnz 就继续分,遇到 jmp 就直接跳转,直到两个代码会合(或者往回跳转),这样可以把原来混乱的 jnz 与 jmp 统一。
说到会合,说起来简单,做起来就要一定技巧了,这里又不能分成两个线程去做,怎么办呢?
简而言之就是,分为两个指令指针,一个指向正常执行的下一条指令,一个指向跳转之后的指令,然后总让小的指针往后执行,直到跳转指令或者相遇。
  1. jnz a
  2. ......   <---- 指针 1
  3. jmp b
  4. a:
  5. ......   <---- 指针 2
  6. jmp c
  7. b:
  8. ......
  9. jmp d
  10. c:
  11. ......
  12. d:
  13. ......
复制代码
  1. jnz a
  2. ......
  3. jmp b
  4. a:
  5. ......   <---- 指针 2
  6. jmp c
  7. b:
  8. ......   <---- 指针 1
  9. jmp d
  10. c:
  11. ......
  12. d:
  13. ......
复制代码
  1. jnz a
  2. ......
  3. jmp b
  4. a:
  5. ......
  6. jmp c
  7. b:
  8. ......   <---- 指针 1
  9. jmp d
  10. c:
  11. ......   <---- 指针 2
  12. d:
  13. ......
复制代码
  1. jnz a
  2. ......
  3. jmp b
  4. a:
  5. ......
  6. jmp c
  7. b:
  8. ......
  9. jmp d
  10. c:
  11. ......
  12. d:
  13. ......   <---- 指针 1 & 指针 2
复制代码
这就是大概的过程,我的代码实现中使用了 JumpException 这个东西,就是为了遇到跳转指令,直接停止指针的继续移动,重新判断移动哪个指针。
关键代码
  1. /**
  2. * 条件分支语句
  3. * @param $jump_pointer
  4. * @param $next_pointer
  5. * home.php?mod=space&uid=155549 mixed
  6. * @throws \Exception
  7. */
  8. protected function _jnz($jump_pointer, $next_pointer)
  9. {
  10.     if ($jump_pointer < $next_pointer) {
  11.         throw new \Exception('jump pointer < next pointer');
  12.     }

  13.     // 备份 $asmTree
  14.     $asmTreeElse = $asmTreeStmts = $asmTree = $this->asmTree;
  15.     $asmTreePointer = count($this->asmTree);

  16.     // 并分别走 stmts 块和 else 块
  17.     ++$this->jnzStack;
  18.     while ($jump_pointer != $next_pointer) {
  19.         if (($jump_pointer > $next_pointer && $next_pointer > 0) || $jump_pointer < 0) {
  20.             $this->asmTree = $asmTreeElse;
  21.             try {
  22.                 $next_pointer = $this->dissect($next_pointer, $jump_pointer);
  23.             } catch (JumpException $exception) {
  24.                 $next_pointer = $exception->jump_pointer;
  25.             }
  26.             $asmTreeElse = $this->asmTree;
  27.         } else {
  28.             $this->asmTree = $asmTreeStmts;
  29.             try {
  30.                 $jump_pointer = $this->dissect($jump_pointer, $next_pointer);
  31.             } catch (JumpException $exception) {
  32.                 $jump_pointer = $exception->jump_pointer;
  33.             }
  34.             $asmTreeStmts = $this->asmTree;
  35.         }
  36.     }
  37.     --$this->jnzStack;

  38.     // 检测循环
  39.     $asmTreeStmtsLastOne = $asmTreeStmts[count($asmTreeStmts) - 1];
  40.     $loop_begin = false;
  41.     if ($asmTreeStmtsLastOne['asm'] == 'loop_end') {
  42.         $loop_begin = $asmTreeStmtsLastOne['args']['begin'];
  43.         array_pop($asmTreeStmts);
  44.         $asmTreeElse[] = [
  45.             'asm' => 'iter_break',
  46.             'args' => [],
  47.         ];
  48.     }

  49.     // 恢复 $asmTree
  50.     $this->asmTree = $asmTree;

  51.     // 构造 if 指令
  52.     $this->asmTree[] = [
  53.         'asm' => 'if               [esp]',
  54.         'args' => [
  55.             'stmts' => array_slice($asmTreeStmts, $asmTreePointer),
  56.             'else' => array_slice($asmTreeElse, $asmTreePointer),
  57.         ],
  58.     ];

  59.     // 构造 loop 指令
  60.     if ($loop_begin !== false) {
  61.         $loop_stmts = array_slice($this->asmTree, $loop_begin);
  62.         $this->asmTree = array_slice($this->asmTree, 0, $loop_begin);
  63.         $this->asmTree[] = [
  64.             'asm' => 'loop',
  65.             'args' => [
  66.                 'stmts' => $loop_stmts,
  67.             ],
  68.         ];
  69.     }

  70.     return $next_pointer;
  71. }
复制代码
代码中对循环也做出了判断,如果指针往已经运行过的地方跳转,就是循环了,循环。
同样,我们还是美观地输出一下我们的成果
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110229mfnfa5tdfbbntkno
这回,我们的代码已经不需要指令指针这个东西了,顺序执行即可。
反编译
这一步,我们需要把已经分块的代码,根据每一个操作指令对栈做出的操作,推算出原本的代码是什么样的。
线性代码
  1. add              esp, 16
  2. push             (null)
  3. db               [esp], 'is_admin'
  4. call (0)         [esp]
  5. not              [esp]
  6. if               [esp]
  7.     pop
  8. else
  9.     pop
  10.     push             (null)
  11.     link             [esp], [ebp+1]
  12.     push             (null)
  13.     db               [esp], 'Grace'
  14.     ......
复制代码
add esp,16 申请局部变量,我们要在程序中生成 16 个局部变量的变量名。
  1. /**
  2. * 分配局部变量
  3. * @param int $count
  4. */
  5. protected function _add($count)
  6. {
  7.     for ($i = 0; $i < $count; ++$i) {
  8.         $this->v[] = new Variable('v' . $i);
  9.     }
  10. }
复制代码
push (null) 这里向栈中压入了一个 null,我们向语法树种压入一个 null 即可
  1. protected function _push()
  2. {
  3.     ++$this->astp;
  4.     $this->ast[$this->astp] = new ConstFetch(new Name('null'));
  5. }
复制代码
为什么要向语法树中压入 null,而不是向一个栈中压入?
因为我们是解释程序代码,反编译,我们分析指令的用途,把指令对栈的操作,转换为构造语法树的操作。所以不能像栈中压入,虽然我们构造语法树的方式也是用压栈、出栈的方式。
db 指令,就直接写入就行了。注意,是构造语法树相应的数据类型的节点,而不是直接输入数据。
  1. /**
  2. * 读取数据
  3. * @param string|int $data
  4. * @throws \Exception
  5. */
  6. protected function _db($data)
  7. {
  8.     if (is_string($data)) {
  9.         $this->ast[$this->astp] = new String_($data);
  10.     } elseif (is_numeric($data)) {
  11.         $this->ast[$this->astp] = new LNumber($data);
  12.     } elseif (is_array($data)) {
  13.         $this->ast[$this->astp] = new Array_($data);
  14.     } else {
  15.         throw new \Exception('Move invalid data.');
  16.     }
  17. }
复制代码
call 那就用“FuncCall 当前位置的表达式”代替当前位置原本的表达式。
  1. protected function _call($argCount)
  2. {
  3.     $args = [];
  4.     for ($i = $argCount - 1; $i >= 0; --$i) {
  5.         $args[] = new \PhpParser\Node\Arg($this->ast[$this->astp - $i]);
  6.     }
  7.     $this->ast[$this->astp - $argCount] = new FuncCall(
  8.         new Name($this->ast[$this->astp - $argCount]->value),
  9.         $args
  10.     );
  11. }
复制代码
not 就是在外面再套一层 Not
  1. protected function _not()
  2. {
  3.     $this->ast[$this->astp] = new BooleanNot($this->ast[$this->astp]);
  4. }
复制代码
现在你应该已经基本了解如何根据指令堆栈的操作来构造语法树了。接下来我们来分析一下条件分支结构。
  1. if               [esp]
  2.     pop
  3. else
  4.     pop
复制代码
对当前位置进行判断,很简单
  1. $this->ast[$this->astp] = new If_($this->ast[$this->astp]);
复制代码
请注意,每句后面都跟着 pop,意思就是我刚才进行判断的表达式这个栈,我完全不要了,这个表达式是一次性使用的。
link [esp], [ebp+1]: link 指令是我自己起的名字,在 php 中就是 =&,设置引用,我们不用设置这个引用,我们只需要把变量的名填上就行了过去就行了。
  1. /**
  2. * 引用
  3. * @param int $offset link [esp], [ebp+{$offset}] 中的 $offset
  4. */
  5. protected function _link($offset)
  6. {
  7.     $this->ast[$this->astp] = $this->v[$offset - 1];
  8. }
复制代码
这里的 v 就是一个储存 Variable 对象的变量。ebp 的含义大家都知道吧,ebp+1 就是第 1 个局部变量,记为 $v0(你愿意记成 $v1 我也没有意见)。
[ebp] = 0, [ebp-1] = -1, [ebp-2] 为输入变量个数,这个在最开始定义过了
同理,你可以完成所有的线性代码了。
条件分支结构
对全篇所有的 jnz 分析,我发现 4 类
  • “逻辑或”短路
  • 三元运算符
  • if 语句
  • 另一种三元运算符的写法
这几种形式主要看是不是 stmts 为空,看看是不是紧跟着一个 pop,看 if 跳出之后栈是否平衡。
三元运算符
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110250p5gw99fc9jgypgyj
逻辑或短路
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110619p4jj1niorws1rvnv
这里讲一部分代码
  1. // 普通 if 语句
  2. $this->_pop();
  3. $cond = $this->ast[$this->astp];
  4. // 备份 AST
  5. $ast = $this->ast;
  6. $astp = $this->astp;
  7. $astbp = $this->astbp;
  8. $stackMap = $this->stackMap;
  9. // 解析 stmts 块
  10. $this->decompile(array_slice($item['args']['stmts'], 1));
  11. $stmts = array_slice($this->ast, $astp + 1, $this->astp - $astp);
  12. // 恢复 AST
  13. $this->ast = $ast;
  14. $this->astp = $astp;
  15. $this->astbp = $astbp;
  16. $this->stackMap = $stackMap;
  17. // 解析 else 块
  18. $this->decompile(array_slice($item['args']['else'], 1));
  19. $else = array_slice($this->ast, $astp + 1, $this->astp - $astp);
  20. // 如果栈差1、只有一条表达式,就换成三元运算符
  21. $is_ternary = ($this->astp - $this->astbp == 1 && count($stmts) == 1 && count($else) == 1
  22.     && $stmts[0] instanceof Expr && $else[0] instanceof Expr);
  23. $this->ast = $ast;
  24. $this->astp = $astp;
  25. $this->astbp = $astbp;
  26. $this->stackMap = $stackMap;
  27. // 构造 AST
  28. if ($is_ternary) {
  29.     $this->ast[$this->astp] = new Ternary($cond, $stmts[0], $else[0]);
  30. } else {
  31.     $this->ast[$this->astp] = new If_($cond);
  32.     if ($stmts) {
  33.         $this->ast[$this->astp]->stmts = $stmts;
  34.     }
  35.     if ($else) {
  36.         $this->ast[$this->astp]->else = new Else_($else);
  37.     }
  38. }
复制代码
循环结构
循环其实并不麻烦,因为虚拟机实现方法为,while(true) 死循环 + if (循环指针结束) break; 的方式,这个直接把 loop 的代码放进 while(true) {} 中即可,这里并不详细叙述了。
看看成果吧(似乎还有一些错误)
  1. function mee_curl_get_contents()
  2. {
  3.     $v0 = !(1 > func_num_args()) ?: null;
  4.     $v1 = curl_init();
  5.     curl_setopt($v1, CURLOPT_URL, $v0);
  6.     curl_setopt($v1, CURLOPT_RETURNTRANSFER, 1);
  7.     curl_setopt($v1, CURLOPT_TIMEOUT, 60);
  8.     $v2 = curl_exec($v1);
  9.     curl_close($v1);
  10.     return $v2;
  11. }
  12. function GetUrlToDomain()
  13. {
  14.     $v0 = !(1 > func_num_args()) ?: null;
  15.     $v1 = !(2 > func_num_args()) ?: null;
  16.     $v2 = get_option('_nice_domain_' . $v0);
  17.     if (!$v2) {
  18.         $v3 = '';
  19.         $v4 = mee_curl_get_contents($v1 . '?do=get_weiba');
  20.         $v5 = json_decode($v4, (bool) 1);
  21.         $v6 = explode('.', $v0);
  22.         $v7 = count($v6) - 1;
  23.         !($v6[$v7] == 'cn') ? $v3 = $v6[$v7 - 1] . '.' . $v6[$v7] : (!in_array($v6[$v7 - 1], $v5) ? $v3 = $v6[$v7 - 1] . '.' . $v6[$v7] : ($v3 = $v6[$v7 - 2] . '.' . $v6[$v7 - 1] . '.' . $v6[$v7]));
  24.         update_option('_nice_domain_' . $v0, $v3);
  25.         return $v3;
  26.     } else {
  27.         return $v2;
  28.     }
  29. }
  30. function get_links_category()
  31. {
  32.     $v0 = get_terms('link_category');
  33.     null;
  34.     $v1 = $v1 . '<div class="show-links-id"><p>相关的链接分类ID:</p><ul>';
  35.     reset($v0);
  36.     while (true) {
  37.         if (!(key($v0) === null || key($v0) === false)) {
  38.             $v2 = key($v0);
  39.             $v3 = current($v0);
  40.             $v1 = $v1 . ('<li>' . $v3->{'name'} . '(' . $v3->{'term_id'} . ')</li>');
  41.             next($v3);
  42.         } else {
  43.             break;
  44.         }
  45.     }
  46.     $v1 = $v1 . '</ul></div>';
  47.     return $v1;
  48. }
复制代码
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110721jm8wigof1ow44na4
代码基本上人就可以读了,再回头看看原本的乱码,很有成就感嘛。
附录
想要代码的话我可以发,其实我的这个代码也没有什么大用,只是专门针对这个文件的,对其他文件要重新修改。其实你想直接读我的代码也不一定能看懂,还不如弄懂原理自己写呢。
想要代码的可以免费评个分,毕竟好歹我研究了四天呢。
本文应该不会继续更新了,其实还差一步代码整理,这个就是体力活了。比如常量折叠、否定否定等于肯定、if 语句 else 和 stmts 互换等等。研究的差不多了,也就不太想做了。
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) zip mfenc-decompiler.zip (166.16 KB, 下载次数: 9, 售价: 1 贡献)
草根吧 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 魔方,解密,自动化,平台,编程 会员分享(<FONT color=#ff0000>加贡献</FONT>) 110804x55luxshuk99dlux

帖子地址: 

17

主题

492

热度

117

贡献

终身赞助ViP

Rank: 7Rank: 7Rank: 7

发表于 2018-3-24 01:46:59 | 显示全部楼层
DZ建站资源有求必应
仰望高手

0

主题

14

热度

7

贡献

步入草根

Rank: 1

发表于 2018-4-9 19:47:19 | 显示全部楼层
模板插件安装服务
魔方二代魔方二代魔方二代

0

主题

0

热度

7

贡献

步入草根

Rank: 1

发表于 2018-4-25 01:27:15 | 显示全部楼层
好东西。收藏了。

0

主题

0

热度

7

贡献

步入草根

Rank: 1

发表于 2018-5-12 11:46:01 | 显示全部楼层
RE: 【原创】PHP魔方加密解密 手动反编译基于栈的指令 魔方解密自动化编程反编译 [修改]

0

主题

4

热度

8

贡献

步入草根

Rank: 1

发表于 2018-5-14 21:23:43 | 显示全部楼层
嘻嘻嘻我来了哈!

0

主题

2

热度

7

贡献

步入草根

Rank: 1

发表于 2018-6-25 22:16:37 | 显示全部楼层
PHP魔方加密解密

6

主题

562

热度

51

贡献

中级草根

Rank: 3Rank: 3

发表于 2018-6-26 02:03:33 | 显示全部楼层
顶,谢谢楼主。

22

主题

187

热度

126

贡献

终身赞助ViP

Rank: 7Rank: 7Rank: 7

发表于 2018-6-27 11:08:13 | 显示全部楼层
收藏起来先

发表回复

高级模式
您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆 用百度帐号登录 一键登录:

本版积分规则

收藏帖子 返回列表 搜索
快速回复 返回顶部 返回列表