├── .idea ├── js-reverse-log.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── kongzhongwang ├── README.md ├── demokongzhongwang.py └── encrydc.js ├── manhuagui └── README.md ├── meituan ├── README.md └── demomeituan.py ├── mihuashi └── 米画师笔记.md ├── qingguo ├── README.md ├── demo002(success).py ├── demo01.js └── imgs │ ├── 123.jpg │ ├── img01.jpg │ ├── img02.jpg │ ├── img03.jpg │ ├── img04.jpg │ ├── img05.jpg │ ├── img06.jpg │ ├── img07.jpg │ ├── img08.jpg │ ├── img09.jpg │ ├── img10.jpg │ ├── img11.jpg │ ├── img12.jpg │ ├── index.jpg │ └── test.cpp └── weinisi └── README.md /.idea/js-reverse-log.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-reverse-log 2 | > 用于记录JS逆向日志以及逆向时遇到的一些坑,会写相对详细的图文教程,也会把答主的思路分享给大家,逆向真的是一件很有趣的事情!欢迎大家一起进入逆向的大门!PS:详细逆向都在拓展里面,本页主要负责收集目录作用,未来可能会更新JS逆向的一些新手教程(大概) 3 | ## 逆向环境 4 | * [![chrome]][chrome_url] 5 | * [![charles]][charles_url] 6 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 7 | * [![pycharm]][pycharm_url] 8 | ## 逆向工程项目(项目里面有详细的日志) 9 | 1. [空中网逆向](https://github.com/q6378561/js-reverse-log/tree/master/kongzhongwang) 10 | 2. [青果教务系统](https://github.com/q6378561/js-reverse-log/tree/master/qingguo) 11 | 3. [漫画柜](https://github.com/q6378561/js-reverse-log/tree/master/manhuagui) 12 | 4. [美团token逆向](https://github.com/q6378561/js-reverse-log/tree/master/meituan) 13 | 5. [威尼斯登录逆向](https://github.com/q6378561/js-reverse-log/tree/master/weinisi) 14 | 6. [米画师登录逆向](https://github.com/q6378561/js-reverse-log/tree/master/mihuashi) 15 | ## 建议意见 16 | 可以发送到邮箱a418570398@gmail.com或直接在github下的Issues中留言 17 | 18 | ## 支持作者 19 | 喜欢我的话点一下下方的按钮哦! 20 | 21 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 22 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 23 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 24 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 25 | 26 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 27 | [chrome_url]: https://www.google.com/chrome/ 28 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 29 | [charles_url]: https://www.charlesproxy.com/ 30 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 31 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 32 | -------------------------------------------------------------------------------- /kongzhongwang/README.md: -------------------------------------------------------------------------------- 1 | # 空中网登录逆向 2 | > 空中网登录逆向相对来说难度不高,但是也有碰到许多坑,故写下此日志记录爬坑 3 | 4 | ## 逆向环境 5 | * [![chrome]][chrome_url] 6 | * [![charles]][charles_url] 7 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 8 | * [![pycharm]][pycharm_url] 9 | 10 | ## 接口解析 11 | 空中网的登录接口采用目前少见的get请求来设置cookie登录,但其中参数是有加密的,我们此次的目标就是逆向出其中的加密方式,首先我们来看一下登录get请求中需要的参数 12 | 13 | 14 | 15 | 可以见到基本的参数是显而易见的,`username`也没有加密,只有`password`和`_`两个参数是我们需要探讨的东西 16 | 17 | 这时候的思路一般往两个方面衍生,1.通过监听事件调试跟进我们需要的加密函数 2.搜索关键词找到我们需要的加密函数 鉴于关键词越不偏僻越不好搜的原则我们这里采用监听事件 可以很容易定位到我们想要的关键函数 18 | 19 | 20 | 21 | 我们在此函数下个断点更进看看有什么收获. 22 | 23 | 24 | 25 | F11不断跟进之后会发现我们来到了VM文件,**VM是浏览器为匿名函数创建的内存空间,是无法清除的。 26 | 匿名函数需要运行,首先需要有一块内存空间来存储它,这块内存空间显示在浏览器调试信息中就是以VM开头的文件(但是其实并非真正的文件)**。右下角可以看见文件真正的来源是另一个JS文件中的eval语句,这也是十分常见的一种JS加密方式,使用chrome浏览器的大括号展开可以得到可读的关键函数代码. 27 | 28 | 29 | 30 | 可以发现login这个函数传入了很多参数 username,password等等 初步判断加密过程就在这里面,继续F11跟进找到关键代码 31 | 32 | 33 | 34 | 经常逆向或者英语同学好的同学知道encrypt就是加密的意思,并且上下行代码也是之前所衔接的参数,所以这函数应该就是我们所需要的加密函数.此时我们可以选择python模拟加密过程进行加密,或者通过直接调用JS函数的方式来实现加密,为了节约时间我们使用第二种方式来进行加密.所以我们只要了解所需要传入的参数就OK,第一个显而易见是我们的密码,第二个则是j_data数据里面的dc变量,通过短暂的分析可以得到dc变量来自于上一个请求中的rep,所以我们传入python时获取上一个请求就能获得我们想要的DC变量. 35 | 36 | 到这里`password`的逆向已经结束了,剩下的`_`如果不逆向JS文件的话通过不断的发送请求可以看出是13位时间戮,逆向的话则可以看见`temptime`这个参数,简而言之就是不难拿到这个参数的值. 37 | 38 | 至此空中网的逆向就告一段落了. 39 | 40 | ## 后记(心得体会) 41 | 1. 小编一开始是用charles代理js文件来解密eval加密函数进行动态调试的,后面才发现原来强大的chrome浏览器有VM模式功能,算是一个小小的坑. 42 | 2. 通过python的requests库中的session会话来保持会话完全模拟从主页面到login等协议请求的cookie获取 43 | 3. 刚写完代码时,提交请求返回的是操作太过频繁,本以为是频繁恶意操作被banIP了,结果换了个IP代理发现还是这样,结合JS文件中对temptime的时间间隔判断(原JS文件只有间隔过长返回NULL的判断,间隔过段的判断应该是在后端,只是联想到了是否有这种可能).于是采用了python文件的time模块的sleep函数等待几秒发现status状态返回成功了. 44 | 4. 虽然可能看日志觉得逆向很快,但还是要一步一步慢慢调试,才获取到的这些信息 45 | 46 | ## 支持作者 47 | 喜欢我的话点一下下方的按钮哦! 48 | 49 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 50 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 51 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 52 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 53 | 54 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 55 | [chrome_url]: https://www.google.com/chrome/ 56 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 57 | [charles_url]: https://www.charlesproxy.com/ 58 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 59 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 60 | 61 | -------------------------------------------------------------------------------- /kongzhongwang/demokongzhongwang.py: -------------------------------------------------------------------------------- 1 | import re 2 | import requests 3 | import execjs 4 | import time 5 | from urllib import parse 6 | 7 | class jskzw(object): 8 | def __init__(self,user,pwd): 9 | self.user = user 10 | self.pwd =pwd 11 | self.s = requests.session() 12 | self.base_login_url = 'https://sso.kongzhong.com/ajaxLogin?' 13 | self.base_url = 'https://passport.kongzhong.com' 14 | self.base_headers = { 15 | "Host": "passport.kongzhong.com", 16 | "Connection": "keep-alive", 17 | "Cache-Control": "max-age=0", 18 | "Upgrade-Insecure-Requests": "1", 19 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36", 20 | "Sec-Fetch-Dest": "document", 21 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 22 | "Sec-Fetch-Site": "same-origin", 23 | "Sec-Fetch-Mode": "navigate", 24 | "Sec-Fetch-User": "?1", 25 | "Referer": "https://passport.kongzhong.com/v/user/userindex?validate=true", 26 | "Accept-Encoding": "gzip, deflate, br", 27 | "Accept-Language": "zh-CN,zh;q=0.9", 28 | 29 | } 30 | self.login_headers = { 31 | "Host": "sso.kongzhong.com", 32 | "Connection": "keep-alive", 33 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36", 34 | "Sec-Fetch-Dest": "script", 35 | "Accept": "*/*", 36 | "Sec-Fetch-Site": "same-site", 37 | "Sec-Fetch-Mode": "no-cors", 38 | "Referer": "https://passport.kongzhong.com/login", 39 | "Accept-Encoding": "gzip, deflate, br", 40 | "Accept-Language": "zh-CN,zh;q=0.9", 41 | } 42 | self.createCode_headers = { 43 | "Host": "sso.kongzhong.com", 44 | "Connection": "keep-alive", 45 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36", 46 | "Sec-Fetch-Dest": "image", 47 | "Accept": "image/webp,image/apng,image/*,*/*;q=0.8", 48 | "Sec-Fetch-Site": "same-site", 49 | "Sec-Fetch-Mode": "no-cors", 50 | "Referer": "https://passport.kongzhong.com/login", 51 | "Accept-Encoding": "gzip, deflate, br", 52 | "Accept-Language": "zh-CN,zh;q=0.9", 53 | } 54 | 55 | 56 | def login(self): 57 | base = self.s.get(url=self.base_url+'/login',headers= self.base_headers,verify=False) 58 | createCode = self.s.get(url='https://sso.kongzhong.com/createQRCode',headers = self.createCode_headers,verify=False) 59 | dc_juery = 'j=j&jsonp=j&service=https://passport.kongzhong.com/&_=%d'%int(round(time.time() * 1000)) 60 | login_exec = self.s.get(url=self.base_login_url+dc_juery,headers = self.login_headers,verify = False).text 61 | dc = re.search('"dc":"(.*?)"',login_exec).group(1) 62 | qr_user = parse.urlencode({'username':self.user}) 63 | jsstr = self.get_js() 64 | ctx = execjs.compile(jsstr) 65 | encrypt = ctx.call('encrypt',self.pwd,dc) 66 | time.sleep(3) 67 | login_juery = 'j=j&&type=1&service=https://passport.kongzhong.com/&%s&password=%s&vcode=&toSave=0&_=%d'%(qr_user,encrypt,int(round(time.time() * 1000))) 68 | login_status = self.s.get(url=self.base_login_url+login_juery,headers = self.login_headers,verify = False) 69 | print(login_status.text) 70 | 71 | def run(self): 72 | self.login() 73 | 74 | def get_js(self): 75 | # f = open("D:/WorkSpace/MyWorkSpace/jsdemo/js/des_rsa.js",'r',encoding='UTF-8') 76 | f = open("encrydc.js", 'r', encoding='UTF-8') 77 | line = f.readline() 78 | htmlstr = '' 79 | while line: 80 | htmlstr = htmlstr + line 81 | line = f.readline() 82 | return htmlstr 83 | 84 | if __name__ == '__main__': 85 | user =input('请输入您的账号:\n') 86 | pwd = input('请输入您的密码:\n') 87 | luanwushencai = jskzw(user,pwd) 88 | luanwushencai.run() 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /kongzhongwang/encrydc.js: -------------------------------------------------------------------------------- 1 | function encrypt(str, pwd) { 2 | if (pwd == null || pwd.length <= 0) { 3 | return null 4 | }; 5 | var prand = ""; 6 | for (var i = 0; i < pwd.length; i++) { 7 | prand += pwd.charCodeAt(i).toString() 8 | }; 9 | var sPos = Math.floor(prand.length / 5); 10 | var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos * 2) + prand.charAt(sPos * 3) + prand.charAt(sPos * 4) + prand.charAt(sPos * 5)); 11 | var incr = Math.ceil(pwd.length / 2); 12 | var modu = Math.pow(2, 31) - 1; 13 | if (mult < 2) { 14 | return null 15 | }; 16 | var salt = Math.round(Math.random() * 1000000000) % 100000000; 17 | prand += salt; 18 | while (prand.length > 10) { 19 | var a = prand.substring(0, 1); 20 | var b = prand.substring(10, prand.length); 21 | if (b.length > 10) { 22 | prand = b 23 | } else { 24 | prand = (parseInt(a) + parseInt(b)).toString() 25 | } 26 | }; 27 | prand = (mult * prand + incr) % modu; 28 | var enc_chr = ""; 29 | var enc_str = ""; 30 | for (var i = 0; i < str.length; i++) { 31 | enc_chr = parseInt(str.charCodeAt(i) ^ Math.floor((prand / modu) * 255)); 32 | if (enc_chr < 16) { 33 | enc_str += "0" + enc_chr.toString(16) 34 | } else enc_str += enc_chr.toString(16); 35 | prand = (mult * prand + incr) % modu 36 | }; 37 | salt = salt.toString(16); 38 | while (salt.length < 8) salt = "0" + salt; 39 | enc_str += salt; 40 | return enc_str 41 | } -------------------------------------------------------------------------------- /manhuagui/README.md: -------------------------------------------------------------------------------- 1 | # 逆向漫画柜图片真实地址加密 2 | > 前期笔者所有的逆向网页都是基于github上有现成的逆向完成代码或者项目来进行的,**注意!**这不代表文章是非原创的,只有笔者逆向思路枯竭的时候会去看代码学习一下逆向思路,下面开始逆向(本篇完全算是日志了,因为虽然逆向出了代码但是还是出了些问题,并且解决失败了,希望有能力的大神可以告诉我为什么要这样解决,由于自己动手解决失败了所以也就不放上py代码了) 3 | 4 | ## 逆向环境 5 | * [![chrome]][chrome_url] 6 | * [![charles]][charles_url] 7 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 8 | * [![pycharm]][pycharm_url] 9 | 10 | ## 图片真实地址请求解析 11 | 现在大部分图片或者小说网站,都会将图片或者文字这些不想被人轻易获得的数据进行加密放入JS渲染,而非直接放入html中给人轻易爬虫.话不多说,直接开始我们的逆向.进入漫画柜中随机点开一副图片,右键审查元素可以看见图片的真实地址,但是html的源代码中没有 12 | 13 | 14 | 说明这里被js渲染了,我们ctrl+shift+f搜索这个id`mangaBox`,很快就能定位到我们想要的js代码处 15 | 16 | 下面是这段代码. 17 | ```javascript 18 | c(function() { 19 | $("span.nopic,#erroraudit_show").remove(), 20 | $("#imgLoading").show(), 21 | pVars.curFile = i(pVars.page - 1), 22 | $('' + t.bname + ').load(function() { 23 | n.success(this) 24 | }).error(function() { 25 | n.error(this) 26 | }).appendTo("#mangaBox"), 27 | SMH.utils.imgGrayScale() 28 | }) 29 | ``` 30 | 可以很明显的看到图片的真实代码来自于这段代码`pVars.curFile = i(pVars.page - 1)`,在这里下个断点看看有什么情况,刷新后成功断下了没有传入什么有效的信息,F11跟进看看 31 | 32 | 继续F11看传入的变量有哪些 33 | ```javascript 34 | r = "https://us.hamreus.com/ps4/g/gmzr_wkhsq/第01回/JOJO_007.jpg.webp?cid=219425", i = undefined 35 | ``` 36 | 再结合前面看到的图片真实地址可以判定这里就是生成图片地址的代码处,继续F11跟进 37 | 38 | 至此到这里我们只要获取到如何生成t变量以及在哪生成我们完成了对图片地址的逆向破解,就可以实现批量下载图片等功能.然后就在这种临门一脚的时候笔者卡壳了 39 | 40 | 不断分析右边的堆栈回溯寻找想要存放生成t变量的地方,分析了足足两个小时还是没有找到想要的关键CALL,吃完饭后重新回到电脑桌前静了下心重新查看t变量 41 | 42 | 才发现生成的t变量其实已经传入了54个图片文件变量,并且由于逆向过于急躁没有认真分析生成的各个参数,在点击下一张图片的时候其实是没有更改md5的值的.分析到这里笔者才明白,当你进入这生成的第一回网页的时候已经将所有图片真实地址生成好了,剩下的只有不断的进行异步交互来给用户提供图片.而笔者在测试断点的时候则是不断的点击下一张来查看断点,所以返回不到我们想要的T变量的生成代码处.于是我们刷新网页重新看看! 43 | 44 | 可以看到此时回溯的后面生成了匿名函数,点进去一看究竟 45 | 46 | 这不就是我们想要的t变量的存放地方吗!顺便提一下什么是匿名函数,可以看到文件的来源来自VM,什么是vm呢?**VM是浏览器为匿名函数创建的内存空间,是无法清除的。 匿名函数需要运行,首先需要有一块内存空间来存储它,这块内存空间显示在浏览器调试信息中就是以VM开头的文件(但是其实并非真正的文件)**而匿名函数是**一种在运行时动态声明的函数。它们之所以被称为匿名函数是因为不同于普通函数,它们并没有函数名**。而要追溯他们真正的来源 47 | 48 | 则是在右下角的地方,点进去看看 49 | 50 | 关键函数get!并且可以看到这段js代码就在网页的源文件中,合乎情理!只有这样才能生成不同的图片地址,而每进入其他的漫画时候也会生成不一样的数据!这也是为什么抓包的时候明明看不到返回了相关的数据,js的文件同时也没有变化.而为什么会有数据来源.原来网页程序员跟你拐了个大弯! 51 | 52 | 于是我们分析这段函数就好了.首先我们知道\x是表示16进制,将开头的\x65\x76\x61\x6c转换成字母也是eval,也就是说这是可以拦住80%逆向人的eval加密吗?一开始笔者也是这么想的,然而写成python代码则会报错 53 | `splic is not a function` 54 | 正常的eval加密应该是split,而这里变成了splic,说明这里调用了自己写的splic函数代码而不是js自带的split函数,我们可以强行把16进制的c改成16进制的t看看会有什么结果 55 | ``` 56 | y.x({"z":4,"B":"A","t":"4.2","s":u,"w":"5","v":["I.2.3","H.2.3","J-L.2.3","K.2.3","D.2.3","C.2.3","E.2.3","G.2.3","F.2.3","r.2.3","b.2.3","c.2.3","e.2.3","d.2.3","a.2.3","6.2.3","7.2.3","9.2.3","8.2.3","n.2.3","o.2.3","q.2.3","p.2.3","l.2.3","h.2.3","f.2.3","i.2.3","k.2.3","j.2.3","m.2.3","M.2.3","1d.2.3","1c.2.3","1e.2.3","1g.2.3","1f.2.3","18.2.3","17.2.3","19.2.3","1b.2.3","1a.2.3","1m.2.3","1p.2.3","1o.2.3","1n.2.3","1h.2.3","1j.2.3","1l.2.3","1k.2.3","1i.2.3","16.2.3","T.2.3","S.2.3","V.2.3"],"U":R,"O":N,"Q":"/P/g/W/5/","13":1,"12":"","15":14,"11":D7BWAcHNgdwUwEbmARgJwBYDMAGYgabxxUD21YAKQHlKB9IgNnKotpQHZGai0PmiAOHiwCsgoilEoATBIwSsoyQwUjKnSe1W9J3TbUkDdOSbMO4FeQ5PGX5l6YZR4AxgEsAJsCQunwK5kkiAGYuADZwAM7ATgB2AIYAtnDALvGQACKxAC6xwADKALIAEp7uwIA82YC2DoDScoDASp5xiaI4DIY4Kky0OBodODg6PTgGA9YDFgP2A7K9tj1Y4kKyYdHA4OGy4FkAFsCBsSHhSYZCM5xC0sHRLuGbcB5HspDxAF4ATtQwANab4QCOwPFuEThEIrF5wACSl0ywAoABUAJ6bAD2AAUhAAhADCZEKKLgABkQtQyAAxUAAV020HAYIAbuCPAgQkinB9qE4fOFsplyZFJJIcFg6PJonAAB6ZBmiIQjThYIZy7py/qcDCy3gYMZyk68LATOUmWYtWbtVXG05ajVKjUqjUKjX6jWmjWG1VYIA,"Y":{"X":"10"}}).Z(); 57 | ``` 58 | 发现解析出来的根本不是我们想要的数据,到这里笔者思路是完全卡壳了.查阅了github上大神的相关代码,发现他调用了一个模块是lzstring,查阅了一下相关的资料发现这个lzstring并不是大众所知的库,不知道相关的大神是如何知道这个模块并加以调用的? 59 | 60 | ## 总结 61 | 逆向到这里也就结束了,总结下来算是逆向的思路有着一定的拓展,但是也知道了自己对js的数据存储之类的不够熟悉,这在逆向中是十分不应该的事情,不熟悉意味着你对这里含糊,对这里含糊意味着你逆向的思路受到限制,这是十分不应该的事情.希望自己能够再努努力,不断的提升自己. 62 | 另外希望有大神能帮忙回答一下这个lzstring的问题,是真心求教的,谢谢! 63 | 64 | ## 支持作者 65 | 喜欢我的话点一下下方的按钮哦! 66 | 67 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 68 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 69 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 70 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 71 | 72 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 73 | [chrome_url]: https://www.google.com/chrome/ 74 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 75 | [charles_url]: https://www.charlesproxy.com/ 76 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 77 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 78 | 79 | -------------------------------------------------------------------------------- /meituan/README.md: -------------------------------------------------------------------------------- 1 | # 逆向分析美团API参数 2 | > 近年来由于python的兴起,爬虫等业务也随即火热起来,那么外卖电商等平台首当其冲是要被爬取的对象.于是一场爬虫与反爬虫的攻坚战就此展开.本文逆向美团外卖平台获取商品信息的加密参数来应对美团采取的反爬虫措施. 3 | 4 | ## 逆向环境 5 | * [![chrome]][chrome_url] 6 | * [![charles]][charles_url] 7 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 8 | * [![pycharm]][pycharm_url] 9 | 10 | ## 分析请求接口 11 | 老规矩,我们获取一次商品信息,然后看一看请求接口都包含了哪些东西 12 | 13 | 14 | 可以看见这个请求地址后面包含了很多参数,带着一样的参数重新发送请求会发现请求失败,所以这里后面的加密参数应该就是验证参数了.由于这样可读性过差我们在chrome上往下拉 15 | 16 | 17 | 这样可读性就高多了,我们可以通过参数的命名来分析每个参数具体的作用,上面的参数就不一一分析了,毕竟我们是带着研究的心态来分析美团的接口,而不是用于爬虫等其他用途. 18 | 重复刷新页面可以发现,唯一变的值只有`_token`这个参数,说明它用于验证请求等操作.于是我们打开chrome的调试窗口 ctrl+shift+f搜索API开头的`getPoiList`这个路由,很快就能定位到我们想要的地方 19 | 20 | 可以发现美团的程序员比想象中的亲切,代码可读性并不低,可以看见`_token`参数来自于变量d,而变量d又来自于`d = window.Rohr_Opt.reload(p);`,于是我们在这一行下个断点跟进看看是怎样进行加密的,重新刷新网页可以发现成功断下,F11跟进看一看 21 | 22 | 23 | `jv = "https://sm.meituan.com/meishi/api/poi/getPoiList?cityName=三明&cateId=17&areaId=0&sort=&dinnerCountAttrId=&page=1&userId=&uuid=fbc5a9a6120f4893bb27.1583068822.1.0.0&platform=1&partner=126&originUrl=https://sm.meituan.com/meishi/c17/pn1/&riskLevel=1&optimusCode=10"` 24 | 25 | 可以发现传入的jv就是不带_token参数的path,继续跟进看看 26 | 27 | 28 | 首先开头代码创建了jw和jx两个变量,然后判断了传入jv变量的类型,`_$_543c[91]`是美团自己弄的一个数组,其中对应的值是string,所以这个if判断了jv是否为文本类型,如果是的话进行parse操作,由于时间有限这里的parse函数我们就不加以分析了,总结就是将传入的文本变为对象传入到jx里面,然后`iP.sign = iJ(jx);`然后传入jx进行iJ函数的加工,因为很关键所以我们还是要跟进去看一看. 29 | 30 | 31 | 分析后就是将传入的je变量对象中的key值进行sort函数排序,然后将重新排序好的对象传入jd中生成相对应的文本值,图中也可以看到jd的内存.然而还要将生成的jd值通过iI函数加工,F11跟进看看 32 | 33 | 34 | 首先`JSON.stringify`函数可以将传入的JSON对象变为字符串,然后忽视前面cD函数看到deflate,经常进行爬虫或者熟悉网页压缩的同学可能懂,deflate是一种压缩算法, 35 | 观察接口的头部也能发现deflate就在其中 36 | 37 | 38 | 那么我们可以默认待会调用python的时候可以直接调用python的压缩算法库,如果出错了可以继续分析cD函数.将jc进行压缩后,会发现把他传入到iD函数中 39 | 先直接跳过iD函数,我们发现jc的值在内存中为 40 | 41 | 42 | 由此我们可以断定,iD函数就是我们想要的加密函数,然而F11跟进去会发现这个函数十分复杂,笔者逆向了一会发现无果,便查阅了网络上的相关资料,网络上的思路通过token结尾的=号猜测是base64加密,decode后返回的是十六进制的ASCii码,考虑原数据可能进行二进制压缩+编码,使用python zlib库对byte数据进行解压. 43 | 44 | 45 | get到加密方式后我们先忽视这里的数据,继续进行逆向 46 | 47 | 48 | 我们发现ip的参数和我们前面解压的数据一模一样,由此可以断定ip就是我们加密前的参数,搜索rId,看看我们是否能得到想要的参数传入. 49 | 50 | 51 | 52 | 至此逆向基本就结束了,我们只要分析每个参数的数值以及对应的函数后写入Python中实现获取美团商品信息,通过不断请求发现只有四个参数是在变动的 53 | `ts``cts`这两个值很明显的可以看出是获取时间戳,写入python的时候直接调用time库就行,`bI`则是返回两个参数,第一个是打开网页的来源网址referer,第二个则是当前网页的地址,也就是说我们变动当前网页的网址参数就OK了,第四个则是`sign`,这个参数其实我们前面就分析过了,就是将未带有token参数的Url进行加密. 54 | 55 | ## 总结 56 | 1. 对网页压缩的三种算法:①gzip②deflate③br有了更深的了解 57 | 2. 对编码转化等理解不够透彻,有关的知识点有**base64**编码解码,**二进制流**的压缩与解压 58 | 3. 未能践行**大胆猜测,小心求证**的逆向思想,如果仔细观看token的末尾可以先尝试base64解码,逆向过程过于死板不够灵活,需要更多的练习以及获取更多相关的知识 59 | 60 | ## 支持作者 61 | 喜欢我的话点一下下方的按钮哦! 62 | 63 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 64 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 65 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 66 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 67 | 68 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 69 | [chrome_url]: https://www.google.com/chrome/ 70 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 71 | [charles_url]: https://www.charlesproxy.com/ 72 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 73 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 74 | -------------------------------------------------------------------------------- /meituan/demomeituan.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import base64 3 | from time import time 4 | import zlib 5 | import urllib.parse 6 | class meituan(object): 7 | def __init__(self): 8 | self.headers = { 9 | "Host": "sm.meituan.com", 10 | "Connection": "keep-alive", 11 | "Accept": "application/json", 12 | "Sec-Fetch-Dest": "empty", 13 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36", 14 | "Sec-Fetch-Site": "same-origin", 15 | "Sec-Fetch-Mode": "cors", 16 | "Referer": "https://sm.meituan.com/meishi/", 17 | "Accept-Encoding": "gzip, deflate, br", 18 | "Accept-Language": "zh-CN,zh;q=0.9", 19 | "Cookie": "_lxsdk_cuid=170bac1274ac8-057fc17c12b568-4313f6a-144000-170bac1274ac8; ci=208; rvct=208; client-id=4afb7696-3ec2-41e6-b9b4-b8e09cf19169; uuid=b44ca28782c24481aaec.1583752549.1.0.0; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; __mta=244276922.1583681206705.1583681206705.1583752553241.2; _lxsdk=170bac1274ac8-057fc17c12b568-4313f6a-144000-170bac1274ac8; _lxsdk_s=170bf022051-b6-7-15a%7C%7C7", 20 | 21 | } 22 | def get_query(self): 23 | #地点名称 24 | cityName = '三明' 25 | #美食类型 26 | cateId = 0 27 | #地区类型 28 | areaId = 0 29 | #分类方法 30 | sort = '' 31 | dinnerCountAttrId = '' 32 | #查询页数 33 | page = 1 34 | userId = '' 35 | uuid = 'b44ca28782c24481aaec.1583752549.1.0.0' 36 | platform = 1 37 | partner = 126 38 | riskLevel = 1 39 | optimusCode = 10 40 | originUrl = 'https://sm.meituan.com/meishi/' 41 | query = 'cityName=%s&cateId=%d&areaId=%d&sort=%s&dinnerCountAttrId=%s&page=%d&userId=%s&uuid=%s&platform=%d&partner=%d&originUrl=%s&riskLevel=%d&optimusCode=%d'%(cityName,cateId,areaId,sort,dinnerCountAttrId,page,userId,uuid,platform,partner,originUrl,riskLevel,optimusCode) 42 | query_after = query.split('&') 43 | "areaId=0&cateId=0&cityName=三明&dinnerCountAttrId=&optimusCode=10&originUrl=https://sm.meituan.com/meishi/&page=1&partner=126&platform=1&riskLevel=1&sort=&userId=&uuid=0ab85fe798e54a26ab30.1583681184.1.0.0" 44 | query_all = [] 45 | true_query = '' 46 | for i in query_after: 47 | i = i.split('=') 48 | if i == query_after[-1]: 49 | query_all.append(i[0] + '=' + i[1] ) 50 | else: 51 | query_all.append(i[0]+'='+i[1]+'&') 52 | for j in sorted(query_all): 53 | true_query += j 54 | true_query = "\""+true_query+"\"" 55 | sign = self.algorithm(true_query) 56 | sign = sign[2:-1] 57 | token =self.get_token(sign) 58 | # 将token进行url编码,否则API后台不识别数据返回数据null 59 | token1 = urllib.parse.quote(token) 60 | return query+'&_token='+token1 61 | def get_token(self,sign): 62 | refer = "https://sm.meituan.com/" 63 | local_href = "https://sm.meituan.com/meishi/" 64 | token ={ 65 | 'rId' : 100900, 66 | 'ver' : "1.0.6", 67 | 'ts' : int(round(time() * 1000)-150 * 1000), 68 | 'cts' : int(round(time() * 1000)), 69 | 'brVD' : [406, 754], 70 | 'brR' : [[1536, 864],[1536, 824],24,24], 71 | 'bI' : [local_href,refer], 72 | 'mT': [], 73 | 'kT': [], 74 | 'aT': [], 75 | 'tT': [], 76 | 'aM': "", 77 | 'sign':"eJwljjFuwzAMRe+SQaMsxbGjFtBQeCpQZMsBmJixiVqSQVEFeoQsmXOUHKjoOSK0038g/v/8G2CE99EbdQbBfyD5PkBA//O4/t5vaqQYkYdUoryJcPWotAqFkoc0ordGJaaJ4pEXP4us+bVpctABSQpEfU6hqZxnatQKUw1UYamV3m57tS4gl8Shnpny5wd+4VI5JxavSsa/f6VQXQYn111w/+Kw28G2h1NrtO1c2ztr3U5bbbTZPAGMkUe8" 78 | } 79 | 80 | # print(str(token)) 81 | result = self.algorithm(token) 82 | return result 83 | def algorithm(self,inf): 84 | # 直接zlib压缩提示要转字节类型 85 | info = str(inf).encode() 86 | # 进行def压缩 87 | temp = zlib.compress(info) 88 | # 压缩后进行base64加密 89 | baseenco = base64.b64encode(temp) 90 | # 加密后转str文本 91 | result = str(baseenco,encoding='utf-8') 92 | return result 93 | 94 | def get_lis(self): 95 | resp = requests.get(url = 'https://sm.meituan.com/meishi/api/poi/getPoiList?'+self.get_query(),headers = self.headers,verify=False) 96 | 97 | 98 | def run(self): 99 | self.get_lis() 100 | 101 | if __name__ == '__main__': 102 | parse = meituan() 103 | parse.run() 104 | 105 | -------------------------------------------------------------------------------- /mihuashi/米画师笔记.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 由于米画师的登录接口有加密 故此分析 4 | 5 | # 尝试 6 | 登录的api:https://mihuashi.com/api/v1/authorization_tokens/ 7 | cookie: 8 | ![](https://i.imgur.com/Mfz2isN.png) 9 | 10 | 表单数据: 11 | ![](https://i.imgur.com/l11ClGr.png) 12 | 13 | # 目标 14 | > 可以看到`phone`和`password`都为明文, 但接口的cookie和表单的`sensor_id`数据进行了加密 15 | > 下面进行分析 16 | 17 | # 分析 18 | ## 表单分析 19 | 20 | 1.首先ctrl+shift+f全局搜索`sensor_id`字符串 21 | ![](https://i.imgur.com/OFcNeEp.png) 22 | 23 | 搜索不到,说明字符串被加密了,只能另寻出路 24 | 来到api的Initiator地方追踪调用 25 | ![](https://i.imgur.com/cbfTaK0.png) 26 | 通过一系列翻阅在从上往上数第10个调用发现异常 27 | ![](https://i.imgur.com/vegoZR4.png) 28 | 我们需要的是`sensor_id`但这里有个 29 | `sensorId: sensors.store.getDistinctId()` 30 | 很有可能就是同一个东西,下个断点看看 31 | ![](https://i.imgur.com/82Y1RrU.png) 32 | 通过发包后看看: 33 | ![](https://i.imgur.com/e9l1kzW.png) 34 | 可以看到是相同的,说明是从这个函数生成的 35 | 通过全局搜索 36 | ![](https://i.imgur.com/TzMtL4w.png) 37 | 然后再通过全局搜索找到关键函数 38 | ![](https://i.imgur.com/q3qFFn2.png) 39 | 可以得出`UUID()`就是生成最初的`sensor_id`参数 40 | 搜索可得uuid的生成函数 41 | 42 | ```ts 43 | _.UUID = function() { 44 | var t = function() { 45 | for (var t = 1 * new Date, e = 0; t == 1 * new Date; ) 46 | e++; 47 | return t.toString(16) + e.toString(16) 48 | } 49 | , e = function() { 50 | return Math.random().toString(16).replace(".", "") 51 | } 52 | , n = function(t) { 53 | function e(t, e) { 54 | var n, r = 0; 55 | for (n = 0; n < e.length; n++) 56 | r |= o[n] << 8 * n; 57 | return t ^ r 58 | } 59 | var n, r, i = navigator.userAgent, o = [], a = 0; 60 | for (n = 0; n < i.length; n++) 61 | r = i.charCodeAt(n), 62 | o.unshift(255 & r), 63 | o.length >= 4 && (a = e(a, o), 64 | o = []); 65 | return o.length > 0 && (a = e(a, o)), 66 | a.toString(16) 67 | }; 68 | return function() { 69 | var r = String(screen.height * screen.width); 70 | r = r && /\d{5,}/.test(r) ? r.toString(16) : String(31242 * Math.random()).replace(".", "").slice(0, 8); 71 | var i = t() + "-" + e() + "-" + n() + "-" + r + "-" + t(); 72 | return i ? (just_test_distinctid_2 = 1, 73 | i) : (just_test_distinctid_2 = 2, 74 | (String(Math.random()) + String(Math.random()) + String(Math.random())).slice(2, 15)) 75 | } 76 | } 77 | 78 | ``` 79 | 80 | 可以看到`uuid`的生成函数也十分简单 参数也十分固定 我们可以通过JS调用或者自写代码实现函数功能,为了节约时间这里直接采用JS调用的方式 81 | 有三个参数我们可以采用写死的方式来直接进行调用 82 | `var r = String(screen.height * screen.width);` 83 | `var n, r, i = navigator.userAgent, o = [], a = 0;` 84 | 改写为 85 | `var n, r, i = "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36\"", o = [], a = 0;` 86 | `var r = String(864*1536);` 87 | 在本地调用看看 88 | ![](https://i.imgur.com/KlWfscQ.png) 89 | 可以看到是没有任何问题的 90 | 这样表单的分析就完成了 91 | 92 | ## cookie分析 93 | 94 | 可以看到有四个cookie 95 | 分别是 96 | `aliyungf_tc` 97 | `__asc` 98 | `__auc` 99 | `sensorsdata2015jssdkcross` 100 | ### aliyungf_tc 101 | 第一个`aliyungf_tc`通过调试发现访问网页后网页会自动返回给你这个cookie,保存即可 102 | 103 | ### asc和auc 104 | 全局搜索`__asc`发现如下关键函数 105 | ![](https://i.imgur.com/ZMvM6V7.png) 106 | 简单往上追踪就能得到 107 | ![](https://i.imgur.com/k85kUTr.png) 108 | 由于asc和auc的参数相同所以调用同一个即可 109 | 110 | 111 | ### sensorsdata2015jssdkcross 112 | 可以发现`sensorsdata2015jssdkcross`这个cookie里面包含了`sensor_id`的参数 113 | 说明我们也可以通过cookie角度来获取`sensor_id`这个参数生成 114 | 全局搜索`sensorsdata2015jssdkcross`可以获取相关的生成信息,这里不做过多的详细说明 115 | 116 | 117 | # 结束 118 | 这样某米的登录分析就这样大致结束了 后面只要添加cookie添加表单数据模拟post访问登录即可 119 | ![](https://i.imgur.com/mc51Mmw.png) 120 | 至此登录分析就告一段落了 121 | -------------------------------------------------------------------------------- /qingguo/README.md: -------------------------------------------------------------------------------- 1 | ![pic](https://s2.ax1x.com/2020/02/17/3i0Pwq.jpg) 2 | # 逆向青果软件有限公司外包的教务系统登录接口 3 | > 很早以前为了抢课就研究过学校的登录接口,当时刚碰JS遇到了许多坑,故记下此篇造福人类,也让自己复习一遍,完整的代码可以点此仓库的py文件模拟登录,但希望点进来观看的你看了此篇教程能有一点点收获就是乱舞神菜最大的荣幸! 4 | 5 | ## 逆向环境 6 | * [![chrome]][chrome_url] 7 | * [![charles]][charles_url] 8 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 9 | * [![pycharm]][pycharm_url] 10 | 11 | ## POST请求解析 12 | 研究登录接口的第一件事就是对登录的post的请求进行解析,看看传入的参数是否加密以及是否有多余的参数.如果没有,那么恭喜你这篇教程不用看了,直接对明文参数进行post进行登录研究. 13 | 14 | 操作前不要忘记勾选上面的 Preserve log 15 | 16 | 首先我们F12打开开发者工具,点NETWORK查看相关的请求,我们先随便输入账号密码和验证码点击登录. 17 | 18 | 19 | 20 | 可以很明显的看出来 http://jwgl.bsuc.edu.cn/bsxyjw/cas/logon.action 这个接口就是我们要研究的登录接口,当然其中的data数据也是进行了加密. 21 | 22 | 加密的参数 23 | 24 | ## data参数解密过程 25 | 逆向JS的方向有很多,例如ctrl+shift+f调用搜索窗口搜params,token这些加密参数,或者对按钮单击事件下断点逆向分析数据等等,笔者这里采用此接口逆向较全面的从按钮处下断点 26 | 27 | 监听事件 28 | 29 | 右键按钮审查元素定位登录按钮在html中的位置,点击右边的Event Listeners将Ancestors all的选项取消,这样就能单独监听按钮的事件,点击click,发现定位到一处代码之中,跟过去看看。 30 | 31 | 鼠标单击事件 32 | 33 | 此时已经定位到按钮事件的单击事件中,我们在左边的行数字那里单击一下就能下断点.再重新提交一次请求看看能不能被断下. 34 | 35 | 断点 36 | 37 | 很明显此时已经被断下了,我们用F11单步步入进去看有关于如何处理本次按钮事件的有关函数. 38 | 39 | 相关函数 40 | 41 | 值得庆幸的事,也是让笔者当初有动力逆向的地方就在于这个接口的JS都是明文没有加密,这也就有助于我们的逆向过程.可以很明显的看到程序员给我们的注释第一个函数是用于输入信息验证的,跟我们想要的params等参数无关,所以直接F10步过.此时来到了 42 | var username = j$("#yhmc").val(); 43 | 44 | 这里,可以很明显的看出来这里是JQUERY的代码,val函数用于取id为yhmc元素里面的值.结合html分析可以很明显的看出来username就是我们输入的账号,以及下面几个变量。 45 | 46 | `password` 密码 47 | 48 | `token` 还是密码("说明等下要将密码进行加密加工变为token") 49 | 50 | `randnumber` 验证码 51 | 52 | `passwordPolicy`为了节省时间就不对此变量的函数进行分析了,总结就是判断密码是否符合密码策略,不符合返回0,符合返回1,后续我们模拟登录的时候可以下死码令他直接等于1 53 | 54 | `url` 当然是我们登录接口的API啦,用于POST用的~ 55 | 56 | `txt_mm_expression` `txt_mm_length` `txt_mm_userzh`这三个变量都是从网页上获取的,都是死码,分别是8,6,0(我们也可以直接获取网页的文本来填写这个三个参数,由于比较麻烦笔者就不这样做了) 57 | 58 | 接下来我们就发现开始对这些变量进行加密了 59 | ``` 60 | passsword = hex_md5(hex_md5(password)+hex_md5(randnumber.toLowerCase())); 61 | ``` 62 | 这段代码显而易见,首先对密码进行md5加密,再对取小写的验证码进行MD5加密,两个相加的文本再进行MD5加密放入变量password中 63 | 64 | 函数分析 65 | 66 | 继续进行分析 67 | 68 | `p_username`就是文本_u加上验证码 69 | 70 | `p_password`就是文本_p加上验证码 71 | 72 | ```angular2 73 | username = base64encode(username+";;"+_sessionid); 74 | ``` 75 | 这段代码就是用户名文本加上;;再加上_sessionid(这个变量从何得来呢,ctrl+shift+f搜一下发现他就在主页中,应该是每次访问随机生成的那种)三者相加进行base64加密. 76 | 77 | ```angular2 78 | var params = p_username+"="+username+"&"+p_password+"="+password+"&randnumber="+randnumber+"&isPasswordPolicy="+passwordPolicy+ 79 | "&txt_mm_expression="+txt_mm_expression+"&txt_mm_length="+txt_mm_length+"&txt_mm_userzh="+txt_mm_userzh; 80 | ``` 81 | 很明显,就是讲前面几个变量相加变为params 82 | 然而我们的逆向分析还没有结束,我们请求中的token很明显是经过加密的,但是逆向分析到此时token还是等于password,于是我们继续逆向 83 | 84 | `params = getEncParams(params); ` 85 | 很明显,getEncParams这个函数才是加密了token,params这些参数的地方,我们F11单步步入进去看看. 86 | 87 | 函数分析 88 | 89 | 很明显这里就是进行综合加密的地方了,data里面的三个数据这里都有,我们一一分析看看. 90 | 91 | `var timestamp = _nowtime;` 这里的文本由 `_nowtime`变量传入,ctrl+f搜索此变量发现就在此jsp文件的顶部,对应的是我们请求的提交时间. 92 | 93 | `var token = md5(md5(params)+md5(timestamp)); `这里的数据比较明显,就是将params进行md5加密加上时间戮进行md5加密的和再进行md5加密就是我们要的token了. 94 | 95 | `var _params = b64_encode(des_encode(params)); `我们发现这段代码要先经过des_encode后返回的文本再进行base64加密 96 | 97 | 函数分析 98 | 99 | F11单步步入点进去看看,我们发现这个网站的程序员确实差点意思,写的都是一层套一层循环嵌套,再F11单步步入进入strEnc这个函数看看. 100 | 101 | 函数分析 102 | 函数分析 103 | 104 | 程序员已经给我们备注了是des加密了,所以我们也就不必进行分析,此时我们有两种选择,一种是直接复制调用这段JS进行加密,另一种则是直接python调用des加密模块进行加密,由于不清楚程序员是否修改了des的加密方式故我们直接采用调用js的加密方式确保不会出错. 105 | 分析到这里我们只要再回头看看对des传入了哪些参数就能解密出来了. 106 | 107 | `function des_encode(data) { return strEnc(data, _deskey, null, null); }` 108 | 很明显data传入的是我们的params,那这个_deskey是什么?,通过ctrl+f搜索发现同样在这个jsp文件顶部,由于_deskey的加密方式搜不到,分析也十分难分析,故笔者这里采用直接获取jsp文本的方式获取这两个变量: 109 | ``` 110 | url = "http://jwgl.bsuc.edu.cn/bsxyjw/custom/js/SetKingoEncypt.jsp" 111 | rep = self.__session.get(self.url, headers=HEADERS) 112 | _deskey = re.search(r"var _deskey = '(.*?)';", rep.text).group(1) 113 | _nowtime = re.search(r"var _nowtime = '(.*?)';", rep.text).group(1) 114 | ``` 115 | 到这里我们的解密工作也就告一段落了. 116 | 117 | ## 调用js 118 | 剩下的小难点就是调用js了,我们可以利用execjs这个库来调用js: 119 | ``` 120 | def get_js(): 121 | # f = open("D:/WorkSpace/MyWorkSpace/jsdemo/js/des_rsa.js",'r',encoding='UTF-8') 122 | f = open("demo01.js", 'r', encoding='UTF-8') 123 | line = f.readline() 124 | htmlstr = '' 125 | while line: 126 | htmlstr = htmlstr + line 127 | line = f.readline() 128 | return htmlstr 129 | jsstr = get_js() 130 | ctx = execjs.compile(jsstr) 131 | ctx.call('strEnc', parms, _deskey) 132 | ``` 133 | 134 | ## 后续 135 | 剩下的就是获取相关的url,构造相关的请求头,以及python调用md5加密和base加密等操作,利用requests库的session来保持会话就能实现我们的模拟登录了. 136 | 137 | 发送请求 138 | 139 | ## 总结 140 | 此次逆向说难不难说简单不简单,算是对新手来说是很方面的加密了,但是综合性很强,我们需要分析协议,下断点,分析js代码,甚至利用正则表达式,好在js代码是明文这让我们的逆向工作简单了很多,希望各位看到这边文章能有所收获,共同进步! 141 | 142 | ## 支持作者 143 | 喜欢我的话点一下下方的按钮哦! 144 | 145 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 146 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 147 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 148 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 149 | 150 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 151 | [chrome_url]: https://www.google.com/chrome/ 152 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 153 | [charles_url]: https://www.charlesproxy.com/ 154 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 155 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 156 | -------------------------------------------------------------------------------- /qingguo/demo002(success).py: -------------------------------------------------------------------------------- 1 | import requests 2 | import re 3 | import hashlib 4 | import base64 5 | import execjs 6 | 7 | def MD5_HEX(str): 8 | m = hashlib.md5() 9 | b = str.encode(encoding='utf-8') 10 | m.update(b) 11 | str_md5 = m.hexdigest() 12 | return str_md5 13 | 14 | def get_js(): 15 | # f = open("D:/WorkSpace/MyWorkSpace/jsdemo/js/des_rsa.js",'r',encoding='UTF-8') 16 | f = open("demo01.js", 'r', encoding='UTF-8') 17 | line = f.readline() 18 | htmlstr = '' 19 | while line: 20 | htmlstr = htmlstr + line 21 | line = f.readline() 22 | return htmlstr 23 | 24 | class UESTC(object): 25 | image_url = "http://jwgl.bsuc.edu.cn/bsxyjw/cas/genValidateCode?dateTime=Sat" 26 | url = "http://jwgl.bsuc.edu.cn/bsxyjw/custom/js/SetKingoEncypt.jsp" 27 | login_url = "http://jwgl.bsuc.edu.cn/bsxyjw/cas/logon.action" 28 | loginafter_url = "http://jwgl.bsuc.edu.cn/bsxyjw/MainFrm.html" 29 | def __init__(self,username,password): 30 | self.__session = requests.session() 31 | self.username = username 32 | self.password = password 33 | 34 | def login(self): 35 | HEADERS = { 36 | 'Host': "jwgl.bsuc.edu.cn", 37 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER", 38 | 'Accept': "image/webp,image/*,*/*;q=0.8", 39 | 'Referer': "http://jwgl.bsuc.edu.cn/bsxyjw/cas/login.action", 40 | 'Accept-Encoding': "gzip, deflate, sdch", 41 | 'Accept-Language': "zh-CN,zh;q=0.8" 42 | } 43 | HEADERS1 = { 44 | 'Host': "jw.tgc.edu.cn", 45 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER", 46 | 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 47 | 'Accept-Encoding': "gzip, deflate, sdch", 48 | 'Accept-Language': "zh-CN,zh;q=0.8" 49 | } 50 | Headers = { 51 | 'Accept': "text/plain, */*; q=0.01", 52 | 'Accept-Encoding': "gzip, deflate", 53 | 'Accept-Language': "zh-CN,zh;q=0.8", 54 | 'Connection': "keep-alive", 55 | 'Content-Length': "1124", 56 | 'Content-Type': "application/x-www-form-urlencoded", 57 | 'Host': "jwgl.bsuc.edu.cn", 58 | 'Origin': "http://jwgl.bsuc.edu.cn", 59 | 'Referer': "http://jwgl.bsuc.edu.cn/bsxyjw/cas/login.action", 60 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER", 61 | 'X-Requested-With': "XMLHttpRequest" 62 | } 63 | username = self.username 64 | password = self.password 65 | txt_mm_expression = "12" 66 | txt_mm_userzh = "0" 67 | txt_mm_length = str(len(password)) 68 | image = self.__session.get(self.image_url, headers=HEADERS) 69 | _sessionid = image.cookies.get("JSESSIONID") 70 | with open('img2.png', 'wb') as f: 71 | f.write(image.content) 72 | randnumber = input("请输入验证码:\n") 73 | p_username = "_u" + randnumber 74 | p_password = "_p" + randnumber 75 | password1 = MD5_HEX(MD5_HEX(password) + MD5_HEX(randnumber)) 76 | username1 = username + ";;" + _sessionid 77 | username1 = str(base64.b64encode(username1.encode("utf-8")), "utf-8") 78 | rep = self.__session.get(self.url, headers=HEADERS) 79 | text = rep.text 80 | _deskey = re.search(r"var _deskey = '(.*?)';", text) 81 | _nowtime = re.search(r"var _nowtime = '(.*?)';", text) 82 | parms = p_username + "=" + username1 + "&" + p_password + "=" + password1 + "&randnumber=" + randnumber + "&isPasswordPolicy=1" + "&txt_mm_expression=" + txt_mm_expression + "&txt_mm_length=" + txt_mm_length + "&txt_mm_userzh=" + txt_mm_userzh 83 | token = MD5_HEX(MD5_HEX(parms) + MD5_HEX(_nowtime.group(1))) 84 | jsstr = get_js() 85 | ctx = execjs.compile(jsstr) 86 | _parms = str(base64.b64encode(ctx.call('strEnc', parms, _deskey.group(1)).encode("utf-8")), "utf-8") 87 | data = 'params='+_parms+'&token='+token+"×tamp="+_nowtime.group(1) 88 | res = self.__session.post(self.login_url,headers=Headers,data=data) 89 | print(res.text) 90 | def run(self): 91 | self.login() 92 | # self.evaluate() 93 | if __name__ == '__main__': 94 | username = input('请输入用户名:\n') 95 | password = input('请输入密码:\n') 96 | spider = UESTC(username,password) 97 | spider.run() 98 | 99 | 100 | -------------------------------------------------------------------------------- /qingguo/demo01.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | function des_encode(data, key){ 4 | if (typeof key == "undefined" || key == null || key.length == 0) { key = _deskey; } 5 | var result = strEnc(data, key, null, null); 6 | return result ; 7 | } 8 | 9 | function des_decode(data, key) { 10 | if (typeof key == "undefined" || key == null || key.length == 0) { key = _deskey; } 11 | return strDec(data, key, null, null); 12 | } 13 | */ 14 | 15 | /** 16 | * DES加密 17 | * encrypt the string to string made up of hex 18 | * return the encrypted string 19 | */ 20 | function strEnc(data,firstKey,secondKey,thirdKey){ 21 | 22 | var leng = data.length; 23 | var encData = ""; 24 | var firstKeyBt,secondKeyBt,thirdKeyBt,firstLength,secondLength,thirdLength; 25 | if(firstKey != null && firstKey != ""){ 26 | firstKeyBt = getKeyBytes(firstKey); 27 | firstLength = firstKeyBt.length; 28 | } 29 | if(secondKey != null && secondKey != ""){ 30 | secondKeyBt = getKeyBytes(secondKey); 31 | secondLength = secondKeyBt.length; 32 | } 33 | if(thirdKey != null && thirdKey != ""){ 34 | thirdKeyBt = getKeyBytes(thirdKey); 35 | thirdLength = thirdKeyBt.length; 36 | } 37 | 38 | if(leng > 0){ 39 | if(leng < 4){ 40 | var bt = strToBt(data); 41 | var encByte ; 42 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != ""){ 43 | var tempBt; 44 | var x,y,z; 45 | tempBt = bt; 46 | for(x = 0;x < firstLength ;x ++){ 47 | tempBt = enc(tempBt,firstKeyBt[x]); 48 | } 49 | for(y = 0;y < secondLength ;y ++){ 50 | tempBt = enc(tempBt,secondKeyBt[y]); 51 | } 52 | for(z = 0;z < thirdLength ;z ++){ 53 | tempBt = enc(tempBt,thirdKeyBt[z]); 54 | } 55 | encByte = tempBt; 56 | }else{ 57 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != ""){ 58 | var tempBt; 59 | var x,y; 60 | tempBt = bt; 61 | for(x = 0;x < firstLength ;x ++){ 62 | tempBt = enc(tempBt,firstKeyBt[x]); 63 | } 64 | for(y = 0;y < secondLength ;y ++){ 65 | tempBt = enc(tempBt,secondKeyBt[y]); 66 | } 67 | encByte = tempBt; 68 | }else{ 69 | if(firstKey != null && firstKey !=""){ 70 | var tempBt; 71 | var x = 0; 72 | tempBt = bt; 73 | for(x = 0;x < firstLength ;x ++){ 74 | tempBt = enc(tempBt,firstKeyBt[x]); 75 | } 76 | encByte = tempBt; 77 | } 78 | } 79 | } 80 | encData = bt64ToHex(encByte); 81 | }else{ 82 | var iterator = parseInt(leng/4); 83 | var remainder = leng%4; 84 | var i=0; 85 | for(i = 0;i < iterator;i++){ 86 | var tempData = data.substring(i*4+0,i*4+4); 87 | var tempByte = strToBt(tempData); 88 | var encByte ; 89 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != ""){ 90 | var tempBt; 91 | var x,y,z; 92 | tempBt = tempByte; 93 | for(x = 0;x < firstLength ;x ++){ 94 | tempBt = enc(tempBt,firstKeyBt[x]); 95 | } 96 | for(y = 0;y < secondLength ;y ++){ 97 | tempBt = enc(tempBt,secondKeyBt[y]); 98 | } 99 | for(z = 0;z < thirdLength ;z ++){ 100 | tempBt = enc(tempBt,thirdKeyBt[z]); 101 | } 102 | encByte = tempBt; 103 | }else{ 104 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != ""){ 105 | var tempBt; 106 | var x,y; 107 | tempBt = tempByte; 108 | for(x = 0;x < firstLength ;x ++){ 109 | tempBt = enc(tempBt,firstKeyBt[x]); 110 | } 111 | for(y = 0;y < secondLength ;y ++){ 112 | tempBt = enc(tempBt,secondKeyBt[y]); 113 | } 114 | encByte = tempBt; 115 | }else{ 116 | if(firstKey != null && firstKey !=""){ 117 | var tempBt; 118 | var x; 119 | tempBt = tempByte; 120 | for(x = 0;x < firstLength ;x ++){ 121 | tempBt = enc(tempBt,firstKeyBt[x]); 122 | } 123 | encByte = tempBt; 124 | } 125 | } 126 | } 127 | encData += bt64ToHex(encByte); 128 | } 129 | if(remainder > 0){ 130 | var remainderData = data.substring(iterator*4+0,leng); 131 | var tempByte = strToBt(remainderData); 132 | var encByte ; 133 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != ""){ 134 | var tempBt; 135 | var x,y,z; 136 | tempBt = tempByte; 137 | for(x = 0;x < firstLength ;x ++){ 138 | tempBt = enc(tempBt,firstKeyBt[x]); 139 | } 140 | for(y = 0;y < secondLength ;y ++){ 141 | tempBt = enc(tempBt,secondKeyBt[y]); 142 | } 143 | for(z = 0;z < thirdLength ;z ++){ 144 | tempBt = enc(tempBt,thirdKeyBt[z]); 145 | } 146 | encByte = tempBt; 147 | }else{ 148 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != ""){ 149 | var tempBt; 150 | var x,y; 151 | tempBt = tempByte; 152 | for(x = 0;x < firstLength ;x ++){ 153 | tempBt = enc(tempBt,firstKeyBt[x]); 154 | } 155 | for(y = 0;y < secondLength ;y ++){ 156 | tempBt = enc(tempBt,secondKeyBt[y]); 157 | } 158 | encByte = tempBt; 159 | }else{ 160 | if(firstKey != null && firstKey !=""){ 161 | var tempBt; 162 | var x; 163 | tempBt = tempByte; 164 | for(x = 0;x < firstLength ;x ++){ 165 | tempBt = enc(tempBt,firstKeyBt[x]); 166 | } 167 | encByte = tempBt; 168 | } 169 | } 170 | } 171 | encData += bt64ToHex(encByte); 172 | } 173 | } 174 | } 175 | return encData; 176 | } 177 | 178 | /* 179 | * DES解密 180 | * decrypt the encrypted string to the original string 181 | * return the original string 182 | */ 183 | function strDec(data,firstKey,secondKey,thirdKey){ 184 | var leng = data.length; 185 | var decStr = ""; 186 | var firstKeyBt,secondKeyBt,thirdKeyBt,firstLength,secondLength,thirdLength; 187 | if(firstKey != null && firstKey != ""){ 188 | firstKeyBt = getKeyBytes(firstKey); 189 | firstLength = firstKeyBt.length; 190 | } 191 | if(secondKey != null && secondKey != ""){ 192 | secondKeyBt = getKeyBytes(secondKey); 193 | secondLength = secondKeyBt.length; 194 | } 195 | if(thirdKey != null && thirdKey != ""){ 196 | thirdKeyBt = getKeyBytes(thirdKey); 197 | thirdLength = thirdKeyBt.length; 198 | } 199 | 200 | var iterator = parseInt(leng/16); 201 | var i=0; 202 | for(i = 0;i < iterator;i++){ 203 | var tempData = data.substring(i*16+0,i*16+16); 204 | var strByte = hexToBt64(tempData); 205 | var intByte = new Array(64); 206 | var j = 0; 207 | for(j = 0;j < 64; j++){ 208 | intByte[j] = parseInt(strByte.substring(j,j+1)); 209 | } 210 | var decByte; 211 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" && thirdKey != null && thirdKey != ""){ 212 | var tempBt; 213 | var x,y,z; 214 | tempBt = intByte; 215 | for(x = thirdLength - 1;x >= 0;x --){ 216 | tempBt = dec(tempBt,thirdKeyBt[x]); 217 | } 218 | for(y = secondLength - 1;y >= 0;y --){ 219 | tempBt = dec(tempBt,secondKeyBt[y]); 220 | } 221 | for(z = firstLength - 1;z >= 0 ;z --){ 222 | tempBt = dec(tempBt,firstKeyBt[z]); 223 | } 224 | decByte = tempBt; 225 | }else{ 226 | if(firstKey != null && firstKey !="" && secondKey != null && secondKey != ""){ 227 | var tempBt; 228 | var x,y,z; 229 | tempBt = intByte; 230 | for(x = secondLength - 1;x >= 0 ;x --){ 231 | tempBt = dec(tempBt,secondKeyBt[x]); 232 | } 233 | for(y = firstLength - 1;y >= 0 ;y --){ 234 | tempBt = dec(tempBt,firstKeyBt[y]); 235 | } 236 | decByte = tempBt; 237 | }else{ 238 | if(firstKey != null && firstKey !=""){ 239 | var tempBt; 240 | var x,y,z; 241 | tempBt = intByte; 242 | for(x = firstLength - 1;x >= 0 ;x --){ 243 | tempBt = dec(tempBt,firstKeyBt[x]); 244 | } 245 | decByte = tempBt; 246 | } 247 | } 248 | } 249 | decStr += byteToString(decByte); 250 | } 251 | return decStr; 252 | } 253 | /* 254 | * chang the string into the bit array 255 | * 256 | * return bit array(it's length % 64 = 0) 257 | */ 258 | function getKeyBytes(key){ 259 | var keyBytes = new Array(); 260 | var leng = key.length; 261 | var iterator = parseInt(leng/4); 262 | var remainder = leng%4; 263 | var i = 0; 264 | for(i = 0;i < iterator; i ++){ 265 | keyBytes[i] = strToBt(key.substring(i*4+0,i*4+4)); 266 | } 267 | if(remainder > 0){ 268 | keyBytes[i] = strToBt(key.substring(i*4+0,leng)); 269 | } 270 | return keyBytes; 271 | } 272 | 273 | /* 274 | * chang the string(it's length <= 4) into the bit array 275 | * 276 | * return bit array(it's length = 64) 277 | */ 278 | function strToBt(str){ 279 | var leng = str.length; 280 | var bt = new Array(64); 281 | if(leng < 4){ 282 | var i=0,j=0,p=0,q=0; 283 | for(i = 0;ij;m--){ 288 | pow *= 2; 289 | } 290 | bt[16*i+j]=parseInt(k/pow)%2; 291 | } 292 | } 293 | for(p = leng;p<4;p++){ 294 | var k = 0; 295 | for(q=0;q<16;q++){ 296 | var pow=1,m=0; 297 | for(m=15;m>q;m--){ 298 | pow *= 2; 299 | } 300 | bt[16*p+q]=parseInt(k/pow)%2; 301 | } 302 | } 303 | }else{ 304 | for(i = 0;i<4;i++){ 305 | var k = str.charCodeAt(i); 306 | for(j=0;j<16;j++){ 307 | var pow=1; 308 | for(m=15;m>j;m--){ 309 | pow *= 2; 310 | } 311 | bt[16*i+j]=parseInt(k/pow)%2; 312 | } 313 | } 314 | } 315 | return bt; 316 | } 317 | 318 | /* 319 | * chang the bit(it's length = 4) into the hex 320 | * 321 | * return hex 322 | */ 323 | function bt4ToHex(binary) { 324 | var hex; 325 | switch (binary) { 326 | case "0000" : hex = "0"; break; 327 | case "0001" : hex = "1"; break; 328 | case "0010" : hex = "2"; break; 329 | case "0011" : hex = "3"; break; 330 | case "0100" : hex = "4"; break; 331 | case "0101" : hex = "5"; break; 332 | case "0110" : hex = "6"; break; 333 | case "0111" : hex = "7"; break; 334 | case "1000" : hex = "8"; break; 335 | case "1001" : hex = "9"; break; 336 | case "1010" : hex = "A"; break; 337 | case "1011" : hex = "B"; break; 338 | case "1100" : hex = "C"; break; 339 | case "1101" : hex = "D"; break; 340 | case "1110" : hex = "E"; break; 341 | case "1111" : hex = "F"; break; 342 | } 343 | return hex; 344 | } 345 | 346 | /* 347 | * chang the hex into the bit(it's length = 4) 348 | * 349 | * return the bit(it's length = 4) 350 | */ 351 | function hexToBt4(hex) { 352 | var binary; 353 | switch (hex) { 354 | case "0" : binary = "0000"; break; 355 | case "1" : binary = "0001"; break; 356 | case "2" : binary = "0010"; break; 357 | case "3" : binary = "0011"; break; 358 | case "4" : binary = "0100"; break; 359 | case "5" : binary = "0101"; break; 360 | case "6" : binary = "0110"; break; 361 | case "7" : binary = "0111"; break; 362 | case "8" : binary = "1000"; break; 363 | case "9" : binary = "1001"; break; 364 | case "A" : binary = "1010"; break; 365 | case "B" : binary = "1011"; break; 366 | case "C" : binary = "1100"; break; 367 | case "D" : binary = "1101"; break; 368 | case "E" : binary = "1110"; break; 369 | case "F" : binary = "1111"; break; 370 | } 371 | return binary; 372 | } 373 | 374 | /* 375 | * chang the bit(it's length = 64) into the string 376 | * 377 | * return string 378 | */ 379 | function byteToString(byteData){ 380 | var str=""; 381 | for(i = 0;i<4;i++){ 382 | var count=0; 383 | for(j=0;j<16;j++){ 384 | var pow=1; 385 | for(m=15;m>j;m--){ 386 | pow*=2; 387 | } 388 | count+=byteData[16*i+j]*pow; 389 | } 390 | if(count != 0){ 391 | str+=String.fromCharCode(count); 392 | } 393 | } 394 | return str; 395 | } 396 | 397 | function bt64ToHex(byteData){ 398 | var hex = ""; 399 | for(i = 0;i<16;i++){ 400 | var bt = ""; 401 | for(j=0;j<4;j++){ 402 | bt += byteData[i*4+j]; 403 | } 404 | hex+=bt4ToHex(bt); 405 | } 406 | return hex; 407 | } 408 | 409 | function hexToBt64(hex){ 410 | var binary = ""; 411 | for(i = 0;i<16;i++){ 412 | binary+=hexToBt4(hex.substring(i,i+1)); 413 | } 414 | return binary; 415 | } 416 | 417 | /* 418 | * the 64 bit des core arithmetic 419 | */ 420 | 421 | function enc(dataByte,keyByte){ 422 | var keys = generateKeys(keyByte); 423 | var ipByte = initPermute(dataByte); 424 | var ipLeft = new Array(32); 425 | var ipRight = new Array(32); 426 | var tempLeft = new Array(32); 427 | var i = 0,j = 0,k = 0,m = 0, n = 0; 428 | for(k = 0;k < 32;k ++){ 429 | ipLeft[k] = ipByte[k]; 430 | ipRight[k] = ipByte[32+k]; 431 | } 432 | for(i = 0;i < 16;i ++){ 433 | for(j = 0;j < 32;j ++){ 434 | tempLeft[j] = ipLeft[j]; 435 | ipLeft[j] = ipRight[j]; 436 | } 437 | var key = new Array(48); 438 | for(m = 0;m < 48;m ++){ 439 | key[m] = keys[i][m]; 440 | } 441 | var tempRight = xor(pPermute(sBoxPermute(xor(expandPermute(ipRight),key))), tempLeft); 442 | for(n = 0;n < 32;n ++){ 443 | ipRight[n] = tempRight[n]; 444 | } 445 | 446 | } 447 | 448 | 449 | var finalData =new Array(64); 450 | for(i = 0;i < 32;i ++){ 451 | finalData[i] = ipRight[i]; 452 | finalData[32+i] = ipLeft[i]; 453 | } 454 | return finallyPermute(finalData); 455 | } 456 | 457 | function dec(dataByte,keyByte){ 458 | var keys = generateKeys(keyByte); 459 | var ipByte = initPermute(dataByte); 460 | var ipLeft = new Array(32); 461 | var ipRight = new Array(32); 462 | var tempLeft = new Array(32); 463 | var i = 0,j = 0,k = 0,m = 0, n = 0; 464 | for(k = 0;k < 32;k ++){ 465 | ipLeft[k] = ipByte[k]; 466 | ipRight[k] = ipByte[32+k]; 467 | } 468 | for(i = 15;i >= 0;i --){ 469 | for(j = 0;j < 32;j ++){ 470 | tempLeft[j] = ipLeft[j]; 471 | ipLeft[j] = ipRight[j]; 472 | } 473 | var key = new Array(48); 474 | for(m = 0;m < 48;m ++){ 475 | key[m] = keys[i][m]; 476 | } 477 | 478 | var tempRight = xor(pPermute(sBoxPermute(xor(expandPermute(ipRight),key))), tempLeft); 479 | for(n = 0;n < 32;n ++){ 480 | ipRight[n] = tempRight[n]; 481 | } 482 | } 483 | 484 | 485 | var finalData =new Array(64); 486 | for(i = 0;i < 32;i ++){ 487 | finalData[i] = ipRight[i]; 488 | finalData[32+i] = ipLeft[i]; 489 | } 490 | return finallyPermute(finalData); 491 | } 492 | 493 | function initPermute(originalData){ 494 | var ipByte = new Array(64); 495 | for (i = 0, m = 1, n = 0; i < 4; i++, m += 2, n += 2) { 496 | for (j = 7, k = 0; j >= 0; j--, k++) { 497 | ipByte[i * 8 + k] = originalData[j * 8 + m]; 498 | ipByte[i * 8 + k + 32] = originalData[j * 8 + n]; 499 | } 500 | } 501 | return ipByte; 502 | } 503 | 504 | function expandPermute(rightData){ 505 | var epByte = new Array(48); 506 | for (i = 0; i < 8; i++) { 507 | if (i == 0) { 508 | epByte[i * 6 + 0] = rightData[31]; 509 | } else { 510 | epByte[i * 6 + 0] = rightData[i * 4 - 1]; 511 | } 512 | epByte[i * 6 + 1] = rightData[i * 4 + 0]; 513 | epByte[i * 6 + 2] = rightData[i * 4 + 1]; 514 | epByte[i * 6 + 3] = rightData[i * 4 + 2]; 515 | epByte[i * 6 + 4] = rightData[i * 4 + 3]; 516 | if (i == 7) { 517 | epByte[i * 6 + 5] = rightData[0]; 518 | } else { 519 | epByte[i * 6 + 5] = rightData[i * 4 + 4]; 520 | } 521 | } 522 | return epByte; 523 | } 524 | 525 | function xor(byteOne,byteTwo){ 526 | var xorByte = new Array(byteOne.length); 527 | for(i = 0;i < byteOne.length; i ++){ 528 | xorByte[i] = byteOne[i] ^ byteTwo[i]; 529 | } 530 | return xorByte; 531 | } 532 | 533 | function sBoxPermute(expandByte){ 534 | 535 | var sBoxByte = new Array(32); 536 | var binary = ""; 537 | var s1 = [ 538 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], 539 | [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], 540 | [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], 541 | [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 ]]; 542 | 543 | /* Table - s2 */ 544 | var s2 = [ 545 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], 546 | [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], 547 | [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], 548 | [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 ]]; 549 | 550 | /* Table - s3 */ 551 | var s3= [ 552 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], 553 | [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], 554 | [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], 555 | [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 ]]; 556 | /* Table - s4 */ 557 | var s4 = [ 558 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], 559 | [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], 560 | [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], 561 | [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 ]]; 562 | 563 | /* Table - s5 */ 564 | var s5 = [ 565 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], 566 | [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], 567 | [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], 568 | [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 ]]; 569 | 570 | /* Table - s6 */ 571 | var s6 = [ 572 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], 573 | [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], 574 | [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], 575 | [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 ]]; 576 | 577 | /* Table - s7 */ 578 | var s7 = [ 579 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], 580 | [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], 581 | [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], 582 | [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]]; 583 | 584 | /* Table - s8 */ 585 | var s8 = [ 586 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], 587 | [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], 588 | [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], 589 | [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]; 590 | 591 | for(m=0;m<8;m++){ 592 | var i=0,j=0; 593 | i = expandByte[m*6+0]*2+expandByte[m*6+5]; 594 | j = expandByte[m * 6 + 1] * 2 * 2 * 2 595 | + expandByte[m * 6 + 2] * 2* 2 596 | + expandByte[m * 6 + 3] * 2 597 | + expandByte[m * 6 + 4]; 598 | switch (m) { 599 | case 0 : 600 | binary = getBoxBinary(s1[i][j]); 601 | break; 602 | case 1 : 603 | binary = getBoxBinary(s2[i][j]); 604 | break; 605 | case 2 : 606 | binary = getBoxBinary(s3[i][j]); 607 | break; 608 | case 3 : 609 | binary = getBoxBinary(s4[i][j]); 610 | break; 611 | case 4 : 612 | binary = getBoxBinary(s5[i][j]); 613 | break; 614 | case 5 : 615 | binary = getBoxBinary(s6[i][j]); 616 | break; 617 | case 6 : 618 | binary = getBoxBinary(s7[i][j]); 619 | break; 620 | case 7 : 621 | binary = getBoxBinary(s8[i][j]); 622 | break; 623 | } 624 | sBoxByte[m*4+0] = parseInt(binary.substring(0,1)); 625 | sBoxByte[m*4+1] = parseInt(binary.substring(1,2)); 626 | sBoxByte[m*4+2] = parseInt(binary.substring(2,3)); 627 | sBoxByte[m*4+3] = parseInt(binary.substring(3,4)); 628 | } 629 | return sBoxByte; 630 | } 631 | 632 | function pPermute(sBoxByte){ 633 | var pBoxPermute = new Array(32); 634 | pBoxPermute[ 0] = sBoxByte[15]; 635 | pBoxPermute[ 1] = sBoxByte[ 6]; 636 | pBoxPermute[ 2] = sBoxByte[19]; 637 | pBoxPermute[ 3] = sBoxByte[20]; 638 | pBoxPermute[ 4] = sBoxByte[28]; 639 | pBoxPermute[ 5] = sBoxByte[11]; 640 | pBoxPermute[ 6] = sBoxByte[27]; 641 | pBoxPermute[ 7] = sBoxByte[16]; 642 | pBoxPermute[ 8] = sBoxByte[ 0]; 643 | pBoxPermute[ 9] = sBoxByte[14]; 644 | pBoxPermute[10] = sBoxByte[22]; 645 | pBoxPermute[11] = sBoxByte[25]; 646 | pBoxPermute[12] = sBoxByte[ 4]; 647 | pBoxPermute[13] = sBoxByte[17]; 648 | pBoxPermute[14] = sBoxByte[30]; 649 | pBoxPermute[15] = sBoxByte[ 9]; 650 | pBoxPermute[16] = sBoxByte[ 1]; 651 | pBoxPermute[17] = sBoxByte[ 7]; 652 | pBoxPermute[18] = sBoxByte[23]; 653 | pBoxPermute[19] = sBoxByte[13]; 654 | pBoxPermute[20] = sBoxByte[31]; 655 | pBoxPermute[21] = sBoxByte[26]; 656 | pBoxPermute[22] = sBoxByte[ 2]; 657 | pBoxPermute[23] = sBoxByte[ 8]; 658 | pBoxPermute[24] = sBoxByte[18]; 659 | pBoxPermute[25] = sBoxByte[12]; 660 | pBoxPermute[26] = sBoxByte[29]; 661 | pBoxPermute[27] = sBoxByte[ 5]; 662 | pBoxPermute[28] = sBoxByte[21]; 663 | pBoxPermute[29] = sBoxByte[10]; 664 | pBoxPermute[30] = sBoxByte[ 3]; 665 | pBoxPermute[31] = sBoxByte[24]; 666 | return pBoxPermute; 667 | } 668 | 669 | function finallyPermute(endByte){ 670 | var fpByte = new Array(64); 671 | fpByte[ 0] = endByte[39]; 672 | fpByte[ 1] = endByte[ 7]; 673 | fpByte[ 2] = endByte[47]; 674 | fpByte[ 3] = endByte[15]; 675 | fpByte[ 4] = endByte[55]; 676 | fpByte[ 5] = endByte[23]; 677 | fpByte[ 6] = endByte[63]; 678 | fpByte[ 7] = endByte[31]; 679 | fpByte[ 8] = endByte[38]; 680 | fpByte[ 9] = endByte[ 6]; 681 | fpByte[10] = endByte[46]; 682 | fpByte[11] = endByte[14]; 683 | fpByte[12] = endByte[54]; 684 | fpByte[13] = endByte[22]; 685 | fpByte[14] = endByte[62]; 686 | fpByte[15] = endByte[30]; 687 | fpByte[16] = endByte[37]; 688 | fpByte[17] = endByte[ 5]; 689 | fpByte[18] = endByte[45]; 690 | fpByte[19] = endByte[13]; 691 | fpByte[20] = endByte[53]; 692 | fpByte[21] = endByte[21]; 693 | fpByte[22] = endByte[61]; 694 | fpByte[23] = endByte[29]; 695 | fpByte[24] = endByte[36]; 696 | fpByte[25] = endByte[ 4]; 697 | fpByte[26] = endByte[44]; 698 | fpByte[27] = endByte[12]; 699 | fpByte[28] = endByte[52]; 700 | fpByte[29] = endByte[20]; 701 | fpByte[30] = endByte[60]; 702 | fpByte[31] = endByte[28]; 703 | fpByte[32] = endByte[35]; 704 | fpByte[33] = endByte[ 3]; 705 | fpByte[34] = endByte[43]; 706 | fpByte[35] = endByte[11]; 707 | fpByte[36] = endByte[51]; 708 | fpByte[37] = endByte[19]; 709 | fpByte[38] = endByte[59]; 710 | fpByte[39] = endByte[27]; 711 | fpByte[40] = endByte[34]; 712 | fpByte[41] = endByte[ 2]; 713 | fpByte[42] = endByte[42]; 714 | fpByte[43] = endByte[10]; 715 | fpByte[44] = endByte[50]; 716 | fpByte[45] = endByte[18]; 717 | fpByte[46] = endByte[58]; 718 | fpByte[47] = endByte[26]; 719 | fpByte[48] = endByte[33]; 720 | fpByte[49] = endByte[ 1]; 721 | fpByte[50] = endByte[41]; 722 | fpByte[51] = endByte[ 9]; 723 | fpByte[52] = endByte[49]; 724 | fpByte[53] = endByte[17]; 725 | fpByte[54] = endByte[57]; 726 | fpByte[55] = endByte[25]; 727 | fpByte[56] = endByte[32]; 728 | fpByte[57] = endByte[ 0]; 729 | fpByte[58] = endByte[40]; 730 | fpByte[59] = endByte[ 8]; 731 | fpByte[60] = endByte[48]; 732 | fpByte[61] = endByte[16]; 733 | fpByte[62] = endByte[56]; 734 | fpByte[63] = endByte[24]; 735 | return fpByte; 736 | } 737 | 738 | function getBoxBinary(i) { 739 | var binary = ""; 740 | switch (i) { 741 | case 0 :binary = "0000";break; 742 | case 1 :binary = "0001";break; 743 | case 2 :binary = "0010";break; 744 | case 3 :binary = "0011";break; 745 | case 4 :binary = "0100";break; 746 | case 5 :binary = "0101";break; 747 | case 6 :binary = "0110";break; 748 | case 7 :binary = "0111";break; 749 | case 8 :binary = "1000";break; 750 | case 9 :binary = "1001";break; 751 | case 10 :binary = "1010";break; 752 | case 11 :binary = "1011";break; 753 | case 12 :binary = "1100";break; 754 | case 13 :binary = "1101";break; 755 | case 14 :binary = "1110";break; 756 | case 15 :binary = "1111";break; 757 | } 758 | return binary; 759 | } 760 | /* 761 | * generate 16 keys for xor 762 | * 763 | */ 764 | function generateKeys(keyByte){ 765 | var key = new Array(56); 766 | var keys = new Array(); 767 | 768 | keys[ 0] = new Array(); 769 | keys[ 1] = new Array(); 770 | keys[ 2] = new Array(); 771 | keys[ 3] = new Array(); 772 | keys[ 4] = new Array(); 773 | keys[ 5] = new Array(); 774 | keys[ 6] = new Array(); 775 | keys[ 7] = new Array(); 776 | keys[ 8] = new Array(); 777 | keys[ 9] = new Array(); 778 | keys[10] = new Array(); 779 | keys[11] = new Array(); 780 | keys[12] = new Array(); 781 | keys[13] = new Array(); 782 | keys[14] = new Array(); 783 | keys[15] = new Array(); 784 | var loop = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]; 785 | 786 | for(i=0;i<7;i++){ 787 | for(j=0,k=7;j<8;j++,k--){ 788 | key[i*8+j]=keyByte[8*k+i]; 789 | } 790 | } 791 | 792 | var i = 0; 793 | for(i = 0;i < 16;i ++){ 794 | var tempLeft=0; 795 | var tempRight=0; 796 | for(j = 0; j < loop[i];j ++){ 797 | tempLeft = key[0]; 798 | tempRight = key[28]; 799 | for(k = 0;k < 27 ;k ++){ 800 | key[k] = key[k+1]; 801 | key[28+k] = key[29+k]; 802 | } 803 | key[27]=tempLeft; 804 | key[55]=tempRight; 805 | } 806 | var tempKey = new Array(48); 807 | tempKey[ 0] = key[13]; 808 | tempKey[ 1] = key[16]; 809 | tempKey[ 2] = key[10]; 810 | tempKey[ 3] = key[23]; 811 | tempKey[ 4] = key[ 0]; 812 | tempKey[ 5] = key[ 4]; 813 | tempKey[ 6] = key[ 2]; 814 | tempKey[ 7] = key[27]; 815 | tempKey[ 8] = key[14]; 816 | tempKey[ 9] = key[ 5]; 817 | tempKey[10] = key[20]; 818 | tempKey[11] = key[ 9]; 819 | tempKey[12] = key[22]; 820 | tempKey[13] = key[18]; 821 | tempKey[14] = key[11]; 822 | tempKey[15] = key[ 3]; 823 | tempKey[16] = key[25]; 824 | tempKey[17] = key[ 7]; 825 | tempKey[18] = key[15]; 826 | tempKey[19] = key[ 6]; 827 | tempKey[20] = key[26]; 828 | tempKey[21] = key[19]; 829 | tempKey[22] = key[12]; 830 | tempKey[23] = key[ 1]; 831 | tempKey[24] = key[40]; 832 | tempKey[25] = key[51]; 833 | tempKey[26] = key[30]; 834 | tempKey[27] = key[36]; 835 | tempKey[28] = key[46]; 836 | tempKey[29] = key[54]; 837 | tempKey[30] = key[29]; 838 | tempKey[31] = key[39]; 839 | tempKey[32] = key[50]; 840 | tempKey[33] = key[44]; 841 | tempKey[34] = key[32]; 842 | tempKey[35] = key[47]; 843 | tempKey[36] = key[43]; 844 | tempKey[37] = key[48]; 845 | tempKey[38] = key[38]; 846 | tempKey[39] = key[55]; 847 | tempKey[40] = key[33]; 848 | tempKey[41] = key[52]; 849 | tempKey[42] = key[45]; 850 | tempKey[43] = key[41]; 851 | tempKey[44] = key[49]; 852 | tempKey[45] = key[35]; 853 | tempKey[46] = key[28]; 854 | tempKey[47] = key[31]; 855 | switch(i){ 856 | case 0: for(m=0;m < 48 ;m++){ keys[ 0][m] = tempKey[m]; } break; 857 | case 1: for(m=0;m < 48 ;m++){ keys[ 1][m] = tempKey[m]; } break; 858 | case 2: for(m=0;m < 48 ;m++){ keys[ 2][m] = tempKey[m]; } break; 859 | case 3: for(m=0;m < 48 ;m++){ keys[ 3][m] = tempKey[m]; } break; 860 | case 4: for(m=0;m < 48 ;m++){ keys[ 4][m] = tempKey[m]; } break; 861 | case 5: for(m=0;m < 48 ;m++){ keys[ 5][m] = tempKey[m]; } break; 862 | case 6: for(m=0;m < 48 ;m++){ keys[ 6][m] = tempKey[m]; } break; 863 | case 7: for(m=0;m < 48 ;m++){ keys[ 7][m] = tempKey[m]; } break; 864 | case 8: for(m=0;m < 48 ;m++){ keys[ 8][m] = tempKey[m]; } break; 865 | case 9: for(m=0;m < 48 ;m++){ keys[ 9][m] = tempKey[m]; } break; 866 | case 10: for(m=0;m < 48 ;m++){ keys[10][m] = tempKey[m]; } break; 867 | case 11: for(m=0;m < 48 ;m++){ keys[11][m] = tempKey[m]; } break; 868 | case 12: for(m=0;m < 48 ;m++){ keys[12][m] = tempKey[m]; } break; 869 | case 13: for(m=0;m < 48 ;m++){ keys[13][m] = tempKey[m]; } break; 870 | case 14: for(m=0;m < 48 ;m++){ keys[14][m] = tempKey[m]; } break; 871 | case 15: for(m=0;m < 48 ;m++){ keys[15][m] = tempKey[m]; } break; 872 | } 873 | } 874 | return keys; 875 | } 876 | -------------------------------------------------------------------------------- /qingguo/imgs/123.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/123.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img01.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img02.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img03.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img04.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img05.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img06.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img07.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img08.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img09.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img10.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img11.jpg -------------------------------------------------------------------------------- /qingguo/imgs/img12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/img12.jpg -------------------------------------------------------------------------------- /qingguo/imgs/index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q6378561/js-reverse-log/2ca2a5bde8a99b172fd207f698bfe695d5c3fe6d/qingguo/imgs/index.jpg -------------------------------------------------------------------------------- /qingguo/imgs/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /weinisi/README.md: -------------------------------------------------------------------------------- 1 | # 逆向某网站的登录接口生成元素加密 2 | > 由于是非法网站所以本文对网址进行了遮挡,但是其中的登录接口加密还是挺有意思的,故写下日志进行逆向,本文仅供参考! 3 | 4 | ## 逆向环境 5 | * [![chrome]][chrome_url] 6 | * [![charles]][charles_url] 7 | * ![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/metabolize/rq-dashboard-on-heroku) 8 | * [![pycharm]][pycharm_url] 9 | 10 | ## 登录接口解析 11 | 还是用我们的老套路,发送登录请求,获取登录的url,来看看参数加密情况 12 | 13 | ![post参数](https://img-blog.csdnimg.cn/20200311234628655.jpg) 14 | 15 | `password`很明显这个参数加密了,作者这里输入了123456但是返回的却是一堆乱码,话不多说直接搜索看看登录的url看看是否能定位到请求代码处 16 | 17 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200311234957962.jpg) 18 | 19 | 很快就找到了登录接口处 20 | 21 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200311235028591.jpg) 22 | 23 | 由于该代码完全明文所以我们也非常容易能看出来代码的作用: 24 | > 判断`isCrypt`是否为真,真的话调用cryptStr函数传入password参数,假则直接返回password,再加上下面的提交的`crypt`参数,小编猜想是否能提交的时候将`crypt`改为0,`password`直接传入明文是否能登录成功,通过charles发包后也证实了此观点,不过本文的目的是为了逆向学习交流分析,故作此下文 25 | 26 | 而我们的逆向思路也显而易见的出来了,在此处下个断点看看cryptStr函数是如何运行的,重新发送登录请求,成功的断下来了,F11跟进看看代码内部逻辑 27 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200311235751178.jpg) 28 | 29 | 下面是这段代码: 30 | 31 | ```javascript 32 | var cryptStr = function (val) { 33 | var temp = $("
"); 34 | var cryptStrInput = temp.find(".cryptStr").val(val); 35 | temp.appendTo(document.body); 36 | temp.find(".btnCrypt").click(); 37 | var cryptStr = cryptStrInput.val(); 38 | temp.remove(); 39 | return cryptStr; 40 | }; 41 | ``` 42 | 首先通过传入的参数可以很明显的看出来是我们输入的明文密码,然后我们逐句分析 43 | * 首先第一行代码就是生成一个元素放入`temp`变量中 44 | * 第二行代码则是定义一个`cryptStrInput`变量,定位生成的元素中`class=cryptStr`样式传入我们的`val`参数也就是我们的明文密码 45 | * 第三行将此元素添加到`body`的最后一行 46 | * 第四行找到`class=btnCrypt`样式进行点击 47 | * 第五行定义一个变量`cryptStr`令它等于变量`cryptStrInput`里的值 48 | * 第六行删除`temp`元素 49 | * 第七行返回`cryptStr` 50 | 51 | 分析完逻辑后很明显的看出来,有一段代码调用了点击函数然后用加密函数在里面进行加密,至于为什么程序员要大费周章的这样生成元素来进行加密而不是直接在`cryptStr`函数编写加密代码段呢?理由也十分简单,因为这样的话内存调用栈也就追踪不到关键加密函数,这是对逆向十分头疼的.但是我们前面分析了关键在于click了btnCrypt,然后调用函数.正所谓办法总比困难多,我们可以在宇宙第一的chrome浏览器中查看监听事件,看看click指向了那里的函数 52 | 53 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200312005254947.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E2Mzc4NTYx,size_16,color_FFFFFF,t_70) 54 | 55 | 可以很明显的看到click只有一个函数参与调用了,不出意外这里面就是关键的加密代码,点进去看看 56 | 57 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200312005553848.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3E2Mzc4NTYx,size_16,color_FFFFFF,t_70) 58 | 到这里也就十分容易分析了,首先获取明文密码,然后获取cookie中的`randomYes`的值,如果密码中已经包含了`randomYes`值说明已经加密过了所以直接返回,下面则是判断三个cookie是否存在,存在就传入到`sessionCookie`变量中,后面的代码如下 59 | 60 | ```javascript 61 | sessionCookie = sessionCookie || "undefined"; 62 | var randomId = encrypt(cryptStr,sdc(sessionCookie + randomYes)); 63 | $(".cryptStr").val(randomId + randomYes); 64 | ``` 65 | 定义`randomId`变量,然后调用`encrypt`函数传入两个参数,一个是明文密码,另一个则是sdc函数将`sessionCookie + randomYes`进行加密返回值,很明显的看出加密函数是AES加密,模式为ECB模式,填充为Pkcs7方式填充(**不熟悉AES加密的同学此时可以百度一下获取新知识,这里不过多描述**)然后传入`text`参数,以及密钥`secKey`,加密完后到文本返回加密后的数值.至于sdc函数小编点进去看了一眼并没有看出个所以然来,所以待会写python登录的时候只能将直接调用JS文件中的sdc函数进行生成(**希望以后回来看自己文章能明白sdc函数的加密方式是什么,当然也可以大牛上github来一起研究看看是什么方式的加密**). 66 | 67 | ## 总结 68 | 理顺了具体的加密逻辑后写python登录也基本上没什么难度了,有兴趣看的同学可以上我的github一起学习交流,本网站逆向主要有两大点需要注意 69 | 1. 当Call Stack追踪不到我们想要的函数时候怎么办?需要从多方面角度分析,办法总比困难多,除非是不存在的代码我们才追溯不到本源! 70 | 2. 遇见了陌生的函数,这在逆向中是十分需要警惕的一件事,当你对加密函数看不懂的时候就代表你逆向出现了障碍,逻辑效率等方面都会出现偏差,sdc函数可能只是改了个名的常用加密方式,但是小编并没有看出来,这是十分不应该的,就算是网站程序员自己写的加密函数也应该一步步去分析其逻辑,但是本网站的加密方法已经禁止了你进行动态调试,追踪这条路自然也无从下手!希望能有大佬站出来为小弟排忧解难 71 | * **顺带一提**,本文分析后的py文件已经放入github中,有需要一起学习分析的同学可以上github查看 72 | 73 | ## 支持作者 74 | 喜欢我的话点一下下方的按钮哦! 75 | 76 | ![GitHub followers](https://img.shields.io/github/followers/q6378561?style=social) 77 | ![GitHub stars](https://img.shields.io/github/stars/q6378561/js-reverse-log?style=social) 78 | ![GitHub forks](https://img.shields.io/github/forks/q6378561/js-reverse-log?style=social) 79 | ![GitHub watchers](https://img.shields.io/github/watchers/q6378561/js-reverse-log?style=social) 80 | 81 | [chrome]: https://img.shields.io/badge/chrome-80.0.3987.122-ff69b4 82 | [chrome_url]: https://www.google.com/chrome/ 83 | [charles]: https://img.shields.io/badge/charles-v3.11.2-brightgreen 84 | [charles_url]: https://www.charlesproxy.com/ 85 | [pycharm]: https://img.shields.io/badge/pycharm-professional-red 86 | [pycharm_url]: https://www.jetbrains.com/pycharm/ 87 | 88 | 89 | --------------------------------------------------------------------------------