本篇文章作者 ius，本文属 i 春秋原创奖励计划，未经许可禁止转载原文链接：https://bbs.ichunqiu.com/thread-63876-1-1.html某稀小程序敲木鱼活动逆向分析前言最近发现某稀在小程序搞了个活动，敲木鱼赢好礼。身为一名程序猿，必然要分析分析请求接口辣image-20240212225711002正文抓包软件，启动!image-20240212225938249然后我们打开活动界面，测试一下image-20240212230100622ok，一切正常image-20240212230051819然后进入游戏，开始抓接口image-20240212230240197可以看到这个就是提交接口的返回内容image-20240212230748721里面的参数只有token是未知的，我们的目标就是搞定这个参数image-20240212230828453而且这个参数每次都会变动，并非固定的image-20240212230944369通过分析抓包流程可以发现，在每次的接口提交中，都会对这个接口发起请求image-20240212232159181我们可以大胆的推测，token就是由这个data来生成的image-20240212231142586先模拟请求一下，把data摘出来image-20240212232520621放到js里运行一下image-20240212232657057提示缺少window，我们在开头补上image-20240212232742120再次运行，可以运行成功，但是没有结果image-20240212232802325我们去浏览器里运行测试看看，是不是检测nodejs环境了在浏览器里控制台会打印一个falseimage-20240213000118100这时候，我们要想办法调试一下，单看肯定看不出来先给代码格式化一下image-20240213000309188这些注释估计就是混淆视听的，我们用正则去除一下image-20240213000354202然后再格式化一下image-20240213000429103在最后下个断点可以发现，在window中生成了一堆乱码这样看来单纯的分析这个js好像没什么头绪了image-20240213000700057在抓包流程中，我们可以发现小程序的首页对于这种内嵌的网页，我们尝试直接从浏览器访问image-20240212233053130访问成功，但是提示我们没有登录image-20240212233124868我们把抓包中的cookie导入到浏览器中，这里需要用到一个Cookie-Editor这个插件，在chrome扩展商店可以下载到复制cookieimage-20240212233311522导入image-20240212233359849刷新界面，这次没有提示我们未登录image-20240212233431180在浏览器调试就舒服多了，随便给下个xhr断点image-20240212233804914再次提交，成功断住image-20240212233848211开始跟栈分析token生成位置此处省略1万字简单说一下这种流程如何跟栈吧我们从这里可以大概的看一下，调用函数名称image-20240212234930444总览一下，看看有哪些函数名比较关键image-20240212235016397这里有个叫startGame的函数，我就选择在这里断点，然后一步一步分析跟到这里的时候，我们可以发现参数token的生成image-20240212235228757image-20240212235302145进去之后，发现是这样的一串代码这个代码的意思是，从window里获取p83d60的值image-20240212235326205我们从控制台测试一下image-20240212235526146看着就像token，我们放行断点，看看发包是不是这个值看着这俩参数长得不能再一样了image-20240212235606115所以这个token是从window里取得值。我们回到刚才测试js里打印一下windowimage-20240212235731699果然有一堆乱七八糟的参数试试打印window['p83d60']undefined？image-20240212235826452我们重新获取一次data，然后再打印试试image-20240212235943352这次成功获取到了，所以看来这个window的值并非固定的。经过测试发现，这个值大概是1个小时会变一次不过，这点小伎俩，怎么可能会阻碍到聪明绝顶的我？？在这个js中，只有一个参数是我们想要的，而其他的值都是用来混淆视听的所以我们可不可以，获取两次data，然后在生成的值中找到两次data相同的值从而获取到当前时间段的真正的值？理论形成，实践开始！为了方便调用js，这次我们直接用nodejs写获取data参数，然后用vm执行js，最后打印windowimage-20240213001447184然后根据上面的思路完善一下，最后运行，测试成功！image-20240213001624647后记参数都搞定了，我们来用代码模拟一下流程试试根据抓包分析可以知道需要请求三次个接口，创建页面，开始游戏，提交分数这里就不赘述了，简单完成一下成功提交image-20240213001927674
本篇文章作者 ius，本文属 i 春秋原创奖励计划，未经许可禁止转载原文链接：https://bbs.ichunqiu.com/thread-63833-1-1.html某教务系统登录逆向分析前言image-20240123081743636这个系统相信很多高校都在用，有时候期末出成绩，我们经常会吃完饭看看，打完游戏看，刷完抖音看看，睡醒一觉也得看。这次带大家分析一下这个系统的登陆参数，还有验证码如何识别。目标参数：image-20240123082014787正文我们全局搜索csrftoken可以看出来这是后端生成的一个随机值，然后被隐藏了起来image-20240123082254962在元素中找到这个值，然后右键复制->复制XPathimage-20240123082603914复制出来的是这样的//*[@id="csrftoken"]我们在后面加上/@value意思是取这个元素中的属性值，运行测试，没有问题image-20240123082818929然后我们全局搜索encrypt为什么搜这个呢，这是因为在前端加密中，经常会用到这个加密函数，如果是没有混淆的网站，搜这个一般都可以搜出来然后我们找到了密码加密的位置打上断点，重新提交测试image-20240123083236707成功断点image-20240123083753922我们可以看出来，这是一个RSA加密，先是将公钥中的模数和指数转为hex，然后进行设置这里的公钥是网络请求出来的image-20240123084130278流程有了，我们先把js代码模拟出来。image-20240123084400816image-20240123084437474跟浏览器的对比一下，可以看出是一样的image-20240123084453940然后测试一下modulusimage-20240123084718775image-20240123084727489可以发现也是没有一点点问题的接着，我们来模拟一下RSA加密先在终端安装node-rsa npm install node-rsaimage-20240123085408092安装成功后我们导入开始模拟经过一番测试之后我发现node中传递公钥需要传递Buffer流所以不需要b64tohex...image-20240123090541865现在加密模拟出来了，我们来模拟登录流程首先，请求登录页面，获取csrftoken然后，请求公钥接口，获取publickey最后，使用公钥对密码进行RSA加密image-20240123091600929加密参数都模拟好了，我们来请求测试一下因为频繁的测试，导致出现了验证码image-20240123092056627这里我们使用ddddocr来识别验证码写入到本地来查看是否相同image-20240123092939976运行可以看到，识别出来的是一样的image-20240123093003010image-20240123093009424然后我们开始提交请求提示我们密码不正确？？？image-20240123100335241这里我们检查一遍流程发现没有任何问题密码也确定是对的那只有一种可能就是加密出来的密码不对然后我们开始一一对照image-20240123100526490我们查看对比浏览器的加密后16进制数因为使用的是rsa加密。所以每次结果都会不相同，这里我们无法对比我们测试加密后的结果，有何不同image-20240123100613288可以发现，经过base64编码后的结果不相同第一个是使用通用base64进行编码第二个是使用此处的base64函数编码可以发现这里的是魔改base64我们选择直接复制此处的base64文件image-20240123100730630image-20240123100949211image-20240123100957709再次编码发现结果相同了image-20240123100924620导出函数image-20240123101505973image-20240123101520502再次运行还是报错？！image-20240123100335241此处我再次怀疑是我加密方式不对然后我选择将整个rsa加密文件扣下来使用他使用的加密方式，我这还能错？？运行测试image-20240123121427998？？？？？？？此处省略1万的问号还说我密码不正确？？？在这里，我浪费了大量的去反复检验流程是否错误加密是否错误检验到最后发现，一样的不能再一样了这是怎么回事？image-20240123100335241其实这里使用了大名鼎鼎的TLS指纹校验TLS指纹不同于ua，是比较底层的一种标识那我们怎么解决呢？使用curl-cffi库，就能完美的解决这个问题pip install curl-cffiimage-20240123122037191image-20240123122051890然后再次运行成功！image-20240123122126297后记这次登陆分析其实没想到会花费这么久的时间因为加密参数很清晰明了只不过没仔细分析是否进行了魔改最后放弃自己模拟，直接扣出魔改后的代码结果还是无法登录这又让我怀疑是不是流程出错了然后又反复调试了半天还是没办法最后的最后，想尝试一下tls指纹结果成功了只能说不可小觑天下英雄没想到这个教务系统还会做tls校验没想到啊，是真没想到啊
本篇文章作者 ius，本文属 i 春秋原创奖励计划，未经许可禁止转载原文地址：https://bbs.ichunqiu.com/thread-63811-1-1.html某易云音乐加密参数逆向分析前言：目标网站：aHR0cHM6Ly9tdXNpYy4xNjMuY29tLw==加密参数：image-20240115092209120正文：参数分析：打开F12，点击右上角搜索进行歌曲搜索image-20240115092611240image-20240115092653244image-20240115092656783查看请求参数发现被加密，我们尝试全局搜索encSecKeyimage-20240115092818504搜到了很多地方，我们先点进第一个查看一下，因为第一个的js文件名带着coreimage-20240115092918041看着很像加密处，我们再查看一下第二个搜索image-20240115093000802这里能明显看出来是请求提交的地方，我们断个点image-20240115093036643因为搜索栏是实时的，我们点击一下搜索栏试试image-20240115093136126成功断住，我们开始分析params: bVi9Z.encText,encSecKey: bVi9Z.encSecKey这两个参数是通过bVi9Z变量取的里面的数值，所以我们直接分析bVi9Z是如何生成的光标放到函数上进行查看，我们点击跳转到函数原位置image-20240115093358070跟我们第一次进入的是同一个位置，说明加密就在此处生成然后我们来分析一下加密函数的构造image-20240115093446177可以明显的发现，我们传递给加密函数的参数是由这四个组成image-20240115093555209我们来控制台一一打印看看image-20240115093732206JSON.stringify(i3x)就是我们的post参数内容bsu5z(["流泪", "强"]) 看着像是一个固定的数字bsu5z(Xo5t.md) 是一串很长的字符串bsu5z(["爱心", "女孩", "惊恐", "大笑"]) 是一串乱码我们为了确定这里面的参数哪些是改变的，哪些是不改变的。所以要进行多次重放测试此处省略测试过程...经过我的多次测试发现，此处的参数只有第一个是改变的，其他的都是固定不变的。知道了加密参数的位置还有传递参数的内容，我们就可以开始尝试还原加密参数了算法还原：我们新建一个文件，开始还原算法image-20240115094422665构造完最基本的内容之后，我们开始运行，运行之后我们缺什么就补什么image-20240115094640307缺少a函数的定义，在上面就可以发现a函数，加密函数也都在这个自执行函数里面，我们直接把这一块全拿出来image-20240115094747406再次运行说缺少window，我们在文件顶部补上 window=globalimage-20240115094836361这次运行后提示缺少CryptoJs我们知道Cryptojs是个js的加密库，我们在刚才的地方搜索一下看看有没有定义，如果存在的话，我们直接复制下来拿来用即可。image-20240115094908219您才怎么着，还真让我搜到了image-20240115095019367我们直接把这一大串复制下来，粘贴到我们自己的js文件中，然后运行image-20240115095339074这次提示我们缺少encrypt这个函数，继续从源文件中寻找image-20240115095741539可以发现这里很像AES的构造，我们把这一大块扣下来运行试试image-20240115095843440提示我们缺少p，在抠出来的函数顶部可以发现p是u.lib.BlockCipherimage-20240115095934987搜索到BlockCipher的位置，我们继续扣扣扣image-20240115100017297这次提示缺少setMaxDigitsimage-20240115100120103再来搜索image-20240115100151959运行，提示缺少BigInt，可以在上面的图片发现，这个函数就下面image-20240115100244518为了减少操作，我们直接都拉下来，然后运行image-20240115100501683这次不提示缺函数了，提示缺变量。。继续搜索发现 bitsPerDigit = biRadixBitsbiRadixBits就在左边=16，我们定义一下image-20240115100548287image-20240115100628415再次运行，提示缺少这个biHalfRadix，继续搜索呗。。。image-20240115100646786image-20240115100734553还是为了方便，我们直接把这一行的变量定义都复制到下来，一个一个分析太浪费时间了image-20240115100851163再次运行ohhhhhhhhhhhhhhhhhh，出结果了！image-20240115102325516我们写一下python代码模拟提交试试看image-20240115102354088成功！image-20240115102412895后记：参数都搞定了，不搞点事情那肯定不信啊，那就简单写一个歌曲搜索功能吧image-20240115102801872附补环境的参数代码：window = global var maxDigits, ZERO_ARRAY, bigZero, bigOne, dpl10, lr10, hexatrigesimalToChar, hexToChar, highBitMasks, lowBitMasks, biRadixBase = 2, biRadixBits = 16, bitsPerDigit = biRadixBits, biRadix = 65536, biHalfRadix = biRadix >>> 1, biRadixSquared = biRadix * biRadix, maxDigitVal = biRadix - 1, maxInteger = 9999999999999998; setMaxDigits(20), dpl10 = 15, lr10 = biFromNumber(1e15), hexatrigesimalToChar = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"), hexToChar = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"), highBitMasks = new Array(0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535), lowBitMasks = new Array(0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535); var CryptoJS = CryptoJS || function (u, p) { var d = {}, l = d.lib = {}, s = function () { }, t = l.Base = { extend: function (a) { s.prototype = this; var c = new s; a && c.mixIn(a); c.hasOwnProperty("init") || (c.init = function () { c.$super.init.apply(this, arguments) }); c.init.prototype = c; c.$super = this; return c }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, r = l.WordArray = t.extend({ init: function (a, c) { a = this.words = a || []; this.sigBytes = c != p ? c : 4 * a.length }, toString: function (a) { return (a || v).stringify(this) }, concat: function (a) { var c = this.words, e = a.words, j = this.sigBytes; a = a.sigBytes; this.clamp(); if (j % 4) for (var k = 0; k < a; k++) c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4); else if (65535 < e.length) for (k = 0; k < a; k += 4) c[j + k >>> 2] = e[k >>> 2]; else c.push.apply(c, e); this.sigBytes += a; return this }, clamp: function () { var a = this.words, c = this.sigBytes; a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4); a.length = u.ceil(c / 4) }, clone: function () { var a = t.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (var c = [], e = 0; e < a; e += 4) c.push(4294967296 * u.random() | 0); return new r.init(c, a) } }), w = d.enc = {}, v = w.Hex = { stringify: function (a) { var c = a.words; a = a.sigBytes; for (var e = [], j = 0; j < a; j++) { var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255; e.push((k >>> 4).toString(16)); e.push((k & 15).toString(16)) } return e.join("") }, parse: function (a) { for (var c = a.length, e = [], j = 0; j < c; j += 2) e[j >>> 3] |= parseInt(a.substr(j, 2), 16) << 24 - 4 * (j % 8); return new r.init(e, c / 2) } }, b = w.Latin1 = { stringify: function (a) { var c = a.words; a = a.sigBytes; for (var e = [], j = 0; j < a; j++) e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255)); return e.join("") }, parse: function (a) { for (var c = a.length, e = [], j = 0; j < c; j++) e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4); return new r.init(e, c) } }, x = w.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(b.stringify(a))) } catch (c) { throw Error("Malformed UTF-8 data") } }, parse: function (a) { return b.parse(unescape(encodeURIComponent(a))) } }, q = l.BufferedBlockAlgorithm = t.extend({ reset: function () { this.i3x = new r.init; this.yN9E = 0 }, Ay9p: function (a) { "string" == typeof a && (a = x.parse(a)); this.i3x.concat(a); this.yN9E += a.sigBytes }, mX6R: function (a) { var c = this.i3x, e = c.words, j = c.sigBytes, k = this.blockSize, b = j / (4 * k), b = a ? u.ceil(b) : u.max((b | 0) - this.Xy5D, 0); a = b * k; j = u.min(4 * a, j); if (a) { for (var q = 0; q < a; q += k) this.sI7B(e, q); q = e.splice(0, a); c.sigBytes -= j } return new r.init(q, j) }, clone: function () { var a = t.clone.call(this); a.i3x = this.i3x.clone(); return a }, Xy5D: 0 }); l.Hasher = q.extend({ cfg: t.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this.mi6c() }, update: function (a) { this.Ay9p(a); this.mX6R(); return this }, finalize: function (a) { a && this.Ay9p(a); return this.oc6W() }, blockSize: 16, mV6P: function (a) { return function (b, e) { return (new a.init(e)).finalize(b) } }, yO9F: function (a) { return function (b, e) { return (new n.HMAC.init(a, e)).finalize(b) } } }); var n = d.algo = {}; return d }(Math); (function () { var u = CryptoJS, p = u.lib.WordArray; u.enc.Base64 = { stringify: function (d) { var l = d.words, p = d.sigBytes, t = this.bD3x; d.clamp(); d = []; for (var r = 0; r < p; r += 3) for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + .75 * v < p; v++) d.push(t.charAt(w >>> 6 * (3 - v) & 63)); if (l = t.charAt(64)) for (; d.length % 4;) d.push(l); return d.join("") }, parse: function (d) { var l = d.length, s = this.bD3x, t = s.charAt(64); t && (t = d.indexOf(t), -1 != t && (l = t)); for (var t = [], r = 0, w = 0; w < l; w++) if (w % 4) { var v = s.indexOf(d.charAt(w - 1)) << 2 * (w % 4), b = s.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4); t[r >>> 2] |= (v | b) << 24 - 8 * (r % 4); r++ } return p.create(t, r) }, bD3x: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" } })(); (function (u) { function p(b, n, a, c, e, j, k) { b = b + (n & a | ~n & c) + e + k; return (b << j | b >>> 32 - j) + n } function d(b, n, a, c, e, j, k) { b = b + (n & c | a & ~c) + e + k; return (b << j | b >>> 32 - j) + n } function l(b, n, a, c, e, j, k) { b = b + (n ^ a ^ c) + e + k; return (b << j | b >>> 32 - j) + n } function s(b, n, a, c, e, j, k) { b = b + (a ^ (n | ~c)) + e + k; return (b << j | b >>> 32 - j) + n } for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) b[x] = 4294967296 * u.abs(u.sin(x + 1)) | 0; r = r.MD5 = v.extend({ mi6c: function () { this.cT3x = new w.init([1732584193, 4023233417, 2562383102, 271733878]) }, sI7B: function (q, n) { for (var a = 0; 16 > a; a++) { var c = n + a, e = q[c]; q[c] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360 } var a = this.cT3x.words, c = q[n + 0], e = q[n + 1], j = q[n + 2], k = q[n + 3], z = q[n + 4], r = q[n + 5], t = q[n + 6], w = q[n + 7], v = q[n + 8], A = q[n + 9], B = q[n + 10], C = q[n + 11], u = q[n + 12], D = q[n + 13], E = q[n + 14], x = q[n + 15], f = a[0], m = a[1], g = a[2], h = a[3], f = p(f, m, g, h, c, 7, b[0]), h = p(h, f, m, g, e, 12, b[1]), g = p(g, h, f, m, j, 17, b[2]), m = p(m, g, h, f, k, 22, b[3]), f = p(f, m, g, h, z, 7, b[4]), h = p(h, f, m, g, r, 12, b[5]), g = p(g, h, f, m, t, 17, b[6]), m = p(m, g, h, f, w, 22, b[7]), f = p(f, m, g, h, v, 7, b[8]), h = p(h, f, m, g, A, 12, b[9]), g = p(g, h, f, m, B, 17, b[10]), m = p(m, g, h, f, C, 22, b[11]), f = p(f, m, g, h, u, 7, b[12]), h = p(h, f, m, g, D, 12, b[13]), g = p(g, h, f, m, E, 17, b[14]), m = p(m, g, h, f, x, 22, b[15]), f = d(f, m, g, h, e, 5, b[16]), h = d(h, f, m, g, t, 9, b[17]), g = d(g, h, f, m, C, 14, b[18]), m = d(m, g, h, f, c, 20, b[19]), f = d(f, m, g, h, r, 5, b[20]), h = d(h, f, m, g, B, 9, b[21]), g = d(g, h, f, m, x, 14, b[22]), m = d(m, g, h, f, z, 20, b[23]), f = d(f, m, g, h, A, 5, b[24]), h = d(h, f, m, g, E, 9, b[25]), g = d(g, h, f, m, k, 14, b[26]), m = d(m, g, h, f, v, 20, b[27]), f = d(f, m, g, h, D, 5, b[28]), h = d(h, f, m, g, j, 9, b[29]), g = d(g, h, f, m, w, 14, b[30]), m = d(m, g, h, f, u, 20, b[31]), f = l(f, m, g, h, r, 4, b[32]), h = l(h, f, m, g, v, 11, b[33]), g = l(g, h, f, m, C, 16, b[34]), m = l(m, g, h, f, E, 23, b[35]), f = l(f, m, g, h, e, 4, b[36]), h = l(h, f, m, g, z, 11, b[37]), g = l(g, h, f, m, w, 16, b[38]), m = l(m, g, h, f, B, 23, b[39]), f = l(f, m, g, h, D, 4, b[40]), h = l(h, f, m, g, c, 11, b[41]), g = l(g, h, f, m, k, 16, b[42]), m = l(m, g, h, f, t, 23, b[43]), f = l(f, m, g, h, A, 4, b[44]), h = l(h, f, m, g, u, 11, b[45]), g = l(g, h, f, m, x, 16, b[46]), m = l(m, g, h, f, j, 23, b[47]), f = s(f, m, g, h, c, 6, b[48]), h = s(h, f, m, g, w, 10, b[49]), g = s(g, h, f, m, E, 15, b[50]), m = s(m, g, h, f, r, 21, b[51]), f = s(f, m, g, h, u, 6, b[52]), h = s(h, f, m, g, k, 10, b[53]), g = s(g, h, f, m, B, 15, b[54]), m = s(m, g, h, f, e, 21, b[55]), f = s(f, m, g, h, v, 6, b[56]), h = s(h, f, m, g, x, 10, b[57]), g = s(g, h, f, m, t, 15, b[58]), m = s(m, g, h, f, D, 21, b[59]), f = s(f, m, g, h, z, 6, b[60]), h = s(h, f, m, g, C, 10, b[61]), g = s(g, h, f, m, j, 15, b[62]), m = s(m, g, h, f, A, 21, b[63]); a[0] = a[0] + f | 0; a[1] = a[1] + m | 0; a[2] = a[2] + g | 0; a[3] = a[3] + h | 0 }, oc6W: function () { var b = this.i3x, n = b.words, a = 8 * this.yN9E, c = 8 * b.sigBytes; n[c >>> 5] |= 128 << 24 - c % 32; var e = u.floor(a / 4294967296); n[(c + 64 >>> 9 << 4) + 15] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360; n[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360; b.sigBytes = 4 * (n.length + 1); this.mX6R(); b = this.cT3x; n = b.words; for (a = 0; 4 > a; a++) c = n[a], n[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360; return b }, clone: function () { var b = v.clone.call(this); b.cT3x = this.cT3x.clone(); return b } }); t.MD5 = v.mV6P(r); t.HmacMD5 = v.yO9F(r) })(Math); (function () { var u = CryptoJS, p = u.lib, d = p.Base, l = p.WordArray, p = u.algo, s = p.EvpKDF = d.extend({ cfg: d.extend({ keySize: 4, hasher: p.MD5, iterations: 1 }), init: function (d) { this.cfg = this.cfg.extend(d) }, compute: function (d, r) { for (var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) { n && s.update(n); var n = s.update(d).finalize(r); s.reset(); for (var a = 1; a < p; a++) n = s.finalize(n), s.reset(); b.concat(n) } b.sigBytes = 4 * q; return b } }); u.EvpKDF = function (d, l, p) { return s.create(p).compute(d, l) } })(); CryptoJS.lib.Cipher || function (u) { var p = CryptoJS, d = p.lib, l = d.Base, s = d.WordArray, t = d.BufferedBlockAlgorithm, r = p.enc.Base64, w = p.algo.EvpKDF, v = d.Cipher = t.extend({ cfg: l.extend(), createEncryptor: function (e, a) { return this.create(this.Hm4q, e, a) }, createDecryptor: function (e, a) { return this.create(this.brN4R, e, a) }, init: function (e, a, b) { this.cfg = this.cfg.extend(b); this.Xx5C = e; this.J3x = a; this.reset() }, reset: function () { t.reset.call(this); this.mi6c() }, process: function (e) { this.Ay9p(e); return this.mX6R() }, finalize: function (e) { e && this.Ay9p(e); return this.oc6W() }, keySize: 4, ivSize: 4, Hm4q: 1, brN4R: 2, mV6P: function (e) { return { encrypt: function (b, k, d) { return ("string" == typeof k ? c : a).encrypt(e, b, k, d) }, decrypt: function (b, k, d) { return ("string" == typeof k ? c : a).decrypt(e, b, k, d) } } } }); d.StreamCipher = v.extend({ oc6W: function () { return this.mX6R(!0) }, blockSize: 1 }); var b = p.mode = {}, x = function (e, a, b) { var c = this.sH7A; c ? this.sH7A = u : c = this.CA0x; for (var d = 0; d < b; d++) e[a + d] ^= c[d] }, q = (d.BlockCipherMode = l.extend({ createEncryptor: function (e, a) { return this.Encryptor.create(e, a) }, createDecryptor: function (e, a) { return this.Decryptor.create(e, a) }, init: function (e, a) { this.vC8u = e; this.sH7A = a } })).extend(); q.Encryptor = q.extend({ processBlock: function (e, a) { var b = this.vC8u, c = b.blockSize; x.call(this, e, a, c); b.encryptBlock(e, a); this.CA0x = e.slice(a, a + c) } }); q.Decryptor = q.extend({ processBlock: function (e, a) { var b = this.vC8u, c = b.blockSize, d = e.slice(a, a + c); b.decryptBlock(e, a); x.call(this, e, a, c); this.CA0x = d } }); b = b.CBC = q; q = (p.pad = {}).Pkcs7 = { pad: function (a, b) { for (var c = 4 * b, c = c - a.sigBytes % c, d = c << 24 | c << 16 | c << 8 | c, l = [], n = 0; n < c; n += 4) l.push(d); c = s.create(l, c); a.concat(c) }, unpad: function (a) { a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255 } }; d.BlockCipher = v.extend({ cfg: v.cfg.extend({ mode: b, padding: q }), reset: function () { v.reset.call(this); var a = this.cfg, b = a.iv, a = a.mode; if (this.Xx5C == this.Hm4q) var c = a.createEncryptor; else c = a.createDecryptor, this.Xy5D = 1; this.eR4V = c.call(a, this, b && b.words) }, sI7B: function (a, b) { this.eR4V.processBlock(a, b) }, oc6W: function () { var a = this.cfg.padding; if (this.Xx5C == this.Hm4q) { a.pad(this.i3x, this.blockSize); var b = this.mX6R(!0) } else b = this.mX6R(!0), a.unpad(b); return b }, blockSize: 4 }); var n = d.CipherParams = l.extend({ init: function (a) { this.mixIn(a) }, toString: function (a) { return (a || this.formatter).stringify(this) } }), b = (p.format = {}).OpenSSL = { stringify: function (a) { var b = a.ciphertext; a = a.salt; return (a ? s.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r) }, parse: function (a) { a = r.parse(a); var b = a.words; if (1398893684 == b[0] && 1701076831 == b[1]) { var c = s.create(b.slice(2, 4)); b.splice(0, 4); a.sigBytes -= 16 } return n.create({ ciphertext: a, salt: c }) } }, a = d.SerializableCipher = l.extend({ cfg: l.extend({ format: b }), encrypt: function (a, b, c, d) { d = this.cfg.extend(d); var l = a.createEncryptor(c, d); b = l.finalize(b); l = l.cfg; return n.create({ ciphertext: b, key: c, iv: l.iv, algorithm: a, mode: l.mode, padding: l.padding, blockSize: a.blockSize, formatter: d.format }) }, decrypt: function (a, b, c, d) { d = this.cfg.extend(d); b = this.Hc4g(b, d.format); return a.createDecryptor(c, d).finalize(b.ciphertext) }, Hc4g: function (a, b) { return "string" == typeof a ? b.parse(a, this) : a } }), p = (p.kdf = {}).OpenSSL = { execute: function (a, b, c, d) { d || (d = s.random(8)); a = w.create({ keySize: b + c }).compute(a, d); c = s.create(a.words.slice(b), 4 * c); a.sigBytes = 4 * b; return n.create({ key: a, iv: c, salt: d }) } }, c = d.PasswordBasedCipher = a.extend({ cfg: a.cfg.extend({ kdf: p }), encrypt: function (b, c, d, l) { l = this.cfg.extend(l); d = l.kdf.execute(d, b.keySize, b.ivSize); l.iv = d.iv; b = a.encrypt.call(this, b, c, d.key, l); b.mixIn(d); return b }, decrypt: function (b, c, d, l) { l = this.cfg.extend(l); c = this.Hc4g(c, l.format); d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt); l.iv = d.iv; return a.decrypt.call(this, b, c, d.key, l) } }) }(); (function () { for (var u = CryptoJS, p = u.lib.BlockCipher, d = u.algo, l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++) a[c] = 128 > c ? c << 1 : c << 1 ^ 283; for (var e = 0, j = 0, c = 0; 256 > c; c++) { var k = j ^ j << 1 ^ j << 2 ^ j << 3 ^ j << 4, k = k >>> 8 ^ k & 255 ^ 99; l[e] = k; s[k] = e; var z = a[e], F = a[z], G = a[F], y = 257 * a[k] ^ 16843008 * k; t[e] = y << 24 | y >>> 8; r[e] = y << 16 | y >>> 16; w[e] = y << 8 | y >>> 24; v[e] = y; y = 16843009 * G ^ 65537 * F ^ 257 * z ^ 16843008 * e; b[k] = y << 24 | y >>> 8; x[k] = y << 16 | y >>> 16; q[k] = y << 8 | y >>> 24; n[k] = y; e ? (e = z ^ a[a[a[G ^ z]]], j ^= a[a[j]]) : e = j = 1 } var H = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], d = d.AES = p.extend({ mi6c: function () { for (var a = this.J3x, c = a.words, d = a.sigBytes / 4, a = 4 * ((this.ZE5J = d + 6) + 1), e = this.bsk5p = [], j = 0; j < a; j++) if (j < d) e[j] = c[j]; else { var k = e[j - 1]; j % d ? 6 < d && 4 == j % d && (k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255]) : (k = k << 8 | k >>> 24, k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255], k ^= H[j / d | 0] << 24); e[j] = e[j - d] ^ k } c = this.bsl5q = []; for (d = 0; d < a; d++) j = a - d, k = d % 4 ? e[j] : e[j - 4], c[d] = 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[k >>> 16 & 255]] ^ q[l[k >>> 8 & 255]] ^ n[l[k & 255]] }, encryptBlock: function (a, b) { this.Cx0x(a, b, this.bsk5p, t, r, w, v, l) }, decryptBlock: function (a, c) { var d = a[c + 1]; a[c + 1] = a[c + 3]; a[c + 3] = d; this.Cx0x(a, c, this.bsl5q, b, x, q, n, s); d = a[c + 1]; a[c + 1] = a[c + 3]; a[c + 3] = d }, Cx0x: function (a, b, c, d, e, j, l, f) { for (var m = this.ZE5J, g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++) var q = d[g >>> 24] ^ e[h >>> 16 & 255] ^ j[k >>> 8 & 255] ^ l[n & 255] ^ c[p++], s = d[h >>> 24] ^ e[k >>> 16 & 255] ^ j[n >>> 8 & 255] ^ l[g & 255] ^ c[p++], t = d[k >>> 24] ^ e[n >>> 16 & 255] ^ j[g >>> 8 & 255] ^ l[h & 255] ^ c[p++], n = d[n >>> 24] ^ e[g >>> 16 & 255] ^ j[h >>> 8 & 255] ^ l[k & 255] ^ c[p++], g = q, h = s, k = t; q = (f[g >>> 24] << 24 | f[h >>> 16 & 255] << 16 | f[k >>> 8 & 255] << 8 | f[n & 255]) ^ c[p++]; s = (f[h >>> 24] << 24 | f[k >>> 16 & 255] << 16 | f[n >>> 8 & 255] << 8 | f[g & 255]) ^ c[p++]; t = (f[k >>> 24] << 24 | f[n >>> 16 & 255] << 16 | f[g >>> 8 & 255] << 8 | f[h & 255]) ^ c[p++]; n = (f[n >>> 24] << 24 | f[g >>> 16 & 255] << 16 | f[h >>> 8 & 255] << 8 | f[k & 255]) ^ c[p++]; a[b] = q; a[b + 1] = s; a[b + 2] = t; a[b + 3] = n }, keySize: 8 }); u.AES = p.mV6P(d) })(); function RSAKeyPair(a, b, c) { this.e = biFromHex(a), this.d = biFromHex(b), this.m = biFromHex(c), this.chunkSize = 2 * biHighIndex(this.m), this.radix = 16, this.barrett = new BarrettMu(this.m) } function twoDigit(a) { return (10 > a ? "0" : "") + String(a) } function encryptedString(a, b) { for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e;) c[e] = b.charCodeAt(e), e++; for (; 0 != c.length % a.chunkSize;) c[e++] = 0; for (f = c.length, g = "", e = 0; f > e; e += a.chunkSize) { for (j = new BigInt, h = 0, i = e; i < e + a.chunkSize; ++h) j.digits[h] = c[i++], j.digits[h] += c[i++] << 8; k = a.barrett.powMod(j, a.e), l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix), g += l + " " } return g.substring(0, g.length - 1) } function decryptedString(a, b) { var e, f, g, h, c = b.split(" "), d = ""; for (e = 0; e < c.length; ++e) for (h = 16 == a.radix ? biFromHex(c[e]) : biFromString(c[e], a.radix), g = a.barrett.powMod(h, a.d), f = 0; f <= biHighIndex(g); ++f) d += String.fromCharCode(255 & g.digits[f], g.digits[f] >> 8); return 0 == d.charCodeAt(d.length - 1) && (d = d.substring(0, d.length - 1)), d } function setMaxDigits(a) { maxDigits = a, ZERO_ARRAY = new Array(maxDigits); for (var b = 0; b < ZERO_ARRAY.length; b++) ZERO_ARRAY[b] = 0; bigZero = new BigInt, bigOne = new BigInt, bigOne.digits[0] = 1 } function BigInt(a) { this.digits = "boolean" == typeof a && 1 == a ? null : ZERO_ARRAY.slice(0), this.isNeg = !1 } function biFromDecimal(a) { for (var d, e, f, b = "-" == a.charAt(0), c = b ? 1 : 0; c < a.length && "0" == a.charAt(c);) ++c; if (c == a.length) d = new BigInt; else { for (e = a.length - c, f = e % dpl10, 0 == f && (f = dpl10), d = biFromNumber(Number(a.substr(c, f))), c += f; c < a.length;) d = biAdd(biMultiply(d, lr10), biFromNumber(Number(a.substr(c, dpl10)))), c += dpl10; d.isNeg = b } return d } function biCopy(a) { var b = new BigInt(!0); return b.digits = a.digits.slice(0), b.isNeg = a.isNeg, b } function biFromNumber(a) { var c, b = new BigInt; for (b.isNeg = 0 > a, a = Math.abs(a), c = 0; a > 0;) b.digits[c++] = a & maxDigitVal, a >>= biRadixBits; return b } function reverseStr(a) { var c, b = ""; for (c = a.length - 1; c > -1; --c) b += a.charAt(c); return b } function biToString(a, b) { var d, e, c = new BigInt; for (c.digits[0] = b, d = biDivideModulo(a, c), e = hexatrigesimalToChar[d[1].digits[0]]; 1 == biCompare(d[0], bigZero);) d = biDivideModulo(d[0], c), digit = d[1].digits[0], e += hexatrigesimalToChar[d[1].digits[0]]; return (a.isNeg ? "-" : "") + reverseStr(e) } function biToDecimal(a) { var c, d, b = new BigInt; for (b.digits[0] = 10, c = biDivideModulo(a, b), d = String(c[1].digits[0]); 1 == biCompare(c[0], bigZero);) c = biDivideModulo(c[0], b), d += String(c[1].digits[0]); return (a.isNeg ? "-" : "") + reverseStr(d) } function digitToHex(a) { var b = 15, c = ""; for (i = 0; 4 > i; ++i) c += hexToChar[a & b], a >>>= 4; return reverseStr(c) } function biToHex(a) { var d, b = ""; for (biHighIndex(a), d = biHighIndex(a); d > -1; --d) b += digitToHex(a.digits[d]); return b } function charToHex(a) { var h, b = 48, c = b + 9, d = 97, e = d + 25, f = 65, g = 90; return h = a >= b && c >= a ? a - b : a >= f && g >= a ? 10 + a - f : a >= d && e >= a ? 10 + a - d : 0 } function hexToDigit(a) { var d, b = 0, c = Math.min(a.length, 4); for (d = 0; c > d; ++d) b <<= 4, b |= charToHex(a.charCodeAt(d)); return b } function biFromHex(a) { var d, e, b = new BigInt, c = a.length; for (d = c, e = 0; d > 0; d -= 4, ++e) b.digits[e] = hexToDigit(a.substr(Math.max(d - 4, 0), Math.min(d, 4))); return b } function biFromString(a, b) { var g, h, i, j, c = "-" == a.charAt(0), d = c ? 1 : 0, e = new BigInt, f = new BigInt; for (f.digits[0] = 1, g = a.length - 1; g >= d; g--) h = a.charCodeAt(g), i = charToHex(h), j = biMultiplyDigit(f, i), e = biAdd(e, j), f = biMultiplyDigit(f, b); return e.isNeg = c, e } function biDump(a) { return (a.isNeg ? "-" : "") + a.digits.join(" ") } function biAdd(a, b) { var c, d, e, f; if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg, c = biSubtract(a, b), b.isNeg = !b.isNeg; else { for (c = new BigInt, d = 0, f = 0; f < a.digits.length; ++f) e = a.digits[f] + b.digits[f] + d, c.digits[f] = 65535 & e, d = Number(e >= biRadix); c.isNeg = a.isNeg } return c } function biSubtract(a, b) { var c, d, e, f; if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg, c = biAdd(a, b), b.isNeg = !b.isNeg; else { for (c = new BigInt, e = 0, f = 0; f < a.digits.length; ++f) d = a.digits[f] - b.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d); if (-1 == e) { for (e = 0, f = 0; f < a.digits.length; ++f) d = 0 - c.digits[f] + e, c.digits[f] = 65535 & d, c.digits[f] < 0 && (c.digits[f] += biRadix), e = 0 - Number(0 > d); c.isNeg = !a.isNeg } else c.isNeg = a.isNeg } return c } function biHighIndex(a) { for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];) --b; return b } function biNumBits(a) { var e, b = biHighIndex(a), c = a.digits[b], d = (b + 1) * bitsPerDigit; for (e = d; e > d - bitsPerDigit && 0 == (32768 & c); --e) c <<= 1; return e } function biMultiply(a, b) { var d, h, i, k, c = new BigInt, e = biHighIndex(a), f = biHighIndex(b); for (k = 0; f >= k; ++k) { for (d = 0, i = k, j = 0; e >= j; ++j, ++i) h = c.digits[i] + a.digits[j] * b.digits[k] + d, c.digits[i] = h & maxDigitVal, d = h >>> biRadixBits; c.digits[k + e + 1] = d } return c.isNeg = a.isNeg != b.isNeg, c } function biMultiplyDigit(a, b) { var c, d, e, f; for (result = new BigInt, c = biHighIndex(a), d = 0, f = 0; c >= f; ++f) e = result.digits[f] + a.digits[f] * b + d, result.digits[f] = e & maxDigitVal, d = e >>> biRadixBits; return result.digits[1 + c] = d, result } function arrayCopy(a, b, c, d, e) { var g, h, f = Math.min(b + e, a.length); for (g = b, h = d; f > g; ++g, ++h) c[h] = a[g] } function biShiftLeft(a, b) { var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt; for (arrayCopy(a.digits, 0, d.digits, c, d.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = d.digits.length - 1, h = g - 1; g > 0; --g, --h) d.digits[g] = d.digits[g] << e & maxDigitVal | (d.digits[h] & highBitMasks[e]) >>> f; return d.digits[0] = d.digits[g] << e & maxDigitVal, d.isNeg = a.isNeg, d } function biShiftRight(a, b) { var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt; for (arrayCopy(a.digits, c, d.digits, 0, a.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = 0, h = g + 1; g < d.digits.length - 1; ++g, ++h) d.digits[g] = d.digits[g] >>> e | (d.digits[h] & lowBitMasks[e]) << f; return d.digits[d.digits.length - 1] >>>= e, d.isNeg = a.isNeg, d } function biMultiplyByRadixPower(a, b) { var c = new BigInt; return arrayCopy(a.digits, 0, c.digits, b, c.digits.length - b), c } function biDivideByRadixPower(a, b) { var c = new BigInt; return arrayCopy(a.digits, b, c.digits, 0, c.digits.length - b), c } function biModuloByRadixPower(a, b) { var c = new BigInt; return arrayCopy(a.digits, 0, c.digits, 0, b), c } function biCompare(a, b) { if (a.isNeg != b.isNeg) return 1 - 2 * Number(a.isNeg); for (var c = a.digits.length - 1; c >= 0; --c) if (a.digits[c] != b.digits[c]) return a.isNeg ? 1 - 2 * Number(a.digits[c] > b.digits[c]) : 1 - 2 * Number(a.digits[c] < b.digits[c]); return 0 } function biDivideModulo(a, b) { var f, g, h, i, j, k, l, m, n, o, p, q, r, s, c = biNumBits(a), d = biNumBits(b), e = b.isNeg; if (d > c) return a.isNeg ? (f = biCopy(bigOne), f.isNeg = !b.isNeg, a.isNeg = !1, b.isNeg = !1, g = biSubtract(b, a), a.isNeg = !0, b.isNeg = e) : (f = new BigInt, g = biCopy(a)), new Array(f, g); for (f = new BigInt, g = a, h = Math.ceil(d / bitsPerDigit) - 1, i = 0; b.digits[h] < biHalfRadix;) b = biShiftLeft(b, 1), ++i, ++d, h = Math.ceil(d / bitsPerDigit) - 1; for (g = biShiftLeft(g, i), c += i, j = Math.ceil(c / bitsPerDigit) - 1, k = biMultiplyByRadixPower(b, j - h); -1 != biCompare(g, k);) ++f.digits[j - h], g = biSubtract(g, k); for (l = j; l > h; --l) { for (m = l >= g.digits.length ? 0 : g.digits[l], n = l - 1 >= g.digits.length ? 0 : g.digits[l - 1], o = l - 2 >= g.digits.length ? 0 : g.digits[l - 2], p = h >= b.digits.length ? 0 : b.digits[h], q = h - 1 >= b.digits.length ? 0 : b.digits[h - 1], f.digits[l - h - 1] = m == p ? maxDigitVal : Math.floor((m * biRadix + n) / p), r = f.digits[l - h - 1] * (p * biRadix + q), s = m * biRadixSquared + (n * biRadix + o); r > s;) --f.digits[l - h - 1], r = f.digits[l - h - 1] * (p * biRadix | q), s = m * biRadix * biRadix + (n * biRadix + o); k = biMultiplyByRadixPower(b, l - h - 1), g = biSubtract(g, biMultiplyDigit(k, f.digits[l - h - 1])), g.isNeg && (g = biAdd(g, k), --f.digits[l - h - 1]) } return g = biShiftRight(g, i), f.isNeg = a.isNeg != e, a.isNeg && (f = e ? biAdd(f, bigOne) : biSubtract(f, bigOne), b = biShiftRight(b, i), g = biSubtract(b, g)), 0 == g.digits[0] && 0 == biHighIndex(g) && (g.isNeg = !1), new Array(f, g) } function biDivide(a, b) { return biDivideModulo(a, b)[0] } function biModulo(a, b) { return biDivideModulo(a, b)[1] } function biMultiplyMod(a, b, c) { return biModulo(biMultiply(a, b), c) } function biPow(a, b) { for (var c = bigOne, d = a; ;) { if (0 != (1 & b) && (c = biMultiply(c, d)), b >>= 1, 0 == b) break; d = biMultiply(d, d) } return c } function biPowMod(a, b, c) { for (var d = bigOne, e = a, f = b; ;) { if (0 != (1 & f.digits[0]) && (d = biMultiplyMod(d, e, c)), f = biShiftRight(f, 1), 0 == f.digits[0] && 0 == biHighIndex(f)) break; e = biMultiplyMod(e, e, c) } return d } function BarrettMu(a) { this.modulus = biCopy(a), this.k = biHighIndex(this.modulus) + 1; var b = new BigInt; b.digits[2 * this.k] = 1, this.mu = biDivide(b, this.modulus), this.bkplus1 = new BigInt, this.bkplus1.digits[this.k + 1] = 1, this.modulo = BarrettMu_modulo, this.multiplyMod = BarrettMu_multiplyMod, this.powMod = BarrettMu_powMod } function BarrettMu_modulo(a) { var i, b = biDivideByRadixPower(a, this.k - 1), c = biMultiply(b, this.mu), d = biDivideByRadixPower(c, this.k + 1), e = biModuloByRadixPower(a, this.k + 1), f = biMultiply(d, this.modulus), g = biModuloByRadixPower(f, this.k + 1), h = biSubtract(e, g); for (h.isNeg && (h = biAdd(h, this.bkplus1)), i = biCompare(h, this.modulus) >= 0; i;) h = biSubtract(h, this.modulus), i = biCompare(h, this.modulus) >= 0; return h } function BarrettMu_multiplyMod(a, b) { var c = biMultiply(a, b); return this.modulo(c) } function BarrettMu_powMod(a, b) { var d, e, c = new BigInt; for (c.digits[0] = 1, d = a, e = b; ;) { if (0 != (1 & e.digits[0]) && (c = this.multiplyMod(c, d)), e = biShiftRight(e, 1), 0 == e.digits[0] && 0 == biHighIndex(e)) break; d = this.multiplyMod(d, d) } return c } function a(a) { var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = ""; for (d = 0; a > d; d += 1) e = Math.random() * b.length, e = Math.floor(e), c += b.charAt(e); return c } function b(a, b) { var c = CryptoJS.enc.Utf8.parse(b), d = CryptoJS.enc.Utf8.parse("0102030405060708"), e = CryptoJS.enc.Utf8.parse(a), f = CryptoJS.AES.encrypt(e, c, { iv: d, mode: CryptoJS.mode.CBC }); return f.toString() } function c(a, b, c) { var d, e; return setMaxDigits(131), d = new RSAKeyPair(b, "", c), e = encryptedString(d, a) } function d(d, e, f, g) { var h = {}, i = a(16); return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h } function e(a, b, d, e) { var f = {}; return f.encText = c(a + e, b, d), f } window.asrsea = d, window.ecnonasr = e params = '{"s":"你好","limit":"8","csrf_token":""}' num = '010001' longStr = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' shortStr = '0CoJUm6Qyw8W8jud' console.log(d(params, num, longStr, shortStr))
本篇文章作者ius，本文属i春秋原创奖励计划，未经许可禁止转载原文地址：https://bbs.ichunqiu.com/thread-63786-1-1.html某安信登录机制逆向分析我们先随意输入账号密码进行测试image-20240105222707586点击登录后出现极验滑块image-20240105155536009极验三代水平不高，直接秒。image-20240105160252840滑块请求成功之后，会向该链接请求公钥https://user.skyeye.qianxin.com/api/v1/encrypt/public-keyimage-20240105155648767查看我们的登录请求image-20240105155946141发现请求是XHR，先用XHR断个点image-20240105155933517断住之后逐步跟栈image-20240105161940048找到key_iv的位置image-20240105160622604f是请求来的公钥image-20240105161902818r=s(),进入s函数，随机生成的image-20240105161705319image-20240105162031991s = function() { for (var e = "", a = 0; a < 32; a++) e += Math.floor(16 * Math.random()).toString(16); return e }然后进入l函数查看,简单的加密，把f替换成crypto即可image-20240105162058606b是个 RSA 对象，原型链存放着加密方法 此处使用的是JSEncryptimage-20240105162922106直接用nodejs实现image-20240105203424759密码加密image-20240105185024165image-20240105171337946把代码抠出来，运行，一模一样image-20240105222837524参数都解决完了，整理一下代码image-20240105202202166运行测试看看image-20240105222959444登录成功image-20240105202347721总体来说，该网站登录流程比较简单，水平不高。下面附上代码：const crypto = require("crypto-js"); window = global const NodeRSA = require('jsencrypt'); function short() { for (var e = "", a = 0; a < 32; a++) e += Math.floor(16 * Math.random()).toString(16); return e } function password(e, a) { var c = a.substring(0, 16); a = crypto.enc.Utf8.parse(a), c = crypto.enc.Utf8.parse(c), e = "string" === typeof e ? e : JSON.stringify(e); var t = crypto.AES.encrypt(e, a, { iv: c, mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7 }); return t.toString() } function key_iv(e, a) { var c = a.substring(0, 16); a = crypto.enc.Utf8.parse(a), c = crypto.enc.Utf8.parse(c); var t = { key: crypto.enc.Base64.stringify(a), iv: crypto.enc.Base64.stringify(c) }; t = JSON.stringify(t) var aa = new NodeRSA() aa.setPublicKey(e); return aa.encrypt(t) } import json import execjs import requests account="11111111111" password="111111" headers = { 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Cookie': 'next=http%3A//forum.***.net/btlogin%3FredirectPath%3Dhttps%3A//forum.***.net/; wzws_sessionid=gjA1NjM4N6Bll33vgTljMWNlMoAyNDA4OjgyMTU6NzIxOToxZTUwOjk1ZDpkYjhlOjhlYzk6ODgzZA==; User-Center=3e7f3775-4f57-46d8-91ec-91adcb46b0bd; User-Center=3e7f3775-4f57-46d8-91ec-91adcb46b0bd', 'Pragma': 'no-cache', 'Referer': 'https://user.skyeye.***.com/user/sign-in?next=http://forum.***.net/btlogin?redirectPath=https://forum.butian.net/', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } def get_pem(): url = "https://user.skyeye.***.com/api/v1/encrypt/public-key" response = requests.request("GET", url, headers=headers) return response.json()["public_key"] yzm = eval(requests.get("https://www.illusory.cn/api/qianxin.php").text) with open("1.js","r",encoding="utf-8") as f: file_data = f.read() cxk = execjs.compile(file_data) short = cxk.call("short") pwd = cxk.call("password",password,short) key = cxk.call("key_iv",get_pem(),short) # exit() url = "https://user.skyeye.***.com/api/v1/sign-in" payload = json.dumps({ "account": account, "password": pwd, "geetest_challenge": yzm[1], "geetest_validate": json.loads(yzm[0])["validate"], "geetest_seccode": json.loads(yzm[0])["validate"]+"|jordan", "next": "http://forum.***.net/btlogin?redirectPath=https://forum.***.net/", "custom_callback_params": { "redirectPath": "https://forum.***.net/" }, "key_iv": key, "csrf_token": "1704457269##ffcf2ba524ba000e09ef4decc5bd632b7720f622" }) headers = { 'Accept': 'application/json', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Cookie': 'next=http%3A//forum.***.net/btlogin%3FredirectPath%3Dhttps%3A//forum.butian.net/; wzws_sessionid=gjA1NjM4N6Bll33vgTljMWNlMoAyNDA4OjgyMTU6NzIxOToxZTUwOjk1ZDpkYjhlOjhlYzk6ODgzZA==; User-Center=3e7f3775-4f57-46d8-91ec-91adcb46b0bd; User-Center=3e7f3775-4f57-46d8-91ec-91adcb46b0bd;csrf_token=1704457269##ffcf2ba524ba000e09ef4decc5bd632b7720f622', 'Origin': 'https://user.skyeye.***.com', 'Pragma': 'no-cache', 'Referer': 'https://user.skyeye.***.com/user/sign-in?next=http://forum.***.net/btlogin?redirectPath=https://forum.butian.net/', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } response = requests.request("POST", url, headers=headers, data=payload) res = response.json() if res["status"]==200: result=requests.get(res["redirect"], headers=headers) print(result.cookies) with open('cookies.json', 'w') as cookie_file: json.dump(result.cookies.get_dict(), cookie_file) 自动登录都有了，顺便写个签到脚本吧import json import requests from lxml import etree session = requests.Session() url = "https://forum.***.net/" headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'referer': 'https://forum.***.net/sign', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } with open('cookies.json', 'r') as cookie_file: cookies = json.load(cookie_file) session.cookies.update(cookies) response = session.request("GET", url, headers=headers) xx = etree.HTML(response.text) xxx = xx.xpath('/html/head/meta[@name="csrf-token"]/@content')[0] url = "https://forum.***.net/sign" response = session.request("POST", url, headers=headers, data='_token='+xxx) print(response)完整代码请从github获取->https://github.com/xvhuan/qianxin
本篇文章作者 ius，本文属 i 春秋原创奖励计划，未经许可禁止转载原文地址：https://bbs.ichunqiu.com/thread-63784-1-1.html微博反爬机制逆向分析前言相信每个接触爬虫的人，肯定会对微博进行一点点的骚操作。但是我们在对微博进行请求的时候，微博是会对cookie校验的，而我们在模拟操作的时候，是直接把浏览器的cookie进行copy，虽然我没有测试过这个cookie的有效期，但我觉得这样只适合日常测试，教学等。不能实现一劳永逸。当我们没有携带cookie进行微博的访问时，会出现Sina Visitor System字样的标题，如果我们是正常的浏览器访问的话，这个空白页面会一闪而逝，然后跳转到微博界面。所以我们可以确定，这个页面就是微博的反爬机制，我们的研究也就从此处开始。正文测试链接：https://s.weibo.com/weibo?q=%E8%9C%98%E8%9B%9B%E4%BE%A0打开一个无痕界面，在F12中打开 停用JavaScript，因为空白界面一闪而逝，我们要先停掉跳转，方便分析。访问链接，我们看到了这个反爬页面的全貌，此处空白是空白页的颜色，，，我们对此处断点，断点后再刚才的地方关掉停用JavaScript，刷新界面，发现断点成功我们的目的是找到生成cookie的位置，知道目的之后，我们开始逐步分析走完html发现进入到了这个js文件内，微博人挺好的，还把每块代码的作用给注释好了虽然我很想把每一步的调试都发出来，但是实际上大部分的操作都没有记录的意义。。。所以我就把关键的部分写出来，节省大家的时间。当我们断点跟到此处的时候，我们发现了设置cookie的代码（其实不单步调试也可以，直接搜cookie也能搜出来。但是在我们逆向之前，我们并不清楚这类代码有没有被混淆。。。所以在最开始我并没有搜索关键字。。）我们向上找堆栈，看看cookie是如何生成的。嘿，您猜怎么着！看到此处的我大吃一惊，因为这并不是js代码。。反而是一个请求返回内容然后继续向上看，跟到了刚才的空白页中。好好好好，你家cookie不从本地生成从网络请求是吧！此处省略无数个点既然知道是从网络请求的cookie了，我们就打开网络面板，揭开他神秘的面纱:)这样看太费劲了，我们copy到postman里分析，同时也是为了方便调试ctrl+o直接把curl甩进去，接着我们请求一下，可以看到请求成功的cookie正安详的躺着。POST的内容也是固定值，看样子没有什么要分析的我们点击右侧边栏的<> 然后转换成python语言，放到ide里好好拷打一番ok，不负众望，我们也是请求成功然后我们使用正则，将cookie中的SUB、SUBP、tid提取出来，然后我们再次运行没有问题，我们包装成函数，方便调用，顺便改个文件名方便导入下面进行测试看看，先导进来看看能不能用请求正常，我们用刚才的测试链接进行访问试试访问失败了？？？！！！！在这里我又进行了无数次的调试，抓包。结果最终发现。。。是这里的cookie名称写错了，应该大写为SUB、SUBP。Tips：tid在后来测试中发现没用使用，然后我就删掉了修改完之后，我们再来测试一下，不出意外的话，这次是真没出意外请求成功了！至此，我们的微博反爬机制成功绕过！此处应该有掌声。经过我多次请求发现，这个地方的cookie是跟IP地址有关的，只有更换IP后才会刷新cookie。当然，如果当前IP的访客cookie过期的话，是会自动更新的。后记写完代码之后，想起来之前写的一个微博扫码登陆程序，最开始的时候是可以用的，后来再次请求的时候会出现Sina Visitor System，当时技术有限，不知道怎么解决。这次既然解决了，把cookie带上看看能不能成功运行不出意外的话，这次肯定不会出意外了。代码成功运行，获取登录cookie成功！ ——圆了高中时期，在手机上逆向微博生成短链接的梦。