2008-02-06

Core Wars

目录:


  1. 简介
  2. Mars机体系结构
  3. RedCode简单语法
  4. 语法实例讲解
  5. 简单实例
  6. 如何实战
  7. 相关资料

一、简介

    CoreWars 磁核大战,是个很古老的编程游戏。
就是大家各自写一个程序,然后把这些程序都载入内存,然后“并发运行”,看最后都结果谁能消灭内存中其他程序而最终存活下来。

    当然这些程序不是运行于家用PC上,也不是使用大家常用的Intel汇编来编写的。其运行平台是Mars机──Mars机是一种简单的计算机,有固定的8000个内存单元,和约10来个指令,当然这些程序都是使用这些指令来完成。

二、Mars体系结构

    Mars机器由一组内存单元,一个CU单元,一个简单进程管理系统,和一组指令集构成。

    Mars机的标准主要参考pMars虚拟机标准,主要有两个:88标准和94标准。一下都是默认为88标准(扩展94标准会专门标识出来。)

    该系统内存大小固定为8000个内存单元,每一个内存单元由5个部分构成:

  1. OPCODE 区:操作数区,该区域指定了机器指令,如:MOV, ADD 等。
  2. A 数据区:一个32位的数据存储区,存放相应数据,如:128, -100。
  3. A 数据区寻址修饰:指定了A数据区的寻址方式。
  4. B 数据区。
  5. B 数据区寻址修饰。
  6. 操作数修饰区。(94标准中新加入)

    系统寻址方式分为:立即寻址,直接寻址,间接寻址。
(94标准中加入:间接A-1寻址、间接A+1寻址、间接B-1寻址、间接B+1寻址)

    系统寻址都是相对于当前IP为基准寻址的,这是Mars系统和传统计算机最大的不同之处。
如:mov 0, 1 表示将当前指令所在内存单元的下一个内存单元。

    CU单元用于执行相应的指令,配合CU单元还有一个隐含的寄存器IP,每次执行单元执行IP指定的内存单元的指令。

    简单进程管理器。系统的进程由一个先进先出队列构成。每次管理系统将当前进程的执行地址出队放入IP寄存器,然后CU单元执行指令,最后将该进 程的下一条指令地址放入队尾。如果该进程产生新进程,则在队尾再加入新进程的起始执行地址。简单进程管理器保证每个进程轮流执行,当一个进程执行了 DAT 或者执行了 被0除 的操作则该进程结束。

系统指令包含:
DAT  MOV  ADD  SUB  JMP  JMZ  JMN  CMP  SLT  DJN  SPL
( 94标准中加入了新的指令: SUB MUL DIV MOD SEQ SNE NOP LDP STP )

    Mars机的汇编程序通常是RedCode,而Mars模拟器几乎都集成了一个RedCode的编译器,将RedCode程序编译为机器码写入内存。

    系统初始时内存中都是 DAT $0,$0 ,每一个程序加载到内存的随机位置,当多个程序都加载完后系统给每个程序创建一个进程,此时每个进程在进程队列中的位置是随机的,然后系统开始运行。


三、RedCode简单语法(按94标准)

指令写法:<label> OPCODE < OP_modify > <A_modify> A_field ,<B_modify> B_field
                             ------------------  ------------------
                                  整体记做A           整体记做B
<>包含的部分可以省略。
通常A表示源地址,B表示目标地址。

OPCODE包含:
DAT     中止进程
MOV     移动数据A到B
ADD     A + B -> B
SUB     A - B -> B
MUL     A * B -> B
DIV     B / A -> B (若A=0进程结束)
MOD     B % A -> B (若A=0进程结束)
JMP     跳转到A
JMZ     若 B == 0 则跳转到A
JMN     若 B != 0 则跳转到A
DJN     先 B - 1, 若 B != 0 跳转到A
SPL     开启一个新进程,新进程起始执行地址为A
SLT     若 A < B 跳过下一条指令
CMP     和SEQ相同
SEQ     若 A == B 跳过下一条指令
SNE     若 A != B 跳过下一条指令
NOP     空指令,什么也不干。

LDP/STP 本地地址操作指令,一般都没有实现,这里就不解释了。

DAT     指令可以只有A区域出现,此时一般编译器会将内存的A区域拷贝一份到B区域。
JMP/SPL 指令也可以只有一个A区域出现,此时一般编译器会将内存的B区域作为 $0

OP_modify包含:.A   指令读写目的地址的A区域
.B   指令读写目的地址的B区域 (如果Op_modify没有指定,则默认使用该规则)
.AB  指令读A指定地址的A区域,结果写入B指定地址的B区域
.BA  指令读A指定地址的B区域,结果写入B指定地址的A区域
.F   指令读A指定地址的A和B区域,操作结果写入B指定地址的A和B区域
.X   指令读取A指定地址的B区域,操作结果写入B指定地址的A区域;
     然后读取A指定地址的A区域,进行同样的操作操作结果写入B指定地址的B区域
.I   指令读写源和目标地址的整个内存单元。
    (mov指令没有指定OP_modify,并且源操作数不是立即数时使用该规则)

Modify指定了寻址方式:#   立即数
$   B直接寻址
@   间接寻址
<   B先减1,再间接寻址
>   B先减1,再间接寻址
*   A间接寻址
{   A先减1,再间接寻址
}   A先加1,再间接寻址

值得注意的是地址跳转相关的指令A区域不能为立即数,加减乘除模指令的B区域不能为立即数,否则要么模拟器报语法错误,要么作为执行非法指令中止进程或者作为空指令处理。

Filed就是指定数据的地方了。

程序注释符为";",从;到行尾均作为注释。但有几个特殊的注释,作为程序信息:
;name   filename  指定程序的名字
;author author    指定程序的作者
;debug  [static|off] 指定是否调试程序,但不是每个模拟器都实现

此外不同的模拟器也有自己特定的特殊符号。

宏一般不同的模拟器都有自己的一套,但通常都实现了EQU,语法
label EQU 表达式    (表达式可以为数字或标签的加减乘除)

ORG 数字或标签       指定程序执行的开始地址
END <数字或标签>     程序结束,此后的部分不会被编译,如果指定了一个数字或标签则作为程序开始地址。


四、语法实例讲解

以下我们写一个完整的RedCode文件:
;name Test
;author cloud

org 1
data  dat 5
    mov data , @ data
    mov -2   , < data
    mov # 3  , data
    jmp -1
end

编译器编译后将各指令的各个区域写入对应内存单元的对应区域。
假如程序放到内存的0001单元到0005单元,写入内存后反汇编过来就是
0001  DAT $ 5 , $ 5
0002  mov $ -1, @ -1
0003  mov $ -2, < -2
0004  mov # 3 , $ -3
0005  jmp $ -1, > 2
程序入口地址org 1,就是程序的第1条指令开始(编号从0开始),就是地址0002

0002处mov指令的寻址如下:
源地址:  $ -1 ,就是直接寻址,以当前地址(0002)相对的-1处的地址作为源地址 ,即 0001
目的地址:@ -1 ,就是间接寻址,以当前地址(0002)相对的-1处的地址的 B区域作为直接寻址地址
就是0001处的B数据区的数据5作为直接寻址地址,取出0001处的5作为直接寻址,5也是一个相对地址,相对于0002就是地址0007,最终目标地址为0007那么最终就是将0001的内存单元拷贝一份到0007

0003处的mov源地址一样,操作的是0001的地址,目标地址为:
 < -2 ,就是取出相对0003地址-2的地址(就是0001)的B数据区(就是5),先将其减1(即为4),然后在存放回去(0001处的B数据区变成4),将得到的结果(4)作为直接寻址,目标地址就是相对当前地址(0003)为4的地址,即0007最终结果和上一条指令一样,但真正内存单元拷贝时001的B数据区已经变成了4

0004处的结果就是把3放到0001的B数据区中

0005处的结果就是把0007的B数据区的数据加1,然后跳转到0006


五、简单实例

作为对抗程序最重要的就是生存,一个最简单的程序就是
jmp 0始终跳转到自身,死循环,自己没有攻击能力,唯一取胜的方法就是期望对手自己死亡,可谓守株待兔。
但这个程序一个弱点是自己在内存中的地址固定,很容易收到攻击。

下面这个程序可以说是非常出名的IMP程序了:
mov 0,1它不断把自己移动到下一个内存单元,然后执行到下一个内存单元执行。

这个程序具有一定的攻击能力,能够覆盖别人的程序,但是覆盖他人程序后也不能取胜,因为不能导致他人程序执行中止进程操作,唯一的取胜方法也是等待他人程序死亡,但自身在内存中的位置不断移动,生存能力比jmp 0强。

对付IMP程序的方法也很简单:
jmp 0 , < -2这段代码始终跳转到自身,但是跳转前会将前面的第2个内存单元的B数据区数据减1,当imp程序将自己移动到该地址后将被修改为 mov 0, 0 这样下次IMP执行时执行mov 0, 0 没能将自己移动,但执行地址已经到达下一个内存单元,而Mars系统初始化时内存单元为 dat 0, 0 这就导致IMP程序执行一个DAT指令而导致进程中止

攻击他人程序通常都是通过扔出一个DAT来覆盖他人程序,使得其他程序因执行DAT而中止比如下面这个程序:
org start
    dat 0,5
start
    mov -1,@-1
    jmp -1,> -2
end
第一次mov -1 , @ -1 将 dat 0, 5 拷贝一个到相对为5的地址处进行一次轰炸
    jmp -1 ,> -2将 dat 0 ,5 修改为 dat 0, 6
然后跳转执行 mov -1, @ -1 ,执行mov时导致将dat 0,6 拷贝到相对为6的地址进行轰炸
如此循环,最终将对整个内存以dat轰炸一遍,可谓杀伤力非常。

像这样的轰炸程序是非常有效的攻击,现在的很多程序都是靠这种手法攻击他人,然后利用IMP的方法移动自己避免被攻击。

当然,你也可以每隔几个单元轰炸一次:
org 1
   dat 0  , 5
   mov -1 , @-1
   add #4 , -2
   jmp -2
end 
这个程序每次将dat 0,5的B数据区加4,然后在MOV处以此寻址进行轰炸,所有长度大于4的程序都容易受到它的攻击。

作为对抗手段,程序可以采用哨兵概念,先在代码前面放一个数据,然后检查该数据是否被改变,如果被改变表示其他程序运行到了该处,或者攻击了该地址,自己就可以采取相应的对策,把自己移动躲避攻击或者发动对该地址的攻击。


六、如何实战

需要一个虚拟机。RedCode扩展名为.red放/redcode/目录下。


七、相关资料

http://www.koth.org/ 官方站点
http://vyznev.net/corewar/guide.html RedCode初学指导



// EOF Read More...

2008-02-01

ScribeFire 中添加 <code> 标签支持

Scribefire具体安装在 <firefox home path>/extensions/ 下的哪个目录可以在 extensions.rdf 文件中找到(搜索 "ScribeFire" 即可),以下文件操作的根目录均为 Scribefire 的安装目录。

1. 自定义 <code> style
文件 /chrome/skin/blank.css 中添加:
code {
  display: block;
  font-size: 110%;
  margin: 5px;
  margin-left: 1.3em;
  padding: 3px;
  padding-left:  10px;
  background: #eee;
  border: 1px solid #ccc;
  border-left: 3px solid #ccc;
}


2. 操作界面外观
文件 /chrome/skin/toolbar/toolbar-code.png
(自己作的“<code>”按钮图片)

文件 /chrome/skin/overlay.css 中设置按钮外观图片
#code-button {
  list-style-image: url(chrome://performancing/skin/toolbar/toolbar-code.png);
}


3. 界面、事件关联

文件 /chrome/content/editor.xul 中:
 1) 按钮事件关联:
<command id="cmd_blockquote" oncommand="performancingMidas.blockquote()"/>
下依葫芦画瓢之:
<command id="cmd_code" oncommand="performancingMidas.code()"/>

 2) 按钮布局:
<toolbarbutton id="blockquote-button" command="cmd_blockquote" tooltiptext="&performancing.editor.bar.quote.tt;"/>
下依葫芦画瓢之:
<toolbarbutton id="code-button" command="cmd_code" tooltiptext="&performancing.editor.bar.code.tt;"/>


文件 /chrome/content/midas.js 中定义事件响应,分别在:
performancingMidas.blockquote = function(){
...
}

case "blockquote":
...

case "blockquote":
...

下依葫芦画瓢之。


文件 /chrome/content/locale-default/overlay.dtd 中定义 tooltips,在
<!ENTITY performancing.editor.bar.quote.tt "Quote">
下依葫芦画瓢之:
<!ENTITY performancing.editor.bar.code.tt "Code">

又做了一件无聊之事~~

//EOF Read More...