【原创】研究《绿色军团》NSF的音乐引擎(2)反汇编

研究《绿色军团》NSF的音乐引擎(2)反汇编

维京猎人

引子

上回说到我们得了音乐引擎的代码。用软件反汇编得到的代码,不是最终代码。因为的部分程序会被数据混淆了。那么如何分析?先要人工区分代码和数据。然后整理变量,清除反汇编信息,才得到真正的代码。才是反汇编成功。本编说说如何人工区分被数据混淆的代码。

介绍一下反汇编的码:

左边是地址,冒号后面是数据,右边是代码,你可看出代码来。中间的数据是代码的机器码。

如果不能识别成代码,就会有以下形式:

addr:        xx DB        $xx

当然数很少是单独的,它的前后都会是数据,但被错认为是指令。

还有代码被混淆后,数据与代码错误相连,做成错位。

只有当一个单字节的指令(BRK除外)被识别出来,它后面很大会重新排对位置。然而碰到新的数据又开始混淆。

一、找出(从开头)连续的程序段。

上次保存的A575.asm可能编码不是太好,用VSCode打开,先编辑器,暂时选文本编辑器好了,或者用别的代码编辑器,这不是重点。

因为确定开始地址就是4710跳转的落脚点,所以我不用担心,肯定程序起始正确。向下看。代码看上去都正常,真到ABE6,它下面的程序隅尔出现数据。这说明数据与程序混淆。我贴部分出来:

ABCF: BD 7F 02  LDA $027F,x
ABD2:    09 02  ORA #$02
ABD4: 9D 7F 02  STA $027F,x
ABD7:       60  RTS
ABD8: BD 7F 02  LDA $027F,x
ABDB:    29 02  AND #$02
ABDD:       48  PHA
ABDE: 5D 7F 02  EOR $027F,x
ABE1: 9D 7F 02  STA $027F,x
ABE4:       68  PLA
ABE5:       60  RTS              ;程序正常的结束。
ABE6: 2C AC CF  BIT $CFAC        ;这一行判断为混淆的开始。
ABE9:         ABDB  $AB
ABEA:         32DB  $32
ABEB: AC 39 AC  LDY $AC39
ABEE:         3FDB  $3F
ABEF: AC 4D AC  LDY $AC4D
ABF2:         64DB  $64
ABF3: AC 6B AC  LDY $AC6B
ABF6:         72DB  $72
ABF7: AC 7E AC  LDY $AC7E
ABFA: CD AC A9  CMP $A9AC
ABFD: AC B5 AC  LDY $ACB5
AC00: 9D AC C1  STA $C1AC,x
AC03: AC E3 AC  LDY $ACE3
AC06:    06 AD  ASL $AD
AC08: 1E AD 25  ASL $25AD,x
AC0B: AD 3D AD  LDA $AD3D
AC0E:         2BDB  $2B

可以看见ABE6上面每几行就一个RTS,这是返回指令。很正常的。就它下面时不时的几个数,识别不出指令,可以认为ABE6开始就是数据。而ABE5是一个RTS。这是程序结束。

当然这样还是有点武断。代码在穿过RTS有3个方法,(1)子程序调用(2)无条件跳转(3)相对跳转。

(1)排除“子程序调用”,我们搜索JSR,并记下地址,范围从开始直到ABE5。新建一个XLSX表格,找出来的JSR指向的地址记录在表格中。记得左边的范围不要超越ABE5。记录的是JSR后面的地址,然后排序,去掉重复。结果出现大于ABE5的地址,说明这些数据并不长。但是,ABE5开始的数据已经打乱了后面程序识别的次序。还要继续找。

(2)排除“无条件跳转“,我们搜索JMP,并记下地址,范围从开始直到ABE5。新建一个XLSX表格,找出来的JMP指向的地址记录在表格中。记得左边的范围不要超越ABE5。记录的是JMP后面的地址,然后排序。没有发现大于ABE5的地址,这很正常。一般不会多写一个指令跳过数据,说明这些数据是依附着附近某个子程序。

然后,我真的发现ABE6是数据的实锤证据:LDA $ABE6,Y

见代码,这段数据长度可能的MAX=256。搜索全部代码只发现这两行。

AB5E:       A8  TAY
AB5F: B9 E6 AB  LDA $ABE6,y    ;证据
AB62:    85 06  STA $06
AB64:       C8  INY
AB65: B9 E6 AB  LDA $ABE6,y    ;证据
AB68:    85 07  STA $07
AB6A:    A4 00  LDY $00
AB6C:       C8  INY
AB6D:    A9 AA  LDA #$AA
AB6F:       48  PHA
AB70:    A9 37  LDA #$37
AB72:       48  PHA
AB73: 6C 06 00  JMP ($0006)    ;这才是难点,有可能是突破点

但是这个JMP($0006),说明这堆数据是一堆跳转地址。

(3)排除”BCC/BCS/BEQ/BNE/BVC/BVS/BMI/BPL“,从ABE5向后数256个字节,即AAE5,找找这几个指令,因为都是B字母开头,我可以搜索:空格+"B"会很快找出来。同样记下指向的地址。(虽然实际机器码是相对地址,但汇编上是写绝对值的)记得不要超越ABE5。然后排序,去掉重复。结果发明同样没有大于ABE5的地址。这就合理了。除非这些代码非常短。写代码的人水平很高,控制代码能力很强。不过这样的人不会写这种麻烦代码。所以相对跳转是不会跳过一段数据的。

最后只有JSR指向在地址越过ABE5。JSR指向的地址都是子程序入口,这非常有价值。(而JMP和相对跳转指向的是条件分支,这一段都在子程序内部。)我们现在将一整个子程序看成一个模块,后面分析和理解就很方便。我们还要对各个子程序段进行注释。

现在确定一段连续的程序是A575-ABE5

而ABE6开始是一段数据,估计长度256以内。

二、确定下一段数据的长度。

上面发现只有JSR指向在地址跨过数据段,那么离ABE6最近的地址应是新一段程序的开始。

JSR

A60C
A6A3
A6DE
A802
AA0F
ABAE
ABC6
ADCE
ADE0
AE31
AFCD
B026
B0DE
B1C4
B3FA
B655
B6D2
B6E9
B8F3
BC7A
BD5B

我从中找到ADCE

我去看了ADCE,它的上几句ADC6正好是RTS,再向上看,也有好多正常的程序。就是说数据打乱了后面的反汇编程序,但过了一段之后又正常了。就是说数据的真正结束点不是ADCE。我猜测ADCE之后的程序会向前调用,(即调用ABE6-ADC6)。我要确定数据段,将混淆的代码找出来,写正确。

工作量有点大,因为我只过了10%的代码。

现在的思路是,(1)缩小混淆代码的范围,(2)试试从近处识一下,有没有向这个区域跳转的或调用的。(3)上面说过这段数据是一对对地址,那就将它们展开,看跳转是否合理。

实施:

(1)我将数据段缩到ABE6-AC38

ABE6: 2C AC CF  BIT $CFAC
ABE9:         ABDB  $AB
ABEA:         32DB  $32
ABEB: AC 39 AC  LDY $AC39
ABEE:         3FDB  $3F
ABEF: AC 4D AC  LDY $AC4D
ABF2:         64DB  $64
ABF3: AC 6B AC  LDY $AC6B
ABF6:         72DB  $72
ABF7: AC 7E AC  LDY $AC7E
ABFA: CD AC A9  CMP $A9AC
ABFD: AC B5 AC  LDY $ACB5
AC00: 9D AC C1  STA $C1AC,x
AC03: AC E3 AC  LDY $ACE3
AC06:    06 AD  ASL $AD
AC08: 1E AD 25  ASL $25AD,x
AC0B: AD 3D AD  LDA $AD3D
AC0E:         2BDB  $2B
AC0F: AD DC AC  LDA $ACDC
AC12:         37DB  $37
AC13: AD 47 AD  LDA $AD47
AC16: 4D AD 59  EOR $59AD
AC19: AD 65 AD  LDA $AD65
AC1C:         74DB  $74
AC1D: AD B3 AD  LDA $ADB3
AC20:    C1 AD  CMP ($AD,x)
AC22:         9CDB  $9C
AC23: AD 90 AD  LDA $AD90
AC26:    56 AC  LSR $AC,x
AC28:    A5 AD  LDA $AD
AC2A: AC AD 20  LDY $20AD
AC2D:       A8  TAY
AC2E:         ABDB  $AB
AC2F: 4C E9 B6  JMP B6E9
AC32: 20 A8 AB  JSR ABA8
AC35: 9D 97 02  STA $0297,x
AC38:       60  RTS

我是怎么做的呢,一来是看见它下面程序都比较合理,二来,我找的是单个60数据。因为60就是RTS。

如果它被其它数连在一起组成了另一个指令,那么后面继续混淆。如它被单独得到,那么后面就会很大机会是正常了。单看这个也不够,还要看后面是否全都识别成程序。因为如果不是向着写程序而定的数据,不可能全都识别的成程序,这个机率太低,总会有出新混淆的现象。如果没有,那么这个 RTS就是混淆的结束。

我的意思是我认为,上面这段的前半段是数据,后半段是混淆的程序。

(2)我试试识别,调用指令。因为其它两种的可能性很低。找过了ABEX,ABFX,AC1X,AC2X,AC3X都没有被调用。(搜索ABE,ABF,AC1,AC2,等)这说明,我上一步错了,这缩小范围是一种错误。以60为结束只是一种巧合。数据段的猜测还是在ABE6-ADC6

继续找AC4X到ADBX都没有被调用的地址。从ABE6到ADBF都没有被其它代码直接作为子程序调用。很有可能完全是数据。

到了ADCX(我是直接搜索ADC)我找到 JSR ADC7,还出现2次。还有之前的 JSR ADCE

ADF2:       38  SEC
ADF3:    E9 01  SBC #$01
ADF5:    E0 03  CPX #$03
ADF7:    F0 34  BEQ $AE2DH
ADF9:       48  PHA
ADFA: AD 0A 02  LDA $020A
ADFD:    29 04  AND #$04
ADFF:    D0 16  BNE $AE17H
AE01: 20 C7 AD  JSR ADC7        ; 看这一行
AE04:       68  PLA
AE05:       0A  ASL A
AE06:    84 00  STY $00
AE08:       A8  TAY
AE09:    B1 0C  LDA ($0C),y
AE0B: 9D AE 02  STA $02AE,x
AE0E:       C8  INY
AE0F:    B1 0C  LDA ($0C),y
AE11: 9D A7 02  STA $02A7,x
AE14:    A4 00  LDY $00
AE16:       60  RTS

这一段是在ADCE之后,可以认为ABE6-ADC6就是一段数据。

(3)这段数据的前256字节有可能是128个地址,我还是整理了出来。同样的用VNES DUMP出来ABE6-ADC6。转成汇编DB之后,从前面截取到35个地址,都是在数据段中的地址,再下两个字节作为地址的话,就不合理了,超出了。所以只取这些来试验。

AC2C	ABCF	AC32	AC39	AC3F	AC4D	AC64	AC6B
AC72	AC7E	ACCD	ACA9	ACB5	AC9D	ACC1	ACE3
AD06	AD1E	AD25	AD3D	AD2B	ACDC	AD37	AD47
AD4D	AD59	AD65	AD74	ADB3	ADC1	AD9C	AD90
AC65	ADA5	ADAC					

又是排序,找出最前的地址ABCF,这是 AB73:  JMP ($0006) 要跳转的第一个落脚点。但这个在ABE6-ADC6数据段之前,是已有程序,找第二个AC2C,这个肯定是被混淆的程序的开始点。这样应该是分辨出程序来了。

再次用VNES DUMP出来,ABE6-AC2B,和 AC2C-ADC6。前一个肯定是数据,后一肯定是程序。

很神奇,ABE6-AC2B,但到的数据,就是上面那35个地址,不多不少。

果然,AC2C-ADC6,反汇编得到的代码很正常。我将它们替换回代码中。

到这里,我完成了第一段数据的确定。

嗯,真是困难呀,没有毅力的同学趁早放弃出坑。

阶段性成果,看看

ABD8: BD 7F 02  LDA $027F,x
ABDB:    29 02  AND #$02
ABDD:       48  PHA
ABDE: 5D 7F 02  EOR $027F,x
ABE1: 9D 7F 02  STA $027F,x
ABE4:       68  PLA
ABE5:       60  RTS
ABE6:                       ;ABE6-AC2B
  .DB $2C, $AC, $CF, $AB, $32, $AC, $39, $AC, $3F, $AC, $4D, $AC, $64, $AC, $6B, $AC
  .DB $72, $AC, $7E, $AC, $CD, $AC, $A9, $AC, $B5, $AC, $9D, $AC, $C1, $AC, $E3, $AC
  .DB $06, $AD, $1E, $AD, $25, $AD, $3D, $AD, $2B, $AD, $DC, $AC, $37, $AD, $47, $AD
  .DB $4D, $AD, $59, $AD, $65, $AD, $74, $AD, $B3, $AD, $C1, $AD, $9C, $AD, $90, $AD
  .DB $56, $AC, $A5, $AD, $AC, $AD
  
AC2C: 20 A8 AB  JSR ABA8
AC2F: 4C E9 B6  JMP B6E9
AC32: 20 A8 AB  JSR ABA8
AC35: 9D 97 02  STA $0297,x
AC38:       60  RTS                ;按第1个思路,就是找到这一行,认定前面就是混淆的段。
AC39:    A9 FF  LDA #$FF
AC3B: 9D 97 02  STA $0297,x
AC3E:       60  RTS

之前凭经验,找到AC38,其实也是对的,只是路子走到头了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值