码农代理免费代理ip端口字段js加密破解
起因
之前挖过爬取免费代理ip的坑,一个比较帅的同事热心发我有免费代理ip的网站,遂研究了下:https://proxy.coderbusy.com/。
解密
因为之前爬过类似的网站有了些经验,大概知道这些家伙都是啥套路于是就随手ctrl+shift+c选了一下端口号:
端口元素有个奇怪的data字段,怀疑是在这个数字8781的基础上生成的8080,查看源代码看看返回的是什么样的:
果然返回的html中的数字跟页面上显示的数字不一致,基本可以确定端口号是在页面加载完成后通过js在data-i字段的基础上生成的新端口号。
在此元素上右击,打一个dom断点并刷新网页:
当此元素被修改的时候自动停在了断点,格式化,分析js:
这是对所有的.port应用b方法,b方法是什么方法呢,在断点调试模式下选中此变量-->在控制台执行:
然后单击一下控制台上的输出,跳到了内存中的某段js,这段就是加密逻辑:
将其拿出:
1 2 3 4 5 6 7 8 9 10 11 12 | $( function () { $( '\x2e\x70\x6f\x72\x74\x2d\x62\x6f\x78' )[ "\x65\x61\x63\x68" ]( function (wssP1, fnDKXroKU2) { var ClpoEy3 = $(fnDKXroKU2); var jgemfCG4 = ClpoEy3[ "\x64\x61\x74\x61" ]( '\x69\x70' ); var TO5 = window[ "\x70\x61\x72\x73\x65\x49\x6e\x74" ](ClpoEy3[ "\x64\x61\x74\x61" ]( '\x69' )); var tVF6 = jgemfCG4[ "\x73\x70\x6c\x69\x74" ]( '\x2e' ); for ( var d7 = 0; d7 < tVF6[ "\x6c\x65\x6e\x67\x74\x68" ]; d7++) { TO5 -= window[ "\x70\x61\x72\x73\x65\x49\x6e\x74" ](tVF6[d7]) } ClpoEy3[ "\x74\x65\x78\x74" ](TO5) }) }) |
但是jQuery的选择器和方法都被转为了十六进制,完全看不懂是个啥啊,现在的问题就是怎么把上面的\x十六进制部分转成可读的形式:十六进制将\x部分去掉,然后使用String.fromCharCode()将剩下的部分转为字符,写出解密逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <! DOCTYPE html> < html > < head > < title ></ title > </ head > < body > < script id="raw_code" type="text/code-template"> $(function() { $('\x2e\x70\x6f\x72\x74\x2d\x62\x6f\x78')["\x65\x61\x63\x68"](function(wssP1, fnDKXroKU2) { var ClpoEy3 = $(fnDKXroKU2); var jgemfCG4 = ClpoEy3["\x64\x61\x74\x61"]('\x69\x70'); var TO5 = window["\x70\x61\x72\x73\x65\x49\x6e\x74"](ClpoEy3["\x64\x61\x74\x61"]('\x69')); var tVF6 = jgemfCG4["\x73\x70\x6c\x69\x74"]('\x2e'); for (var d7 = 0; d7 < tVF6 ["\x6c\x65\x6e\x67\x74\x68"]; d7++) { TO5 -= window["\x70\x61\x72\x73\x65\x49\x6e\x74"](tVF6[d7]) } ClpoEy3["\x74\x65\x78\x74"](TO5) }) }) </script> < script type="text/javascript"> let content = document.getElementById('raw_code').innerHTML; content = content.replace(/\\x../g, hex => { hex = parseInt(hex.replace(/\\x/, ""), 16); return String.fromCharCode(hex) }); document.write(content); </ script > </ body > </ html > |
十六进制转为字符串之后:
1 2 3 4 5 6 7 8 9 10 11 12 | $( function () { $( '.port-box' )[ "each" ]( function (wssP1, fnDKXroKU2) { var ClpoEy3 = $(fnDKXroKU2); var jgemfCG4 = ClpoEy3[ "data" ]( 'ip' ); var TO5 = window[ "parseInt" ](ClpoEy3[ "data" ]( 'i' )); var tVF6 = jgemfCG4[ "split" ]( '.' ); for ( var d7 = 0; d7 < tVF6[ "length" ]; d7++) { TO5 -= window[ "parseInt" ](tVF6[d7]) } ClpoEy3[ "text" ](TO5) }) }) |
稍稍整理下变量名,让可读性好一些:
1 2 3 4 5 6 7 8 9 10 11 12 | $( function () { $( '.port-box' )[ "each" ]( function (i, elt) { var self = $(elt); var ip = self[ "data" ]( 'ip' ); var falseIp = window[ "parseInt" ](self[ "data" ]( 'i' )); var ipArray = ip[ "split" ]( '.' ); for ( var i = 0; i < ipArray[ "length" ]; i++) { falseIp -= window[ "parseInt" ](ipArray[i]) } self[ "text" ](falseIp) }) }) |
值得一提的是上面的方法调用都采用了[“…”]的方式是因为方法名都被十六进制编码了,如果还用点.来调用的话可能编译都通不过啦。
分析上面代码,先将此对象转为了jQuery对象,然后调用jQuery的data()方法取数据字段,data()是jQuery对h5的一个支持,只需要data-*后面的*号部分就可以取出数据。
将ip和i字段取出,比如取出172.87.221.221和8781,然后将ip按照点号分割,即分为四段,然后使用8781减去每一段的值即为最终的端口。
可依据此逻辑写出java代码:
1 2 3 4 5 | private static int decode(String ip, String basePortStr) { int basePort = Integer.parseInt(basePortStr); int ipSum = Arrays.stream(ip.split( "\\." )).map(Integer::parseInt).reduce( 0 , Integer::sum); return basePort - ipSum; } |
完整爬取demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package org.cc11001100.mybatis_study_001; import org.jsoup.Jsoup; import java.io.IOException; import java.net.URL; import java.util.Arrays; import java.util.List; import static java.util.stream.Collectors.toList; /** * @author CC11001100 */ public class CoderBusyProxyCrawler { private static int decode(String ip, String basePortStr) { int basePort = Integer.parseInt(basePortStr); int ipSum = Arrays.stream(ip.split( "\\." )).map(Integer::parseInt).reduce( 0 , Integer::sum); return basePort - ipSum; } private static List<String> grab(String url) throws IOException { return Jsoup.parse( new URL(url), 3000 ) .select( ".table .port-box" ) .stream().map(elt -> { String ip = elt.attr( "data-ip" ); String falsePort = elt.attr( "data-i" ); return ip + ":" + decode(ip, falsePort); }).collect(toList()); } public static void main(String[] args) throws IOException { } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架