日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从nginx日志原始二进制数据还原文件

發(fā)布時間:2025/7/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从nginx日志原始二进制数据还原文件 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

nginx的access日志自定義格式記錄了post請求數(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)義自身。也就是說,print的時候,"\"被轉(zhuǎn)義成"",所以我們看到的是"\x0D",實(shí)際文件中儲存的是"\\0D",而我們真正需要寫入文件的,是"\x0D"這個轉(zhuǎn)義字符,而不是"\x0D"這個字符串。

由于文件是以二進(jìn)制方式打開的,讀取到的都是原始的流,所以在匹配的時候需要使用"\\\\"來表示"\\"。

使用re庫處理日志:

原始數(shù)據(jù)前面是Content-Transfer-Encoding: binary加上兩個\r\n,后面是一個\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ù)中本來就含有\(zhòng)\x0D\\x0A--的話,就會丟失原始數(shù)據(jù),但目前為止還沒遇到這種情況有效。更麻煩的做法是先匹配出表單分割字符串,然后以此為界分割出數(shù)據(jù)后,再刪除兩邊多余的字符和回車換行。

成功提取出原始數(shù)據(jù)后,對數(shù)據(jù)進(jìn)行解碼,并寫入.jpg文件。如果沒有解碼這一步,所有的數(shù)據(jù)都會被當(dāng)做字符串寫入文件,而不會被當(dāng)成轉(zhuǎn)義字符。簡單做個測試:

把"\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時因?yàn)榉葱备鼙晦D(zhuǎn)義成字符,所以變成一根。解碼后的數(shù)據(jù)本身只有一根斜杠,print時打印出轉(zhuǎn)義字符本身,也就是亂碼。

接下來就可以分割文件,然后還原圖片了。

#!/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 #第一張圖片序號為1while True:l = f.readline().strip() #讀取一行并去掉末尾的換行符\nif not l: #文件讀完返回''時退出循環(huán)breakl = re.sub(pf, '', l) #將數(shù)據(jù)前的字符替換為空l = 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 #下一張圖片序號加1 except IOError:pass

access.log含有多條日志,每條日志都包含圖片數(shù)據(jù)。日志較大,所以不使用readlines(),本來使用list也很占內(nèi)存,且這樣處理比較慢,要等程序把整個文件讀完。按行讀取日志后提取出圖片數(shù)據(jù)后解碼寫入文件即可。

在這里放一個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日志原始二进制数据还原文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。