JVMTI应用分析,针对 TBCedit 的类加载器的JVMTI实现

Author:vhly[FR]
dAtE:2009/06/30

记得之前论坛里面有人问 tbcredit 中的类文件的问题,前两天比较闲,就抽出几天详细看了一下。

TBC 是一个刷xxx信誉的软件,给予Eclipse实现,或者说是类似于Eclipse的外观样式。

首先查看一下整体的软件目录结构

TBC
    jre
        bin
    lib      -- 类库目录,主要是在这个地方
    Privoxy  --- 未知 
    resource   资源
    .....

    run.bat

直接运行 run.bat,会调用 main.jar中的类文件,进行软件更新操作,更新的内容都保存在
lib目录中,同时,经过反编译发现,更新成功之后,程序会解析 lib/update.xml,通过其中的
设置,进行真正软件的调用,配置数据如下

 <command><![CDATA[javaw,-agentlib:./lib/run,-Djava.library.path=.\lib,-jar,.\lib\com.xxxtaobao.tbcredit.jar]]></command>

这部分的内容,会组合成 javaw -agentlib:./lib/run -Djava.library.path=.\lib -jar .\lib\com.xxxtaobao.tbcredit.jar

就是调用了 xxx.jar文件,执行相应的内容而已。

直接找到 com.xxx.xxx.jar 这个文件,打开,找到Main-Class ,使用DJ Decompiler 进行反编译
结果,DJ 崩溃了,一般这种情况,就是类文件经过加密了。

于是将一个.class 解压出来,使用HEdit打开,发现原来的 CA FE BA BE 不见了,现在变成了
AA BB CC DD,这种,经过检查所有的class 都是这个开头,那么可以认为,这几个数值
就是某个格式的Magic

分析了半天,还是没有发现应该如何解密。由于以前做过Java Profiler的使用,那些东西基本上
都是提供一个 agentlib的,看看上面的指令,才想起来,原来是用了这种方式

那么就要针对 lib/run.dll 作分析了。

分析run.dll的步骤

1、使用IDA 进行汇编代码分析,发现 run.dll 中有几个导出  Agent_Onload Agent_OnUnload
     以及  doXor, doExchange4Bits, encryptBuffer等等的方法,

2、当时并不了解JVMTI,只是看着 onload,觉得应该看一看,后来就分析代码,结果是一头雾水,
因为没有真实运行,很多的数据不知道什么功能。

3、动态调试部分,适用OllyICE进行调试,调试由于采用库的形式,因此,只能够调试java.exe,同时指定命令行参数,这样应该可以装载到run.dll中。

使用OllyICE 装载Java 之后指定参数,重新加载。

1)由于使用了agentlib,因此在启动之前在LoadLibraryA指令上下断点,此处的断点主要是
     定位jvm.dll的装载,因为实际 agent的加载等操作,都在 jvm.dll中完成

2)进入jvm.dll空间,再次下LoadLibraryA的端点,当执行第二次的时候,会停止在 装载 agentlib部分

如图



此时一旦装载了 run.dll,那么就进入到实际的调试过程中了

3)查看run.dll中的方法

Agent_OnLoad, Agent_OnUnload, doExchange4Bits, doXor,encryptBuffer, 
getEncryptMarks, getEncryptMarkSize, getHeaderSize,getLogined, login,

4)在Agent_OnLoad中下断点,单步查看执行流程

 __stdcall Agent_OnLoad(x, x, x)
.text:10001000                 public _Agent_OnLoad@12
.text:10001000 _Agent_OnLoad@12 proc near
.text:10001000
.text:10001000 var_A4          = dword ptr -0A4h
.text:10001000 Dst             = byte ptr -94h
.text:10001000 var_84          = dword ptr -84h
.text:10001000 var_8           = dword ptr -8
.text:10001000 var_4           = dword ptr -4
.text:10001000 arg_0           = dword ptr  8
.text:10001000 arg_4           = dword ptr  0Ch
.text:10001000
.text:10001000                 push    ebp
.text:10001001                 mov     ebp, esp
.text:10001003                 sub     esp, 0A4h
.text:10001009                 push    30010021h
.text:1000100E                 push    offset dword_10008B60
.text:10001013                 mov     eax, [ebp+arg_0]
.text:10001016                 push    eax
.text:10001017                 mov     ecx, [ebp+arg_0]
.text:1000101A                 mov     edx, [ecx]
.text:1000101C                 call    dword ptr [edx+18h] ; GetEnv
.text:1000101F                 mov     [ebp+var_8], eax
.text:10001022                 cmp     [ebp+var_8], 0
.text:10001026                 jz      short loc_10001046
.text:10001028                 mov     eax, [ebp+var_8]
.text:1000102B                 push    eax
.text:1000102C                 push    offset Format   ; "ERROR: Unable to create jvmtiEnv, GetEn"...
.text:10001031                 push    offset File     ; File
.text:10001036                 call    _fprintf
.text:1000103B                 add     esp, 0Ch
.text:1000103E                 or      eax, 0FFFFFFFFh
.text:10001041                 jmp     loc_100011C1
.text:10001046 ; ---------------------------------------------------------------------------
.text:10001046
.text:10001046 loc_10001046:                           ; CODE XREF: Agent_OnLoad(x,x,x)+26j
.text:10001046                 lea     ecx, [ebp+var_A4]
.text:1000104C                 push    ecx
.text:1000104D                 mov     edx, dword_10008B60
.text:10001053                 push    edx
.text:10001054                 mov     eax, dword_10008B60
.text:10001059                 mov     ecx, [eax]
.text:1000105B                 call    dword ptr [ecx+160h] ; GetCapabilities
.text:10001061                 mov     [ebp+var_4], eax
.text:10001064                 cmp     [ebp+var_4], 0
.text:10001068                 jz      short loc_10001080
.text:1000106A                 mov     edx, [ebp+var_4]
.text:1000106D                 push    edx
.text:1000106E                 push    offset aErrorGetcapabi ; "ERROR: GetCapabilities failed, error=%d"...
.text:10001073                 push    offset File     ; File
.text:10001078                 call    _fprintf
.text:1000107D                 add     esp, 0Ch
.text:10001080
.text:10001080 loc_10001080:                           ; CODE XREF: Agent_OnLoad(x,x,x)+68j
.text:10001080                 mov     eax, [ebp+var_A4]
.text:10001086                 or      eax, 4000000h
.text:1000108B                 mov     [ebp+var_A4], eax
.text:10001091                 lea     ecx, [ebp+var_A4]
.text:10001097                 push    ecx
.text:10001098                 mov     edx, dword_10008B60
.text:1000109E                 push    edx
.text:1000109F                 mov     eax, dword_10008B60
.text:100010A4                 mov     ecx, [eax]
.text:100010A6                 call    dword ptr [ecx+234h] ; add JVMTI
.text:100010AC                 mov     [ebp+var_4], eax
.text:100010AF                 cmp     [ebp+var_4], 0
.text:100010B3                 jz      short loc_100010D5
.text:100010B5                 mov     edx, [ebp+var_4]
.text:100010B8                 push    edx
.text:100010B9                 push    offset aErrorUnableToA ; "ERROR: Unable to add necessary JVMTI ca"...
.text:100010BE                 push    offset File     ; File
.text:100010C3                 call    _fprintf
.text:100010C8                 add     esp, 0Ch
.text:100010CB                 mov     eax, 0FFFFFFFEh
.text:100010D0                 jmp     loc_100011C1
.text:100010D5 ; ---------------------------------------------------------------------------
.text:100010D5
.text:100010D5 loc_100010D5:                           ; CODE XREF: Agent_OnLoad(x,x,x)+B3j
.text:100010D5                 push    8Ch             ; Size
.text:100010DA                 push    0               ; Val
.text:100010DC                 lea     eax, [ebp+Dst]
.text:100010E2                 push    eax             ; Dst
.text:100010E3                 call    _memset
.text:100010E8                 add     esp, 0Ch
.text:100010EB                 mov     [ebp+var_84], offset sub_10001280         注意此处
.text:100010F5                 push    8Ch
.text:100010FA                 lea     ecx, [ebp+Dst]
.text:10001100                 push    ecx
.text:10001101                 mov     edx, dword_10008B60
.text:10001107                 push    edx
.text:10001108                 mov     eax, dword_10008B60
.text:1000110D                 mov     ecx, [eax]
.text:1000110F                 call    dword ptr [ecx+1E4h]
.text:10001115                 push    0
.text:10001117                 push    37h                                  注意此处
.text:10001119                 push    1
.text:1000111B                 mov     edx, dword_10008B60
.text:10001121                 push    edx
.text:10001122                 mov     eax, dword_10008B60
.text:10001127                 mov     ecx, [eax]
.text:10001129                 call    dword ptr [ecx+4]
.text:1000112C                 add     esp, 10h
.text:1000112F                 push    0
.text:10001131                 push    36h                                    注意此处
.text:10001133                 push    1
.text:10001135                 mov     edx, dword_10008B60
.text:1000113B                 push    edx
.text:1000113C                 mov     eax, dword_10008B60
.text:10001141                 mov     ecx, [eax]
.text:10001143                 call    dword ptr [ecx+4]
.text:10001146                 add     esp, 10h
.text:10001149                 push    offset unk_10008B64
.text:1000114E                 push    offset aLock    ; "lock"
.text:10001153                 mov     edx, dword_10008B60
.text:10001159                 push    edx
.text:1000115A                 mov     eax, dword_10008B60
.text:1000115F                 mov     ecx, [eax]
.text:10001161                 call    dword ptr [ecx+78h] ; create raw monitor
.text:10001164                 mov     [ebp+var_4], eax
.text:10001167                 cmp     [ebp+var_4], 0
.text:1000116B                 jz      short loc_1000118A
.text:1000116D                 mov     edx, [ebp+var_4]
.text:10001170                 push    edx
.text:10001171                 push    offset aErrorUnableT_0 ; "ERROR: Unable to create raw monitor: %d"...
.text:10001176                 push    offset File     ; File
.text:1000117B                 call    _fprintf
.text:10001180                 add     esp, 0Ch
.text:10001183                 mov     eax, 0FFFFFFFDh
.text:10001188                 jmp     short loc_100011C1
.text:1000118A ; ---------------------------------------------------------------------------
.text:1000118A
.text:1000118A loc_1000118A:                           ; CODE XREF: Agent_OnLoad(x,x,x)+16Bj
.text:1000118A                 call    lib_Init
.text:1000118F                 test    eax, eax
.text:10001191                 jz      short loc_1000119A
.text:10001193                 mov     eax, 0FFFFFFFCh
.text:10001198                 jmp     short loc_100011C1
.text:1000119A ; ---------------------------------------------------------------------------
.text:1000119A
.text:1000119A loc_1000119A:                           ; CODE XREF: Agent_OnLoad(x,x,x)+191j
.text:1000119A                 cmp     [ebp+arg_4], 0
.text:1000119E                 jz      short loc_100011BF
.text:100011A0                 push    offset aDebug   ; "debug"
.text:100011A5                 mov     eax, [ebp+arg_4]
.text:100011A8                 push    eax
.text:100011A9                 call    unknown_libname_1 ; Microsoft VisualC 2-8/net runtime
.text:100011AE                 add     esp, 8
.text:100011B1                 test    eax, eax
.text:100011B3                 jnz     short loc_100011BF
.text:100011B5                 mov     dword_10008B6C, 1
.text:100011BF
.text:100011BF loc_100011BF:                           ; CODE XREF: Agent_OnLoad(x,x,x)+19Ej
.text:100011BF                                         ; Agent_OnLoad(x,x,x)+1B3j
.text:100011BF                 xor     eax, eax
.text:100011C1
.text:100011C1 loc_100011C1:                           ; CODE XREF: Agent_OnLoad(x,x,x)+41j
.text:100011C1                                         ; Agent_OnLoad(x,x,x)+D0j ...
.text:100011C1                 mov     esp, ebp
.text:100011C3                 pop     ebp
.text:100011C4                 retn    0Ch

上面就是 Agent_OnLoad的代码,通过ICE执行的时候,单步跟踪,但是此处实际上只是做了
初始化的处理和设置。

经过多次调试,终于找到3处重要位置,即代码中标记 注意此处 的几个部分。

后来发现,这个代码实际上使用了 JVMTI技术,就 google了,以下,结果找到了 在 Java SDK安装的Include中有描述,其中的 0x36 0x37 这两个 是一个标志位,代表了 ClassLoader的实现

经过仔细阅读代码,调试,终于找到  mov     [ebp+var_84], offset sub_10001280 
这一句,十分重要,应该是设置回调方法,一旦需要进行Class Load的时候,就调用这个方法

基本的JVMTI实现中的 OnLoad方法,大体上是这样的

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) 

    ... 
    MethodTraceAgent* agent = new MethodTraceAgent(); 
    agent->Init(vm); 
    agent->ParseOptions(options); 
    agent->AddCapability(); 
    agent->RegisterEvent();   // 此处注册希望监听的事件类型, 0x36 0x37
    ... 
}

5)查找字符串,上面的回调地址就是用字符串定位的

在IDA 中显示字符串,发现 %d. class %s decrypted successfully, class data size: %d bytes.  这一句,看来一切都没有问题,确实是通过JVMTI进行类解密的了

定位代码位置

子程序 sub_10001280

看来和上面定位到的 offset 10001280是一致的,因此在 sub_10001280 处下断点,
继续执行程序

6)分析实际的类加载/解密过程 

 int __stdcall sub_10001280(int, int, int, int, int, int, int filelength, void *Buf1, int, int)
.text:10001280 sub_10001280    proc near               ; DATA XREF: Agent_OnLoad(x,x,x)+EBo
.text:10001280
.text:10001280 var_4           = dword ptr -4
.text:10001280 arg_0           = dword ptr  8
.text:10001280 arg_10          = dword ptr  18h
.text:10001280 filelength      = dword ptr  20h
.text:10001280 Buf1            = dword ptr  24h
.text:10001280 arg_20          = dword ptr  28h
.text:10001280 arg_24          = dword ptr  2Ch
.text:10001280
.text:10001280                 push    ebp             ; asdf
.text:10001280                                         ; dfasdf
.text:10001281                 mov     ebp, esp
.text:10001283                 push    ecx
.text:10001284                 cmp     [ebp+filelength], 1Ch
.text:10001288                 jle     loc_10001350
.text:1000128E                 push    4               ; Size
.text:10001290                 push    offset MAGIC    ; Buf2
.text:10001290                                         ; AA BB CC DD
.text:10001290                                         ;
.text:10001295                 mov     eax, [ebp+Buf1]
.text:10001298                 push    eax             ; Buf1
.text:10001299                 call    _memcmp
.text:1000129E                 add     esp, 0Ch
.text:100012A1                 test    eax, eax
.text:100012A3                 jnz     loc_10001350    ;  不是以 AA BB CC DD 开头,直接返回,否则进入解密部分
.text:100012A9                 call    lib_Init                  此方法 进行初始化
.text:100012AE                 test    eax, eax
.text:100012B0                 jz      short loc_100012B7
.text:100012B2                 jmp     loc_10001350
.text:100012B7 ; ---------------------------------------------------------------------------
.text:100012B7
.text:100012B7 loc_100012B7:                           ; CODE XREF: sub_10001280+30j
.text:100012B7                 mov     ecx, dword_10008B68
.text:100012BD                 add     ecx, 1
.text:100012C0                 mov     dword_10008B68, ecx
.text:100012C6                 mov     edx, [ebp+filelength]
.text:100012C9                 sub     edx, 1Ch
.text:100012CC                 mov     eax, [ebp+arg_20]
.text:100012CF                 mov     [eax], edx
.text:100012D1                 mov     ecx, [ebp+arg_20]
.text:100012D4                 mov     edx, [ecx]
.text:100012D6                 push    edx
.text:100012D7                 mov     eax, [ebp+arg_0]
.text:100012DA                 push    eax
.text:100012DB                 call    sub_10001360
.text:100012E0                 add     esp, 8
.text:100012E3                 mov     ecx, [ebp+arg_24]
.text:100012E6                 mov     [ecx], eax
.text:100012E8                 mov     edx, [ebp+arg_20]
.text:100012EB                 push    edx
.text:100012EC                 mov     eax, [ebp+arg_24]
.text:100012EF                 mov     ecx, [eax]
.text:100012F1                 push    ecx
.text:100012F2                 mov     edx, [ebp+filelength]
.text:100012F5                 push    edx
.text:100012F6                 mov     eax, [ebp+Buf1]
.text:100012F9                 push    eax
.text:100012FA                 call    sub_10001800             // 针对 文件数据的解密
.text:100012FF                 add     esp, 10h
.text:10001302                 mov     [ebp+var_4], eax
.text:10001305                 cmp     [ebp+var_4], 0
.text:10001309                 jz      short loc_10001325
.text:1000130B                 mov     ecx, [ebp+var_4]
.text:1000130E                 push    ecx
.text:1000130F                 mov     edx, [ebp+arg_10]
.text:10001312                 push    edx
.text:10001313                 push    offset aDecryptClassSF ; "Decrypt class: %s fail! Return code: %d"...
.text:10001318                 push    offset File     ; File
.text:1000131D                 call    _fprintf
.text:10001322                 add     esp, 10h
.text:10001325
.text:10001325 loc_10001325:                           ; CODE XREF: sub_10001280+89j
.text:10001325                 cmp     dword_10008B6C, 0
.text:1000132C                 jz      short loc_10001350
.text:1000132E                 mov     eax, [ebp+arg_20]
.text:10001331                 mov     ecx, [eax]
.text:10001333                 push    ecx
.text:10001334                 mov     edx, [ebp+arg_10]
.text:10001337                 push    edx
.text:10001338                 mov     eax, dword_10008B68
.text:1000133D                 push    eax
.text:1000133E                 push    offset aD_ClassSDecryp ; "%d. class %s decrypted successfully, cl"...
.text:10001343                 push    offset File     ; File
.text:10001348                 call    _fprintf
.text:1000134D                 add     esp, 14h
.text:10001350
.text:10001350 loc_10001350:                           ; CODE XREF: sub_10001280+8j
.text:10001350                                         ; sub_10001280+23j ...
.text:10001350                 mov     esp, ebp
.text:10001352                 pop     ebp
.text:10001353                 retn    28h
.text:10001353 sub_10001280    endp

解密字节数据的部分实际上在  .text:100012FA                 call    sub_10001800
部分

6)数据解密部分

10001800  /$  55            push    ebp
10001801  |.  8BEC          mov     ebp, esp
10001803  |.  83EC 34       sub     esp, 34
10001806  |.  833D 748B0010>cmp     dword ptr [10008B74], 0
1000180D  |.  75 11         jnz     short 10001820
1000180F  |.  8B45 14       mov     eax, dword ptr [ebp+14]
10001812  |.  C700 00000000 mov     dword ptr [eax], 0
10001818  |.  83C8 FF       or      eax, FFFFFFFF
1000181B  |.  E9 B6000000   jmp     100018D6
10001820  |>  837D 0C 1C    cmp     dword ptr [ebp+C], 1C            ;  buflen
10001824  |.  7D 13         jge     short 10001839
10001826  |.  8B4D 14       mov     ecx, dword ptr [ebp+14]
10001829  |.  C701 00000000 mov     dword ptr [ecx], 0
1000182F  |.  B8 FEFFFFFF   mov     eax, -2
10001834  |.  E9 9D000000   jmp     100018D6
10001839  |>  6A 18         push    18
1000183B  |.  8B55 08       mov     edx, dword ptr [ebp+8]           ;  buf
1000183E  |.  83C2 04       add     edx, 4
10001841  |.  52            push    edx
10001842  |.  8D45 E8       lea     eax, dword ptr [ebp-18]
10001845  |.  50            push    eax
10001846  |.  E8 75020000   call    10001AC0                         ;  _memcpy(dest,buf,0x18)
1000184B  |.  83C4 0C       add     esp, 0C
1000184E  |.  6A 18         push    18
10001850  |.  8D4D E8       lea     ecx, dword ptr [ebp-18]
10001853  |.  51            push    ecx
10001854  |.  E8 17FDFFFF   call    doExchange4Bits                  ;  doExchange4Bits(dst,18)
10001859  |.  83C4 08       add     esp, 8
1000185C  |.  8D55 D0       lea     edx, dword ptr [ebp-30]
1000185F  |.  52            push    edx
10001860  |.  6A 18         push    18
10001862  |.  8D45 E8       lea     eax, dword ptr [ebp-18]          ;  dest
10001865  |.  50            push    eax
10001866  |.  6A 04         push    4
10001868  |.  68 14820010   push    10008214                         ;  87 76 65 54   head key
1000186D  |.  E8 8EFCFFFF   call    doXor
10001872  |.  83C4 14       add     esp, 14
10001875  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]           ;  buf
10001878  |.  83C1 1C       add     ecx, 1C
1000187B  |.  894D CC       mov     dword ptr [ebp-34], ecx
1000187E  |.  8B55 0C       mov     edx, dword ptr [ebp+C]           ;  buflen
10001881  |.  83EA 1C       sub     edx, 1C
10001884  |.  8B45 14       mov     eax, dword ptr [ebp+14]          ;  arg_C
10001887  |.  8910          mov     dword ptr [eax], edx             ;  edx 剩余的长度
10001889  |.  8B4D 10       mov     ecx, dword ptr [ebp+10]          ;  arg_8 result
1000188C  |.  51            push    ecx
1000188D  |.  8B55 14       mov     edx, dword ptr [ebp+14]
10001890  |.  8B02          mov     eax, dword ptr [edx]
10001892  |.  50            push    eax                              ;  剩余文件长度
10001893  |.  8B4D CC       mov     ecx, dword ptr [ebp-34]          ;  剩余的文件数据
10001896  |.  51            push    ecx
10001897  |.  6A 18         push    18
10001899  |.  8D55 D0       lea     edx, dword ptr [ebp-30]          ;  经过doXor 之后的 header部分
1000189C  |.  52            push    edx
1000189D  |.  E8 5EFCFFFF   call    doXor
100018A2  |.  83C4 14       add     esp, 14
100018A5  |.  8B45 14       mov     eax, dword ptr [ebp+14]
100018A8  |.  8B08          mov     ecx, dword ptr [eax]
100018AA  |.  51            push    ecx                              ;  剩余文件长度
100018AB  |.  8B55 10       mov     edx, dword ptr [ebp+10]
100018AE  |.  52            push    edx
100018AF  |.  E8 BCFCFFFF   call    doExchange4Bits
100018B4  |.  83C4 08       add     esp, 8
100018B7  |.  8B45 10       mov     eax, dword ptr [ebp+10]          ;  result2
100018BA  |.  50            push    eax
100018BB  |.  8B4D 14       mov     ecx, dword ptr [ebp+14]
100018BE  |.  8B11          mov     edx, dword ptr [ecx]
100018C0  |.  52            push    edx                              ;  剩余长度
100018C1  |.  8B45 10       mov     eax, dword ptr [ebp+10]
100018C4  |.  50            push    eax
100018C5  |.  6A 10         push    10
100018C7  |.  68 04820010   push    10008204
100018CC  |.  E8 2FFCFFFF   call    doXor
100018D1  |.  83C4 14       add     esp, 14
100018D4  |.  33C0          xor     eax, eax
100018D6  |>  8BE5          mov     esp, ebp
100018D8  |.  5D            pop     ebp
100018D9  \.  C3            retn

7)上述汇编代码是在看着郁闷,因此,用Java实现一套完整的类文件解密方法

自己太懒了,用代码说话

// 类解密入口,fbuf 为实际的文件数据 
 private static byte[] decrypt(byte[] fbuf) {

        boolean bOK = true;
        byte[] ret = null;
        int flen = fbuf.length;
                                             // 0x1C 为加密后的文件的头部分
        if (flen < 0x1c) {
            bOK = false;
        }
        int off = 0;
                                                  // 检查 AA BB CC DD
        for (; off < 4; off++) {
            if (fbuf[off] != magic[off]) {
                bOK = false;
                break;
            }
        }

        if (bOK) {
            // TODO 解密操作
            int hlen = 0x18;                  // 0x1c - 4 = 0x18 刨除去得 4 字节 magic
            ByteArrayOutputStream bout = null;
            byte[] header = null;
            try {
                bout = new ByteArrayOutputStream();
                int headerEnd = hlen + off;
                for (; off < headerEnd; off++) {
                    bout.write(fbuf[off]);
                }
                header = bout.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (bout != null) {
                    try {
                        bout.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    bout = null;
                }
            }
            if (header != null) {
                // TODO 此处继续执行
                doExchange4Bits(header);                   // 针对头部的数据进行处理,生成整个类
                                                                           // 文件的解密密钥

                byte[] clsKey = doXor(header_key, header);  // 计算后的密钥
                byte[] cfbuf = null;
                try {
                    bout = new ByteArrayOutputStream();
                    // TODO 由于 off 一直在变化 因此使用 off 作为偏移变量
                    for (; off < flen; off++) {
                        bout.write(fbuf[off]);
                    }
                    cfbuf = bout.toByteArray();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (bout != null) {
                        try {
                            bout.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                if (cfbuf != null) {
                    // TODO 解密文件                  使用密钥解密类文件的剩余部分
                    byte[] dcbuf = doXor(clsKey, cfbuf);           // XOR 操作
                    doExchange4Bits(dcbuf);                           // 高低4位交换
                    ret = doXor(cls_key, dcbuf);                      // 再次进行XOR
                }

            }
        }
        return ret;                 // 生成真正的 Class 文件内容,又看到了 亲切的
                                         // CAFE BABE了
    }

    private static byte[] doXor(byte[] key, byte[] buf) {
        byte[] ret = null;
        if (key != null && buf != null) {
            int klen = key.length;
            int blen = buf.length;
            int j = 0;
            ret = new byte[blen];
            for (int i = 0; i < blen; i++) {
                ret[i] = (byte) (buf[i] ^ key[j]);
                j++;
                if (j >= klen) {
                    j = 0;
                }
            }
        }
        return ret;
    }

    private static void doExchange4Bits(byte[] buf) {
        if (buf != null) {
            int len = buf.length;
            int h, l, ret;
            for (int i = 0; i < len; i++) {
                l = (buf[i] & 0x0f);
                h = (buf[i] & 0x0f0);
                ret = ((l << 4) + (h >> 4));
                buf[i] = (byte) ret;
            }
        }
    }

8)总结:

    主要针对 Agent_OnLoad的设置,来查找相应的事件监听,根据事件,进行类文件解密操作,

    通过指定  agentlib来进行类加载实现。

通过编写代码,进行jar文件中所有class文件的解密,解密后的文件比解密之前少了 0x1C个字节

类文件又能够反编译了,

不过使用了混淆器,进行了字符串加密,不过,已经是不安全了。

Have a Good Day!