本文由作者 胡川港 投稿,如果你在 CSDN、博客园、掘金等平台有写技术博客的习惯,想让自己的原创博客被更多人看到,可以来 Java后端 投稿。
最近在新公司学习 Golang,心血来潮想写一个脚手架,脚手架的简单的功能就是 输入 关键词,然后工具帮我自动的去搜索比如 掘金 知乎 CSDN 的热门博客返回给我。
但是在调用知乎的接口发现 知乎的接口有签名 不能修改参数。修改了参数就让提示升级客户端了。这怎么能忍,岂能因为一个小小的知乎搜索阻挠我学习 Golang 的脚步呢「手动狗头」,今天就来逆向破解知乎。
并将导出的 cURL 导入到 postman 中
在 postman 这里直接发送请求是可以的
但是如果我们一旦尝试修改查询参数的时候就会报错
其实到这里很明显的知道肯定是因为参数加签导致的,但是我们需要知道具体是哪个参数导致,才好解决问题
咋一看 url 里面的这些参数 都很标准 应该没有动过什么手脚,我们把目标转向 header
请求头里面这么多参数感觉是有问题的
测试方法也很简单,依次去除 单个字段,然后请求接口看是否可行即可,最后发现在没有修改请求参数 的情况下 去除 请求头里面的 x-zse-96 就会出现同样的问题,问题复现
定位加密文件
在分析问题环节我们已经找到了加签后的验证字段 x-zse-96,最后其实我们值需要找到这个字段对应的加签规则即可。
基本的思路为:通过 Chrome 中的 JS 栈调用为入口
如上图所示,在 Network 中 搜索 API 请求记录中有一项属性 Initiator [发起者],将鼠标移动上去就会显示 JS 的调用栈情况
JS call stack
有一点可以明确下来就是 针对参数 x-zse-96 这个参数的加签肯定是在这个调用栈中完成的,我的思路就是一次在这些调用过的 js 文件中搜索这个字段,先定位到在哪个 JS 文件中的,好巧不巧的事知乎 的js 应该是打包后的 所有的 js 文件都在一个代码文件中。
我直接在这个文件中进行搜索就行了
代码位置已找到,开始表演😈
接下来就是分析加密代码的时候了
从图中可以看出,x-zse-96
是由 2.0_ + signature
组成的
然后我们就需要查看组成 signature
代码的 a()(l()(s))
的组成
在 signature 上方打上一个断点, 再次点击搜索,让断点停在此处
a() 直接在控制台中打印一下 a 方法,a 方法返回的一个方法为 __g._encrypt(encodeURIComponent(e))
这里我们先将 a 方法放置在这里,先看里面的方法 l()(s),先看参数 s 基本上就是查询参数拼接成一个字符串, 暂时先可以不看
l(), 我现在带入几个参数对这个加密算法进行了尝试,我实在感觉这个有点像 MD5
\
抱着试一试的心态找了一个在线的 Md5 加密网站测试了一下 1,卧槽 🐂🍺 还真的是,我直呼 666
回顾下加密算法
现在我们只需要展开 外层的 a()() 方法就 OK 了,在展开一点点__g._encrypt(encodeURIComponent(md5(s)))
接下其实就去找这个 __g._encrypt 这个方法就可以了,打上断点在执行一下,尝试运行,看到这个玩意确实是没啥思路,而且在整个 JS 文件中 __g._encrypt 这个方法就只出现了这一次,就在没有出现过了, 所以通过猜测加密算法的方式就不太可行了
思路就是直接将加密算法模块的 JS 文件提取出来 然后 用 golang 的 otto 包来直接调用 JS 代码即可
提取出来的加密 JS 文件为:
Hello worldconst jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`
window = dom.window;
document = window.document;
XMLHttpRequest = window.XMLHttpRequest;
function t(e) {return (t = "function" == typeof Symbol && "symbol" == typeof Symbol.A ? function(e) {return typeof e}: function(e) {return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e})(e)
}
Object.defineProperty(exports, "__esModule", {value: !0
});
var A = "2.0", __g = {};
function s() {}
function i(e) {this.t = (2048 & e) >> 11,this.s = (1536 & e) >> 9,this.i = 511 & e,this.h = 511 & e
}
function h(e) {this.s = (3072 & e) >> 10,this.h = 1023 & e
}
function a(e) {this.a = (3072 & e) >> 10,this.c = (768 & e) >> 8,this.n = (192 & e) >> 6,this.t = 63 & e
}
function c(e) {this.s = e >> 10 & 3,this.i = 1023 & e
}
function n() {}
function e(e) {this.a = (3072 & e) >> 10,this.c = (768 & e) >> 8,this.n = (192 & e) >> 6,this.t = 63 & e
}
function o(e) {this.h = (4095 & e) >> 2,this.t = 3 & e
}
function r(e) {this.s = e >> 10 & 3,this.i = e >> 2 & 255,this.t = 3 & e
}
s.prototype.e = function(e) {e.o = !1
},i.prototype.e = function(e) {switch (this.t) {case 0:e.r[this.s] = this.i;break;case 1:e.r[this.s] = e.k[this.h]}},h.prototype.e = function(e) {e.k[this.h] = e.r[this.s]},a.prototype.e = function(e) {switch (this.t) {case 0:e.r[this.a] = e.r[this.c] + e.r[this.n];break;case 1:e.r[this.a] = e.r[this.c] - e.r[this.n];break;case 2:e.r[this.a] = e.r[this.c] * e.r[this.n];break;case 3:e.r[this.a] = e.r[this.c] / e.r[this.n];break;case 4:e.r[this.a] = e.r[this.c] % e.r[this.n];break;case 5:e.r[this.a] = e.r[this.c] == e.r[this.n];break;case 6:e.r[this.a] = e.r[this.c] >= e.r[this.n];break;case 7:e.r[this.a] = e.r[this.c] || e.r[this.n];break;case 8:e.r[this.a] = e.r[this.c] && e.r[this.n];break;case 9:e.r[this.a] = e.r[this.c] !== e.r[this.n];break;case 10:e.r[this.a] = t(e.r[this.c]);break;case 11:e.r[this.a] = e.r[this.c]in e.r[this.n];break;case 12:e.r[this.a] = e.r[this.c] > e.r[this.n];break;case 13:e.r[this.a] = -e.r[this.c];break;case 14:e.r[this.a] = e.r[this.c]
var k = function(e) {for (var t = 66, n = [], r = 0; r
function Q(e) {this.t = (4095 & e) >> 10,this.s = (1023 & e) >> 8,this.i = 1023 & e,this.h = 63 & e
}
function C(e) {this.t = (4095 & e) >> 10,this.a = (1023 & e) >> 8,this.c = (255 & e) >> 6
}
function B(e) {this.s = (3072 & e) >> 10,this.h = 1023 & e
}
function f(e) {this.h = 4095 & e
}
function g(e) {this.s = (3072 & e) >> 10
}
function u(e) {this.h = 4095 & e
}
function w(e) {this.t = (3840 & e) >> 8,this.s = (192 & e) >> 6,this.i = 63 & e
}
function G() {this.r = [0, 0, 0, 0],this.C = 0,this.Q = [],this.k = [],this.B = [],this.f = [],this.g = [],this.u = !1,this.G = [],this.b = [],this.o = !1,this.w = null,this.U = null,this.F = [],this.R = 0,this.J = {0: s,1: i,2: h,3: a,4: c,5: n,6: e,7: o,8: r,9: Q,10: C,11: B,12: f,13: g,14: u,15: w}
}
Q.prototype.e = function(e) {switch (this.t) {case 0:e.f.push(e.r[this.s]);break;case 1:e.f.push(this.i);break;case 2:e.f.push(e.k[this.h]);break;case 3:e.f.push(k(e.b[this.h]))}
},C.prototype.e = function(A) {switch (this.t) {case 0:var t = A.f.pop();A.r[this.a] = A.r[this.c][t];break;case 1:var s = A.f.pop(), i = A.f.pop();A.r[this.c][s] = i;break;case 2:var h = A.f.pop();A.r[this.a] = eval(h)}},B.prototype.e = function(e) {e.r[this.s] = k(e.b[this.h])},f.prototype.e = function(e) {e.w = this.h},g.prototype.e = function(e) {throw e.r[this.s]},u.prototype.e = function(e) {var t = this, n = [0];e.k.forEach((function(e) {n.push(e)}));var r = function(r) {var i = new G;return i.k = n,i.k[0] = r,i.v(e.G, t.h, e.b, e.F),i.r[3]};r.toString = function() {return "() { [native code] }"},e.r[3] = r},w.prototype.e = function(e) {switch (this.t) {case 0:for (var t = {}, n = 0; n
1 && (new G).v("AxjgB5MAnACoAJwBpAAAABAAIAKcAqgAMAq0AzRJZAZwUpwCqACQACACGAKcBKAAIAOcBagAIAQYAjAUGgKcBqFAuAc5hTSHZAZwqrAIGgA0QJEAJAAYAzAUGgOcCaFANRQ0R2QGcOKwChoANECRACQAsAuQABgDnAmgAJwMgAGcDYwFEAAzBmAGcSqwDhoANECRACQAGAKcD6AAGgKcEKFANEcYApwRoAAxB2AGcXKwEhoANECRACQAGAKcE6AAGgKcFKFANEdkBnGqsBUaADRAkQAkABgCnBagAGAGcdKwFxoANECRACQAGAKcGKAAYAZx+rAZGgA0QJEAJAAYA5waoABgBnIisBsaADRAkQAkABgCnBygABoCnB2hQDRHZAZyWrAeGgA0QJEAJAAYBJwfoAAwFGAGcoawIBoANECRACQAGAOQALAJkAAYBJwfgAlsBnK+sCEaADRAkQAkABgDkACwGpAAGAScH4AJbAZy9rAiGgA0QJEAJACwI5AAGAScH6AAkACcJKgAnCWgAJwmoACcJ4AFnA2MBRAAMw5gBnNasCgaADRAkQAkABgBEio0R5EAJAGwKSAFGACcKqAAEgM0RCQGGAYSATRFZAZzshgAtCs0QCQAGAYSAjRFZAZz1hgAtCw0QCQAEAAgB7AtIAgYAJwqoAASATRBJAkYCRIANEZkBnYqEAgaBxQBOYAoBxQEOYQ0giQKGAmQABgAnC6ABRgBGgo0UhD/MQ8zECALEAgaBxQBOYAoBxQEOYQ0gpEAJAoYARoKNFIQ/zEPkAAgChgLGgkUATmBkgAaAJwuhAUaCjdQFAg5kTSTJAsQCBoHFAE5gCgHFAQ5hDSCkQAkChgBGgo0UhD/MQ+QACAKGAsaCRQCOYGSABoAnC6EBRoKN1AUEDmRNJMkCxgFGgsUPzmPkgAaCJwvhAU0wCQFGAUaCxQGOZISPzZPkQAaCJwvhAU0wCQFGAUaCxQMOZISPzZPkQAaCJwvhAU0wCQFGAUaCxQSOZISPzZPkQAaCJwvhAU0wCQFGAkSAzRBJAlz/B4FUAAAAwUYIAAIBSITFQkTERwABi0GHxITAAAJLwMSGRsXHxMZAAk0Fw8HFh4NAwUABhU1EBceDwAENBcUEAAGNBkTGRcBAAFKAAkvHg4PKz4aEwIAAUsACDIVHB0QEQ4YAAsuAzs7AAoPKToKDgAHMx8SGQUvMQABSAALORoVGCQgERcCAxoACAU3ABEXAgMaAAsFGDcAERcCAxoUCgABSQAGOA8LGBsPAAYYLwsYGw8AAU4ABD8QHAUAAU8ABSkbCQ4BAAFMAAktCh8eDgMHCw8AAU0ADT4TGjQsGQMaFA0FHhkAFz4TGjQsGQMaFA0FHhk1NBkCHgUbGBEPAAFCABg9GgkjIAEmOgUHDQ8eFSU5DggJAwEcAwUAAUMAAUAAAUEADQEtFw0FBwtdWxQTGSAACBwrAxUPBR4ZAAkqGgUDAwMVEQ0ACC4DJD8eAx8RAAQ5GhUYAAFGAAAABjYRExELBAACWhgAAVoAQAg/PTw0NxcQPCQ5C3JZEBs9fkcnDRcUAXZia0Q4EhQgXHojMBY3MWVCNT0uDhMXcGQ7AUFPHigkQUwQFkhaAkEACjkTEQspNBMZPC0ABjkTEQsrLQ==");var b = function(e) {return __g._encrypt(encodeURIComponent(e))
};module.exports = {Q: b
}
最后本地测试一下,对上了 对上了
在看一下 s 的值 s=r+c+i, 101_3_3.0+/api/v4/search/suggest?qv=vim+AHCY5gT4iBKPTsBQivYRPUIbDLBctuaZZzs=|1611209467 其他都是固定的 只需要改一下请求的 url query 就可以了
在经过测试后 成功 修改参数 起飞
在接入 Go 调用过程中发现一个问题,不知道是 golang otto 包的问题还是怎样,就是在 js 文件里面 如果有 require() 第三包的情况下 就会提示语法错误, 因为这个加密 js 文件中会调用 windows.atob 方法 所以需要借助第三方包 jsdom 来实现
错误表现
在经过冥思苦想不知道怎么解决的时候,去问了下团队里的爬虫老师傅,他说一般遇到这种情况就直接起一个 Node 的服务专门来处理这种解密的事就 OK 了,恍然大悟 原来如此
有点偷懒不想自己搞个服务来玩这个了,想直接用云厂商的试用版,看了一下 以前 用过的 LeanCloud 的 感觉不太行,后面选择了腾讯的 ServerLess 直接部署了一个服务器
https://service-denf06ck-1253616191.gz.apigw.tencentcs.com/release/secret/:content
替换掉上面的 content 就可以了,最后 Go 脚手架项目也完成了,项目地址:https://github.com/xiaoxiunique/go-cli/tree/master
投稿作者:胡川港
知乎主页:zhihu.com/people/hu-chuan-gang-58
GitHub主页:https://github.com/xiaoxiunique
#投 稿 通 道#
让你的博客被更多人看到
如果你在 CSDN、博客园、掘金等平台有写技术博客的习惯,想让自己的原创博客被更多人看到,可以来 Java后端 投稿。
Java后端 鼓励读者投稿个人技术博客、面试经验、教程。不管是入门的图文教程、还是热门技术讲解,只要你喜欢写东西,我们欢迎你来投稿。
📝 稿件基本要求:
• 文章确系个人原创作品,如果在其他非公众号渠道有过发表也可以,只要是个人原创即可。
• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰、语句通顺。
• 如果被采纳的原创稿件,我们将提供稿费以及个人影响力曝光,具体依据文章阅读量和质量结算稿费。
📬 投稿通道:
• 投稿请联系下方微信,备注:原创投稿
△长按添加 Java后端 小编