从nginx日志原始二进制数据还原文件
nginx的access日志自定義格式記錄了post請(qǐng)求數(shù)據(jù),因?yàn)橐恍┰蛐枰獜脑紨?shù)據(jù)恢復(fù)出jpg格式圖片。
首先處理日志,篩選出含有圖片數(shù)據(jù)的日志條目,取出其中一條進(jìn)行分析,大致格式如下,為了便于查看,做一下?lián)Q行處理:
- | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 | --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9 \x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9 \x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\abc.jpg\x22 \x0D\x0AContent-Type: application/octet-stream \x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00 ... \xD2_\xA0\x1A\x7F\xFF\xD9\x0D\x0A --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9-- \x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123其中,--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9是表單的分隔字段,\x0D\x0A是回車和換行的轉(zhuǎn)義字符,分別等同于\r和\n,\xFF\xD8\xFF\xE0\x00\x10JFIF\x00 ... \xD2_\xA0\x1A\x7F\xFF\xD9部分就是圖片原始數(shù)據(jù)。
使用上述單行日志文件tmp.log進(jìn)行調(diào)試:
>>> f = open('tmp.log', 'rb') >>> data = f.read() >>> data "- | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 | --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\x221512806410245.jpg\x22\x0D\x0AContent-Type: application/octet-stream\x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00 ... \xBC'\xF1\x8C\xCC\x83,\xFAo\xD2_\xA0\x1A\x7F\xFF\xD9 \x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123" >>> print data - | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 | --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\x221512806410245.jpg\x22\x0D\x0AContent-Type: application/octet-stream\x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00 ... \xBC'\xF1\x8C\xCC\x83,\xFAo\xD2_\xA0\x1A\x7F\xFF\xD9\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123 >>> f.close()可以看出,打印到屏幕上的轉(zhuǎn)義字符,如"\x0D\x0A"在文件中的原始字符串實(shí)際上是"\\x0D\\x0A"。因?yàn)?#34;"是用于轉(zhuǎn)義的,所以想要顯示反斜杠本身就必須轉(zhuǎn)義自身。也就是說(shuō),print的時(shí)候,"\"被轉(zhuǎn)義成"",所以我們看到的是"\x0D",實(shí)際文件中儲(chǔ)存的是"\\0D",而我們真正需要寫入文件的,是"\x0D"這個(gè)轉(zhuǎn)義字符,而不是"\x0D"這個(gè)字符串。
由于文件是以二進(jìn)制方式打開(kāi)的,讀取到的都是原始的流,所以在匹配的時(shí)候需要使用"\\\\"來(lái)表示"\\"。
使用re庫(kù)處理日志:
原始數(shù)據(jù)前面是Content-Transfer-Encoding: binary加上兩個(gè)\r\n,后面是一個(gè)\r\n跟上表單分割字符串--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--
>>> import re >>> pf = re.compile('^.*Content-Transfer-Encoding: binary\\\\x0D\\\\x0A\\\\x0D\\\\x0A') >>> pb = re.compile('\\\\x0D\\\\x0A--.*$') >>> data = re.sub(pf,'',data) >>> data "\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00 ... \\xBC'\\xF1\\x8C\\xCC\\x83,\\xFAo\\xD2_\\xA0\\x1A\\x7F\\xFF\\xD9\\x0D\\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\\x0D\\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123" >>> data = re.sub(pb,'',data) >>> data "\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00 ... \\xBC'\\xF1\\x8C\\xCC\\x83,\\xFAo\\xD2_\\xA0\\x1A\\x7F\\xFF\\xD9"這樣的匹配處理并不嚴(yán)謹(jǐn),假設(shè)原始數(shù)據(jù)中本來(lái)就含有\(zhòng)\x0D\\x0A--的話,就會(huì)丟失原始數(shù)據(jù),但目前為止還沒(méi)遇到這種情況有效。更麻煩的做法是先匹配出表單分割字符串,然后以此為界分割出數(shù)據(jù)后,再刪除兩邊多余的字符和回車換行。
成功提取出原始數(shù)據(jù)后,對(duì)數(shù)據(jù)進(jìn)行解碼,并寫入.jpg文件。如果沒(méi)有解碼這一步,所有的數(shù)據(jù)都會(huì)被當(dāng)做字符串寫入文件,而不會(huì)被當(dāng)成轉(zhuǎn)義字符。簡(jiǎn)單做個(gè)測(cè)試:
把"\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01"保存到文件test.log。
>>> f = open('test.log', 'rb') >>> data = f.read() >>> data '\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\n' >>> print data \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01>>> import codecs as c >>> c.decode(data, 'string_escape') '\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\n' >>> print c.decode(data, 'string_escape') ����JFIF>>>讀取的原始數(shù)據(jù)是兩根反斜杠,print時(shí)因?yàn)榉葱备鼙晦D(zhuǎn)義成字符,所以變成一根。解碼后的數(shù)據(jù)本身只有一根斜杠,print時(shí)打印出轉(zhuǎn)義字符本身,也就是亂碼。
接下來(lái)就可以分割文件,然后還原圖片了。
#!/usr/bin/env pythonimport codecs, reifile = 'access.log' suffix = 'jpg'pf = re.compile('^.*Content-Transfer-Encoding: binary\\\\x0D\\\\x0A\\\\x0D\\\\x0A') pb = re.compile('\\\\x0D\\\\x0A--.*$')try:with open(ifile, 'rb') as f:number = 1 #第一張圖片序號(hào)為1while True:l = f.readline().strip() #讀取一行并去掉末尾的換行符\nif not l: #文件讀完返回''時(shí)退出循環(huán)breakl = re.sub(pf, '', l) #將數(shù)據(jù)前的字符替換為空l(shuí) = re.sub(pb, '', l) #將數(shù)據(jù)后的字符替換為空img_file = '.'.join([str(number), suffix]) #圖片文件名稱print img_file #打印名字方便看進(jìn)度with open(img_file, 'wb') as i: #解碼并寫入文件i.write(codecs.decode(l, 'string_escape'))number += 1 #下一張圖片序號(hào)加1 except IOError:passaccess.log含有多條日志,每條日志都包含圖片數(shù)據(jù)。日志較大,所以不使用readlines(),本來(lái)使用list也很占內(nèi)存,且這樣處理比較慢,要等程序把整個(gè)文件讀完。按行讀取日志后提取出圖片數(shù)據(jù)后解碼寫入文件即可。
在這里放一個(gè)demo日志,方便以后改進(jìn)和調(diào)試。
https://files.cnblogs.com/files/nampp/demo.tar.gz
EOF
轉(zhuǎn)載于:https://www.cnblogs.com/nampp/p/8044085.html
總結(jié)
以上是生活随笔為你收集整理的从nginx日志原始二进制数据还原文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mysql 关联关系
- 下一篇: 理解PeopleSoft集成代理(Int