有两种方法,一种是顺序搜索,另一种是跟踪搜索。
最开始我就是想的顺序搜索,遇到 jnz 和 jmp 都不跳转,只是记录他们的位置和相关信息,之后统一处理,顺序分析代码,之后再区分 stmts 块和 else 块。后来发现代码中有一些难点不太好实现。比如两个位置同时跳往同一处,到这里时,我需要同时组装两个 if 块,有时候两个 if 的代码块并不容易区分。
后来我就改用跟踪搜索了,先说 jnz,遇到 jnz 的话,代码分两支执行,遇到 jnz 就继续分,遇到 jmp 就直接跳转,直到两个代码会合(或者往回跳转),这样可以把原来混乱的 jnz 与 jmp 统一。
说到会合,说起来简单,做起来就要一定技巧了,这里又不能分成两个线程去做,怎么办呢?
简而言之就是,分为两个指令指针,一个指向正常执行的下一条指令,一个指向跳转之后的指令,然后总让小的指针往后执行,直到跳转指令或者相遇。
jnz a
...... <---- 指针 1
jmp b
a:
...... <---- 指针 2
jmp c
b:
......
jmp d
c:
......
d:
......jnz a
......
jmp b
a:
...... <---- 指针 2
jmp c
b:
...... <---- 指针 1
jmp d
c:
......
d:
......jnz a
......
jmp b
a:
......
jmp c
b:
...... <---- 指针 1
jmp d
c:
...... <---- 指针 2
d:
......jnz a
......
jmp b
a:
......
jmp c
b:
......
jmp d
c:
......
d:
...... <---- 指针 1 & 指针 2
这就是大概的过程,我的代码实现中使用了 JumpException 这个东西,就是为了遇到跳转指令,直接停止指针的继续移动,重新判断移动哪个指针。
关键代码
/**
* 条件分支语句
* @param $jump_pointer
* @param $next_pointer
* home.php?mod=space&uid=155549 mixed
* @throws \Exception
*/
protected function _jnz($jump_pointer, $next_pointer)
{
if ($jump_pointer < $next_pointer) {
throw new \Exception('jump pointer < next pointer');
}
// 备份 $asmTree
$asmTreeElse = $asmTreeStmts = $asmTree = $this->asmTree;
$asmTreePointer = count($this->asmTree);
// 并分别走 stmts 块和 else 块
++$this->jnzStack;
while ($jump_pointer != $next_pointer) {
if (($jump_pointer > $next_pointer && $next_pointer > 0) || $jump_pointer < 0) {
$this->asmTree = $asmTreeElse;
try {
$next_pointer = $this->dissect($next_pointer, $jump_pointer);
} catch (JumpException $exception) {
$next_pointer = $exception->jump_pointer;
}
$asmTreeElse = $this->asmTree;
} else {
$this->asmTree = $asmTreeStmts;
try {
$jump_pointer = $this->dissect($jump_pointer, $next_pointer);
} catch (JumpException $exception) {
$jump_pointer = $exception->jump_pointer;
}
$asmTreeStmts = $this->asmTree;
}
}
--$this->jnzStack;
// 检测循环
$asmTreeStmtsLastOne = $asmTreeStmts[count($asmTreeStmts) - 1];
$loop_begin = false;
if ($asmTreeStmtsLastOne['asm'] == 'loop_end') {
$loop_begin = $asmTreeStmtsLastOne['args']['begin'];
array_pop($asmTreeStmts);
$asmTreeElse[] = [
'asm' => 'iter_break',
'args' => [],
];
}
// 恢复 $asmTree
$this->asmTree = $asmTree;
// 构造 if 指令
$this->asmTree[] = [
'asm' => 'if [esp]',
'args' => [
'stmts' => array_slice($asmTreeStmts, $asmTreePointer),
'else' => array_slice($asmTreeElse, $asmTreePointer),
],
];
// 构造 loop 指令
if ($loop_begin !== false) {
$loop_stmts = array_slice($this->asmTree, $loop_begin);
$this->asmTree = array_slice($this->asmTree, 0, $loop_begin);
$this->asmTree[] = [
'asm' => 'loop',
'args' => [
'stmts' => $loop_stmts,
],
];
}
return $next_pointer;
}
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-67089-3.html
虽说吨位超过了
升級當天用了20來分鐘