病毒样本快到碗里来,一个样本下载爬虫的实现

简介

Malwar是一个使用了Cuckoo Sanbox的在线恶意软件分析系统,由于它提供一些病毒样本下载,就想能不能写个爬虫把样本下下来。顺便写篇博客记录下。

页面分析

打开 https://malwr.com/analysis ,我们可以当前页看到有TimeStamp、md5、文件名、文件类型和杀软查杀数,下一页类似。

图片说明

只有MD5的超链接可以点,点进去看看,

图片说明

我写这个的爬虫的目的是下载样本,只关心样本的下载地址,并不关心其他的信息。

现在我们可以理清下思路:

  • 获取每一页的网页源码
  • 解析当前页的每一个md5对应的详细信息链接
  • 在详细信息页面解析下载地址。

提取规则

先从第一页开始爬起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from scrapy.spiders import CrawlSpider
from scrapy.http import Request
from malwr.items import MalwrItem
from scrapy.selector import Selector
class BasicSpider(CrawlSpider):
name = "basic"
allowed_domains = ["malwr.com"]
strat_urls = ['https://malwr.com/analysis/?page=1']
def start_requests(self):
url = self.strat_urls[0]
yield Request(url, callback=self.parse_item)
def parse_item(self):
pass

scrapy支持xss选择器和css选择器,一般是用哪个比较方便就用哪个。

图片说明,

对应链接的xpath选择器为 //td/a/@href,

图片说明,

下一页的链接的xpath选择器为 //a[contains(text(),'Next')]/@href

图片说明,

要得到下载地址需要账号登陆,这里先不管它,单击Download下载文件对应的下载链接提取规则xpath选择器

//a[contains(@class,'btn-primary')]/@href.

现在来补充下代码,

1
2
3
4
5
6
7
8
9
10
11
12
13
def parse_item(self):
# 当前页所有md5对应的详细页面
url = response.xpath("//td/a/@href").extract()
for u in url:
url = urlparse.urljoin("https://malwr.com",u.encode("utf-8"))
yield Request(url,self.parse_downurl)
# 下一页
nextpage = response.xpath("//a[contains(text(),'Next')]/@href").extract()[0].encode('utf-8')
url = urlparse.urljoin("https://malwr.com",nextpage)
yield Request(url,self.parse_item)
#解析下载地址
def parse_downurl(self):
pass

登陆

之前写爬虫的时候,是可以直接通过post登陆的,而我开始写这篇文章的时候开始加上了google的ReCaptcha验证码。

只能换种方式使用selenium来登陆Malwr.得到登陆后的cookie后,我们就可以用这个cookie开始下载样本了。

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
def get_cookie():
loginurl = 'https://malwr.com/account/login/'
# 当然Ie也可以换成其他的,比如firefox、chrom等等
driver = webdriver.Ie()
driver.get(loginurl)
WebDriverWait(driver,3).until(lambda x:x.find_element_by_link_text('Logout'))
cookies = driver.get_cookies()
driver.close()
return cookies

使用Scrapy有一点很蛋疼,cookie它不会自动传递,需要自己在Request中传递下去,才能使后面的网络请求使用这个cookie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class BasicSpider(CrawlSpider):
name = "basic"
allowed_domains = ["malwr.com"]
strat_urls = ['https://malwr.com/analysis/?page=1']
custom_settings = {
"COOKIES":get_cookie(),
}
def start_requests(self):
url = self.strat_urls[0]
# cookiejar cookie传递
yield Request(url,cookies=self.custom_settings['COOKIES'],
callback=self.parse_item,meta = {'cookiejar' : 1})
def parse_item(self,response):
# 当前页所有md5对应的详细页面
url = response.xpath("//td/a/@href").extract()
for u in url:
url = urlparse.urljoin("https://malwr.com",u.encode("utf-8"))
yield Request(url,self.parse_downurl,meta = {'cookiejar' : 1})
# 下一页
nextpage = response.xpath("//a[contains(text(),'Next')]/@href").extract()[0].encode('utf-8')
url = urlparse.urljoin("https://malwr.com",nextpage)
yield Request(url,self.parse_item,meta = {'cookiejar' : 1})

我们在start_request函数里获取到登陆后的cookie,使用meta = {'cookiejar' : 1}将cookie传递到parse_item函数。

parse_item函数的Request的两个Request也加上cookiejar,一个把cookie传给parse_item,一个传给parse_downurl,

下载样本

scrapy本身就已经为我们提供了很好的文件下载、图片下载的功能,

修改custom_settings,加上我们要用的ITEM_PIPELINES(在这里面实现下载功能)和FILES_STORE(保存到的文件夹)

1
2
3
4
5
custom_settings = {
"COOKIES":get_cookie(),
'ITEM_PIPELINES':{'malwr.pipelines.MalwrPipeline':1},
"FILES_STORE":"E:\\Virus\\Malwr"
}

FilesPipeline是从file_urls中拿到下载地址然后开始下载文件,因此我们还需要Item.py定义一个file_urls.

1
2
3
4
5
6
7
8
9
10
11
12
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class MalwrItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
file_urls = scrapy.Field()

在piplines.py文件实现文件的下载方法,继承FilesPipeline下载文件。

由于下载文件需要cookie,因此重写了构造函数和get_media_requests函数,在Request中加上了cookies参数.

我们在custom_settings中保存了cookie,这里直接使用getlist拿到登陆后的cookie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy.http import Request
from scrapy.pipelines.files import FilesPipeline
class MalwrPipeline(FilesPipeline):
cookies = []
def __init__(self, store_uri, download_func=None, settings=None):
super(MalwrPipeline,self).__init__(store_uri, download_func,settings)
self.cookies = settings.getlist("COOKIES")
# 开始下载文件
def get_media_requests(self, item, info):
for file_url in item['file_urls']:
yield Request(file_url,cookies=self.cookies)

解析下载地址,将解析到的下载链接列表放入item的file_urls,返回item。pipeline会file_urls拿到链接开始调用get_media_requests下载文件

1
2
3
4
5
6
7
8
9
10
11
12
def parse_downurl(self,response):
try:
# 提取点击下载按钮的下载地址
url = response.xpath("//a[contains(@class,'btn-primary')]/@href").extract()[0].encode('utf-8')
url = urlparse.urljoin("https://malwr.com",url)
item = MalwrItem()
item['file_urls'] = [url]
return item
except Exception,e:
pass
return

最后附上完整的源码 https://github.com/ydc1992/Malwr

文章目录
  1. 1. 简介
  2. 2. 页面分析
  3. 3. 提取规则
  4. 4. 登陆
  5. 5. 下载样本
|