EgのExcelHome上传附件:
类MultipartEncoder:
2参boundary的-及字母数字的数量同Content-Type的bdr,而postData中各键首行的boundary则在开头补了俩-:其__init__内有句self.boundary = '--{0}'.format(self.boundary_value)。
postData中各键值对的结构:--boundary行、……key行、[文件流独有的Content-Type行]、空行、value行。列完各键值对,postData最后以--boundary--行结尾。
默认方法.to_string(),能如F12界面所示的postData那样显示。上传的文件名不能有中文,如F12下看到的是filename="测.rar",mpe却会给处理为filename*=utf-8''%E6%B5%8B.rar而上传失败,故而要么文件名无中文,要么m.to_string()后再作替换。
*********************分割线*********************
import os,random,string,requests
from fake_useragent import UserAgent as ua
from requests_toolbelt import MultipartEncoder
from tkinter.filedialog import askopenfilenames as ofn
def data参数法(fileData):
postData={'uid':uid,'hash':hash,'Filedata':fileData}
boundary='-'*10+''.join(random.choices(string.ascii_letters+string.digits,k=30))
m=MultipartEncoder(postData,boundary)
headers['Content-Type']=m.content_type
print(requests.post(url,m,headers=headers).text)
def files参数法(fileData):
#files参数的模式{k:(fileName,v,对应的Content-Type),},若v非文件流则(*)的首尾可为空
#files={'uid':(None,uid),'hash':(None,hash),'Filedata':fileData}
#r=requests.post(url,files=files,headers=headers)
r=requests.post(url,{'uid':uid,'hash':hash},files={'Filedata':fileData},headers=headers)
print(r.text)
if __name__ == '__main__':
url='http://club.excelhome.net/misc.php?mod=swfupload&fid=2&action=swfupload&operation=upload'
headers={'User-Agent':ua().random}
uid='24810??' #本例postData中的uid,hash,Filedata为必需键;??为我的学号-1
hash='8dfae1d86bfc714ec489e6518094d2b2'
filesName=ofn(initialdir=os.path.dirname(__file__),title='选择上传的各文件,路径不能有中文')
for fileName in filesName:
with open(fileName,'rb') as f:
fileData=(os.path.basename(fileName),f) #末项'application/octet-stream'等CT的值可不写
#data参数法(fileData)
files参数法(fileData)
出现7位数字为成功,到论坛如下的发帖界面,可看到"未使用的附件"的提示:
http://club.excelhome.net/forum.php?mod=post&action=newthread&fid=2。
****************************************分割线****************************************
Egの登录知呼:
import requests,time,execjs,json,random,string
from parsel import Selector
from requests_toolbelt import MultipartEncoder
def getData(username,password,captcha=''):
client_id='c3cef7c66a1843f8b3a9e6a1e3160e20'
timestamp=int(time.time()*1000)
with open('D:/signature.js','r',encoding='utf8') as f:
text=f.read()
signature=execjs.compile(text).call('run',client_id,timestamp)
data={'username':username,'password':password,'captcha':captcha,
'client_id':client_id,'grant_type':'password','timestamp':str(timestamp),
'source':'com.zhihu.web','signature':signature}
return data
def getHeaders():
headers=originalHeaders
response=Selector(s.get(indexUrl,headers=headers).text)
jsons=response.css('div#data::attr(data-state)').extract_first()
jsons=json.loads(jsons)['token']
xudid,xsrf=jsons['xUDID'],jsons['xsrf']
headers.update({'x-udid':xudid,'x-xsrftoken':xsrf,
'Referer':indexUrl+'signup?next=%2F',
'authorization':'oauth c3cef7c66a1843f8b3a9e6a1e3160e20'})
return headers
def checkCaptcha(headers,cn=True):
captchaUrl=indexUrl+'api/v3/oauth/captcha?lang='+('cn' if cn else 'en')
headers.pop('x-xsrftoken')
s.get(captchaUrl,headers=headers)
def login(username,password):
headers=getHeaders()
checkCaptcha(headers) #无论拦路虎是否跳出,中途路过都得打个招呼
postData=getData(username,password)
boundary='-'*27+''.join(random.choices(string.digits,k=11))
m=MultipartEncoder(postData,boundary)
headers['Content-Type']=m.content_type
s.post(indexUrl+'api/v3/oauth/sign_in',m,headers=headers)
s.headers=originalHeaders
response=s.get(indexUrl+'settings/profile').text
print(response)
if '个性域名'in response:
print(response,'登录成功',sep='\n\n')
if __name__=='__main__':
s=requests.Session()
indexUrl='https://www.zhihu.com/'
originalHeaders={'User-Agent':'Mozilla/5.0 Firefox/57.0 Chrome/64.0'}
username='+86'+'手机号'
password='密码'
login(username,password)
*********************分割线*********************
D:/signature.js:
………………截取自js文件的a函数及在同一个父函数块下的众兄弟函数………………
function run(client_id,timestamp) {
r = new a("SHA-1", "TEXT");
r.setHMACKey("d1b964811afb40118a12068ff74a12f4", "TEXT");
r.update('password'); //grantType:e,postData中已知grantType为'password'
r.update(client_id); //clientId:i.a,i.a同样讨巧取自postData的clientId,不再在js摸索
r.update("com.zhihu.web");
r.update(String(timestamp));
return r.getHMAC("HEX")
}
js解析历程:
①:定位到postData参数signature的出处:function r(e,t){var n=Date.now(),r=new a.a("SHA-1","TEXT");return r.setHMACKey("d1b964811afb40118a12068ff74a12f4","TEXT"),r.update(e),r.update(i.a),r.update("com.zhihu.web"),r.update(String(n)),s({clientId:i.a,grantType:e,timestamp:n,source:"com.zhihu.web",signature:r.getHMAC("HEX")},t)}。
②:测试发现末行的s({k:v,},t)并没对各键值对再做加工,可忽略s(*),然后改装为上文的run函数。形参t没被r引用故不管它,e没精力探索了,因其又是s({})的grantType的value,而比较几次抓包所得的401验证Session的postData,发现grantType是固定的,clientId也是,于是就取巧把它俩各自对应的e、i.a直接给出,用于r.update(*)。
③:r来自函数名a,并且用到了a的3个子函数setHMACKey、update、getHMAC。用便搜的EditPlus打开js文件:任搜个子函数,如搜function getHMAC或getHMAC=function或{key:"getHMAC",value:function(){。光标定位至此,再搜上一个function a或a=function或{key:"a",value:function(){,这样就找到a的开头,上文的js可不理了。把a函数头至js文件尾格式化下,格式化后光标移至文首搜^},即第1个出现在行首的},就是a函数的结尾。
④:把a的函数块及改装的run函数块贴到Console回车,最后执行run('hello',666),然而报错缺参数。看来只有a的函数块不行,于是范围先扩大为a的父函数块。本例a的上1行恰好就是其父的function头故不必搜头了,取父头至js文件尾再格式化,然后搜^},得a父的function块。取出父(*){…}的…函数正文,删除最后几句不明所以且惹错的void 0 !==(){…}.call()…,末尾的var那几行没用到也可不要,然后和run的function块、run('hello',666)先后在Console回车,出结果了。