How To Decompile Actionscript Code In Swf -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

    Author: xy7[80sec]

    EMail: xy7#80sec.com/xuanmumu@gmail.com

    Site: http://www.80sec.com

    Date: 2009-7-27

    [ 目录 ]

    0x00 前言

    0x01 swf 文件头分析

    0x02 swf 文件结构

    0x03 swf tag类型

    0x04 寻找Actionscript代码

    0x05 还原带有变量的AS代码段

    0x06 总结

    0x07 代码示例

    0x00 前言

    随着Flash的应用越来越广泛,针对Flash的安全性问题也被人们所重视,

How To Decompile Actionscript Code In Swf

。通过提取Flash文件中的AS代码,可以进行恶意行为检测(网马分析),代码安全性分析(HP的SWFSCAN)或者针对AS代码中出现的URL进行进一步检测分析(APPSCAN)。本文只是一篇工作笔记,通过实现一个简单的Swf Decompiler来提取SWF文件中的AS代码。

    0x01 swf 文件头分析

    首先新建一个flash文件,默认大小,背景,无任何其他元素,只添加一个geturl函数,里面随便写上一个链接http://1.1.1.1。这个新创建的flash文件就作为我们的分析对象。

    SWF文件头格式如下:

    SWF File Header

    Field Type Comment

    Signature UI8 Signature byte:

    “F” indicates uncompressed

    “C” indicates compressed (SWF 6 and later only)

    Signature UI8 Signature byte always “W”

    Signature UI8 Signature byte always “S”

    Version UI8 Single byte file version (for example, 0x06 for SWF 6)

    FileLength UI32 Length of entire file in bytes

    FrameSize RECT Frame. size in twips

    FrameRate UI16 Frame. delay in 8.8 fixed number of frames per second

    FrameCount UI16 Total number of frames in file

    UE打开新建的flash文件,首先读前三个字节,46 57 53=FWS,说明该文件是未经过zlib压缩。

    下一个字节是代表flash的版本,0a=10。

    继续后四个字节代表flash文件的大小4E 05 00 00 ,这里需要转成大尾存储0000054e=1385bytes。

    接下来是flash的尺寸,这里定义了一个RECT结构用来存储这些数据。

    RECT结构如下:

    RECT

    Field Type Comment

    Nbits UB[5] Bits in each rect value field

    Xmin SB[Nbits] x minimum position for rect

    Xmax SB[Nbits] x maximum position for rect

    Ymin SB[Nbits] y minimum position for rect

    Ymax SB[Nbits] y maximum position for rect

    继续往后读1个字节78,转换成2进制补齐8位78=01111000,根据RECT的格式取前五位01111=15,表示剩下的数据要按每15位进行划分,由于还剩下4组数据要表示,所以需要60位,60位至少需要读8个字节,所以RECT结构需要的字节数为00000008h: 78 00 05 5F 00 00 0F A0 00,都转为2进制进行15位分割,最后不足15位的舍去,最后得出的结果为 twip(20twip=1像素),所以最后输入应该是这样:

    01111

    000000000000000

    010101011111000

    000000000000000

    001111101000000

    RECT binary value:0

    RECT binary value:550

    RECT binary value:0

    RECT binary value:400

    接下来2个字节代表帧速 00 18 =>18 00 =>24

    最后2个字节代表帧数 01 00 =>00 01 => 1

    至此,swf文件头就结束了。

    0x02 swf 文件结构

    Swf文件主体是由一连串的tag组成,最终以一个end tag为结尾。大概结构如下:

    [文件头]---[ FileAttributes]---[tag]---[tag]…[end tag]

    | |

    [tag type] [tag type]

    | |

    [tag data] [tag data]

    0x03 swf tag类型

    根据tag头可以将tag分为短tag和长tag,具体格式如下:

    RECORDHEADER (short)

    Field Type Comment

    TagCodeAndLength UI16 Upper 10 bits: tag type

    Lower 6 bits: tag length

    RECORDHEADER (long)

    Field Type Comment

    TagCodeAndLength UI16 Tag type and length of 0x3F

    Packed together as in short header

    Length SI32 Length of tag

    也就是说高10位都代表着tag类型,后6位如果长度大于等于0x3f则为长tag,其余的则是短tag。

    按tag类型来分,tag也可分做2类:Definition tags和Control tags。其中Definition tags主要包含了flash中的文本,声音,图像等资源,而Control tags则包含了一些文件的处理流程等控制元素。

    既然已经分清楚了swf tag类型,那么就可以进一步分析tag以寻找我们需要的AS代码。

    文件头已经分析完毕,接下来就来分析第一个tag:FileAttributes。继续上面的例子,到了分析tag的时候依次读2个字节:44 11 =>11 44 => 0001000101000100 => 0001000101(高10位) => 69,在swf文件格式中查找Tag type = 69,正是FileAttributes,接着低6位代表tag长度:000100 =>4,向后再读4个字节:10 00 00 00 => 00 00 00 10 =>16 => 00010000,这段数据就是FileAttributes tag的数据内容了,FileAttributes tag到此结束。数据可以对照着FileAttributes数据结构来看,结构比较长就不贴出来了,其中第4位是1,表明这个 flash中含有matadata,继续往后读2个字节 7F 13,按位取高10位=77。

    Metadata

    Field Type Comment

    Header RECORDHEADER Tag type = 77

    Metadata STRING XML Metadata

    低6位全为1,所以说matadata是第一个长tag,长tag需要4个字节来表示数据长度,这些数据对实际分析没有什么作用,所以分析出数据长度就可以跳过了。

    0x04寻找Actionscript代码

    通过前面2个tag的分析,已经能够清楚的知道swf tag的固定格式以及取数据的方法了,那么我们定义的AS2中geturl函数到底存在哪里呢?继续读下2个字节:3f 03 => 03 3f ,取高10位0000001100 =>12,这个12是什么意思呢,查swf文档:

    DoAction

    Field Type Comment

    Header RECORDHEADER Tag type = 12

    Actions ACTIONRECORD [zero or more] List of actions to perform. (see

    following table, ActionRecord)

    ActionEndFlag UI8 = 0 Always set to 0

    注意ACTIONRECORD

    Field Type Comment

    ActionCode UI8 An action code

    Length If code >= 0x80, UI16 The number of bytes in the

    ACTIONRECORDHEADER, not

    counting the ActionCode

    现在可以确定ActionCode是包含在Actions ACTIONRECORD中,Actions ACTIONRECORD又是属于DoAction。判断低6位得知DoAction是长tag,继续读后面4个字节来确定DoAction的数据长度:1A 00 00 00 =>26。至此就可以判定我们之前建立的AS代码就包含在这26个字节中。接下来根据ACTIONRECORD的定义,1个字节代表ActionCode,继续读:83,查找swf文档:

    Field Type Comment

    ActionGetURL ACTIONRECORDHEADER ActionCode = 0x83

    UrlString STRING Target URL string

    TargetString STRING Target string

    找到了我们定义的geturl函数,所以继续读下面2个字节16 00 =>00 16=>22,那么这22个字节就是geturl函数里的参数了,提取出来还原一下看看:

    687474703a2f2f312e312e312e31005f626c616e6b00

    http://1.1.1.1 _blank

    最终通过对swf文件格式的分析,还原出了一段最简单的AS代码:

    _getURL('http://1.1.1.1' '_blank')

    _end0x05 还原带有变量的AS代码段

    假设有如下代码段:

    on (release)

    {

    var host = "http://10.1.5.16";

    var url = host + "/sql/index.php";

    getURL(url, "_blank");

    }Geturl获取的是通过变量拼接组成的url。首先看一段数据:

    88 32 00 05 00 68 6f 73 74 00 68 74 74 70 3a 2f 2f 31 30 2e 31 2e 35 2e 31 36 00 75 72 6c 00 2f 73 71 6c 2f 69 6e 64 65 78 2e 70 68 70 00 5f 62 6c 61 6e 6b 00

    第一个字节:0x88

    ActionConstantPool

    ActionConstantPool creates a new constant pool, and replaces the old constant pool if one

    already exists.

    Field Type Comment

    ActionConstantPool ACTIONRECORDHEADER ActionCode = 0x88

    Count UI16 Number of constants to follow

    ConstantPool STRING[Count] String constants

    所有的变量会被预先存放在ConstantPool中,上面的数据还原后如下:

    _constantPool "host" "http://10.1.5.16" "url" "/sql/index.php" "_blank"

    接着往下读一个字节:96 = ActionPush,后两个字节04 00 =>00 04 一共要往后再读4个字节的数据,合并起来就是:

    96 04 00 08 00 08 01

    08代表ActionPush结构中的type 00代表ConstantPool中的第一个算素,01代表ConstantPool中第2个元素,还原就是:

    _push "host" http://10.1.5.16

    读下一个字节:3c

    Field Type Comment

    ActionDefineLocal ACTIONRECORDHEADER ActionCode = 0x3C

    赋值操作:var host = http://10.1.5.16

    继续读,push操作跟前面一样:

    96 04 00 08 02 08 00

    还原:

    _push "url" "host"

    继续读:1c

    ActionGetVariable ACTIONRECORDHEADER ActionCode = 0x1C

    url = host

    96 02 00 08 03

    _push "/sql/index.php"

    47

    ActionAdd2 ACTIONRECORDHEADER ActionCode = 0x47

    url = host+"/sql/index.php"

    3c

    ActionDefineLocal

    Var url = host+"/sql/index.php"

    //96 02 00 08 02

    _push "url"

    //1c

    _getVariable

    //96 02 00 08 04

    _push "_blank"

    获取url _blank

    //9a 01 00 00

    _getURL2 flag 0

    调用geturl2函数……

    其中包含的一些actioncode具体可以查看swf文档,有很详细的操作说明,篇幅问题这里就不贴出来了。

    0x06 总结

    终于到总结了……呃,这篇文章只是比较简单的描述如何通过静态分析swf文件格式来提取出AS代码,举的例子也都比较简单,但实际分析过程中程序还需要考虑很多因素,包括载入swf tag字典,判断哪些tag是包含AS代码的,那些是Definition tags可以跳过的,当然,如果是想开发一个完整的SWFDecompiler,那么所有的tag都需要逐个分析。工作笔记难免有没说清楚和理解错误的地方,还望多多包涵。

    0x07 代码示例

    #!/usr/bin/php

   

    /*--------------------------

    Author: xy7[80sec]

    EMail: xy7#80sec.com/xuanmumu@gmail.com

    Site: http://www.80sec.com

    Date: 2009-5-31

    ---------------------------*/

    if ($argc < 2) {

    print_r('

    Usage: php '.$argv[0].' filename

    Example:

    php '.$argv[0].' hello.swf

    ');

    }

    error_reporting(7);

    ini_set('max_execution_time', 0);

    $swffile = $argv[1];

    function SingleHexToDec($hex)

    {

    $v=Ord($hex);

    if(47<$v&&$v<58)

    return $v-48;

    if(64<$v&&$v<71)

    return $v-65+10;

    if(96<$v&&$v<103)

    return $v-97+10;

    }

    function UnsetFromHexString($str)

    {

    if(!$str)

    return false;

    $code="";

    for($i=0;$i

    {

    $code.=chr(SingleHexToDec(substr($str,$i,1))*16+SingleHexToDec(substr($str,$i+1,1)));

    }

    return $code;

    }

    function openswfflie(){

    global $swffile;

    $fp=@fopen($swffile,"r");

    if($fp){

    echo "OUTPUT:".$swffile."------------OPENED \r\n\r\n";

    $sign=fread($fp,3);

    echo "Output:".bin2hex($sign)."\r\n";

    if($sign!="FWS" && $sign!="CWS"){

    echo "OUTPUT:".$swffile."is not a SWF file!\r\n";

    }

    if (substr($sign,0,1)=="F")

    echo "Indicates uncompressed\r\n";

    else

    echo "Indicates compressed";

    $version=bin2hex(fread($fp,1));

    echo "OUTPUT:".$version."\r\n";

    echo "version is ".hexdec($version)."\r\n";

    //读文件大小

    for ($i=0;$i<4;$i++){

    $size = fread($fp,1);

    echo "Output:File size read 1 byte:".bin2hex($size)."\r\n";

    $nsize = ord($size);

    //echo "Output:Partial size is".($nsize<<(8*$i))."\r\n";

    $totalsize +=($nsize<<(8*$i));

    }

    echo "Swf size is:".$totalsize."bytes\r\n";

    //RECT

    $firstbyte = fread($fp,1);

    $newfirstbyte = bin2hex($firstbyte);

    $change_newfirstbyte = decbin(hexdec($newfirstbyte));

    if (strlen($change_newfirstbyte)!=8){

    $new_change_newfirstbyte = str_pad($change_newfirstbyte,8,"0",STR_PAD_LEFT);

    }

    //echo $new_change_newfirstbyte;

    $fieldsize = ord($firstbyte)>>3;

    echo "RECT field size:".$fieldsize."\r\n";

    $needbytes = ceil($fieldsize*4/8)+1;

    //echo $needbytes;

    for($i=0;$i<8;$i++){

    $nextbytes = bin2hex(fread($fp,1));

    echo $nextbytes."\r\n";

    $change_bin = decbin(hexdec($nextbytes));

    //echo $change_bin."
";

    if (strlen($change_bin)<=8)

    $new_change_bin = str_pad($change_bin,8,"0",STR_PAD_LEFT);

    //echo $new_change_bin."
";

    $bytes .=$new_change_bin;

    }

    //echo $bytes;

    $allbytes = $new_change_newfirstbyte.$bytes;

    //echo $allbytes."
";

    $thefirstfivebits = substr($allbytes,0,5);

    echo $thefirstfivebits."\r\n";

    $next1 = substr($allbytes,6,$fieldsize);

    echo $next1."\r\n";

    $next2 = substr($allbytes,20,$fieldsize);

    echo $next2."\r\n";

    $next3 = substr($allbytes,35,$fieldsize);

    echo $next3."\r\n";

    $next4 = substr($allbytes,50,$fieldsize);

    echo $next4."\r\n";

    echo "RECT binary value:".(bindec($next1)/20)."\r\n";

    echo "RECT binary value:".(bindec($next2)/20)."\r\n";

    echo "RECT binary value:".(bindec($next3)/20)."\r\n";

    echo "RECT binary value:".(bindec($next4)/20)."\r\n";

    //Frame. rate

    for($i=0;$i<2;$i++){

    $framerate = ord(fread($fp,1));

    echo $framerate."\r\n";

    }

    //Frame

    $newframe. = 0;

    for($i=0;$i<2;$i++){

    $frame. = ord(fread($fp,1));

    $newframe. += ($frame<<(8*$i));

    //echo $newframe;

    }

    echo "Frames:".$newframe."\r\n";

    //---------------------FileAttribute-----------------------//

    for($i=0;$i<2;$i++){

    $file = ord(fread($fp,1));

    $newfile += ($file<<(8*$i));

    }

    echo $newfile."\r\n";

    echo $high10 = $newfile>>6;

    $low6 = substr(decbin($newfile),-6);

    //echo decbin($low6)."\r\n";

    if ($high10 == 69){

    echo "Found FileAttribute Tag!\r\n";

    }

    echo "FileAttributeDataBytes is:".bindec($low6)."\r\n";

    //---------------------FileAttribute-----------------------//

    //---------------------FileAttributeData-------------------//

    for($i=0;$i<4;$i++){

    $data = ord(fread($fp,1));

    $newdata += ($data<<(8*$i));

    }

    $newdata = str_pad(decbin($newdata),8,"0",STR_PAD_LEFT);

    echo $newdata."\r\n";

    if (substr($newdata ,3,1) == 1){

    echo "Found Metadata!\r\n";

    }else{

    echo "Not found Metadata!\r\n";

    }

    //---------------------FileAttributeData-------------------//

    //---------------------HasMetadata-------------------//

    for($i=0;$i<2;$i++){

    $metatag = ord(fread($fp,1));

    $newmetatag += ($metatag<<(8*$i));

    }

    $high10 = $newmetatag>>6;

    if ($high10 == 77){

    echo "Metadata Type:".$high10."\r\n";

    }

    $bin = decbin($newmetatag);

    $low6 = substr($bin,-6);

    if ($low6 == 111111){

    echo "Long Tag - Need 4 bytes\r\n";

    }else{

    echo "Tag type wrong\r\n";

    }

    for ($i=0;$i<4;$i++){

    $metalength = ord(fread($fp,1));

    $newmetalength += ($metalength<<(8*$i));

    }

    echo "Damn Metadata Length is ".$newmetalength."\r\n";

    //---------------------HasMetadata-------------------//

    //----------------------SetBackgroundColor------------------//

    fseek($fp,$newmetalength,SEEK_CUR);

    for ($i=0;$i<2;$i++){

    $tag = ord(fread($fp,1));

    $newtag += ($tag<<(8*$i));

    }

    echo $high10 = $newtag>>6;

    if ($high10 == 9){

    echo "Found SetBackgroundColor!\r\n";

    }

    for ($i=0;$i<3;$i++){

    $color = bin2hex(fread($fp,1));

    echo "".$color."\r\n";

    }

    //----------------------SetBackgroundColor------------------//

    //----------------------DoAction------------------//

    for ($i=0;$i<2;$i++){

    $nexttag = bin2hex(fread($fp,1));

    echo $nexttag;

    $newnexttag += ($nexttag<<(8*$i));

    }

    echo $high10 = $newnexttag>>6;

    if ($high10 == 12){

    echo "Found First DoAction!\r\n";

    }else{

    for ($i=0;$i<2;$i++){

    $nexttag = ord(fread($fp,1));

    $newnexttag += ($nexttag<<(8*$i));

    }

    echo ($high10 = $newnexttag>>6)."\r\n";

    }

    for ($i=0;$i<4;$i++){

    $actionlength = ord(fread($fp,1));

    $length += ($actionlength<<(8*$i));

    }

    echo "First DoAction Length is ".$length."\r\n";

    $actioncode = bin2hex(fread($fp,1));

    echo "0x".$actioncode."\r\n";

    if ($actioncode == 83){

    echo "_geturl\r\n";

    }elseif($actioncode == "9a"){

    echo "_geturl2\r\n";

    }else{

    echo "NULL";

    }

    for ($i=0;$i<2;$i++){

    $q = ord(fread($fp,1));

    $len += ($q<<(8*$i));

    }

    for ($i=0;$i<$len;$i++){

    $c = fread($fp,1);

    $hexcontent .= bin2hex($c);

    //$hexcontent.=$hexcontent;

    }

    echo "[Action in Frame. 1]\r\n";

    echo $hexcontent."\r\n";

    echo UnsetFromHexString($hexcontent);

    }

    }

    openswfflie();

    //FindGoOn();

    ?>

最新文章