- 浏览: 28624 次
- 性别:
- 来自: 广州
-
最新评论
-
谁解怨妇心:
night_stalker 写道您谦虚了 …… 代码写得相当的 ...
在BMP位图中隐藏数据 -
RednaxelaFX:
话说我想起了Brainloller和Braincopter
在BMP位图中隐藏数据 -
RednaxelaFX:
32位BMP在某些软件(例如Windows自带的图片浏览器)里 ...
在BMP位图中隐藏数据 -
night_stalker:
您谦虚了 …… 代码写得相当的好看,命名非常的标准正统 口牙
在BMP位图中隐藏数据
前言:游戏汉化中的一个重要的步骤是对资源的解包以及封包。虽然现在有很多软件可以实现解包的功能,但要将资源修改后再封包的话,不熟悉解密的算法是无法写出逆算法的。脚本是游戏中的一个最重要的资源,负责控制游戏引擎的行为以及存储了游戏的文本。
首先是老规矩,对CreateFileA下段,来到下面的地方:
00473839 > \6A 00 PUSH 0 ; /hTemplateFile = NULL
0047383B . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00473840 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00473842 . 6A 00 PUSH 0 ; |pSecurity = NULL
00473844 . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00473846 . 8D95 D0FEFFFF LEA EDX,DWORD PTR SS:[EBP-130] ; |
0047384C . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00473851 . 52 PUSH EDX ; |FileName= "I:\Program Files\Navel\壌偨偪偵梼偼側偄\SCRIPT.LPK"
00473852 . FF15 B0D14E00 CALL DWORD PTR DS:[<&KERNEL32.CreateFile>; \CreateFileA
00473858 . 8BD8 MOV EBX,EAX
0047385A . 83FB FF CMP EBX,-1 ; 判断打开是否成功,句柄为-1则不跳
0047385D . 895D E0 MOV DWORD PTR SS:[EBP-20],EBX ; 保存句柄,我的机器上句柄为90
00473860 . 75 18 JNZ SHORT ORE_TUBA.0047387A ; 句柄有效则跳转
00473862 . B8 03000000 MOV EAX,3
00473867 . 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
0047386A . 64:890D 00000>MOV DWORD PTR FS:[0],ECX
00473871 . 5F POP EDI
00473872 . 5E POP ESI
00473873 . 5B POP EBX
00473874 . 8BE5 MOV ESP,EBP
00473876 . 5D POP EBP
00473877 . C2 1400 RETN 14
0047387A > 8B07 MOV EAX,DWORD PTR DS:[EDI]
0047387C . 56 PUSH ESI ; 文件名“SCRIPT.LPK"
0047387D . 8BCF MOV ECX,EDI
0047387F . FF50 5C CALL DWORD PTR DS:[EAX+5C] ; 处理字符串的函数,将文件名转换为大写,解密的时候要用到
00473882 . 85C0 TEST EAX,EAX ; 判断是否成功
00473884 . 74 52 JE SHORT ORE_TUBA.004738D8
00473886 . 8B10 MOV EDX,DWORD PTR DS:[EAX]
00473888 . 8D4D 08 LEA ECX,DWORD PTR SS:[EBP+8]
0047388B . 51 PUSH ECX
0047388C . 8D8D D0FEFFFF LEA ECX,DWORD PTR SS:[EBP-130]
00473892 . 53 PUSH EBX
00473893 . 51 PUSH ECX
00473894 . 8BC8 MOV ECX,EAX
00473896 . FF52 04 CALL DWORD PTR DS:[EDX+4] ; 重要函数,跟进去看看
00473899 . 85C0 TEST EAX,EAX
0047389B . 8945 E8 MOV DWORD PTR SS:[EBP-18],EAX
运气不错,第一次断下就是要打开脚本文件“SCRIPT.LPK”了。一直跟到call 00485D20这个地方,有点可疑,跟进去看看。
00485D20 . 55 PUSH EBP
00485D21 . 8BEC MOV EBP,ESP
00485D23 . 6A FF PUSH -1
00485D25 . 68 EBB44E00 PUSH ORE_TUBA.004EB4EB ; SE 处理程序安装
00485D2A . 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00485D30 . 50 PUSH EAX
00485D31 . 64:8925 00000>MOV DWORD PTR FS:[0],ESP
00485D38 . 81EC 54020000 SUB ESP,254
00485D3E . 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C] ; 取句柄
00485D41 . 53 PUSH EBX
00485D42 . 56 PUSH ESI
00485D43 . 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
00485D46 . 57 PUSH EDI
00485D47 . 33FF XOR EDI,EDI
00485D49 . 83F8 FF CMP EAX,-1 ; 判断句柄是否有效
00485D4C . 8965 F0 MOV DWORD PTR SS:[EBP-10],ESP
00485D4F . 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
00485D52 . 897D D0 MOV DWORD PTR SS:[EBP-30],EDI
00485D55 . 897D FC MOV DWORD PTR SS:[EBP-4],EDI
00485D58 . 75 4D JNZ SHORT ORE_TUBA.00485DA7 ; 句柄有效则跳转
00485D5A . 3BF7 CMP ESI,EDI
00485D5C . 75 15 JNZ SHORT ORE_TUBA.00485D73
00485D5E . 8D45 A8 LEA EAX,DWORD PTR SS:[EBP-58]
00485D61 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485D66 . 50 PUSH EAX ; |Arg1
00485D67 . C745 A8 05000>MOV DWORD PTR SS:[EBP-58],5 ; |
00485D6E . E8 1F5F0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485D73 > 57 PUSH EDI ; /hTemplateFile
00485D74 . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00485D79 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00485D7B . 57 PUSH EDI ; |pSecurity
00485D7C . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00485D7E . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00485D83 . 56 PUSH ESI ; |FileName
00485D84 . FF15 B0D14E00 CALL DWORD PTR DS:[<&KERNEL32.CreateFile>; \CreateFileA
00485D8A . 83F8 FF CMP EAX,-1
00485D8D . 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
00485D90 . 75 15 JNZ SHORT ORE_TUBA.00485DA7
00485D92 . 8D4D B4 LEA ECX,DWORD PTR SS:[EBP-4C]
00485D95 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485D9A . 51 PUSH ECX ; |Arg1
00485D9B . C745 B4 03000>MOV DWORD PTR SS:[EBP-4C],3 ; |
00485DA2 . E8 EB5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DA7 > 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-1C] ; 取句柄
00485DAA . 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14] ; 缓冲区
00485DAD . 57 PUSH EDI ; /pOverlapped
00485DAE . 52 PUSH EDX ; |pBytesRead
00485DAF . 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] ; |
00485DB2 . 6A 08 PUSH 8 ; |BytesToRead = 8
00485DB4 . 50 PUSH EAX ; |Buffer
00485DB5 . 51 PUSH ECX ; |hFile
00485DB6 . FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
00485DBC . 85C0 TEST EAX,EAX ; 测试是否成功
00485DBE . 75 15 JNZ SHORT ORE_TUBA.00485DD5 ; 不为0则跳
00485DC0 . 8D55 AC LEA EDX,DWORD PTR SS:[EBP-54]
00485DC3 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485DC8 . 52 PUSH EDX ; |Arg1
00485DC9 . C745 AC 07000>MOV DWORD PTR SS:[EBP-54],7 ; |
00485DD0 . E8 BD5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DD5 > 817D C8 4C504>CMP DWORD PTR SS:[EBP-38],314B504C ; 跳到这里,判断文件开头四个字节是否”LPK1“
00485DDC . 74 15 JE SHORT ORE_TUBA.00485DF3 ; 是的话跳转进行处理
00485DDE . 8D45 C4 LEA EAX,DWORD PTR SS:[EBP-3C]
00485DE1 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485DE6 . 50 PUSH EAX ; |Arg1
00485DE7 . C745 C4 01000>MOV DWORD PTR SS:[EBP-3C],1 ; |
00485DEE . E8 9F5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DF3 > 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485DF9 . 6A 01 PUSH 1
00485DFB . 51 PUSH ECX
00485DFC . 56 PUSH ESI
00485DFD . E8 0EEEFEFF CALL ORE_TUBA.00474C10 ; 此处也是一个字符串复制的函数,经过一串比较后复制了”SCRIPT.LPK"字符串,但具体作用未知- -
00485E02 . 83C4 0C ADD ESP,0C ; 平衡堆栈
00485E05 . 8D95 A0FDFFFF LEA EDX,DWORD PTR SS:[EBP-260]
00485E0B . 8D85 A0FEFFFF LEA EAX,DWORD PTR SS:[EBP-160]
00485E11 . 52 PUSH EDX
00485E12 . 50 PUSH EAX
00485E13 . E8 08F0FEFF CALL ORE_TUBA.00474E20 ; 又是处理文件名的函数,去掉"SCRIPT"的后缀名
00485E18 . 83C4 08 ADD ESP,8 ; 平衡堆栈
00485E1B . 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485E21 . 51 PUSH ECX ; /StringOrChar
00485E22 . FF15 58D34E00 CALL DWORD PTR DS:[<&USER32.CharUpperA>] ; \CharUpperA
00485E28 . 8D95 A0FEFFFF LEA EDX,DWORD PTR SS:[EBP-160] ; 取“SCRIPT”的地址
00485E2E . 52 PUSH EDX ; /String
00485E2F . FF15 FCD04E00 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] ; \lstrlenA
00485E35 . 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX
00485E38 . 8D8405 9FFEFF>LEA EAX,DWORD PTR SS:[EBP+EAX-161]
00485E3F . 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485E45 . C745 DC 6BACB>MOV DWORD PTR SS:[EBP-24],A5B9AC6B
00485E4C . C745 D4 E59D6>MOV DWORD PTR SS:[EBP-2C],9A639DE5
00485E53 . 8945 B0 MOV DWORD PTR SS:[EBP-50],EAX
00485E56 . 894D BC MOV DWORD PTR SS:[EBP-44],ECX
00485E59 . 8B45 DC MOV EAX,DWORD PTR SS:[EBP-24]
00485E5C . 8B5D D4 MOV EBX,DWORD PTR SS:[EBP-2C]
00485E5F . 8B75 B0 MOV ESI,DWORD PTR SS:[EBP-50]
00485E62 . 8B7D BC MOV EDI,DWORD PTR SS:[EBP-44]
00485E65 . 8B4D E0 MOV ECX,DWORD PTR SS:[EBP-20] ; 上述都是一些初始化步骤,下面开始是第一个解密算法
00485E68 > 3206 XOR AL,BYTE PTR DS:[ESI]
00485E6A . 321F XOR BL,BYTE PTR DS:[EDI]
00485E6C . C1C8 07 ROR EAX,7
00485E6F . 83C7 01 ADD EDI,1
00485E72 . 83EE 01 SUB ESI,1
00485E75 . C1C3 07 ROL EBX,7
00485E78 . 49 DEC ECX
00485E79 .^ 75 ED JNZ SHORT ORE_TUBA.00485E68 ; 循环6次
00485E7B . 8945 EC MOV DWORD PTR SS:[EBP-14],EAX ; 保存eax,值为0xA8E7FAF1
00485E7E . 895D D8 MOV DWORD PTR SS:[EBP-28],EBX ; 保存ebx,值为0xA742F274
这里就是读取SCRIPT.LPK的内容然后进行判断了。首先从文件中读取8个字节,然后比较前面四个是否为“LPK1”,不是的话退出,是的话对密钥进行解密运算,那个算法写成C代码如下:
但从文件中读取的后四个字节还不知道有什么用,先不管。下面继续单步跟过去:
00485E81 . 6A 3C PUSH 3C
00485E83 . E8 155B0500 CALL ORE_TUBA.004DB99D ; 分配一段内存,从下面的代码来看是一个结构,与解密的算法关系不大
00485E88 . 8BC8 MOV ECX,EAX
00485E8A . 83C4 04 ADD ESP,4
00485E8D . 894D DC MOV DWORD PTR SS:[EBP-24],ECX
00485E90 . 85C9 TEST ECX,ECX
00485E92 . C645 FC 01 MOV BYTE PTR SS:[EBP-4],1
00485E96 . 74 09 JE SHORT ORE_TUBA.00485EA1 ; 判断上面分配内存是否成功,失败则跳转
00485E98 . E8 A3010000 CALL ORE_TUBA.00486040 ; 上面分配的内存的初始化代码
00485E9D . 8BF8 MOV EDI,EAX
00485E9F . EB 02 JMP SHORT ORE_TUBA.00485EA3
00485EA1 > 33FF XOR EDI,EDI
00485EA3 > 85FF TEST EDI,EDI
00485EA5 . C645 FC 00 MOV BYTE PTR SS:[EBP-4],0
00485EA9 . 897D D0 MOV DWORD PTR SS:[EBP-30],EDI
00485EAC . 75 15 JNZ SHORT ORE_TUBA.00485EC3
00485EAE . 8D55 A4 LEA EDX,DWORD PTR SS:[EBP-5C]
00485EB1 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485EB6 . 52 PUSH EDX ; |Arg1
00485EB7 . C745 A4 08000>MOV DWORD PTR SS:[EBP-5C],8 ; |
00485EBE . E8 CF5D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485EC3 > A1 483F5000 MOV EAX,DWORD PTR DS:[503F48]
00485EC8 . 8B75 EC MOV ESI,DWORD PTR SS:[EBP-14]
00485ECB . 33C6 XOR EAX,ESI
00485ECD . 8947 14 MOV DWORD PTR DS:[EDI+14],EAX
00485ED0 . 8B45 D8 MOV EAX,DWORD PTR SS:[EBP-28]
00485ED3 . 8B15 503F5000 MOV EDX,DWORD PTR DS:[503F50]
00485ED9 . 8B75 CC MOV ESI,DWORD PTR SS:[EBP-34] ; [ebp-34]存的就是刚才从SCRIPT.LPK中读的第二个双字的内容
00485EDC . 33C2 XOR EAX,EDX
00485EDE . 33F0 XOR ESI,EAX ;与0xA742F274异或
00485EE0 . 8945 D8 MOV DWORD PTR SS:[EBP-28],EAX
00485EE3 . 8BC6 MOV EAX,ESI
00485EE5 . 81E6 FFFFFF00 AND ESI,0FFFFFF ;舍去高四位
00485EEB . C1E8 18 SHR EAX,18
00485EEE . 8845 EA MOV BYTE PTR SS:[EBP-16],AL
00485EF1 . 24 01 AND AL,1
00485EF3 . 8975 E0 MOV DWORD PTR SS:[EBP-20],ESI
00485EF6 . 8845 EB MOV BYTE PTR SS:[EBP-15],AL
00485EF9 . 74 09 JE SHORT ORE_TUBA.00485F04
00485EFB . C1E6 0B SHL ESI,0B
00485EFE . 83EE 08 SUB ESI,8
00485F01 . 8975 E0 MOV DWORD PTR SS:[EBP-20],ESI
00485F04 > 56 PUSH ESI ; esi=将要从文件读取的大小
00485F05 . E8 935A0500 CALL ORE_TUBA.004DB99D ; 分配空间
00485F0A . 83C4 04 ADD ESP,4 ; 平衡堆栈
00485F0D . 8947 1C MOV DWORD PTR DS:[EDI+1C],EAX
00485F10 . 85C0 TEST EAX,EAX
00485F12 . 75 15 JNZ SHORT ORE_TUBA.00485F29
00485F14 . 8D4D C0 LEA ECX,DWORD PTR SS:[EBP-40]
00485F17 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485F1C . 51 PUSH ECX ; |Arg1
00485F1D . C745 C0 08000>MOV DWORD PTR SS:[EBP-40],8 ; |
00485F24 . E8 695D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485F29 > 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14]
00485F2C . 6A 00 PUSH 0 ; /pOverlapped = NULL
00485F2E . 52 PUSH EDX ; |pBytesRead
00485F2F . 56 PUSH ESI ; |BytesToRead
00485F30 . 50 PUSH EAX ; |Buffer=0x00AE4008,记下这个地址
00485F31 . 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] ; |
00485F34 . 50 PUSH EAX ; |hFile
00485F35 . FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
00485F3B . 85C0 TEST EAX,EAX ; 判断是否成功
00485F3D . 75 15 JNZ SHORT ORE_TUBA.00485F54 ; 成功则跳转
00485F3F . 8D4D B8 LEA ECX,DWORD PTR SS:[EBP-48]
00485F42 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485F47 . 51 PUSH ECX ; |Arg1
00485F48 . C745 B8 07000>MOV DWORD PTR SS:[EBP-48],7 ; |
00485F4F . E8 3E5D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485F54 > 8B57 1C MOV EDX,DWORD PTR DS:[EDI+1C]
00485F57 . C745 D4 85627>MOV DWORD PTR SS:[EBP-2C],31746285
00485F5E . 8955 DC MOV DWORD PTR SS:[EBP-24],EDX
00485F61 . 8B45 D8 MOV EAX,DWORD PTR SS:[EBP-28] ; eax=前面算出来的第二个解密算子
00485F64 . 8B4D D4 MOV ECX,DWORD PTR SS:[EBP-2C] ; ecx=常数,用于解密
00485F67 . 8B5D E0 MOV EBX,DWORD PTR SS:[EBP-20] ; ebx=缓冲区大小
00485F6A . 8B75 DC MOV ESI,DWORD PTR SS:[EBP-24] ; esi=缓冲区地址
00485F6D . C1EB 02 SHR EBX,2 ; 这里到下面的跳转都是对缓冲区的解密换算
00485F70 > C1C1 04 ROL ECX,4
00485F73 . 3106 XOR DWORD PTR DS:[ESI],EAX
00485F75 . D3C8 ROR EAX,CL
00485F77 . 83C6 04 ADD ESI,4
00485F7A . 4B DEC EBX
00485F7B .^ 75 F3 JNZ SHORT ORE_TUBA.00485F70 ;跳回去继续循环
00485F7D . 8A45 EB MOV AL,BYTE PTR SS:[EBP-15]
00485F80 . 8B12 MOV EDX,DWORD PTR DS:[EDX] ; [EDX]为解密后的第一个双字的内容,为0x1a2,会是啥呢,我猜测是脚本文件的个数
看到了没?[EBP-34]中存储的就是从文件中读出的第二个双字,将它与上面的算法算出来的b相异或就得到0x3f03,然后可以看出分配了0x3f03个字节的内存空间,再从文件中读出0x3f03个字节的内容。然后对读出的内容进行解密,写成C代码如下:
后面还有一段读取缓冲区的操作:
0048621F . 8B3E MOV EDI,DWORD PTR DS:[ESI] ; 读取缓冲区的数据,这个是什么,下面就知道
00486221 . 83C6 04 ADD ESI,4
00486224 . 84C0 TEST AL,AL
00486226 . 8973 20 MOV DWORD PTR DS:[EBX+20],ESI
00486229 . 74 1A JE SHORT ORE_TUBA.00486245
0048622B . 8D4D EC LEA ECX,DWORD PTR SS:[EBP-14]
0048622E . 8D95 D0FEFFFF LEA EDX,DWORD PTR SS:[EBP-130]
00486234 . 51 PUSH ECX
00486235 . 52 PUSH EDX
00486236 . 56 PUSH ESI
00486237 . 8BCB MOV ECX,EBX
00486239 . C745 EC FFFFF>MOV DWORD PTR SS:[EBP-14],-1
00486240 . E8 BB020000 CALL ORE_TUBA.00486500
00486245 > 03F7 ADD ESI,EDI ; 将前面读出的双子与缓冲区偏移地址相加,再保存起来
00486247 > 8A45 10 MOV AL,BYTE PTR SS:[EBP+10]
0048624A . 8973 2C MOV DWORD PTR DS:[EBX+2C],ESI
再看看数据窗口中缓冲区的数据:
00AE4008 A2 01 00 00 00 00 BF 29 00 00 ?....?..
a
可以看出第一个双字是疑似脚本文件个数,中间两个字节作用未知,然后一个双字是一个偏移,用结构可以表达如下:
这个函数剩下的地方都没什么猛料,回到原来的函数后继续跟发现都是一些字符串的处理,一直跟到下面地址为0x004750eb的一个call,看到压栈的参数有一个为字符串“SCRIPT/LL_APP.SYS”,跟进去看看:
00472ED0 . 55 PUSH EBP
00472ED1 . 8BEC MOV EBP,ESP
00472ED3 . 6A FF PUSH -1
00472ED5 . 68 C0AB4E00 PUSH ORE_TUBA.004EABC0 ; SE 处理程序安装
00472EDA . 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00472EE0 . 50 PUSH EAX
00472EE1 . 64:8925 00000>MOV DWORD PTR FS:[0],ESP
00472EE8 . 81EC 44020000 SUB ESP,244
00472EEE . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00472EF1 . 53 PUSH EBX
00472EF2 . 56 PUSH ESI
00472EF3 . 57 PUSH EDI
00472EF4 . 8BD9 MOV EBX,ECX
00472EF6 . 8965 F0 MOV DWORD PTR SS:[EBP-10],ESP
00472EF9 . 6A 00 PUSH 0
00472EFB . 8D8D B4FEFFFF LEA ECX,DWORD PTR SS:[EBP-14C]
00472F01 . 50 PUSH EAX
00472F02 . 51 PUSH ECX
00472F03 . 895D E4 MOV DWORD PTR SS:[EBP-1C],EBX
00472F06 . C745 E8 FFFFF>MOV DWORD PTR SS:[EBP-18],-1
00472F0D . C745 FC 00000>MOV DWORD PTR SS:[EBP-4],0
00472F14 . E8 A71A0000 CALL ORE_TUBA.004749C0 ; 处理字符串
00472F19 . 83C4 0C ADD ESP,0C
00472F1C . 8945 EC MOV DWORD PTR SS:[EBP-14],EAX
00472F1F . 85C0 TEST EAX,EAX
00472F21 . 75 15 JNZ SHORT ORE_TUBA.00472F38
00472F23 . 8D55 CC LEA EDX,DWORD PTR SS:[EBP-34]
00472F26 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00472F2B . 52 PUSH EDX ; |Arg1
00472F2C . C745 CC 05000>MOV DWORD PTR SS:[EBP-34],5 ; |
00472F33 . E8 5A8D0600 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00472F38 > 8B35 B0D14E00 MOV ESI,DWORD PTR DS:[<&KERNEL32.CreateF>; kernel32.CreateFileA
00472F3E . 6A 00 PUSH 0 ; /hTemplateFile = NULL
00472F40 . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00472F45 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00472F47 . 6A 00 PUSH 0 ; |pSecurity = NULL
00472F49 . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00472F4B . 8D85 B4FEFFFF LEA EAX,DWORD PTR SS:[EBP-14C] ; |
00472F51 . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00472F56 . 50 PUSH EAX ; |FileName="I:\Program Files\Navel\壌偨偪偵梼偼側偄\SCRIPT\LL_APP.SYS"
00472F57 . FFD6 CALL ESI ; \CreateFileA
00472F59 . 8BF8 MOV EDI,EAX
00472F5B . 83FF FF CMP EDI,-1
00472F5E . 897D E8 MOV DWORD PTR SS:[EBP-18],EDI
00472F61 . 74 7E JE SHORT ORE_TUBA.00472FE1 ;跳转
这个打开一个不存在的文件,但看文件名应该猜到,这个就是解密后的脚本文件了。但由于现在文件还不存在,所以会跳到后面的代码继续执行,一开始还是一些字符串处理的流程,然后到了0x0047310d的一个call,跟进去看看,一开始是一个嵌套的循环,处理字符串的.循环后出来到了下面一个call
00486840 . 81EC 04010000 SUB ESP,104 ; 抬高堆栈
00486846 . 56 PUSH ESI
00486847 . 8BF1 MOV ESI,ECX
00486849 . 8A46 0D MOV AL,BYTE PTR DS:[ESI+D]
0048684C . 84C0 TEST AL,AL
0048684E . 74 6C JE SHORT ORE_TUBA.004868BC
00486850 . 8B8424 0C0100>MOV EAX,DWORD PTR SS:[ESP+10C]
00486857 . 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4]
0048685B . 50 PUSH EAX ; /String2
0048685C . 51 PUSH ECX ; |String1
0048685D . FF15 00D14E00 CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
00486863 . 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00486867 . 52 PUSH EDX ; /StringOrChar
00486868 . FF15 54D34E00 CALL DWORD PTR DS:[<&USER32.CharLowerA>] ; \CharLowerA
0048686E . 8A4E 24 MOV CL,BYTE PTR DS:[ESI+24]
00486871 . 8B56 20 MOV EDX,DWORD PTR DS:[ESI+20] ; [ESI+20]中的数据是0x00AE4012,而前面从文件中读取0x3f03字节的内容的缓冲区是0x00AE4008
00486874 . 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4] ; 取“ll_app.sys”地址
00486878 . 6A 00 PUSH 0
0048687A . 50 PUSH EAX
0048687B . 51 PUSH ECX
0048687C . 52 PUSH EDX
0048687D . E8 DEC30400 CALL ORE_TUBA.004D2C60 ; 又是一个处理字符串的call
然后一直单步到下面这个call:
00486898 . 51 PUSH ECX ; /Arg5
00486899 . 8B8C24 180100>MOV ECX,DWORD PTR SS:[ESP+118] ; |
004868A0 . 52 PUSH EDX ; |Arg4
004868A1 . 8B9424 180100>MOV EDX,DWORD PTR SS:[ESP+118] ; |
004868A8 . 51 PUSH ECX ; |Arg3
004868A9 . 52 PUSH EDX ; |Arg2
004868AA . 50 PUSH EAX ; |Arg1
004868AB . 8BCE MOV ECX,ESI ; |
004868AD . E8 5E000000 CALL ORE_TUBA.00486910 ;
\重要,跟进去看看,发现又是一个嵌套循环,循环完了后又有一个call,跟进去后又是一个
call……再进去后,发现到达目标了
00486780 /$ 8B41 38 MOV EAX,DWORD PTR DS:[ECX+38]
00486783 |. 56 PUSH ESI
00486784 |. 85C0 TEST EAX,EAX
00486786 |. 57 PUSH EDI
00486787 |. 74 26 JE SHORT ORE_TUBA.004867AF
00486789 |. 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
0048678D |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10]
00486791 |. 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+14]
00486795 |. 8B44D0 04 MOV EAX,DWORD PTR DS:[EAX+EDX*8+4]
00486799 |. 8A50 1D MOV DL,BYTE PTR DS:[EAX+1D]
0048679C |. 8816 MOV BYTE PTR DS:[ESI],DL
0048679E |. 8B50 04 MOV EDX,DWORD PTR DS:[EAX+4]
004867A1 |. 8B70 14 MOV ESI,DWORD PTR DS:[EAX+14]
004867A4 |. 8917 MOV DWORD PTR DS:[EDI],EDX
004867A6 |. 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8]
004867A9 |. 8B5424 18 MOV EDX,DWORD PTR SS:[ESP+18]
004867AD |. EB 5F JMP SHORT ORE_TUBA.0048680E
004867AF |> 8A51 0F MOV DL,BYTE PTR DS:[ECX+F]
004867B2 |. 33C0 XOR EAX,EAX
004867B4 |. 84D2 TEST DL,DL
004867B6 |. 0F95C0 SETNE AL
004867B9 |. 83C0 02 ADD EAX,2
004867BC |. 8B71 2C MOV ESI,DWORD PTR DS:[ECX+2C] ; esi=缓冲区内文件头的偏移起始地址
004867BF |. 8D0485 010000>LEA EAX,DWORD PTR DS:[EAX*4+1]
004867C6 |. 0FAF4424 0C IMUL EAX,DWORD PTR SS:[ESP+C] ; eax=文件头大小*文件的号数
004867CB |. 8A1430 MOV DL,BYTE PTR DS:[EAX+ESI]
004867CE |. 03C6 ADD EAX,ESI
004867D0 |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10]
004867D4 |. 40 INC EAX
004867D5 |. 83C0 04 ADD EAX,4
004867D8 |. 8816 MOV BYTE PTR DS:[ESI],DL
004867DA |. 8B70 FC MOV ESI,DWORD PTR DS:[EAX-4] ; 目标文件数据在SCRIPT.LPK内的偏移,esi=0x15EDF
004867DD |. 8A51 0C MOV DL,BYTE PTR DS:[ECX+C]
004867E0 |. 84D2 TEST DL,DL
004867E2 |. 74 03 JE SHORT ORE_TUBA.004867E7
004867E4 |. C1E6 0B SHL ESI,0B
004867E7 |> 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+14]
004867EB |. 8B10 MOV EDX,DWORD PTR DS:[EAX] ; 目标文件的大小,edx=0x19
004867ED |. 53 PUSH EBX
004867EE |. 8917 MOV DWORD PTR DS:[EDI],EDX
004867F0 |. 8B5424 1C MOV EDX,DWORD PTR SS:[ESP+1C]
004867F4 |. C702 00000000 MOV DWORD PTR DS:[EDX],0
004867FA |. 8A59 0F MOV BL,BYTE PTR DS:[ECX+F]
从这一串代码中可以看出很多有用的信息,配合数据窗口中的数据,就能写出文件头的结构了:
我怎么能确定上面的结构是否正确?因为紧跟着这段代码的是如下的调用:
00486813 |. 6A 00 PUSH 0 ; /Origin = FILE_BEGIN
00486815 |. 6A 00 PUSH 0 ; |pOffsetHi = NULL
00486817 |. 56 PUSH ESI ; |OffsetLow=15EDF
00486818 |. 51 PUSH ECX ; |hFile
00486819 |. FF15 50D24E00 CALL DWORD PTR DS:[<&KERNEL32.SetFilePoi>; \SetFilePointer
这个函数这样就完了。继续
0048694E |. E8 2DFEFFFF CALL ORE_TUBA.00486780 ; 这个上面的函数
00486953 |. 85C0 TEST EAX,EAX
00486955 |. 74 0E JE SHORT ORE_TUBA.00486965
00486957 |. 5F POP EDI
00486958 |. 5E POP ESI
00486959 |. B8 06000000 MOV EAX,6
0048695E |. 5B POP EBX
0048695F |. 8BE5 MOV ESP,EBP
00486961 |. 5D POP EBP
00486962 |. C2 1400 RETN 14
00486965 |> 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
00486968 |. 85C0 TEST EAX,EAX
0048696A |. 75 0E JNZ SHORT ORE_TUBA.0048697A
0048696C |. 5F POP EDI
0048696D |. 5E POP ESI
0048696E |. B8 02000000 MOV EAX,2
00486973 |. 5B POP EBX
00486974 |. 8BE5 MOV ESP,EBP
00486976 |. 5D POP EBP
00486977 |. C2 1400 RETN 14
0048697A |> 8B4D 10 MOV ECX,DWORD PTR SS:[EBP+10]
0048697D |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
00486980 |. 6A 00 PUSH 0
00486982 |. 51 PUSH ECX
00486983 |. 52 PUSH EDX
00486984 |. FFD6 CALL ESI ; 这个是分配缓冲区,edx就是刚才的0x19,这样就可以真正确定文件头的结构了
再经过一大串跳转,终于开始读数据了:
00486A51 |. 6A 00 PUSH 0 ; /pOverlapped = NULL
00486A53 |. 50 PUSH EAX ; |pBytesRead
00486A54 |. 56 PUSH ESI ; |BytesToRead=0x19
00486A55 |. 51 PUSH ECX ; |Buffer=0015C198
00486A56 |. 8B4F 08 MOV ECX,DWORD PTR DS:[EDI+8] ; |
00486A59 |. 51 PUSH ECX ; |hFile
00486A5A |. FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
读取文件成功后,下面终于到达我们的最终目标——解密过程了。解密过程分两段,第一段如下所示:
00486A78 |> \8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] ; 上面读入数据缓冲区地址
00486A7B |> 8A45 0F MOV AL,BYTE PTR SS:[EBP+F]
00486A7E |. 84C0 TEST AL,AL
00486A80 |. 74 27 JE SHORT ORE_TUBA.00486AA9
00486A82 |. 85F6 TEST ESI,ESI ; esi为读入数据的大小
00486A84 |. 8975 08 MOV DWORD PTR SS:[EBP+8],ESI
00486A87 |. 74 20 JE SHORT ORE_TUBA.00486AA9
00486A89 |> 8A01 /MOV AL,BYTE PTR DS:[ECX]
00486A8B |. 8AD0 |MOV DL,AL
00486A8D |. 34 50 |XOR AL,50
00486A8F |. 80F2 FD |XOR DL,0FD
00486A92 |. 80E2 0F |AND DL,0F
00486A95 |. C0E2 04 |SHL DL,4
00486A98 |. C0E8 04 |SHR AL,4
00486A9B |. 02D0 |ADD DL,AL
00486A9D |. 8811 |MOV BYTE PTR DS:[ECX],DL
00486A9F |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
00486AA2 |. 41 |INC ECX
00486AA3 |. 48 |DEC EAX
00486AA4 |. 8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
00486AA7 |.^ 75 E0 \JNZ SHORT ORE_TUBA.00486A89
转成c代码如下:
第二段解密过程为:
00486AC6 |> \8B47 14 MOV EAX,DWORD PTR DS:[EDI+14] ; 前面所算出来的第一个密钥,0xA8E7FAF1
00486AC9 |. C745 F0 85627>MOV DWORD PTR SS:[EBP-10],31746285 ; 立即数解密密钥
00486AD0 |. 8945 EC MOV DWORD PTR SS:[EBP-14],EAX
00486AD3 |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] ; eax=0xA8E7FAF1
00486AD6 |. 8B4D F0 MOV ECX,DWORD PTR SS:[EBP-10] ; ecx=0x31746285
00486AD9 |. 8B75 FC MOV ESI,DWORD PTR SS:[EBP-4] ; esi=0x0015C198,缓冲区地址
00486ADC |. 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8] ; ebx=0x06,即为缓冲区大小,此时每次循环解密一个双字
00486ADF |> C1C9 04 /ROR ECX,4
00486AE2 |. 3106 |XOR DWORD PTR DS:[ESI],EAX
00486AE4 |. D3C0 |ROL EAX,CL
00486AE6 |. 83C6 04 |ADD ESI,4
00486AE9 |. 4B |DEC EBX
00486AEA |.^ 75 F3 \JNZ SHORT ORE_TUBA.00486ADF ; 跳回去继续循环
这段代码跟前面的是不是很像?不过有些不一样,首先是解密的密钥用的是第一个不是用第二个,然后是循环移位的方向与前面的相反了,写成c代码如下:
好了,整个解密算法就完了。怎样知道答案对不对?用专门的解包软件(如crass)将脚本文件解密,用WinHex打开对应的文件然后对比一下就知道了。
另外,上述的解密过程一次解密一个双字的,所以最后剩下一个字节没有做任何操作。
剩下最后一个问题是,前面所说脚本文件的个数是0x1a2,这个怎样确定?由于有些脚本文件在游戏过程中才进行解密的,所以我用一个很笨的方法来确定:用软件解密脚本文件后,脚本文件总数418,即16进制的0x1a2.
限于篇幅,其他资源的解密过程就不贴了,其他资源的解密要用到解密后的脚本文件中的数据做key,至于解密后各个脚本的文件名么……没分析出来orz。
首先是老规矩,对CreateFileA下段,来到下面的地方:
00473839 > \6A 00 PUSH 0 ; /hTemplateFile = NULL
0047383B . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00473840 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00473842 . 6A 00 PUSH 0 ; |pSecurity = NULL
00473844 . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00473846 . 8D95 D0FEFFFF LEA EDX,DWORD PTR SS:[EBP-130] ; |
0047384C . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00473851 . 52 PUSH EDX ; |FileName= "I:\Program Files\Navel\壌偨偪偵梼偼側偄\SCRIPT.LPK"
00473852 . FF15 B0D14E00 CALL DWORD PTR DS:[<&KERNEL32.CreateFile>; \CreateFileA
00473858 . 8BD8 MOV EBX,EAX
0047385A . 83FB FF CMP EBX,-1 ; 判断打开是否成功,句柄为-1则不跳
0047385D . 895D E0 MOV DWORD PTR SS:[EBP-20],EBX ; 保存句柄,我的机器上句柄为90
00473860 . 75 18 JNZ SHORT ORE_TUBA.0047387A ; 句柄有效则跳转
00473862 . B8 03000000 MOV EAX,3
00473867 . 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
0047386A . 64:890D 00000>MOV DWORD PTR FS:[0],ECX
00473871 . 5F POP EDI
00473872 . 5E POP ESI
00473873 . 5B POP EBX
00473874 . 8BE5 MOV ESP,EBP
00473876 . 5D POP EBP
00473877 . C2 1400 RETN 14
0047387A > 8B07 MOV EAX,DWORD PTR DS:[EDI]
0047387C . 56 PUSH ESI ; 文件名“SCRIPT.LPK"
0047387D . 8BCF MOV ECX,EDI
0047387F . FF50 5C CALL DWORD PTR DS:[EAX+5C] ; 处理字符串的函数,将文件名转换为大写,解密的时候要用到
00473882 . 85C0 TEST EAX,EAX ; 判断是否成功
00473884 . 74 52 JE SHORT ORE_TUBA.004738D8
00473886 . 8B10 MOV EDX,DWORD PTR DS:[EAX]
00473888 . 8D4D 08 LEA ECX,DWORD PTR SS:[EBP+8]
0047388B . 51 PUSH ECX
0047388C . 8D8D D0FEFFFF LEA ECX,DWORD PTR SS:[EBP-130]
00473892 . 53 PUSH EBX
00473893 . 51 PUSH ECX
00473894 . 8BC8 MOV ECX,EAX
00473896 . FF52 04 CALL DWORD PTR DS:[EDX+4] ; 重要函数,跟进去看看
00473899 . 85C0 TEST EAX,EAX
0047389B . 8945 E8 MOV DWORD PTR SS:[EBP-18],EAX
运气不错,第一次断下就是要打开脚本文件“SCRIPT.LPK”了。一直跟到call 00485D20这个地方,有点可疑,跟进去看看。
00485D20 . 55 PUSH EBP
00485D21 . 8BEC MOV EBP,ESP
00485D23 . 6A FF PUSH -1
00485D25 . 68 EBB44E00 PUSH ORE_TUBA.004EB4EB ; SE 处理程序安装
00485D2A . 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00485D30 . 50 PUSH EAX
00485D31 . 64:8925 00000>MOV DWORD PTR FS:[0],ESP
00485D38 . 81EC 54020000 SUB ESP,254
00485D3E . 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C] ; 取句柄
00485D41 . 53 PUSH EBX
00485D42 . 56 PUSH ESI
00485D43 . 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
00485D46 . 57 PUSH EDI
00485D47 . 33FF XOR EDI,EDI
00485D49 . 83F8 FF CMP EAX,-1 ; 判断句柄是否有效
00485D4C . 8965 F0 MOV DWORD PTR SS:[EBP-10],ESP
00485D4F . 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
00485D52 . 897D D0 MOV DWORD PTR SS:[EBP-30],EDI
00485D55 . 897D FC MOV DWORD PTR SS:[EBP-4],EDI
00485D58 . 75 4D JNZ SHORT ORE_TUBA.00485DA7 ; 句柄有效则跳转
00485D5A . 3BF7 CMP ESI,EDI
00485D5C . 75 15 JNZ SHORT ORE_TUBA.00485D73
00485D5E . 8D45 A8 LEA EAX,DWORD PTR SS:[EBP-58]
00485D61 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485D66 . 50 PUSH EAX ; |Arg1
00485D67 . C745 A8 05000>MOV DWORD PTR SS:[EBP-58],5 ; |
00485D6E . E8 1F5F0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485D73 > 57 PUSH EDI ; /hTemplateFile
00485D74 . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00485D79 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00485D7B . 57 PUSH EDI ; |pSecurity
00485D7C . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00485D7E . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00485D83 . 56 PUSH ESI ; |FileName
00485D84 . FF15 B0D14E00 CALL DWORD PTR DS:[<&KERNEL32.CreateFile>; \CreateFileA
00485D8A . 83F8 FF CMP EAX,-1
00485D8D . 8945 E4 MOV DWORD PTR SS:[EBP-1C],EAX
00485D90 . 75 15 JNZ SHORT ORE_TUBA.00485DA7
00485D92 . 8D4D B4 LEA ECX,DWORD PTR SS:[EBP-4C]
00485D95 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485D9A . 51 PUSH ECX ; |Arg1
00485D9B . C745 B4 03000>MOV DWORD PTR SS:[EBP-4C],3 ; |
00485DA2 . E8 EB5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DA7 > 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-1C] ; 取句柄
00485DAA . 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14] ; 缓冲区
00485DAD . 57 PUSH EDI ; /pOverlapped
00485DAE . 52 PUSH EDX ; |pBytesRead
00485DAF . 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] ; |
00485DB2 . 6A 08 PUSH 8 ; |BytesToRead = 8
00485DB4 . 50 PUSH EAX ; |Buffer
00485DB5 . 51 PUSH ECX ; |hFile
00485DB6 . FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
00485DBC . 85C0 TEST EAX,EAX ; 测试是否成功
00485DBE . 75 15 JNZ SHORT ORE_TUBA.00485DD5 ; 不为0则跳
00485DC0 . 8D55 AC LEA EDX,DWORD PTR SS:[EBP-54]
00485DC3 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485DC8 . 52 PUSH EDX ; |Arg1
00485DC9 . C745 AC 07000>MOV DWORD PTR SS:[EBP-54],7 ; |
00485DD0 . E8 BD5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DD5 > 817D C8 4C504>CMP DWORD PTR SS:[EBP-38],314B504C ; 跳到这里,判断文件开头四个字节是否”LPK1“
00485DDC . 74 15 JE SHORT ORE_TUBA.00485DF3 ; 是的话跳转进行处理
00485DDE . 8D45 C4 LEA EAX,DWORD PTR SS:[EBP-3C]
00485DE1 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485DE6 . 50 PUSH EAX ; |Arg1
00485DE7 . C745 C4 01000>MOV DWORD PTR SS:[EBP-3C],1 ; |
00485DEE . E8 9F5E0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485DF3 > 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485DF9 . 6A 01 PUSH 1
00485DFB . 51 PUSH ECX
00485DFC . 56 PUSH ESI
00485DFD . E8 0EEEFEFF CALL ORE_TUBA.00474C10 ; 此处也是一个字符串复制的函数,经过一串比较后复制了”SCRIPT.LPK"字符串,但具体作用未知- -
00485E02 . 83C4 0C ADD ESP,0C ; 平衡堆栈
00485E05 . 8D95 A0FDFFFF LEA EDX,DWORD PTR SS:[EBP-260]
00485E0B . 8D85 A0FEFFFF LEA EAX,DWORD PTR SS:[EBP-160]
00485E11 . 52 PUSH EDX
00485E12 . 50 PUSH EAX
00485E13 . E8 08F0FEFF CALL ORE_TUBA.00474E20 ; 又是处理文件名的函数,去掉"SCRIPT"的后缀名
00485E18 . 83C4 08 ADD ESP,8 ; 平衡堆栈
00485E1B . 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485E21 . 51 PUSH ECX ; /StringOrChar
00485E22 . FF15 58D34E00 CALL DWORD PTR DS:[<&USER32.CharUpperA>] ; \CharUpperA
00485E28 . 8D95 A0FEFFFF LEA EDX,DWORD PTR SS:[EBP-160] ; 取“SCRIPT”的地址
00485E2E . 52 PUSH EDX ; /String
00485E2F . FF15 FCD04E00 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] ; \lstrlenA
00485E35 . 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX
00485E38 . 8D8405 9FFEFF>LEA EAX,DWORD PTR SS:[EBP+EAX-161]
00485E3F . 8D8D A0FEFFFF LEA ECX,DWORD PTR SS:[EBP-160]
00485E45 . C745 DC 6BACB>MOV DWORD PTR SS:[EBP-24],A5B9AC6B
00485E4C . C745 D4 E59D6>MOV DWORD PTR SS:[EBP-2C],9A639DE5
00485E53 . 8945 B0 MOV DWORD PTR SS:[EBP-50],EAX
00485E56 . 894D BC MOV DWORD PTR SS:[EBP-44],ECX
00485E59 . 8B45 DC MOV EAX,DWORD PTR SS:[EBP-24]
00485E5C . 8B5D D4 MOV EBX,DWORD PTR SS:[EBP-2C]
00485E5F . 8B75 B0 MOV ESI,DWORD PTR SS:[EBP-50]
00485E62 . 8B7D BC MOV EDI,DWORD PTR SS:[EBP-44]
00485E65 . 8B4D E0 MOV ECX,DWORD PTR SS:[EBP-20] ; 上述都是一些初始化步骤,下面开始是第一个解密算法
00485E68 > 3206 XOR AL,BYTE PTR DS:[ESI]
00485E6A . 321F XOR BL,BYTE PTR DS:[EDI]
00485E6C . C1C8 07 ROR EAX,7
00485E6F . 83C7 01 ADD EDI,1
00485E72 . 83EE 01 SUB ESI,1
00485E75 . C1C3 07 ROL EBX,7
00485E78 . 49 DEC ECX
00485E79 .^ 75 ED JNZ SHORT ORE_TUBA.00485E68 ; 循环6次
00485E7B . 8945 EC MOV DWORD PTR SS:[EBP-14],EAX ; 保存eax,值为0xA8E7FAF1
00485E7E . 895D D8 MOV DWORD PTR SS:[EBP-28],EBX ; 保存ebx,值为0xA742F274
这里就是读取SCRIPT.LPK的内容然后进行判断了。首先从文件中读取8个字节,然后比较前面四个是否为“LPK1”,不是的话退出,是的话对密钥进行解密运算,那个算法写成C代码如下:
#include<stdio.h> #define ror(m,n) (m>>n)|(m<<(8*sizeof(int)-n)) #define rol(m,n) (m<<n)|(m>>(8*sizeof(int)-n)) main() { char name[]="SCRIPT"; unsigned int a=0xa5b9ac6b,b=0x9a639de5; printf("0x%08x 0x%02x\n",a,b); for(int i=0,j=5;i<6;i++,j--) { *(char*)&a^=name[j]; *(char*)&b^=name[i]; a=ror(a,7); b=rol(b,7); printf("0x%08x 0x%08x\n",a,b); } printf("0x%08x 0x%08x\n",a,b); }
但从文件中读取的后四个字节还不知道有什么用,先不管。下面继续单步跟过去:
00485E81 . 6A 3C PUSH 3C
00485E83 . E8 155B0500 CALL ORE_TUBA.004DB99D ; 分配一段内存,从下面的代码来看是一个结构,与解密的算法关系不大
00485E88 . 8BC8 MOV ECX,EAX
00485E8A . 83C4 04 ADD ESP,4
00485E8D . 894D DC MOV DWORD PTR SS:[EBP-24],ECX
00485E90 . 85C9 TEST ECX,ECX
00485E92 . C645 FC 01 MOV BYTE PTR SS:[EBP-4],1
00485E96 . 74 09 JE SHORT ORE_TUBA.00485EA1 ; 判断上面分配内存是否成功,失败则跳转
00485E98 . E8 A3010000 CALL ORE_TUBA.00486040 ; 上面分配的内存的初始化代码
00485E9D . 8BF8 MOV EDI,EAX
00485E9F . EB 02 JMP SHORT ORE_TUBA.00485EA3
00485EA1 > 33FF XOR EDI,EDI
00485EA3 > 85FF TEST EDI,EDI
00485EA5 . C645 FC 00 MOV BYTE PTR SS:[EBP-4],0
00485EA9 . 897D D0 MOV DWORD PTR SS:[EBP-30],EDI
00485EAC . 75 15 JNZ SHORT ORE_TUBA.00485EC3
00485EAE . 8D55 A4 LEA EDX,DWORD PTR SS:[EBP-5C]
00485EB1 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485EB6 . 52 PUSH EDX ; |Arg1
00485EB7 . C745 A4 08000>MOV DWORD PTR SS:[EBP-5C],8 ; |
00485EBE . E8 CF5D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485EC3 > A1 483F5000 MOV EAX,DWORD PTR DS:[503F48]
00485EC8 . 8B75 EC MOV ESI,DWORD PTR SS:[EBP-14]
00485ECB . 33C6 XOR EAX,ESI
00485ECD . 8947 14 MOV DWORD PTR DS:[EDI+14],EAX
00485ED0 . 8B45 D8 MOV EAX,DWORD PTR SS:[EBP-28]
00485ED3 . 8B15 503F5000 MOV EDX,DWORD PTR DS:[503F50]
00485ED9 . 8B75 CC MOV ESI,DWORD PTR SS:[EBP-34] ; [ebp-34]存的就是刚才从SCRIPT.LPK中读的第二个双字的内容
00485EDC . 33C2 XOR EAX,EDX
00485EDE . 33F0 XOR ESI,EAX ;与0xA742F274异或
00485EE0 . 8945 D8 MOV DWORD PTR SS:[EBP-28],EAX
00485EE3 . 8BC6 MOV EAX,ESI
00485EE5 . 81E6 FFFFFF00 AND ESI,0FFFFFF ;舍去高四位
00485EEB . C1E8 18 SHR EAX,18
00485EEE . 8845 EA MOV BYTE PTR SS:[EBP-16],AL
00485EF1 . 24 01 AND AL,1
00485EF3 . 8975 E0 MOV DWORD PTR SS:[EBP-20],ESI
00485EF6 . 8845 EB MOV BYTE PTR SS:[EBP-15],AL
00485EF9 . 74 09 JE SHORT ORE_TUBA.00485F04
00485EFB . C1E6 0B SHL ESI,0B
00485EFE . 83EE 08 SUB ESI,8
00485F01 . 8975 E0 MOV DWORD PTR SS:[EBP-20],ESI
00485F04 > 56 PUSH ESI ; esi=将要从文件读取的大小
00485F05 . E8 935A0500 CALL ORE_TUBA.004DB99D ; 分配空间
00485F0A . 83C4 04 ADD ESP,4 ; 平衡堆栈
00485F0D . 8947 1C MOV DWORD PTR DS:[EDI+1C],EAX
00485F10 . 85C0 TEST EAX,EAX
00485F12 . 75 15 JNZ SHORT ORE_TUBA.00485F29
00485F14 . 8D4D C0 LEA ECX,DWORD PTR SS:[EBP-40]
00485F17 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485F1C . 51 PUSH ECX ; |Arg1
00485F1D . C745 C0 08000>MOV DWORD PTR SS:[EBP-40],8 ; |
00485F24 . E8 695D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485F29 > 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14]
00485F2C . 6A 00 PUSH 0 ; /pOverlapped = NULL
00485F2E . 52 PUSH EDX ; |pBytesRead
00485F2F . 56 PUSH ESI ; |BytesToRead
00485F30 . 50 PUSH EAX ; |Buffer=0x00AE4008,记下这个地址
00485F31 . 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] ; |
00485F34 . 50 PUSH EAX ; |hFile
00485F35 . FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
00485F3B . 85C0 TEST EAX,EAX ; 判断是否成功
00485F3D . 75 15 JNZ SHORT ORE_TUBA.00485F54 ; 成功则跳转
00485F3F . 8D4D B8 LEA ECX,DWORD PTR SS:[EBP-48]
00485F42 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00485F47 . 51 PUSH ECX ; |Arg1
00485F48 . C745 B8 07000>MOV DWORD PTR SS:[EBP-48],7 ; |
00485F4F . E8 3E5D0500 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00485F54 > 8B57 1C MOV EDX,DWORD PTR DS:[EDI+1C]
00485F57 . C745 D4 85627>MOV DWORD PTR SS:[EBP-2C],31746285
00485F5E . 8955 DC MOV DWORD PTR SS:[EBP-24],EDX
00485F61 . 8B45 D8 MOV EAX,DWORD PTR SS:[EBP-28] ; eax=前面算出来的第二个解密算子
00485F64 . 8B4D D4 MOV ECX,DWORD PTR SS:[EBP-2C] ; ecx=常数,用于解密
00485F67 . 8B5D E0 MOV EBX,DWORD PTR SS:[EBP-20] ; ebx=缓冲区大小
00485F6A . 8B75 DC MOV ESI,DWORD PTR SS:[EBP-24] ; esi=缓冲区地址
00485F6D . C1EB 02 SHR EBX,2 ; 这里到下面的跳转都是对缓冲区的解密换算
00485F70 > C1C1 04 ROL ECX,4
00485F73 . 3106 XOR DWORD PTR DS:[ESI],EAX
00485F75 . D3C8 ROR EAX,CL
00485F77 . 83C6 04 ADD ESI,4
00485F7A . 4B DEC EBX
00485F7B .^ 75 F3 JNZ SHORT ORE_TUBA.00485F70 ;跳回去继续循环
00485F7D . 8A45 EB MOV AL,BYTE PTR SS:[EBP-15]
00485F80 . 8B12 MOV EDX,DWORD PTR DS:[EDX] ; [EDX]为解密后的第一个双字的内容,为0x1a2,会是啥呢,我猜测是脚本文件的个数
看到了没?[EBP-34]中存储的就是从文件中读出的第二个双字,将它与上面的算法算出来的b相异或就得到0x3f03,然后可以看出分配了0x3f03个字节的内存空间,再从文件中读出0x3f03个字节的内容。然后对读出的内容进行解密,写成C代码如下:
int data[]//缓冲区地址 int n=0x31746285; int key=0xa742f274; for(i=0;i<0x3f03/4;i++) { n=rol(n,4); data[i]^=key; key=ror(key,*(char*)&n); }
后面还有一段读取缓冲区的操作:
0048621F . 8B3E MOV EDI,DWORD PTR DS:[ESI] ; 读取缓冲区的数据,这个是什么,下面就知道
00486221 . 83C6 04 ADD ESI,4
00486224 . 84C0 TEST AL,AL
00486226 . 8973 20 MOV DWORD PTR DS:[EBX+20],ESI
00486229 . 74 1A JE SHORT ORE_TUBA.00486245
0048622B . 8D4D EC LEA ECX,DWORD PTR SS:[EBP-14]
0048622E . 8D95 D0FEFFFF LEA EDX,DWORD PTR SS:[EBP-130]
00486234 . 51 PUSH ECX
00486235 . 52 PUSH EDX
00486236 . 56 PUSH ESI
00486237 . 8BCB MOV ECX,EBX
00486239 . C745 EC FFFFF>MOV DWORD PTR SS:[EBP-14],-1
00486240 . E8 BB020000 CALL ORE_TUBA.00486500
00486245 > 03F7 ADD ESI,EDI ; 将前面读出的双子与缓冲区偏移地址相加,再保存起来
00486247 > 8A45 10 MOV AL,BYTE PTR SS:[EBP+10]
0048624A . 8973 2C MOV DWORD PTR DS:[EBX+2C],ESI
再看看数据窗口中缓冲区的数据:
00AE4008 A2 01 00 00 00 00 BF 29 00 00 ?....?..
a
可以看出第一个双字是疑似脚本文件个数,中间两个字节作用未知,然后一个双字是一个偏移,用结构可以表达如下:
struct header{ DWORD numberoffile; BYTE unknow[2]; DWORD offset; ……};
这个函数剩下的地方都没什么猛料,回到原来的函数后继续跟发现都是一些字符串的处理,一直跟到下面地址为0x004750eb的一个call,看到压栈的参数有一个为字符串“SCRIPT/LL_APP.SYS”,跟进去看看:
00472ED0 . 55 PUSH EBP
00472ED1 . 8BEC MOV EBP,ESP
00472ED3 . 6A FF PUSH -1
00472ED5 . 68 C0AB4E00 PUSH ORE_TUBA.004EABC0 ; SE 处理程序安装
00472EDA . 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00472EE0 . 50 PUSH EAX
00472EE1 . 64:8925 00000>MOV DWORD PTR FS:[0],ESP
00472EE8 . 81EC 44020000 SUB ESP,244
00472EEE . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00472EF1 . 53 PUSH EBX
00472EF2 . 56 PUSH ESI
00472EF3 . 57 PUSH EDI
00472EF4 . 8BD9 MOV EBX,ECX
00472EF6 . 8965 F0 MOV DWORD PTR SS:[EBP-10],ESP
00472EF9 . 6A 00 PUSH 0
00472EFB . 8D8D B4FEFFFF LEA ECX,DWORD PTR SS:[EBP-14C]
00472F01 . 50 PUSH EAX
00472F02 . 51 PUSH ECX
00472F03 . 895D E4 MOV DWORD PTR SS:[EBP-1C],EBX
00472F06 . C745 E8 FFFFF>MOV DWORD PTR SS:[EBP-18],-1
00472F0D . C745 FC 00000>MOV DWORD PTR SS:[EBP-4],0
00472F14 . E8 A71A0000 CALL ORE_TUBA.004749C0 ; 处理字符串
00472F19 . 83C4 0C ADD ESP,0C
00472F1C . 8945 EC MOV DWORD PTR SS:[EBP-14],EAX
00472F1F . 85C0 TEST EAX,EAX
00472F21 . 75 15 JNZ SHORT ORE_TUBA.00472F38
00472F23 . 8D55 CC LEA EDX,DWORD PTR SS:[EBP-34]
00472F26 . 68 68604F00 PUSH ORE_TUBA.004F6068 ; /Arg2 = 004F6068
00472F2B . 52 PUSH EDX ; |Arg1
00472F2C . C745 CC 05000>MOV DWORD PTR SS:[EBP-34],5 ; |
00472F33 . E8 5A8D0600 CALL ORE_TUBA.004DBC92 ; \ORE_TUBA.004DBC92
00472F38 > 8B35 B0D14E00 MOV ESI,DWORD PTR DS:[<&KERNEL32.CreateF>; kernel32.CreateFileA
00472F3E . 6A 00 PUSH 0 ; /hTemplateFile = NULL
00472F40 . 68 80000000 PUSH 80 ; |Attributes = NORMAL
00472F45 . 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
00472F47 . 6A 00 PUSH 0 ; |pSecurity = NULL
00472F49 . 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00472F4B . 8D85 B4FEFFFF LEA EAX,DWORD PTR SS:[EBP-14C] ; |
00472F51 . 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00472F56 . 50 PUSH EAX ; |FileName="I:\Program Files\Navel\壌偨偪偵梼偼側偄\SCRIPT\LL_APP.SYS"
00472F57 . FFD6 CALL ESI ; \CreateFileA
00472F59 . 8BF8 MOV EDI,EAX
00472F5B . 83FF FF CMP EDI,-1
00472F5E . 897D E8 MOV DWORD PTR SS:[EBP-18],EDI
00472F61 . 74 7E JE SHORT ORE_TUBA.00472FE1 ;跳转
这个打开一个不存在的文件,但看文件名应该猜到,这个就是解密后的脚本文件了。但由于现在文件还不存在,所以会跳到后面的代码继续执行,一开始还是一些字符串处理的流程,然后到了0x0047310d的一个call,跟进去看看,一开始是一个嵌套的循环,处理字符串的.循环后出来到了下面一个call
00486840 . 81EC 04010000 SUB ESP,104 ; 抬高堆栈
00486846 . 56 PUSH ESI
00486847 . 8BF1 MOV ESI,ECX
00486849 . 8A46 0D MOV AL,BYTE PTR DS:[ESI+D]
0048684C . 84C0 TEST AL,AL
0048684E . 74 6C JE SHORT ORE_TUBA.004868BC
00486850 . 8B8424 0C0100>MOV EAX,DWORD PTR SS:[ESP+10C]
00486857 . 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4]
0048685B . 50 PUSH EAX ; /String2
0048685C . 51 PUSH ECX ; |String1
0048685D . FF15 00D14E00 CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
00486863 . 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00486867 . 52 PUSH EDX ; /StringOrChar
00486868 . FF15 54D34E00 CALL DWORD PTR DS:[<&USER32.CharLowerA>] ; \CharLowerA
0048686E . 8A4E 24 MOV CL,BYTE PTR DS:[ESI+24]
00486871 . 8B56 20 MOV EDX,DWORD PTR DS:[ESI+20] ; [ESI+20]中的数据是0x00AE4012,而前面从文件中读取0x3f03字节的内容的缓冲区是0x00AE4008
00486874 . 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4] ; 取“ll_app.sys”地址
00486878 . 6A 00 PUSH 0
0048687A . 50 PUSH EAX
0048687B . 51 PUSH ECX
0048687C . 52 PUSH EDX
0048687D . E8 DEC30400 CALL ORE_TUBA.004D2C60 ; 又是一个处理字符串的call
然后一直单步到下面这个call:
00486898 . 51 PUSH ECX ; /Arg5
00486899 . 8B8C24 180100>MOV ECX,DWORD PTR SS:[ESP+118] ; |
004868A0 . 52 PUSH EDX ; |Arg4
004868A1 . 8B9424 180100>MOV EDX,DWORD PTR SS:[ESP+118] ; |
004868A8 . 51 PUSH ECX ; |Arg3
004868A9 . 52 PUSH EDX ; |Arg2
004868AA . 50 PUSH EAX ; |Arg1
004868AB . 8BCE MOV ECX,ESI ; |
004868AD . E8 5E000000 CALL ORE_TUBA.00486910 ;
\重要,跟进去看看,发现又是一个嵌套循环,循环完了后又有一个call,跟进去后又是一个
call……再进去后,发现到达目标了
00486780 /$ 8B41 38 MOV EAX,DWORD PTR DS:[ECX+38]
00486783 |. 56 PUSH ESI
00486784 |. 85C0 TEST EAX,EAX
00486786 |. 57 PUSH EDI
00486787 |. 74 26 JE SHORT ORE_TUBA.004867AF
00486789 |. 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
0048678D |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10]
00486791 |. 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+14]
00486795 |. 8B44D0 04 MOV EAX,DWORD PTR DS:[EAX+EDX*8+4]
00486799 |. 8A50 1D MOV DL,BYTE PTR DS:[EAX+1D]
0048679C |. 8816 MOV BYTE PTR DS:[ESI],DL
0048679E |. 8B50 04 MOV EDX,DWORD PTR DS:[EAX+4]
004867A1 |. 8B70 14 MOV ESI,DWORD PTR DS:[EAX+14]
004867A4 |. 8917 MOV DWORD PTR DS:[EDI],EDX
004867A6 |. 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8]
004867A9 |. 8B5424 18 MOV EDX,DWORD PTR SS:[ESP+18]
004867AD |. EB 5F JMP SHORT ORE_TUBA.0048680E
004867AF |> 8A51 0F MOV DL,BYTE PTR DS:[ECX+F]
004867B2 |. 33C0 XOR EAX,EAX
004867B4 |. 84D2 TEST DL,DL
004867B6 |. 0F95C0 SETNE AL
004867B9 |. 83C0 02 ADD EAX,2
004867BC |. 8B71 2C MOV ESI,DWORD PTR DS:[ECX+2C] ; esi=缓冲区内文件头的偏移起始地址
004867BF |. 8D0485 010000>LEA EAX,DWORD PTR DS:[EAX*4+1]
004867C6 |. 0FAF4424 0C IMUL EAX,DWORD PTR SS:[ESP+C] ; eax=文件头大小*文件的号数
004867CB |. 8A1430 MOV DL,BYTE PTR DS:[EAX+ESI]
004867CE |. 03C6 ADD EAX,ESI
004867D0 |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10]
004867D4 |. 40 INC EAX
004867D5 |. 83C0 04 ADD EAX,4
004867D8 |. 8816 MOV BYTE PTR DS:[ESI],DL
004867DA |. 8B70 FC MOV ESI,DWORD PTR DS:[EAX-4] ; 目标文件数据在SCRIPT.LPK内的偏移,esi=0x15EDF
004867DD |. 8A51 0C MOV DL,BYTE PTR DS:[ECX+C]
004867E0 |. 84D2 TEST DL,DL
004867E2 |. 74 03 JE SHORT ORE_TUBA.004867E7
004867E4 |. C1E6 0B SHL ESI,0B
004867E7 |> 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+14]
004867EB |. 8B10 MOV EDX,DWORD PTR DS:[EAX] ; 目标文件的大小,edx=0x19
004867ED |. 53 PUSH EBX
004867EE |. 8917 MOV DWORD PTR DS:[EDI],EDX
004867F0 |. 8B5424 1C MOV EDX,DWORD PTR SS:[ESP+1C]
004867F4 |. C702 00000000 MOV DWORD PTR DS:[EDX],0
004867FA |. 8A59 0F MOV BL,BYTE PTR DS:[ECX+F]
从这一串代码中可以看出很多有用的信息,配合数据窗口中的数据,就能写出文件头的结构了:
struct fileheader{ BYTE type; //? DWORD offset; DWORD size; DWORD unknow; };
我怎么能确定上面的结构是否正确?因为紧跟着这段代码的是如下的调用:
00486813 |. 6A 00 PUSH 0 ; /Origin = FILE_BEGIN
00486815 |. 6A 00 PUSH 0 ; |pOffsetHi = NULL
00486817 |. 56 PUSH ESI ; |OffsetLow=15EDF
00486818 |. 51 PUSH ECX ; |hFile
00486819 |. FF15 50D24E00 CALL DWORD PTR DS:[<&KERNEL32.SetFilePoi>; \SetFilePointer
这个函数这样就完了。继续
0048694E |. E8 2DFEFFFF CALL ORE_TUBA.00486780 ; 这个上面的函数
00486953 |. 85C0 TEST EAX,EAX
00486955 |. 74 0E JE SHORT ORE_TUBA.00486965
00486957 |. 5F POP EDI
00486958 |. 5E POP ESI
00486959 |. B8 06000000 MOV EAX,6
0048695E |. 5B POP EBX
0048695F |. 8BE5 MOV ESP,EBP
00486961 |. 5D POP EBP
00486962 |. C2 1400 RETN 14
00486965 |> 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
00486968 |. 85C0 TEST EAX,EAX
0048696A |. 75 0E JNZ SHORT ORE_TUBA.0048697A
0048696C |. 5F POP EDI
0048696D |. 5E POP ESI
0048696E |. B8 02000000 MOV EAX,2
00486973 |. 5B POP EBX
00486974 |. 8BE5 MOV ESP,EBP
00486976 |. 5D POP EBP
00486977 |. C2 1400 RETN 14
0048697A |> 8B4D 10 MOV ECX,DWORD PTR SS:[EBP+10]
0048697D |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
00486980 |. 6A 00 PUSH 0
00486982 |. 51 PUSH ECX
00486983 |. 52 PUSH EDX
00486984 |. FFD6 CALL ESI ; 这个是分配缓冲区,edx就是刚才的0x19,这样就可以真正确定文件头的结构了
再经过一大串跳转,终于开始读数据了:
00486A51 |. 6A 00 PUSH 0 ; /pOverlapped = NULL
00486A53 |. 50 PUSH EAX ; |pBytesRead
00486A54 |. 56 PUSH ESI ; |BytesToRead=0x19
00486A55 |. 51 PUSH ECX ; |Buffer=0015C198
00486A56 |. 8B4F 08 MOV ECX,DWORD PTR DS:[EDI+8] ; |
00486A59 |. 51 PUSH ECX ; |hFile
00486A5A |. FF15 3CD24E00 CALL DWORD PTR DS:[<&KERNEL32.ReadFile>] ; \ReadFile
读取文件成功后,下面终于到达我们的最终目标——解密过程了。解密过程分两段,第一段如下所示:
00486A78 |> \8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] ; 上面读入数据缓冲区地址
00486A7B |> 8A45 0F MOV AL,BYTE PTR SS:[EBP+F]
00486A7E |. 84C0 TEST AL,AL
00486A80 |. 74 27 JE SHORT ORE_TUBA.00486AA9
00486A82 |. 85F6 TEST ESI,ESI ; esi为读入数据的大小
00486A84 |. 8975 08 MOV DWORD PTR SS:[EBP+8],ESI
00486A87 |. 74 20 JE SHORT ORE_TUBA.00486AA9
00486A89 |> 8A01 /MOV AL,BYTE PTR DS:[ECX]
00486A8B |. 8AD0 |MOV DL,AL
00486A8D |. 34 50 |XOR AL,50
00486A8F |. 80F2 FD |XOR DL,0FD
00486A92 |. 80E2 0F |AND DL,0F
00486A95 |. C0E2 04 |SHL DL,4
00486A98 |. C0E8 04 |SHR AL,4
00486A9B |. 02D0 |ADD DL,AL
00486A9D |. 8811 |MOV BYTE PTR DS:[ECX],DL
00486A9F |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
00486AA2 |. 41 |INC ECX
00486AA3 |. 48 |DEC EAX
00486AA4 |. 8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
00486AA7 |.^ 75 E0 \JNZ SHORT ORE_TUBA.00486A89
转成c代码如下:
unsigned char data[];//缓冲区 unsigned char a,b; for(int i=0;i<0x19;i++) { a=data[i]; b=a; a^=0x50; b^=0xfd; b&=0x0f; b<<=4; a>>=4; b+=a; data[i]=b; }
第二段解密过程为:
00486AC6 |> \8B47 14 MOV EAX,DWORD PTR DS:[EDI+14] ; 前面所算出来的第一个密钥,0xA8E7FAF1
00486AC9 |. C745 F0 85627>MOV DWORD PTR SS:[EBP-10],31746285 ; 立即数解密密钥
00486AD0 |. 8945 EC MOV DWORD PTR SS:[EBP-14],EAX
00486AD3 |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] ; eax=0xA8E7FAF1
00486AD6 |. 8B4D F0 MOV ECX,DWORD PTR SS:[EBP-10] ; ecx=0x31746285
00486AD9 |. 8B75 FC MOV ESI,DWORD PTR SS:[EBP-4] ; esi=0x0015C198,缓冲区地址
00486ADC |. 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8] ; ebx=0x06,即为缓冲区大小,此时每次循环解密一个双字
00486ADF |> C1C9 04 /ROR ECX,4
00486AE2 |. 3106 |XOR DWORD PTR DS:[ESI],EAX
00486AE4 |. D3C0 |ROL EAX,CL
00486AE6 |. 83C6 04 |ADD ESI,4
00486AE9 |. 4B |DEC EBX
00486AEA |.^ 75 F3 \JNZ SHORT ORE_TUBA.00486ADF ; 跳回去继续循环
这段代码跟前面的是不是很像?不过有些不一样,首先是解密的密钥用的是第一个不是用第二个,然后是循环移位的方向与前面的相反了,写成c代码如下:
int data[]//缓冲区地址 int n=0x31746285; int key=0xa8e7faf1;; for(i=0;i<0x19/4;i++) { n=ror(n,4); data[i]^=key; key=rol(key,*(char*)&n); }
好了,整个解密算法就完了。怎样知道答案对不对?用专门的解包软件(如crass)将脚本文件解密,用WinHex打开对应的文件然后对比一下就知道了。
另外,上述的解密过程一次解密一个双字的,所以最后剩下一个字节没有做任何操作。
剩下最后一个问题是,前面所说脚本文件的个数是0x1a2,这个怎样确定?由于有些脚本文件在游戏过程中才进行解密的,所以我用一个很笨的方法来确定:用软件解密脚本文件后,脚本文件总数418,即16进制的0x1a2.
限于篇幅,其他资源的解密过程就不贴了,其他资源的解密要用到解密后的脚本文件中的数据做key,至于解密后各个脚本的文件名么……没分析出来orz。
发表评论
-
未完的病毒分析func.dll
2009-09-20 17:33 1420这个dll会释放出一个驱动,于是就被这个诡异的驱动迷惑了很久。 ... -
随手捉了个毒来分析
2009-09-11 00:39 1107本来暑假前就以外地感染的这个病毒,不过我的系统装了影子 ... -
《星空のメモリア》脚本简单分析
2009-08-31 20:55 1002脚本是编译型的脚本,虽然没加密,但不去调试的话就很难分 ... -
OllyDBG插件OllyGal1.10
2009-07-30 01:48 1932嗯,不知道发汉化技术版还是发多媒体教室。发在这里应该多点人 ... -
OllGal1.10正式版
2009-07-30 01:31 0详细内容待更新 -
丢个OD插件娱乐一下大众
2009-07-23 11:06 1242看到来福丢了个JAVA做的纸牌出来应援,又看到有人丢了个F ... -
Gift汉化相关
2009-07-13 15:53 1440虽然大概没什么人来看这个blog,但还是要稍微更新一下 ... -
DC2PC汉化实战
2009-06-17 01:11 3403前一段时间写了篇 ... -
字符编码与游戏中的字符边界检查
2009-06-17 00:55 5925在汉化以及开发国 ...
相关推荐
级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均衡管理,级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均衡管理,不平衡电网下的svg无功补偿,级联H桥svg无功补偿statcom,采用三层控制策略。 (1)第一层采用电压电流双闭环pi控制,电压电流正负序分离,电压外环通过产生基波正序有功电流三相所有H桥模块直流侧平均电压恒定,电流内环采用前馈解耦控制; (2)第二层相间电压均衡控制,注入零序电压,控制通过注入零序电压维持相间电压平衡; (3)第三层相内电压均衡控制,使其所有子模块吸收的有功功率与其损耗补,从而保证所有H桥子模块直流侧电压值等于给定值。 有参考资料。 639,核心关键词: 1. 不平衡电网下的SVG无功补偿 2. 级联H桥SVG无功补偿STATCOM 3. 三层控制策略 4. 电压电流双闭环PI控制 5. 电压电流正负序分离 6. 直流侧平均电压恒定 7. 前馈解耦控制 8. 相间电压均衡控制 9. 零序电压注入 10. 相内电压均衡控制 以上十个关键词用分号分隔的格式为:不
GTX 1080 PCB图纸,内含图纸查看软件
内容概要:本文档详细介绍了利用 DeepSeek 进行文本润色和问答交互时提高效果的方法和技巧,涵盖了从明确需求、提供适当上下文到尝试开放式问题以及多轮对话的十个要点。每一部分内容都提供了具体的示范案例,如指定回答格式、分步骤提问等具体实例,旨在指导用户更好地理解和运用 DeepSeek 提升工作效率和交流质量。同时文中还强调了根据不同应用场景调整提示词语气和风格的重要性和方法。 适用人群:适用于希望通过优化提问技巧以获得高质量反馈的企业员工、科研人员以及一般公众。 使用场景及目标:本文针对所有期望提高 DeepSeek 使用效率的人群,帮助他们在日常工作中快速获取精准的答案或信息,特别是在撰写报告、研究材料准备和技术咨询等方面。此外还鼓励用户通过不断尝试不同形式的问题表述来进行有效沟通。 其他说明:该文档不仅关注实际操作指引,同样重视用户思维模式转变——由简单索取答案向引导 AI 辅助创造性解决问题的方向发展。
基于FPGA与W5500实现的TCP网络通信测试平台开发——Zynq扩展口Verilog编程实践,基于FPGA与W5500芯片的TCP网络通信测试及多路Socket实现基于zynq开发平台和Vivado 2019软件的扩展开发,基于FPGA和W5500的TCP网络通信 测试平台 zynq扩展口开发 软件平台 vivado2019.2,纯Verilog可移植 测试环境 压力测试 cmd命令下ping电脑ip,同时采用上位机进行10ms发包回环测试,不丢包(内部数据回环,需要时间处理) 目前实现单socket功能,多路可支持 ,基于FPGA; W5500; TCP网络通信; Zynq扩展口开发; 纯Verilog可移植; 测试平台; 压力测试; 10ms发包回环测试; 单socket功能; 多路支持。,基于FPGA与W5500的Zynq扩展口TCP通信测试:可移植Verilog实现的高效网络通信
Labview液压比例阀伺服阀试验台多功能程序:PLC通讯、液压动画模拟、手动控制与调试、传感器标定、报警及记录、自动实验、数据处理与查询存储,报表生成与打印一体化解决方案。,Labview液压比例阀伺服阀试验台多功能程序:PLC通讯、液压动画模拟、手动控制与调试、传感器标定、报警管理及实验自动化,labview液压比例阀伺服阀试验台程序:功能包括,同PLC通讯程序,液压动画,手动控制及调试,传感器标定,报警设置及报警记录,自动实验,数据处理曲线处理,数据库存储及查询,报表自动生成及打印,扫码枪扫码及信号录入等~ ,核心关键词:PLC通讯; 液压动画; 手动控制及调试; 传感器标定; 报警设置及记录; 自动实验; 数据处理及曲线处理; 数据库存储及查询; 报表生成及打印; 扫码枪扫码。,Labview驱动的智能液压阀测试系统:多功能控制与数据处理
华为、腾讯、万科员工职业发展体系建设与实践.pptx
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
电网不对称故障下VSG峰值电流限制的柔性控制策略:实现电流平衡与功率容量的优化利用,电网不对称故障下VSG峰值电流限制的柔性控制策略:兼顾平衡电流与功率控制切换的动态管理,电网不对称故障下VSG峰值电流限制的柔性不平衡控制(文章完全复现)。 提出一种在不平衡运行条件下具有峰值电流限制的可变不平衡电流控制方法,可灵活地满足不同操作需求,包括电流平衡、有功或无功恒定运行(即电流控制、有功控制或无功控制之间的相互切),注入电流保持在安全值内,以更好的利用VSG功率容量。 关键词:VSG、平衡电流控制、有功功率控制、无功功率控制。 ,VSG; 峰值电流限制; 柔性不平衡控制; 电流平衡控制; 有功功率控制; 无功功率控制。,VSG柔性控制:在电网不对称故障下的峰值电流限制与平衡管理
1、文件内容:libpinyin-tools-0.9.93-4.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/libpinyin-tools-0.9.93-4.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
数据集是一个以经典动漫《龙珠》为主题的多维度数据集,广泛应用于数据分析、机器学习和图像识别等领域。该数据集由多个来源整合而成,涵盖了角色信息、战斗力、剧情片段、台词以及角色图像等多个方面。数据集的核心内容包括: 角色信息:包含《龙珠》系列中的主要角色及其属性,如名称、种族、所属系列(如《龙珠》《龙珠Z》《龙珠超》等)、战斗力等级等。 图像数据:提供角色的图像资源,可用于图像分类和角色识别任务。这些图像来自动画剧集、漫画和相关衍生作品。 剧情与台词:部分数据集还包含角色在不同故事中的台词和剧情片段,可用于文本分析和自然语言处理任务。 战斗数据:记录角色在不同剧情中的战斗力变化和战斗历史,为研究角色成长和剧情发展提供支持。 数据集特点 多样性:数据集整合了角色、图像、文本等多种类型的数据,适用于多种研究场景。 深度:不仅包含角色的基本信息,还涵盖了角色的成长历程、技能描述和与其他角色的互动关系。 实用性:支持多种编程语言(如Python、R)的数据处理和分析,提供了详细的文档和示例代码。
基于protues仿真的多功公交站播报系统设计(仿真图、源代码) 该设计为基于protues仿真的多功公交站播报系统,实现温度显示、时间显示、和系统公交站播报功能; 具体功能如下: 1、系统使用51单片机为核心设计; 2、时钟芯片进行时间和日期显示; 3、温度传感器进行温度读取; 4、LCD12864液晶屏进行相关显示; 5、按键设置调节时间; 6、按键设置报站; 7、仿真图、源代码; 操作说明: 1、下行控制报站:首先按下(下行设置按键),(下行指示灯)亮,然后按下(手动播报)按键控制播报下一站; 2、上行控制报站:首先按上(上行设置按键),(上行指示灯)亮,然后按下(手动播报)按键控制播报下一站; 3、按下关闭播报按键,则关闭播报功能和清除显示
采用Java后台技术和MySQL数据库,在前台界面为提升用户体验,使用Jquery、Ajax、CSS等技术进行布局。 系统包括两类用户:学生、管理员。 学生用户 学生用户只要实现了前台信息的查看,打开首页,查看网站介绍、琴房信息、在线留言、轮播图信息公告等,通过点击首页的菜单跳转到对应的功能页面菜单,包括网站首页、琴房信息、注册登录、个人中心、后台登录。 学生用户通过账户账号登录,登录后具有所有的操作权限,如果没有登录,不能在线预约。学生用户退出系统将注销个人的登录信息。 管理员通过后台的登录页面,选择管理员权限后进行登录,管理员的权限包括轮播公告管理、老师学生信息管理和信息审核管理,管理员管理后点击退出,注销登录信息。 管理员用户具有在线交流的管理,琴房信息管理、琴房预约管理。 在线交流是对前台用户留言内容进行管理,删除留言信息,查看留言信息。
MATLAB可以用于开发人脸识别考勤系统。下面是一个简单的示例流程: 1. 数据采集:首先收集员工的人脸图像作为训练数据集。可以要求员工提供多张照片以获得更好的训练效果。 2. 图像预处理:使用MATLAB的图像处理工具对采集到的人脸图像进行预处理,例如灰度化、裁剪、缩放等操作。 3. 特征提取:利用MATLAB的人脸识别工具包,如Face Recognition Toolbox,对处理后的图像提取人脸特征,常用的方法包括主成分分析(PCA)和线性判别分析(LDA)等。 4. 训练模型:使用已提取的人脸特征数据集训练人脸识别模型,可以选择支持向量机(SVM)、卷积神经网络(CNN)等算法。 5. 考勤系统:在员工打卡时,将摄像头捕获的人脸图像输入到训练好的模型中进行识别,匹配员工信息并记录考勤数据。 6. 结果反馈:根据识别结果,可以自动生成考勤报表或者实时显示员工打卡情况。 以上只是一个简单的步骤,实际开发过程中需根据具体需求和系统规模进行定制和优化。MATLAB提供了丰富的图像处理和机器学习工具,是开发人脸识别考勤系统的一个很好选择。
hjbvbnvhjhjg
HCIP、软考相关学习PPT提供下载
绿豆BOX UI8版:反编译版六个全新UI+最新后台直播管理源码 最新绿豆BOX反编译版六个UI全新绿豆盒子UI8版本 最新后台支持直播管理 作为UI6的升级版,UI8不仅修复了前一版本中存在的一些BUG,还提供了6套不同的UI界面供用户选择,该版本有以下特色功能: 在线管理TVBOX解析 在线自定义TVBOX 首页布局批量添加会员信息 并支持导出批量生成卡密 并支持导出直播列表管理功能
vue3的一些语法以及知识点
西门子大型Fanuc机器人汽车焊装自动生产线程序经典解析:PLC博图编程与MES系统通讯实战指南,西门子PLC博图汽车焊装自动生产线FANUC机器人程序经典结构解析与MES系统通讯,西门子1500 大型程序fanuc 机器人汽车焊装自动生产线程序 MES 系统通讯 大型程序fanuc机器人汽车焊装自动生产线程序程序经典结构清晰,SCL算法堆栈,梯形图和 SCL混编使用博图 V14以上版本打开 包括: 1、 PLC 博图程序 2 触摸屏程序 ,西门子1500; 大型程序; fanuc机器人; 汽车焊装自动生产线; MES系统通讯; SCL算法; 梯形图; SCL混编; 博图V14以上版本。,西门子博图大型程序:汽车焊装自动生产线MES系统通讯与机器人控制
DeepSeek:从入门到精通
计及信息间隙决策与多能转换的综合能源系统优化调度模型:实现碳经济最大化与源荷不确定性考量,基于信息间隙决策与多能转换的综合能源系统优化调度模型:源荷不确定性下的高效碳经济调度策略,计及信息间隙决策及多能转的综合能源系统优化调度 本代码构建了含风电、光伏、光热发电系统、燃气轮机、燃气锅炉、电锅炉、储气、储电、储碳、碳捕集装置的综合能源系统优化调度模型,并考虑P2G装置与碳捕集装置联合运行,从而实现碳经济的最大化,最重要的是本文引入了信息间隙决策理论考虑了源荷的不确定性(本代码的重点)与店铺的47代码形成鲜明的对比,注意擦亮眼睛,认准原创,该代码非常适合修改创新,,提供相关的模型资料 ,计及信息间隙决策; 综合能源系统; 优化调度; 多能转换; 碳经济最大化; 风电; 光伏; 燃气轮机; 储气; 储电; 储碳; 碳捕集装置; P2G装置联合运行; 模型资料,综合能源系统优化调度模型:基于信息间隙决策和多能转换的原创方案