研究《绿色军团》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,其实也是对的,只是路子走到头了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/125710.html