• 标 题:一个Java反汇编器的修改 (7千字)
  • 作 者:slangmgh
  • 时 间:2003-08-19 12:31:25
  • 链 接:http://bbs.pediy.com


不知大家是否有用Java的,现在Java的反编译器很多(当然Jad最牛,而且是用C写的),而反汇编的不多,JCD version 2A是一个挺老的反汇编程序,不过我自己觉得挺好用(有的时候被扰码后的.class不能反编译,所以必须反汇编)。不过Jcd有一个问题,就是不能显示字节代码,并且在代码中的偏移也是相对的,而不是文件偏移,这使得修改前还的自己计算,所以我们需要改进这些地方:
首先我们要找到反汇编Method的代码,用W32dasm的StringRef可以看到以下的代码:

* Possible StringData Ref from Data Obj ->"%8d%8caconst_null"
                                  |
:004144B2 68DE0B4200              push 00420BDE
:004144B7 E875D3FFFF              call 00411831
:004144BC 83C40C                  add esp, 0000000C
:004144BF E9F9250000              jmp 00416ABD
:004144C4 6A20                    push 00000020
:004144C6 8BC3                    mov eaxebx
:004144C8 43                      inc ebx
:004144C9 50                      push eax

再向前看,可以看到:

* Referenced by a CALL at Address:
|:00413395   
|
:00414063 55                      push ebp
:00414064 8BEC                    mov ebpesp
:00414066 81C4E0F3FFFF            add esp, FFFFF3E0
:0041406C 53                      push ebx
:0041406D 56                      push esi
:0041406E 57                      push edi
:0041406F 8B7D10                  mov edidword ptr [ebp+10] ; 代码偏移指针
:00414072 33F6                    xor esiesi
:00414074 8B1F                    mov ebxdword ptr [edi]
:00414076 8B4508                  mov eaxdword ptr [ebp+08] ;当前文件
:00414079 8B550C                  mov edxdword ptr [ebp+0C] ;当前方法
:0041407C 03C2                    add eaxedx
:0041407E 0FB60418                movzx eaxbyte ptr [eax+ebx]
:00414082 3DFF000000              cmp eax, 000000FF
:00414087 0F871D2A0000            ja 00416AAA
:0041408D FF248594404100          jmp dword ptr [4*eax+00414094]

:00414094 94444100                DWORD 00414494
:00414098 AC444100                DWORD 004144AC

下面还有许多DWORD,明显这是一个根据代码第一字节的跳转表。因而JCD的单条指令
的反编译代码就是00414063。再看代码413395处:

:0041338D 8D45BC                  lea eaxdword ptr [ebp-44]
:00413390 50                      push eax ;代码偏移指针
:00413391 53                      push ebx ;当前方法
:00413392 57                      push edi ;当前文件
:00413393 E8CB0C0000              call 00414063
:00413398 83C40C                  add esp, 0000000C
:0041339B 85C0                    test eaxeax ;代码正确
:0041339D 7522                    jne 004133C1
:0041339F 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"JCD"
                                  |
:004133A1 6870004200              push 00420070
:004133A6 8D8631080000            lea eaxdword ptr [esi+00000831]
:004133AC 50                      push eax
:004133AD FF7508                  push [ebp+08]

* Reference To: USER32.MessageBoxA, Ord:0000h
                                  |
:004133B0 E8EA9E0000              Call 0041D29F
:004133B5 33C0                    xor eaxeax
:004133B7 A374004200              mov dword ptr [00420074], eax
:004133BC E97F0C0000              jmp 00414040

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041339D(C)
|
:004133C1 8B45BC                  mov eaxdword ptr [ebp-44]
:004133C4 3B45D0                  cmp eaxdword ptr [ebp-30] ;当前方法的长度
:004133C7 7CC4                    jl 0041338D

这是一段循环直到改方法反汇编结束。再跟踪进入方法414063,发现它将反编译完成的字符
串放在[42008C]所指的DWORD所指的指针中。格式为"%8d%8c%s"。现在,一切都清楚了,以下
是我们所要做的。

:00413390 FF30                    push dword ptr [eax] ;我们需要保存上次的偏移
:00413392 50                      push eax
:00413393 53                      push ebx
:00413394 57                      push edi
:00413395 E8C90C0000              call 00414063
:0041339A 83C40C                  add esp, 0000000C
:0041339D 85C0                    test eaxeax
:0041339F 0F849B0C0000            je 00414040
:004133A5 58                      pop eax ;上次的偏移
:004133A6 60                      pushad ;保存寄存器
:004133A7 8B4DBC                  mov ecxdword ptr [ebp-44] ;这次的偏移
:004133AA BEF4D34100              mov esi, 0041D3F9 ;Format
:004133AF 2BC8                    sub ecxeax ;这条指令的字节数
:004133B1 83F903                  cmp ecx, 00000003 ;我们只显示三个字节
:004133B4 7E05                    jle 004133BB
:004133B6 B903000000              mov ecx, 00000003

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004133B4(C)
|
:004133BB E99F9F0000              jmp 0041D35F ;代码在这里写不下
:004133C0 90                      nop


上面的代码跳到这里

:0041D35F 03FB                    add ediebx
:0041D361 03F8                    add edieax ;EDI指向该指令的第一个字节
:0041D363 33D2                    xor edxedx
:0041D365 890DE3D34100            mov dword ptr [0041D3E3], ecx ;保存ECX的值,在调用函数wsprintf后清栈用

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041D37B(U)
|
:0041D36B 49                      dec ecx
:0041D36C 740F                    je 0041D37D
:0041D36E 8A140F                  mov dlbyte ptr [edi+ecx]
:0041D371 52                      push edx ;将指令字节推入栈
:0041D372 C70625303258            mov dword ptr [esi], 58323025 ;Format字符串增加"%02X"
:0041D378 83C604                  add esi, 00000004
:0041D37B EBEE                    jmp 0041D36B

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041D36C(C)
|
:0041D37D 8A17                    mov dlbyte ptr [edi] ;该指令的第一个字节入栈
:0041D37F 52                      push edx
:0041D380 90                      nop
:0041D381 90                      nop
:0041D382 90                      nop
:0041D383 50                      push eax
:0041D384 03C3                    add eaxebx ;当前指令偏移
:0041D386 50                      push eax
:0041D387 A18C004200              mov eaxdword ptr [0042008C]
:0041D38C 68EBD34100              push 0041D3EB ;Format字符串(目前应为“%08X %02X[%02X...]”)
:0041D391 FF30                    push dword ptr [eax] ;目的字符串
:0041D393 C60600                  mov byte ptr [esi], 00 ;Format字符串最后一位为0

* Reference To: USER32.wsprintfA, Ord:0000h
                                  |
:0041D396 E8AAFEFFFF              Call 0041D245
:0041D39B 83C410                  add esp, 00000014 ;先清4个DWORD(buff,format,offset,offset, first byte
:0041D39E 8B0DE3D34100            mov ecxdword ptr [0041D3E3]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041D3A8(U)
|
:0041D3A4 49                      dec ecx
:0041D3A5 7403                    je 0041D3AA
:0041D3A7 58                      pop eax ;根据ECX清除其他的栈中的数据
:0041D3A8 EBFA                    jmp 0041D3A4

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041D3A5(C)
|
:0041D3AA A18C004200              mov eaxdword ptr [0042008C]
:0041D3AF 8B10                    mov edxdword ptr [eax]
:0041D3B1 52                      push edx
:0041D3B2 52                      push edx

* Reference To: KERNEL32.lstrlenA, Ord:0000h
  |
:0041D3B3 E84FFDFFFF              Call 0041D107
:0041D3B8 5A                      pop edx
:0041D3B9 C6041020                mov byte ptr [eax+edx], 20 ;我们必须将空格改回来
:0041D3BD 61                      popad
:0041D3BE E9FE5FFFFF              jmp 004133C ;一切Over!

Format数据 "%04X:%03d %02X"
:0041D3EB 253034583A              and eax, 3A583430
:0041D3F0 2530336420              and eax, 20643330
:0041D3F5 2530325800              and eax, 00583230