JS逆向steam登录
优采云 发布时间: 2022-05-03 09:57JS逆向steam登录
前言
我们爬虫有时候,会遇到登录才能获取到数据的情况,最开始的时候我们只需要加入请求的data参数就可以,可是现在网站为了反爬,对登录的密码或者账号都做了加密处理,如果我们不破解出这些加密的密码或者账号,就没办法实现请求登录,所以我们就需要破解出这些加密后的密码或者账号,才能实现请求登录,这些加密的密码或者账号都是用JavaScript写的,后面我们统一都简称为js。
了解js加密的种类
现在市面上的js加密,大至分为五类,base64加密、sha1加密、MD5加密、AES/DES加密解密、RSA加密,前面两种稍微简单一点,后面的3种就会复杂一点,特别是RSA加密,因为它是一种非对称加密算法,所以加密的稳定性和加密的复杂程度也就会最高,因为steam这个网站的登录就是使用的RSA加密技术,所以本文就只着重讲RSA加密,至于其它4种加密技术,本文不做过多赘述,想了解其他加密技术的小伙伴,可以自行百度搜索了解。
什么是RSA加密
RSA算法是一种非对称加密算法,那么何为非对称加密算法呢?
一般我们理解上的加密是这样子进行的:原文经过了一把钥匙(密钥)加密后变成了密文,然后将密文传递给接收方,接收方再用这把钥匙(密钥)解开密文。在这个过程中,其实加密和解密使用的是同一把钥匙,这种加密方式称为对称加密。如图:
而非对称加密就是和对称加密相对,加密用的钥匙和解密所用的钥匙,并不是同一把钥匙。非对称加密首先会创建两把钥匙,而这两把钥匙是成对的分别称为公钥和私钥。在进行加密时我们使用公钥进行加密,而在解密的时候就必须要使用私钥才能进行解密,这就是非对称加密算法。如图:
实战讲解
登录目标网站,然后点击登录页面,如图:
进入到登录页面,
现在我们打开开发者调试工具,或者按F12打开,定位到Network选项,勾选上Preserve log选项和Disable cache选项,如图:
然后我们在登录窗口,随便输入些账号和密码,点击登录按钮,这个时候Network选项下就会抓取到请求到的数据,
我们先看第一个数据,点击后,定位到preview选项,可以看到给我们返回的一些数据,这其实就是RSA的密钥数据。
我们说过RSA加密是需要公钥和私钥的,这里面的就是我们能需要的解密用到的私钥数据,下面我们能看第二个数据,这个就是我们登录需要请求的数据网址,我们定位带headers选项,可以看请求方式是POST请求,那就必须要有请求参数才行,然后找到from data项,如图:
可以很明显看出,username是我们输入的账号名,password是一堆看不懂的乱码,其实这就是经过RSA加密过的密码数据,我们就需要加密这个密码数据。
我们通过全局搜素,搜索password这个关键字,可以看出有很多包含password这个关键字的数据,那么这么多数据那个才是我们能需要找到的数据呢,
这里有个小技巧,如果发现搜索出来很多关于关键字的信息,可以在该关键字后面给一个=号在搜索,如图:
经过再次的搜索可以看出这次就没有那么多内容了,便于我们更好的找到需要的数据位置,我们通过搜索出的数据可以看出,有一个encryptedpassword这行的数据,从字面的意思就能知道这是加密的密码的意思,所以我们就先定位带这条数据的里面,看看是什么样的,经过定位到里面我们看到是一个用js代码写的文件,里面有很多js代码,
既然我们认为这个就是加密的密码数据,那么我们可以给这行数据下一个断点,然后点击登录,看能不能断下这条数据,通过断点,我们确实可以断下这条数据,那么也就是代表这里就是加密密码的方法,RSA.encrypt是一个函数,里面的参数分别就是,password就是我们输入的密码。
pubkey是一个对象,这个对象具体是什么还需要进入到这个函数里面看具体的情形。
我们按F11进入到这个函数里面,可以看到有一个js的文件,这个文件就是RSA加密的整个过程的文件。
逆向改写
通过上面的分析,我们已经知道了大概密码的加密过程,现在就需要我们通过工具给它的这些代码进行一些改写,从而获取到真正加密的密码数据,这里我用到的改写工具是鬼鬼JS调试工具7.5工具可以自行搜索下载。
navigator = {};var dbits;<br />// JavaScript engine analysisvar canary = 0xdeadbeefcafe;var j_lm = ((canary & 0xffffff) == 0xefcafe);<br />// (public) Constructorfunction BigInteger(a, b, c) { if (a != null) if ("number" == typeof a) this.fromNumber(a, b, c); else if (b == null && "string" != typeof a) this.fromString(a, 256); else this.fromString(a, b);}<br />// return new, unset BigIntegerfunction nbi() { return new BigInteger(null);}<br />// am: Compute w_j += (x*this_i), propagate carries,// c is initial carry, returns final carry.// c < 3*dvalue, x < 2*dvalue, this_i < dvalue// We need to select the fastest one that works in this environment.<br />// am1: use a single mult and divide to get the high bits,// max digit bits should be 26 because// max internal value = 2*dvalue^2-2*dvalue (< 2^53)function am1(i, x, w, j, c, n) { while (--n >= 0) { var v = x * this[i++] + w[j] + c; c = Math.floor(v / 0x4000000); w[j++] = v & 0x3ffffff; } return c;}// am2 avoids a big mult-and-extract completely.// Max digit bits should be > 15; while (--n >= 0) { var l = this[i] & 0x7fff; var h = this[i++] >> 15; var m = xh * l + h * xl; l = xl * l + ((m & 0x7fff) >> 30) + (m >>> 15) + xh * h + (c >>> 30); w[j++] = l & 0x3fffffff; } return c;}// Alternately, set max digit bits to 28 since some// browsers slow down when dealing with 32-bit numbers.function am3(i, x, w, j, c, n) { var xl = x & 0x3fff, xh = x >> 14; while (--n >= 0) { var l = this[i] & 0x3fff; var h = this[i++] >> 14; var m = xh * l + h * xl; l = xl * l + ((m & 0x3fff) > 28) + (m >> 14) + xh * h; w[j++] = l & 0xfffffff; } return c;}<br /><br />// Digit conversionsvar BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";var BI_RC = new Array();var rr, vv;rr = "0".charCodeAt(0);for (vv = 0; vv a, - if this < a, 0 if equalfunction bnCompareTo(a) { var r = this.s - a.s; if (r != 0) return r; var i = this.t; r = i - a.t; if (r != 0) return r; while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; return 0;}<br />// returns bit length of the integer xfunction nbits(x) { var r = 1, t; if ((t = x >>> 16) != 0) { x = t; r += 16; } if ((t = x >> 8) != 0) { x = t; r += 8; } if ((t = x >> 4) != 0) { x = t; r += 4; } if ((t = x >> 2) != 0) { x = t; r += 2; } if ((t = x >> 1) != 0) { x = t; r += 1; } return r;}<br />// (public) return the number of bits in "this"function bnBitLength() { if (this.t = 0; --i) r[i] = 0; r.t = this.t + n; r.s = this.s;}<br />// (protected) r = this >> n*DBfunction bnpDRShiftTo(n, r) { for (var i = n; i < this.t; ++i) r[i - n] = this[i]; r.t = Math.max(this.t - n, 0); r.s = this.s;}<br />// (protected) r = this cbs) | c; c = (this[i] & bm) = 0; --i) r[i] = 0; r[ds] = c; r.t = this.t + ds + 1; r.s = this.s; r.clamp();}<br />// (protected) r = this >> nfunction bnpRShiftTo(n, r) { r.s = this.s; var ds = Math.floor(n / this.DB); if (ds >= this.t) { r.t = 0; return; } var bs = n % this.DB; var cbs = this.DB - bs; var bm = (1 > bs; for (var i = ds + 1; i < this.t; ++i) { r[i - ds - 1] |= (this[i] & bm) > bs; } if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) >= this.DB; } if (a.t < this.t) { c -= a.s; while (i < this.t) { c += this[i]; r[i++] = c & this.DM; c >>= this.DB; } c += this.s; } else { c += this.s; while (i < a.t) { c -= a[i]; r[i++] = c & this.DM; c >>= this.DB; } c -= a.s; } r.s = (c < 0) ? -1 : 0; if (c < -1) r[i++] = this.DV + c; else if (c > 0) r[i++] = c; r.t = i; r.clamp();}<br />// (protected) r = this * a, r != this,a (HAC 14.12)// "this" should be the larger one if appropriate.function bnpMultiplyTo(a, r) { var x = this.abs(), y = a.abs(); var i = x.t; r.t = i + y.t; while (--i >= 0) r[i] = 0; for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); r.s = 0; r.clamp(); if (this.s != a.s) BigInteger.ZERO.subTo(r, r);}<br />// (protected) r = this^2, r != this (HAC 14.16)function bnpSquareTo(r) { var x = this.abs(); var i = r.t = 2 * x.t; while (--i >= 0) r[i] = 0; for (i = 0; i < x.t - 1; ++i) { var c = x.am(i, x[i], r, 2 * i, 0, 1); if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { r[i + x.t] -= x.DV; r[i + x.t + 1] = 1; } } if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); r.s = 0; r.clamp();}<br />// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)// r != q, this != m. q or r may be null.function bnpDivRemTo(m, q, r) { var pm = m.abs(); if (pm.t 0) { pm.lShiftTo(nsh, y); pt.lShiftTo(nsh, r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; var y0 = y[ys - 1]; if (y0 == 0) return; var yt = y0 * (1 1) ? y[ys - 2] >> this.F2 : 0); var d1 = this.FV / yt, d2 = (1 = 0) { // Estimate quotient digit var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out y.dlShiftTo(j, t); r.subTo(t, r); while (r[i] < --qd) r.subTo(t, r); } } if (q != null) { r.drShiftTo(ys, q); if (ts != ms) BigInteger.ZERO.subTo(q, q); } r.t = ys; r.clamp(); if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder if (ts < 0) BigInteger.ZERO.subTo(r, r);}<br />// (public) this mod afunction bnMod(a) { var r = nbi(); this.abs().divRemTo(a, null, r); if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); return r;}<br />// Modular reduction using "classic" algorithmfunction Classic(m) { this.m = m;}<br />function cConvert(x) { if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); else return x;}<br />function cRevert(x) { return x;}<br />function cReduce(x) { x.divRemTo(this.m, null, x);}<br />function cMulTo(x, y, r) { x.multiplyTo(y, r); this.reduce(r);}<br />function cSqrTo(x, r) { x.squareTo(r); this.reduce(r);}<br />Classic.prototype.convert = cConvert;Classic.prototype.revert = cRevert;Classic.prototype.reduce = cReduce;Classic.prototype.mulTo = cMulTo;Classic.prototype.sqrTo = cSqrTo;<br />// (protected) return "-1/this % 2^DB"; useful for Mont. reduction// justification:// xy == 1 (mod m)// xy = 1+km// xy(2-xy) = (1+km)(1-km)// x[y(2-xy)] = 1-k^2m^2// x[y(2-xy)] == 1 (mod m^2)// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.// JS multiply "overflows" differently from C/C++, so care is needed here.function bnpInvDigit() { if (this.t < 1) return 0; var x = this[0]; if ((x & 1) == 0) return 0; var y = x & 3; // y == 1/x mod 2^2 y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 // last step - calculate inverse mod DV directly; // assumes 16 < DB 0) ? this.DV - y : -y;}<br />// Montgomery reductionfunction Montgomery(m) { this.m = m; this.mp = m.invDigit(); this.mpl = this.mp & 0x7fff; this.mph = this.mp >> 15; this.um = (1 0) this.m.subTo(r, r); return r;}<br />// x/R mod mfunction montRevert(x) { var r = nbi(); x.copyTo(r); this.reduce(r); return r;}<br />// x = x/R mod m (HAC 14.32)function montReduce(x) { while (x.t > 15) * this.mpl) & this.um) = x.DV) { x[j] -= x.DV; x[++j]++; } } x.clamp(); x.drShiftTo(this.m.t, x); if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);}<br />// r = "x^2/R mod m"; x != rfunction montSqrTo(x, r) { x.squareTo(r); this.reduce(r);}<br />// r = "xy/R mod m"; x,y != rfunction montMulTo(x, y, r) { x.multiplyTo(y, r); this.reduce(r);}<br />Montgomery.prototype.convert = montConvert;Montgomery.prototype.revert = montRevert;Montgomery.prototype.reduce = montReduce;Montgomery.prototype.mulTo = montMulTo;Montgomery.prototype.sqrTo = montSqrTo;<br />// (protected) true iff this is evenfunction bnpIsEven() { return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;}<br />// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)function bnpExp(e, z) { if (e > 0xffffffff || e < 1) return BigInteger.ONE; var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1; g.copyTo(r); while (--i >= 0) { z.sqrTo(r, r2); if ((e & (1 0) z.mulTo(r2, g, r); else { var t = r; r = r2; r2 = t; } } return z.revert(r);}<br />// (public) this^e % m, 0 1; if (t > lowprimes.length) t = lowprimes.length; var a = nbi(); for (var i = 0; i < t; ++i) { a.fromInt(lowprimes[i]); var y = a.modPow(r, this); if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { var j = 1; while (j++ < k && y.compareTo(n1) != 0) { y = y.modPowInt(2, this); if (y.compareTo(BigInteger.ONE) == 0) return false; } if (y.compareTo(n1) != 0) return false; } } return true;}<br />// protectedBigInteger.prototype.chunkSize = bnpChunkSize;BigInteger.prototype.toRadix = bnpToRadix;BigInteger.prototype.fromRadix = bnpFromRadix;BigInteger.prototype.fromNumber = bnpFromNumber;BigInteger.prototype.bitwiseTo = bnpBitwiseTo;BigInteger.prototype.changeBit = bnpChangeBit;BigInteger.prototype.addTo = bnpAddTo;BigInteger.prototype.dMultiply = bnpDMultiply;BigInteger.prototype.dAddOffset = bnpDAddOffset;BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;BigInteger.prototype.modInt = bnpModInt;BigInteger.prototype.millerRabin = bnpMillerRabin;<br />// publicBigInteger.prototype.clone = bnClone;BigInteger.prototype.intValue = bnIntValue;BigInteger.prototype.byteValue = bnByteValue;BigInteger.prototype.shortValue = bnShortValue;BigInteger.prototype.signum = bnSigNum;BigInteger.prototype.toByteArray = bnToByteArray;BigInteger.prototype.equals = bnEquals;BigInteger.prototype.min = bnMin;BigInteger.prototype.max = bnMax;BigInteger.prototype.and = bnAnd;BigInteger.prototype.or = bnOr;BigInteger.prototype.xor = bnXor;BigInteger.prototype.andNot = bnAndNot;BigInteger.prototype.not = bnNot;BigInteger.prototype.shiftLeft = bnShiftLeft;BigInteger.prototype.shiftRight = bnShiftRight;BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;BigInteger.prototype.bitCount = bnBitCount;BigInteger.prototype.testBit = bnTestBit;BigInteger.prototype.setBit = bnSetBit;BigInteger.prototype.clearBit = bnClearBit;BigInteger.prototype.flipBit = bnFlipBit;BigInteger.prototype.add = bnAdd;BigInteger.prototype.subtract = bnSubtract;BigInteger.prototype.multiply = bnMultiply;BigInteger.prototype.divide = bnDivide;BigInteger.prototype.remainder = bnRemainder;BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;BigInteger.prototype.modPow = bnModPow;BigInteger.prototype.modInverse = bnModInverse;BigInteger.prototype.pow = bnPow;BigInteger.prototype.gcd = bnGCD;BigInteger.prototype.isProbablePrime = bnIsProbablePrime;<br />// BigInteger interfaces not implemented in jsbn:<br />// BigInteger(int signum, byte[] magnitude)// double doubleValue()// float floatValue()// int hashCode()// long longValue()// static BigInteger valueOf(long val)<br /><br /><br />var RSAPublicKey = function($modulus_hex, $encryptionExponent_hex) { this.modulus = new BigInteger($modulus_hex, 16); this.encryptionExponent = new BigInteger($encryptionExponent_hex, 16);};<br />var Base64 = { base64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode: function($input) { if (!$input) { return false; } var $output = ""; var $chr1, $chr2, $chr3; var $enc1, $enc2, $enc3, $en*敏*感*词*; var $i = 0; do { $chr1 = $input.charCodeAt($i++); $chr2 = $input.charCodeAt($i++); $chr3 = $input.charCodeAt($i++); $enc1 = $chr1 >> 2; $enc2 = (($chr1 & 3) > 4); $enc3 = (($chr2 & 15) > 6); $en*敏*感*词* = $chr3 & 63; if (isNaN($chr2)) $enc3 = $en*敏*感*词* = 64; else if (isNaN($chr3)) $en*敏*感*词* = 64; $output += this.base64.charAt($enc1) + this.base64.charAt($enc2) + this.base64.charAt($enc3) + this.base64.charAt($en*敏*感*词*); } while ($i < $input.length); return $output; }, decode: function($input) { if (!$input) return false; $input = $input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); var $output = ""; var $enc1, $enc2, $enc3, $en*敏*感*词*; var $i = 0; do { $enc1 = this.base64.indexOf($input.charAt($i++)); $enc2 = this.base64.indexOf($input.charAt($i++)); $enc3 = this.base64.indexOf($input.charAt($i++)); $en*敏*感*词* = this.base64.indexOf($input.charAt($i++)); $output += String.fromCharCode(($enc1 > 4)); if ($enc3 != 64) $output += String.fromCharCode((($enc2 & 15) > 2)); if ($en*敏*感*词* != 64) $output += String.fromCharCode((($enc3 & 3) > 4) & 0xf) + this.hex.charAt($k & 0xf); } while ($i < $input.length); return $output; }, decode: function($input) { if (!$input) return false; $input = $input.replace(/[^0-9abcdef]/g, ""); var $output = ""; var $i = 0; do { $output += String.fromCharCode(((this.hex.indexOf($input.charAt($i++)) > 3); if (!$data) return false; $data = $data.modPowInt($pubkey.encryptionExponent, $pubkey.modulus); if (!$data) return false; $data = $data.toString(16); if (($data.length & 1) == 1) $data = "0" + $data; return Base64.encode(Hex.decode($data)); },<br /> pkcs1pad2: function($data, $keysize) { if ($keysize < $data.length + 11) return null; var $buffer = []; var $i = $data.length - 1; while ($i >= 0 && $keysize > 0) $buffer[--$keysize] = $data.charCodeAt($i--); $buffer[--$keysize] = 0; while ($keysize > 2) $buffer[--$keysize] = Math.floor(Math.random() * 254) + 1; $buffer[--$keysize] = 2; $buffer[--$keysize] = 0; return new BigInteger($buffer); }}<br /> function get_pwd(pwd,mod, exp) { var pubKey = RSA.getPublicKey(mod, exp); password = pwd.replace(/[^\x00-\x7F]/g, ''); var encryptedPassword = RSA.encrypt(password, pubKey); return encryptedPassword; }
通过自定义一个get_pwd的函数,来调用上面加密的整个过程,函数上面的部分,就是加密的整个过程的代码,这些代码也不是我写的,是别人写好的,我只是复制粘贴过来,改动一下,利用自己定义的函数,调用了这些代码,从而实现加密的目的。该函数需要传递2个参数,这个2个参数分别是未加密前的密码和私钥,私钥就在我们请求网页的时候,返回的第一个数据里面(publickey_exp和publickey_mod)。
mod就是publickey_mod,exp就是publickey_exp,通过传递参数,在调用定义的get_pwd这个函数,就能得到加密的密码结果了,加密后的密码也是动态的,每次都不一样。
效果展示
结语
因为本文涉及很多JavaScript的知识,所以需要对JavaScript有些了解,另外在改写JS代码的时候,会遇到很多的错误提示,限于篇幅的有限,我没有过多的写出如何排除这些错误提示,我们就需要利用一些JavaScript的知识一点点的排除这些错误提示,直到提示加载成功,才可以运行代码的,所以还是推荐用js的调试工具,这样更方便一些。头一次写这样的技术文章,有很多写的不到位的地方,还请多多提出宝贵意见。
END
B站账号:
https://space.bilibili.com/351138764
欢迎扫码
关注我