X‘moe Project » 日志
[更新中][FVP]玩♂弄favorite直播现场
2014-3-2 21:08 /
FVP就是favorite御用引擎。
为移植色鸟鸟做准备的。
色鸟鸟的移植将会是去H的日文版
没有为什么
玩得很渣请多多谅解~
反正blog也没人会来,于是就当做是笔记吧。
首先是FVP的内部函数,记录在offset为[0043DC40]的stack里面。
[0043F131]为stack结束处。
其实这个就是vm真正的输入表。在hcb里面的输入表只是供脚本按照编号调用。
应该是有144条,脚本里输入表之前的WORD就是ImportTableCount。
在vm中,每条systemFunction对应的结构就是:
有生之年锁定本blog发布233
===============================================
继续更新。管他作业没做,反正做不完放弃了
脚本就是hcb文件
但是hcb文件的名字是没有锁定的,以系统找到的第一个hcb脚本为准的【旁边有API调用的
所以说,汉化的时候把hcb的后缀改成其他名字如“pck”是最省力的。
简直迷233
多废话几句,FVP优先读取未封包的图片【完全就是方便汉化啊23333333333
说一点正经的吧,最开始我先追到这一段
由此看出,0x00在VM脚本里面等于机器码的NOP,而47F5A0就是我们要关注的地址。
多说一点,到这里,我也不知道ESI+81C这个地址的具体含义是什么,但是我们可以看出来:
1.[ESI+81C]里面的值在执行的过程中会加1
2.[ESI+81C]这个地址在之后的出现很频繁。
3.[ESI+81C]会和0x00做比较,那么[ESI+81C]保存的值一定和VMCode有关。
疑问我们在后面再解答。
每次都是通过一个DWORD值来确定call的地址,也就说,47F5A0之后的某一段就是VM的字节码相关的。其实这个不难看出,每次都是EDX*4(DWORD)来取offset的。
找到这个地址,然后就会发现...
这里就是call Table
整整齐齐0x27条指令,接下来的分析也就不是很难了。
[容我吃个饭,回来再更]
=====================================================
码字麻烦,我在写一个0x0E的。
为了方便说明,我们设定*zFile为VM脚本在内存里的首地址
zPos为当前VM脚本的地址(类似于EIP)
uchar就是unsigned __int8
哪天有心情再更。
这样可以看出了,为什么色鸟、光鸟的补丁要如此加密。
还不是被因为hcb文件太简单了。
题外话:
汉化有两个办法
1、提取Str,再封包回去(你得注意各种call、jmp的offset)
我是用map暴力的。
2、hook PushStr,比较简单,又方便调试。
为移植色鸟鸟做准备的。
色鸟鸟的移植将会是去H的日文版
没有为什么
玩得很渣请多多谅解~
反正blog也没人会来,于是就当做是笔记吧。
首先是FVP的内部函数,记录在offset为[0043DC40]的stack里面。
[0043F131]为stack结束处。
其实这个就是vm真正的输入表。在hcb里面的输入表只是供脚本按照编号调用。
应该是有144条,脚本里输入表之前的WORD就是ImportTableCount。
在vm中,每条systemFunction对应的结构就是:
typedef struct hcbImportTable{
uchar VarCount; //参数个数
uchar NameLen;
char* FuncName;
};
有生之年锁定本blog发布233
===============================================
继续更新。管他作业没做,反正做不完放弃了
脚本就是hcb文件
但是hcb文件的名字是没有锁定的,以系统找到的第一个hcb脚本为准的【旁边有API调用的
所以说,汉化的时候把hcb的后缀改成其他名字如“pck”是最省力的。
简直迷233
004374CF |. 50 PUSH EAX ; /pFindFileData
004374D0 |. 68 A4BB4500 PUSH World.0045BBA4 ; |FileName = "*.hcb"
004374D5 |. 8BF9 MOV EDI,ECX ; |
004374D7 |. FF15 BCB04500 CALL DWORD PTR DS:[<&KERNEL32.FindFirstF>; \FindFirstFileA
多废话几句,FVP优先读取未封包的图片【完全就是方便汉化啊23333333333
说一点正经的吧,最开始我先追到这一段
004434F0 > 8B86 1C080000 MOV EAX,DWORD PTR DS:[ESI+81C]
004434F6 . 8B4E 04 MOV ECX,DWORD PTR DS:[ESI+4]
004434F9 . 0FB60C01 MOVZX ECX,BYTE PTR DS:[ECX+EAX]
004434FD . 0FB6D1 MOVZX EDX,CL
00443500 . 40 INC EAX
00443501 . 8986 1C080000 MOV DWORD PTR DS:[ESI+81C],EAX
00443507 . 8B0495 A0F5470>MOV EAX,DWORD PTR DS:[EDX*4+47F5A0] ;轮番取出指令
0044350E . 8BCE MOV ECX,ESI ;
00443510 . FFD0 CALL EAX ;调用当前指令
00443512 . 80BE 10080000 >CMP BYTE PTR DS:[ESI+810],0 ;当前指令与0比较,如果是0,就nop并且进入下一次循环
00443519 .^74 D5 JE SHORT World.004434F0
0044351B > 5E POP ESI ;弹出当前对象
0044351C . C3 RETN
由此看出,0x00在VM脚本里面等于机器码的NOP,而47F5A0就是我们要关注的地址。
多说一点,到这里,我也不知道ESI+81C这个地址的具体含义是什么,但是我们可以看出来:
1.[ESI+81C]里面的值在执行的过程中会加1
2.[ESI+81C]这个地址在之后的出现很频繁。
3.[ESI+81C]会和0x00做比较,那么[ESI+81C]保存的值一定和VMCode有关。
疑问我们在后面再解答。
每次都是通过一个DWORD值来确定call的地址,也就说,47F5A0之后的某一段就是VM的字节码相关的。其实这个不难看出,每次都是EDX*4(DWORD)来取offset的。
找到这个地址,然后就会发现...
0047F5A0 0041A500 .. Entry address
0047F5A4 004438D0 ?D. World.004438D0
0047F5A8 00443900 .9D. World.00443900
0047F5AC 00443980 €9D. World.00443980
0047F5B0 00442920 )D. World.00442920
0047F5B4 00442940 @)D. World.00442940
0047F5B8 00443A50 P:D. World.00443A50
0047F5BC 00443A70 p:D. World.00443A70
0047F5C0 00442980 €)D. World.00442980
0047F5C4 004429A0 ?D. World.004429A0
0047F5C8 00443AB0 ?D. World.00443AB0
0047F5CC 00443AE0 ?D. World.00443AE0
0047F5D0 00443B20 ;D. World.00443B20
0047F5D4 00443B50 P;D. World.00443B50
0047F5D8 00443B90 ?D. World.00443B90
0047F5DC 00443BD0 ?D. World.00443BD0
0047F5E0 00443C60 `<D. World.00443C60
0047F5E4 00443CF0 ?D. World.00443CF0
0047F5E8 00443D70 p=D. World.00443D70
0047F5EC 004429C0 ?D. World.004429C0
0047F5F0 00442A30 0*D. World.00442A30
0047F5F4 00443DF0 ?D. World.00443DF0
0047F5F8 00443E40 @>D. World.00443E40
0047F5FC 004442C0 繠D. World.004442C0
0047F600 00444370 pCD. World.00444370
0047F604 00442A60 `*D. World.00442A60
0047F608 00442AA0 ?D. World.00442AA0
0047F60C 00442C90 ?D. World.00442C90
0047F610 00442D20 -D. World.00442D20
0047F614 00442DB0 ?D. World.00442DB0
0047F618 00442E30 0.D. World.00442E30
0047F61C 00442E80 €.D. World.00442E80
0047F620 00442ED0 ?D. World.00442ED0
0047F624 00442F10 /D. World.00442F10
0047F628 00442F50 P/D. World.00442F50
0047F62C 00443060 `0D. World.00443060
0047F630 004430A0 ?D. World.004430A0
0047F634 004431C0 ?D. World.004431C0
0047F638 00443200 .2D. World.00443200
0047F63C 00443320 3D. World.00443320
这里就是call Table
整整齐齐0x27条指令,接下来的分析也就不是很难了。
[容我吃个饭,回来再更]
=====================================================
码字麻烦,我在写一个0x0E的。
为了方便说明,我们设定*zFile为VM脚本在内存里的首地址
zPos为当前VM脚本的地址(类似于EIP)
uchar就是unsigned __int8
00443B90 . 8B81 20080000 MOV EAX,DWORD PTR DS:[ECX+820]
00443B96 . 8D50 01 LEA EDX,DWORD PTR DS:[EAX+1]
00443B99 . 8991 20080000 MOV DWORD PTR DS:[ECX+820],EDX
00443B9F . C644C1 08 04 MOV BYTE PTR DS:[ECX+EAX*8+8],4
00443BA4 . 8B91 1C080000 MOV EDX,DWORD PTR DS:[ECX+81C] ;EDX = zPos
00443BAA . 56 PUSH ESI
00443BAB . 8B71 04 MOV ESI,DWORD PTR DS:[ECX+4]
00443BAE . 0FB63416 MOVZX ESI,BYTE PTR DS:[ESI+EDX] ;一个op
00443BB2 . 42 INC EDX
00443BB3 . 8991 1C080000 MOV DWORD PTR DS:[ECX+81C],EDX ;Str的位置
00443BB9 . 8954C1 0C MOV DWORD PTR DS:[ECX+EAX*8+C],EDX ;拿去执行,PushStr
00443BBD . 01B1 1C080000 ADD DWORD PTR DS:[ECX+81C],ESI ;zPos+=callLength(指令长度)
00443BC3 . 5E POP ESI
00443BC4 . C3 RETN
哪天有心情再更。
这样可以看出了,为什么色鸟、光鸟的补丁要如此加密。
还不是被因为hcb文件太简单了。
题外话:
汉化有两个办法
1、提取Str,再封包回去(你得注意各种call、jmp的offset)
我是用map暴力的。
2、hook PushStr,比较简单,又方便调试。