目标网站
aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luLz9yZWRpcj0mcmVkaXJfc3NsPTEmc25yPTFfNF80X19nbG9iYWwtaGVhZGVyTWVuZw==
查找加密接口
在发起请求之前,先把Network打开,然后清空所有记录,清空之后再发起请求,这样可以捕获到此次所有的请求,方便查找具体的加密接口。
在这里我们发现Network中只有两个请求是可能包含加密逻辑的请求,其余为字体和图片文件,这就很舒服了,如下图:
接下来,我们详细看一下这两个xhr请求,其中第一个请求是post请求,其携带两个参数,一个是donotcache,另一个是用户名。这里盲猜donotcacha是时间戳。当然也可以多请求几次,去找找规律。
也可以在控制台输出一下当前时间戳,可以发现两者非常相似,因此第一个请求并没有加密参数。
接下来看第二个请求,同样是一个post请求,该请求携带的参数较多,但大多数都为空,这里最明显的加密就是password,看其形式类似base64编码,可以直接使用base64加密试一下(如果能直接得到和加密后的密文相同的密文,那就不需要继续下面的分析了),(也有可能是先进行了其他加密,最后使用了base64编码),这里在使用base64对明文加密后发现和密文对应不上,所以我们需要接下来的分析。
这里发现还有一个参数rsatimestamp,这个参数是动态变化的。所以我们需要对该参数进行分析。
password分析
首先对password进行分析,可以直接对password参数进行搜索从而找到加密逻辑位置,也可以通过xhr断点进行调试,然后找到对应的位置。
直接搜索
直接搜索password参数,可以找到包含该参数的六个文件。
这里可以进一步搜索,比如加等号或冒号,这里只有六个文件,我们可以一一来对其进行分析。首先排除两个css文件,接下来去查看js中对应password的逻辑,我们可以发现在login.js文件中,存在大量包含password的逻辑。同时还有一个password:encryptedPassword的操作,这里我们可以猜测encryptedPassword就是加密后的password
再找到具体的加密逻辑所在的文件之后,我们就需要进入文件内部进行更详细的分析。
进入文件之后,我们可以首先对文件进行格式化,以便调试,在该文件内部继续搜索password参数,这里我们找打了23处符合条件的位置,还能接受。当然也可以使用小技巧进一步缩小范围。
在这里我们一个一个查看,可以发现在387行,有一个RSA.encrypt(password, pubKey),其余地方没有。因此我们可以在此处打上断点以验证我们是否找到了真正的加密位置。
打上断点之后,再发起一次请求,我们可以看到,程序在我们打下断点的位置停住了。
这就说明我们找到了正确的加密位置,接下来就是要对具体的加密逻辑进行分析了。
分析加密逻辑
在这里我们可以看到该加密函数使用了两个参数,一个是password明文,另一个是pubKey(密钥)。
其中pubKey我们可以在文件的上方找到,
我们可以看到生成该密钥需要两个参数,我们可以进一步搜索一下这两个参数。在该文件并没有找到对应的生成位置,这时细心的朋友可以发现这两个参数和第一次请求返回的结果有些关系,(publickey_exp和publickey_mod),同时可以发现timestamp和我们之前找到的rsatimestamp有些类似,进一步查看,可以发现这里的timestamp和rsatimestamp是相同的。那么这个问题也就解决了。
接下来继续分析加密的逻辑,这里主要是使用了RSA这个对象的encrypt方法,在这里打上断点,我们可以跟到具体的方法里面,去看一下。
这里我们就可以看到具体的encrypt方法了,可以看到这里使用了Base64.encode() 和 Hex.decode() 方法,代表我们也需要找到这两个对应的方法。幸运的是,我们在该文件的上方找到了对应的方法实现。那么我们就可以确定该文件整体都是在加密的逻辑中不可或缺的。在接下来的写代码环节,我们就需要把其整体拿下来。
JS调试及实现
在这里,我们首先把上一步我们找到的加密文件的所有内容拿下来,放到一个js文件里面。然后,开始我们的调试(不确定只有这一段逻辑是否能够实现加密,所以在调试过程中要不断添加修改js代码)。
在把当前加密逻辑写入js文件之后,我们可以尝试运行一下js,可以使用python第三方库pyexecjs来模拟运行,也可以使用node.js运行,或者可以直接使用浏览器运行js。
这里,我们先把获取pubKey的函数所需的两个参数写成固定值,方便调试,放到浏览器里面运行结果如下:
这里提示BigInter is not defined 。也就是说,程序缺少了BigInter 这个对象,那么我们现在要做的就是去往现有代码中添加对象。现在我们去刚才找到的代码文件中,去搜索一下这个关键字,可以发现其全是以new BigInteger 形式出现的,那么就代表其为一个对象,那么我们可以在对应位置设置断点,然后重新发起请求,并单步执行到BigInteger 的位置,然后跟进去,即可找到BigInteger对应的逻辑,如下图:
在进入到包含 BigInteger 对象逻辑的文件中后,我们便可以看到该文件内对BigInteger的具体实现,那么我们就可以把它拿出来,添加到我们已有的代码当中(这里也可以直接把整个文件中的代码全拿出来,测试是否可以执行(在这里结果是可行的,但并不是所有的都可行)),这次我并没有直接把所有代码拿出来,而是慢慢的去补充所需的代码。这里我们把 BigInterger 补进去,然后再次执行。出现如下报错:
提示 this.fromString 不是一个函数,所以我们下一步就是要去找到其对应的代码,然后添加到我们的代码中。在源代码中搜索fromString,我们可以找到其对应的逻辑,在这里this表示的是拥有该函数的对象,即为BigInteger对象。通过搜索关键字我们可以发现其逻辑,如下图:
在下面还有一行代码,将该函数添加到BigInterger对象中。所以我们也要把这行代码添加进去。
BigInteger.prototype.fromString = bnpFromString;添加完之后,我们再次执行代码,出现如下提示:
那我们就需要继续查找所缺的内容,并将其添加到我们的代码中去。直到找到所有的代码逻辑。在此过程要细心,最好是先打上断点(这里的断点要详细,我们主要是看其运行流程),跟一遍流程,然后我们就可以大概明白了其具体加密中用到了那些逻辑,然后我们在添加代码的过程中就可以对其留有印象,从而避免失误。
最终补完的代码如下:
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function nbv(i){
var r = nbi();
this.t = 1;
this.s = (i<0)?-1:0;
if(i > 0) this[0] = i;
else if(i < -1) this[0] = i+DV;
else this.t = 0;
return r; }
BigInteger.ONE = nbv(1);
// 修改了name
var navigator = {'Name':"Netscape"};
var j_lm = true;
var dbits;
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)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
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;
}
if(j_lm && (navigator.Name == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.Name != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= 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);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function barrettRevert(x) { return x; }
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.convert = barrettConvert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }
NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
function 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);
}
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
function 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;
}
function bnAbs() { return (this.s<0)?this.negate():this; }
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 <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
function nbi() { return new BigInteger(null); }
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<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) {
this.t = 1;
this.s = (0<0)?-1:0;
if(0 > 0) this[0] = x;
else if(0 < -1) this[0] = 0+DV;
else this.t = 0;
};
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 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<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 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);
}
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
function 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;
}
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= 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();
}
function 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)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
function 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;
}
BigInteger.prototype.DV = (1<<dbits);
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// 注意this.t
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.modPowInt = bnModPowInt;
BigInteger.prototype.exp = bnpExp;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
// BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.bitLength = bnBitLength;
// 注意
BigInteger.prototype.DB = dbits;
var RSAPublicKey = function($modulus_hex, $encryptionExponent_hex) {
this.modulus = new BigInteger( $modulus_hex, 16);
this.encryptionExponent = new BigInteger( $encryptionExponent_hex, 16);
};
var Base64 = {
base64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function($input) {
if (!$input) {
return false;
}
var $output = "";
var $chr1, $chr2, $chr3;
var $enc1, $enc2, $enc3, $enc4;
var $i = 0;
do {
$chr1 = $input.charCodeAt($i++);
$chr2 = $input.charCodeAt($i++);
$chr3 = $input.charCodeAt($i++);
$enc1 = $chr1 >> 2;
$enc2 = (($chr1 & 3) << 4) | ($chr2 >> 4);
$enc3 = (($chr2 & 15) << 2) | ($chr3 >> 6);
$enc4 = $chr3 & 63;
if (isNaN($chr2)) $enc3 = $enc4 = 64;
else if (isNaN($chr3)) $enc4 = 64;
$output += this.base64.charAt($enc1) + this.base64.charAt($enc2) + this.base64.charAt($enc3) + this.base64.charAt($enc4);
} 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, $enc4;
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++));
$enc4 = this.base64.indexOf($input.charAt($i++));
$output += String.fromCharCode(($enc1 << 2) | ($enc2 >> 4));
if ($enc3 != 64) $output += String.fromCharCode((($enc2 & 15) << 4) | ($enc3 >> 2));
if ($enc4 != 64) $output += String.fromCharCode((($enc3 & 3) << 6) | $enc4);
} while ($i < $input.length);
return $output;
}
};
var Hex = {
hex: "0123456789abcdef",
encode: function($input) {
if(!$input) return false;
var $output = "";
var $k;
var $i = 0;
do {
$k = $input.charCodeAt($i++);
$output += this.hex.charAt(($k >> 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++)) << 4) & 0xf0) | (this.hex.indexOf($input.charAt($i++)) & 0xf));
} while ($i < $input.length);
return $output;
}
};
var RSA = {
getPublicKey: function( $modulus_hex, $exponent_hex ) {
return new RSAPublicKey( $modulus_hex, $exponent_hex );
},
encrypt: function($data, $pubkey) {
if (!$pubkey) return false;
$data = this.pkcs1pad2($data,($pubkey.modulus.bitLength()+7)>>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)
console.log($data)
$data = "0" + $data;
return Base64.encode(Hex.decode($data));
},
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);
}
};
var publickey_mod = 'xxxxxx';
publickey_exp = 'xxxxxx';
var pubKey = RSA.getPublicKey(publickey_mod, publickey_exp);
console.log(RSA.encrypt("123456", pubKey));
我们再次运行这个代码,发现其不报错了,但是其返回的结果是C+y+wA==
运行后发现结果不对劲,内心很崩溃呀。
在这里,我们就要思考问题所在了,可能是有哪些变量没补正确,也可能是那些逻辑写错了。
由于结果不正确,所以在此处,我又重新进行了一次js逆向,一步一步的去不代码。最总发现结果还是一样的。
此时,已经可以感觉到不同寻常了 ,是不是代码都补正确了,但是在执行的过程中出现了一些偏差。
于是乎,我就在最终结果生成逻辑的位置加了一下console.log()以输出一些结果的值。最终发现,在$data转化为字符串时出现了错误,本该生成字符串的,但在我这里生成了[object Object],把本地代码放到浏览器运行也是同样的结果。(toString() 可以将对象转化为字符串也可以转化为[object Object],但同样的对象出现两种不同的结果,很迷惑)。
于是乎,我就在想,只要将其表示为字符串应该就没问题了,所以在此处我将其改为了json.stringify(),转换为字符串后在进行tostring(16)进行转换。最终得到了正确的结果(因为rsa每次结果都不一样,每次都会对数据进行随机填充);
注意事项
在该案例中,需要注意的地方有以下几点:
1、在补代码的时候需要注意this.xxx这种变量,特别是当其只表示一个变量的时候,我们需要在源代码中找到其真正的数据,因为在我们本地使用nodejs执行js的时候,有些this.xxx我们没有找到正确的值,但运行并不会报错,只是可能造成的结果不一致。所以我们需要拿到源代码中真正的this.xxx 变量,才能保证我们最终获取到真正的结果。
**
**
2、在本次补代码的过程中,添加了一个函数,但在执行过程中,该函数被判定为对象,然后报没有该函数的错误,我在这里直接将其函数逻辑写到了代码中,并没有去调用。暂时没有想到原因。
(为啥使用function 定义的函数被判定为对象?很奇怪)
**
**
3、有些时候,我们补完代码运行后的结果和真正的结果有偏差,这时我们首先要做的是检查阶段结果是否和正确结果保持一致,从而找到错误位置。而不是直接全盘否定,这样会浪费大量时间和精力。