Thursday, November 27, 2008

磨练UNIX工具技艺:.......盗链

在一个网站上我想下载月华剑士II的音乐集:
http://gh.ffshrine.org/soundtracks/599

发觉结构很简单,歌曲是以CD为目录存放的地址。
比如上面那个url就是编号为599的CD的url,链接进去有这支专辑的各首歌的对应url,比如第一首为:
http://gh.ffshrine.org/song/599/1

歌曲的真实地址也在这个页面里给出了。的确,浏览器里能看到歌曲的地址。
按道理很简单,直接wget就行了。

不过看了页面src后才发觉,事情没想象中那么容易。
歌曲的地址被javascript混淆过,是形如下面的一段东西:
<script>
var data = "var%20addr%20%3D%20%222 (此处删除若干) 0A%09%09";
eval(unescape(data));
</script>

那么首先要decodeurl的encoding(what's this? refer here: http://www.blooberry.com/indexdot/html/topics/urlencoding.htm)
(
一开头我搞错了,以为是html的entity问题,这个可以使用perl的HTML::Entities模块来完成,具体如下:
// data是形如"&nbsp; &lt;"这种东东
use HTML::Entities;
use decode_entities($data);
)
使用python可以很方便地decode,具体如下:
import urllib;
urllib.unquote("%20%3D");

于是得到了预期的东西:
'var addr = "http://dl1.ffshrine.org........ '

接下来,则要使用javascript的eval功能来执行这段javascript代码了……这里我考虑了两种方法:

1. 使用某个实际的html engine,如php…… 来得到最终链接地址
2. 使用某个独立的html engine,我搜索到了javax.script

具体代码如下:(script.java)
import javax.script.*;
import java.net.URL;
import java.io.*;

class script{
        public static void main(String [] args){
                ScriptEngineManager mgr = new ScriptEngineManager();
                ScriptEngine engine = mgr.getEngineByName("JavaScript");

                try{
                        URL url = new URL( "http://gh.ffshrine.org/song/598/1" );
                        InputStreamReader reader = new FileReader( "/home/iveney/workspace/test.js" );

                        Object obj = engine.eval(reader);
                        System.out.println(obj);
               }catch(Exception e){
                        e.printStackTrace();
                }
        }
}

最主要的一步是engine.eval,等同于原javascript中的eval.
编译能通过,但事实上他是有问题的。因为解码后我得到的是这样的东东(我把whitespace escape了):
var addr = "http://dl1.ffshrine.org/soundtracks/dl/598/00720b/Last Blade 2 Arrange/01_-_the_flower_that_blooms_in_the_moonlight.mp3";
function decode(address) {
var Address = "";
Address = address;
Address = unescape(Address);


function unescapesome(thesome) {
        var newString = thesome;
        newString = newString.replace(/%3A/g, ":");
        newString = newString.replace(/%27/g, "'");
        newString = newString.replace(/%28/g, "(");
        newString = newString.replace(/%29/g, ")");
        newString = newString.replace(/%21/g, "!");
        newString = newString.replace(/%7E/g, "~");
        return newString;
}

document.getElementById("linkcode").innerHTML = "<a href='" + unescapesome(escape(Address)) + "'>click here</a>";}

decode(addr);

里面有用到DOM model的document,发觉不能正常运行……
后来我没有深究究竟java这条路要怎么做,因为事实上我看到真实链接已经出来了 -。- 就在第一行!!!

于是马上用curl下载一系列的url,然后准备用相似步骤还原成原来的javascript得到地址。
但是没有得到预期的结果――还原成的是这样的东西:

var addr = new Array()
addr[0] = "http://dl1.f";
addr[1] = "fshrine.org/";
addr[2] = "soundtracks/";
addr[3] = "dl/598/00720";
addr[4] = "b/Last Blade";
addr[5] = " 2 Arrange/m";
addr[6] = "vovld01_-_th";
addr[7] = "e_flower_tha";
addr[8] = "t_blooms_in_";
addr[9] = "the_moonligh";
addr[10] = "t.mp3";
function decode(address) {
var Address = "";
for (var i = 0; i != address.length; i++) {
Address += address[i]
}
Address = unescape(Address);
Address = Address.replace(/\/mvovld/, "/");


function unescapesome(thesome) {
        var newString = thesome;
        newString = newString.replace(/%3A/g, ":");
        newString = newString.replace(/%27/g, "'");
        newString = newString.replace(/%28/g, "(");
        newString = newString.replace(/%29/g, ")");
        newString = newString.replace(/%21/g, "!");
        newString = newString.replace(/%7E/g, "~");
        return newString;
}

document.getElementById("linkcode").innerHTML = "<a href='" + unescapesome(escape(Address)) + "'>click here</a>";}

decode(addr);
我日!原来地址还被一个function wrap着呢!
难道又要我用java来解释吗?
但是为什么我直接查看页面源代码会得到已经被解释过的javascript代码呢?

我猜测原因是因为referer的问题。用curl我没有构造合适的http参数,也许原来的页面有检查我是从哪个referer进去的。
然后我换wget试了试,却惊讶的发觉问题解决了:我能用wget得到正确的解释过的url……
由于最近忙,这里我先不深究,迟点问高手。

最后的shell代码如下:
#!/bin/bash

DIR=/tmp/mp3
#curl "http://gh.ffshrine.org/song/598/[1-12]" --create-dirs /tmp/mp3 -o "/tmp/mp3/#1.html"
mkdir $DIR
for i in `seq 1 48`
do
       wget http://gh.ffshrine.org/song/599/$i -O "$DIR/$i.html"
done

for i in `seq 1 48`
do
        str=`grep "var data" "$DIR/$i.html"`
        str=`echo $str | cut -d '"' -f 2`
        #echo $str
        command="import urllib; js=urllib.unquote('$str'); print js"
        # we get javascript now
        js=`echo $command | python`
        addr=`echo $js | head -n 1 | cut -d \" -f 2`
        echo $addr
        a=`wget "$addr" -P /win/d/Music/LastBladeIIOST/`
done

上面用到了不少工具,待我稍微注释。
首先是一个for loop用wget把所有页面下回来,存到相应位置。注意-O是指定输出文件名,而-P是指定输出的目录。
然后一个for loop处理decode stuff。
1.把该html文件中有用的那一段grep出来。
2.用cut把引号部分提取出来
3.用python decode之
4.再用cut把链接提取出来
5.用wget进行下载

今晚浪费了不少时间,不过当作锻炼一下自己吧。

No comments: