这是「进击的Coder」的第 858 篇技术分享
作者:TheWeiJun
来源:逆向与爬虫的故事
“
阅读本文大概需要 9 分钟。
”大家好,我是 TheWeiJun。在这篇文章中,我们将探索如何使用 Python 通过文件上传来与对方服务器进行交互,并将文件转换为其他格式。我们将使用 Python 中的 MultipartEncoder 来编码参数,从而实现整个流程的逆向操作。无论是上传图片、音频还是文档,本文将为你揭示这个神奇的魔法过程,让你轻松驾驭文件处理的艺术。让我们一起进入这个充满奇迹和创造力的世界吧!
特别声明:本公众号文章只作为学术研究,不作为其他不法用途;如有侵权请联系作者删除。
立即加星标
每月看好文
目录
一、前言介绍
二、网站分析
三、参数分析
四、参数还原
五、思路总结
趣味模块
Kim,一个热爱阅读小说的小伙子,沉迷于 mobi 格式的书籍中。然而,他面临一个问题 - 他找不到开源的包来将 mobi 转换为 txt 格式。绝望之际,他听说了一个神秘的逆向网站服务。他好奇而兴奋地决定一试身手。通过黑客般的技能,他将 mobi 文件投入逆向网站的深渊,瞬间,文件以 txt 的形式重新浮现!从此,Kim 在无数的字母中探索着无尽的故事。他的阅读世界因为那个奇特而有趣的模块而变得更加多姿多彩。
一、前言介绍
在当今数字化的时代,文件格式转换成为了日常生活中不可或缺的一部分。从文档、图片到音频和视频,我们经常需要将它们在不同的格式之间进行转换。然而,有时我们会遇到一些特殊的格式,它们并不常见或缺乏对应的开源转换工具。这时,我们就需要动用一些独特的技术手段。
本文将带你进入一个神秘的逆向网站服务的世界,它以其独特的技术能力而闻名。我们将以一个喜欢阅读小说的年轻人 Kim 为主人公,他面临将 mobi 格式转换为 txt 的问题。无法找到开源的解决方案,Kim 最终决定尝试逆向网站的服务。我们将探索他的奇妙冒险,见证他如何通过逆向技术改变了他的阅读体验。现在,让我们踏入这个充满创意和神秘的文件格式转换的世界吧!
二、网站分析
1、首先打开我们本次需要转换格式的网站,页面功能截图大致如下所示:
2、点击页面中的选择文件按钮,上传我们需要转换的文件,然后点击 Target 选择转换格式。并在 Network 中查看请求包,截图如下所示:
3、 查看 Google 浏览器页面中文件输出结果,截图如下所示:
4、 点击打开转换后的 txt 文件,看看转换结果是否准确,截图如下所示:
总结:文件是转换成功了,就是字体是乱码了(这个我们稍后一起解决)。接下来我们分析一下请求转换接口参数,来模拟下整个转换流程如何用代码去实现吧。
三、参数分析
点击 Payload 栏目,查看完整请求包 Form Data,截图如下所示:
file: 二进制文件流
targetformat: 转换格式
code: 每个格式是固定的
filelocation: local固定不变
legal: 固定的,网站警告
2. 直接开干,感觉没啥难度,也没啥加密值。复制整个请求到 curl 在线工具中,截图如下所示:
3. copy 代码到 pycharm 中,直接请求发包后,输出截图如下所示:
总结:我们查看打印结果,发现输出结果报错了。很显然,我们需要对 data 体进行重新组包了,浏览器里 curl 出来的是无法通过对方服务器校验的。接下来,我们一起进入 data 请求体组包环节吧。
四、参数还原
1. 通过分析 headers 中的 Content-Type 参数值,我们可以看到数据类型为 multipart/form-data,接下来编辑完整代码如下:
import requests
from requests_toolbelt import MultipartEncoder
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryCOUm5fNrq0VkgLsv',
'Origin': 'https://www.xxx.com',
'Pragma': 'no-cache',
'Referer': 'https://www.xxx.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
}
fields = {
"file": open('data/转换数据测试文件.mobi', "rb"),
"targetformat": "txt",
"code": "86000",
"oAuthToken": "",
"filelocation": "local",
"legal": "Our PHP programs can only be used in aconvert.com. We DO NOT allow using our PHP programs in any third-party websites, software or apps. We will report abuse to your cloud provider, Google Play and App store if illegal usage found!",
}
url = f"https://s5.xxx.com/convert/convert9.php"
m_data = MultipartEncoder(fields, boundary='----WebKitFormBoundaryqol36GPsnkBoFwtK')
headers['Content-Type'] = m_data.content_type
data = m_data.to_string()
response = requests.post('https://s5.xxxx.com/convert/convert9.php', headers=headers, data=data)
print(response.text)
2. 代码编辑完毕后,运行代码,response 出现对方服务警告,截图如下:
总结:我们不允许从任何第三方网站,软件或应用程序直接运行我们的 PHP 程序! 可以啊,居然还能识别到。我感觉这个网站没有这么严格,肯定是缺少一些关键性参数。
3. 经过认真分析后,观察初始 data、payload 中的 Content-Type 参数值,截图如下:
4. 将刚刚看到的 Content-Type 参数值类型加入到file字段中,完整代码如下:
import requests
from loguru import logger
from requests_toolbelt import MultipartEncoder
def covert_mobi(filename):
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Origin': 'https://www.xxx.com',
'Pragma': 'no-cache',
'Referer': 'https://www.xxx.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
}
fields = {
"file": (filename, open(filename, "rb"), "application/octet-stream"),
"targetformat": "txt",
"ocrlan": "0",
"oAuthToken": "",
"filelocation": "local",
"legal": "Our PHP programs can only be used in aconvert.com. We DO NOT allow using our PHP programs in any third-party websites, software or apps. We will report abuse to your cloud provider, Google Play and App store if illegal usage found!",
}
server_code = 30 if filename.endswith("pdf") else 5
url = f"https://s{server_code}.xxx.com/convert/convert9.php"
m_data = MultipartEncoder(fields, boundary='----WebKitFormBoundaryqol36GPsnkBoFwtK')
headers['Content-Type'] = m_data.content_type
data = m_data.to_string()
try:
resp = requests.post(url=url, headers=headers, data=data)
filename = resp.json().get("filename")
server = resp.json().get("server")
url = f"https://s{server}.xxxx.com/convert/p3r68-cdx67/{filename}"
logger.debug(url)
response = requests.get(url, headers=headers, proxies={})
response.encoding = 'utf-8'
except Exception as e:
print(e)
else:
return response.text
if __name__ == '__main__':
data = covert_mobi("data/转换数据测试文件.mobi")
print(data)
5. 代码运行后,最后展示打印输出截图如下:
6. kim 兄弟给我说,军哥能不能给爬虫提提速改成 Go 语言版本。经过研究后,附上 Go 语言版本代码如下:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
type ResItem struct {
Filename string `json:"filename"`
Ext string `json:"ext"`
Server string `json:"server"`
State string `json:"state"`
}
func main() {
// 创建一个缓冲区来构建multipart请求体
item := convertMobi()
filename := item.Filename
server := item.Server
convert_url := fmt.Sprintf("https://s%s.xxx.com/convert/p3r68-cdx67/%s", server, filename)
content := download(convert_url)
fmt.Println(string(content))
}
func download(url string) []byte {
// 创建GET请求并设置请求头
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
}
req.Header.Set("Referer", "https://www.xxx.com/")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
// 发送请求并获取响应
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
// 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
return respBody
}
func convertMobi() ResItem {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 添加文件字段
file, err := os.Open("/Users/xxxx/Desktop/转换数据测试文件.mobi")
if err != nil {
fmt.Println(err)
}
defer file.Close()
part, err := writer.CreateFormFile("file", "转换数据测试文件.mobi")
if err != nil {
fmt.Println(err)
}
io.Copy(part, file)
fmt.Println(writer.FormDataContentType())
// 添加其他字段
err = writer.WriteField("targetformat", "txt")
err = writer.WriteField("oAuthToken", "")
err = writer.WriteField("ocrlan", "0")
err = writer.WriteField("filelocation", "local")
err = writer.WriteField("legal", "Our PHP programs can only be used in aconvert.com. We DO NOT allow using our PHP programs in any third-party websites, software or apps. We will report abuse to your cloud provider, Google Play and App store if illegal usage found!")
// 结束写入请求体
err = writer.Close()
if err != nil {
fmt.Println(err)
}
// 创建POST请求并设置请求头
req, err := http.NewRequest("POST", "https://s5.xxx.com/convert/convert9.php", body)
if err != nil {
fmt.Println(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Referer", "https://www.xxx.com/")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
// 发送请求并获取响应
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
// 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
item := ResItem{}
err = json.Unmarshal(respBody, &item)
if err != nil {
fmt.Println(err)
}
return item
}
五、思路总结
回顾整个分析流程,本次总结主要概括为以下几点:
不常见爬虫如何分析参数组包
multipart/form-data 类型初识
Python、Go 语言版本代码实现
Content-Type 参数类型判断
本篇分享到这里就结束了,欢迎大家关注下期,我们不见不散☀️☀️✌️
End
欢迎大家加入【ChatGPT&AI 变现圈】,零门槛掌握 AI 神器!我们带你从小白到高手,解锁智能问答、自动化创作、技术变现的无限可能。与我们共同成长,开启 AI 新征程!立即行动,未来已来!(详情请戳:知识星球:ChatGPT&AI 变现圈,正式上线!)
扫码加入:
好文和朋友一起看~