首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
v2ray节点搭建
985 阅读
2
软件添加id功能按钮
903 阅读
3
QQ扫码无法登录的解决方案
794 阅读
4
网易云音乐歌单ID获取教程
660 阅读
5
typecho非常有特色的模块
646 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
rust
flutter
esp32c3
springboot
安卓
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
尽意
累计撰写
110
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
110
篇与
的结果
2024-08-16
idea插件开发
插件商店下载插件 Plugin Devkit新建项目 选择IDE插件jdk版本跟ida参考https://plugins.jetbrains.com/docs/intellij/creating-plugin-project.html#creating-a-plugin-with-new-project-wizard2024.2+ 版本需要使用java212022.2 - 2024.2 版本需要使用java17 支持创建项目完成之后,先停止gradle的构建修改几个文件将build.gradle.kts setting.gradle.kts 的后缀名.kts去掉kotlin目录修改为java修改后的目录修改plugin.xml配置文件,可用看到这些爆红的地方都是要修改的,不修改也不影响运行,还是改一下好了修改build.gradle文件plugins { id 'java' id 'org.jetbrains.kotlin.jvm' version '1.9.23' id 'org.jetbrains.intellij' version '1.17.3' } group = 'com.suxii' version = '1.0.0' repositories { mavenCentral() } intellij { // 改为本地路径 localPath = 'D:\\Program Files\\JetBrains\\IntelliJ IDEA 2024.1.2' plugins = [/* Plugin Dependencies */] } tasks.withType(JavaCompile).configureEach { sourceCompatibility = '17' targetCompatibility = '17' options.encoding = "UTF-8" } tasks.patchPluginXml { sinceBuild = '232' untilBuild = '242.*' } tasks.signPlugin { certificateChain = System.getenv('CERTIFICATE_CHAIN') privateKey = System.getenv('PRIVATE_KEY') password = System.getenv('PRIVATE_KEY_PASSWORD') } tasks.publishPlugin { token = System.getenv('PUBLISH_TOKEN') } 修改完成后点击图标重新同步一下右键新建-》选择Swing UI设计器-》GUI窗体输入窗体名字可用看到自动生成了.form文件点这个.form文件就可用进行可视化布局补充一下相关布局模式的简单说明布局模式的简单说明: BorderLayout(边框布局): 描述:这是一个经典的布局管理器,将组件分布在五个区域:北(North)、南(South)、东(East)、西(West)和中(Center)。 使用场景:适用于创建结构良好的界面,主要内容位于中央,次要组件(如工具栏或状态栏)位于边缘。 FlowLayout(流式布局): 描述:组件依次水平或垂直排列,空间不足时会自动换行。 使用场景:适合简单表单或工具栏的布局,组件应该自然流动。 GridLayout(网格布局): 描述:将容器分成均匀的网格,每个组件占据一个网格单元。 使用场景:适用于需要将多个组件整齐排列成行列的场景,例如表格或控制面板。 GridBagLayout(网格包布局): 描述:类似于网格布局,但每个组件可以跨越多个网格单元,且可以对齐、填充和加权。 使用场景:适合复杂的用户界面,组件大小不一致且需要精确定位。 CardLayout(卡片布局): 描述:允许在同一个容器内切换多个组件,每次只显示一个组件。 使用场景:适合实现多步表单或选项卡式界面,每张卡片代表一个页面。 GroupLayout(组布局): 描述:组件根据指定的水平和垂直分组对齐,通常与布局设计器一起使用。 使用场景:适合需要精确控制组件对齐和排列的复杂界面。简单搭建一下布局注意要给最顶上的JPanel一个field name属性然后在我们的类中提供一个set方法新建一个工厂方法实现 ToolWindowFactory 接口public class WindowFactory implements ToolWindowFactory { @Override public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { // 实例化窗口类 CodeWindow codeWindow = new CodeWindow(); // 获取上下文 ContentFactory cf = ContentFactory.getInstance(); Content content = cf.createContent(codeWindow.getRoot(), "", false); toolWindow.getContentManager().addContent(content); } } 在文件将工厂类注册 <extensions defaultExtensionNs="com.intellij"> <!-- 创建一个窗体 --> <toolWindow factoryClass="com.suxii.WindowFactory" id="super code" anchor="right"/> </extensions>factoryClass为工厂类,anchor是窗体显示的位置,inco属性是设置图标,不设置使用默认的运行插件项目双击 Run Plugin 这时候会重新启动一个idea,等待加载完成可用看到插件已经显示出来了项目打包双击jar任务生成的文件在build目录下的libs里本地安装插件打开插件市场,选择从磁盘安装,找到我们打包的插件安装即可已经按照成功了{dotted startColor="#ff6c6c" endColor="#1989fa"/}这里还会有个问题,在设置完field name属性之后的按钮,在设置了点击事件后,按钮的文字就不显示了 copyBtn.setAction(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // 点击事件 } })解决方案:AbstractAction copyAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // 点击事件 } }; copyAction.putValue(Action.NAME,"复制");
2024年08月16日
118 阅读
0 评论
3 点赞
2024-08-13
js逆向xhs(二)
分享另一种思路,要明确我们最终的目的都是要拿到加密函数的返回值,这期间做的任何准备都是为了加密值做服务的,所以可以使用websocket作为通信,间接调用加密函数,拿到加密值使用netty作为ws服务器public class WebSocketServer { public static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); public static void main(String[] args) throws Exception { NioEventLoopGroup bossGroup = new NioEventLoopGroup(); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); p.addLast(new HttpServerCodec()); p.addLast(new HttpObjectAggregator(65536)); p.addLast(new HttpContentCompressor()); p.addLast(new WebSocketServerProtocolHandler("/")); p.addLast(new WebSocketFrameHandler()); } }); System.out.println("server run ..."); ChannelFuture future = bootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } static class WebSocketFrameHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); System.out.println(ctx.channel().remoteAddress()+":connect"); channelGroup.add(ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); channelGroup.remove(ctx.channel()); System.out.println(ctx.channel().remoteAddress()+":disconnect"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof WebSocketFrame) { WebSocketFrame frame = (WebSocketFrame) msg; if (frame instanceof TextWebSocketFrame) { if (channelGroup.size()<2) return; channelGroup.stream().filter(ch->ch != ctx.channel()).forEach(ch->ch.writeAndFlush(frame)); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { System.out.println(ctx.channel().remoteAddress()+"连接出错:"+cause.getMessage()); ctx.close(); } } }在浏览器环境运行jsxhs这里由于用到了window全局变量储存加密函数,所以我们只要在控制台或者代码段地方执行我们直接的代码逻辑,因为是在xhs的网站作用域下,是可以直接使用全局变量的const singletonFunction = (function() { let instance; let executed = false; function createInstance() { if (!executed) { var sgin = window._webmsxyw; const ws = new WebSocket('ws://127.0.0.1:8080/'); ws.onopen = function(evt){ ws.send("我已连接,状态良好"); console.log("我已连接,状态良好"); } ws.onmessage = function(evt){ var data = JSON.parse(evt.data); console.log(data,data.c,data.i); ws.send(JSON.stringify(sgin(data.c,data.i))); } executed = true; } } return function() { if (!instance) { instance = createInstance; } return instance; }; })(); singletonFunction()();这里注入的方案有很多,也可以使用代理添加我们自己的逻辑,或者编写油猴脚本也是可以的。说一下手动注入吧,原理都大差不差点开全部选项,选择替换点击选择放置替换的文件夹选择文件夹后允许,勾选上本地替换依旧是找到加密函数的位置,通过搜索匹配右键该文件,选择替换内容将我们的代码添加到指定位置即可现在保存再刷新网页,就会走我们本地的js代码启动服务端先用websocket在线测试网站查看运行情况https://www.wetools.com/websocket是能成功拿到加密后的数据的接下来就是在项目中拿到这里加密的内容了使用okhttp连接ws服务器def client = new OkHttpClient() def request = new Request.Builder().url("ws://localhost:8080").build() client.newWebSocket(request,new WebSocketListener() { @Override void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text) println text } })现在加密值也拿到了,带上加密值正常请求接口即可
2024年08月13日
197 阅读
0 评论
3 点赞
封装和抽象 Java 的构建者模式
创建一个通用的 Builder 接口或抽象类,并将具体的构建过程委托给子类或具体实现类。这样做可以复用构建逻辑,并且提高代码的可扩展性和维护性。1. 抽象 Builder 类首先,我们可以定义一个抽象的 Builder 类,这个类会定义一个通用的构建流程,并且要求子类去实现具体的构建步骤。public abstract class Builder<T> { protected T instance; // 提供创建对象的通用方法 protected abstract T createInstance(); // 每个具体的构建步骤由子类实现 public Builder<T> with(Consumer<T> setter) { setter.accept(instance); return this; } // 返回最终构建的对象 public T build() { return instance; } }2. 具体实现 UserBuilder继承上面的抽象 Builder 类,针对具体的 User 对象,我们可以实现一个 UserBuilder:public class User { private String firstName; private String lastName; private int age; private String phone; private String address; // 公开构造函数或私有构造函数 public User() { } // getter和setter public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } public class UserBuilder extends Builder<User> { @Override protected User createInstance() { return new User(); } public UserBuilder() { this.instance = createInstance(); } public UserBuilder firstName(String firstName) { return this.with(user -> user.setFirstName(firstName)); } public UserBuilder lastName(String lastName) { return this.with(user -> user.setLastName(lastName)); } public UserBuilder age(int age) { return this.with(user -> user.setAge(age)); } public UserBuilder phone(String phone) { return this.with(user -> user.setPhone(phone)); } public UserBuilder address(String address) { return this.with(user -> user.setAddress(address)); } }3. 使用方式使用抽象后的 Builder 类构建 User 对象:public class Main { public static void main(String[] args) { User user = new UserBuilder() .firstName("John") .lastName("Doe") .age(30) .phone("123456789") .address("123 Main St") .build(); System.out.println(user.getFirstName()); System.out.println(user.getLastName()); System.out.println(user.getAge()); System.out.println(user.getPhone()); System.out.println(user.getAddress()); } }4. 优势通过这种方式:通用性:Builder 类可以复用,也可以通过创建不同的具体构建器(如 UserBuilder)来构建不同的对象。可扩展性:如果以后有其他复杂对象需要使用构建者模式,只需要继承 Builder 类,提供具体的实现即可。简化构建流程:with 方法的引入使得构建过程更加简洁和灵活。这个模式让你的代码不仅易于维护,而且能很好地应对未来的扩展需求。
2024年08月13日
114 阅读
0 评论
2 点赞
groovy调用node执行js
目前好像没有更好的方案去实现在node环境下执行js,这种方案需要配合js代码,目标js只能为这个groovy服务groovy代码// Groovy script to execute Node.js script static def runJsScript(String scriptPath, String argument) { // Create a command def command = ["node", scriptPath, argument] // Create a ProcessBuilder with the command def processBuilder = new ProcessBuilder(command) // Start the process def process = processBuilder.start() // Capture the output of the process def output = new StringWriter() def error = new StringWriter() process.consumeProcessOutput(output, error) // Wait for the process to complete process.waitFor() // Return the output and error streams return [output.toString().trim(), error.toString().trim()] } // Path to the JavaScript file def scriptPath = "./a.js" // Argument to pass to the JavaScript function def argument = "World" // Call the JavaScript function def (output, error) = runJsScript(scriptPath, argument) if (error) { println "Error: $error" } else { println "Output: $output" } js代码function greet(name) { return `Hello, ${name}!`; } console.log(greet(process.argv[2]));需要传参多少个参数,需要在js块拼接 process.argv[2] process.argv[3]执行多个参数groovystatic def runJsScript(String scriptPath, String... arguments) { def command = ["node", scriptPath] arguments.each {command.add(it)} def processBuilder = new ProcessBuilder(command) def process = processBuilder.start() def output = new StringWriter() def error = new StringWriter() process.consumeProcessOutput(output, error) process.waitFor() return [output.toString().trim(), error.toString().trim()] }jsfunction greet(name,value) { return `Hello, ${name}: ${value}`; } console.log(greet(process.argv[2],process.argv[3]));{dotted startColor="#ff6c6c" endColor="#1989fa"/}其实还是有个小坑 传参json会被解析成js对象,就导致js拿到的数据已经是处理过的了,后面的流程是会受影响的。 解决方案:可以在添加额外参数时进行检验,判断当前字符串是否可以被转换为json对象,如果可以就做转义处理,str.replace('"','\"') 可以封装成一个工具类使用 class Execute { static def js(String scriptPath, String... arguments) { def command = ["node", scriptPath] // 如果是json格式,就进行转义,防止传参过程中被解析 arguments.each { if (isValidJson(it)) { command.add(it.replace('"','\\""')) }else command.add(it) } def processBuilder = new ProcessBuilder(command) def process = processBuilder.start() def output = new StringWriter() def error = new StringWriter() process.consumeProcessOutput(output, error) process.waitFor() if (error!=null && (error as String) != ""){ throw new Exception("js脚本出错:+$error") } return output.toString().trim() } // 判断字符串是不是json格式 static private boolean isValidJson(String str) { def s = new JsonSlurper() try { s.parseText(str) return true } catch (Exception e) { return false } } }
2024年08月12日
181 阅读
0 评论
4 点赞
2024-08-11
js逆向xhs
首先需要先确定目标接口打开开发者工具选择网络网络请求比较多,不太好定位,先Ctrl+L清空一下网络日志随便点击一个标签,从上往下看可以看到 hoomfeed 的响应是有我们要的数据可以多刷新几次,看一下请求头里面的参数哪些是固定的然后右键复制,以curl bash格式到这个网站生成一下请求代码测试一下能不能跑通https://curlconverter.com/我使用的okhttp不过有一点需要注意,请求体的json字符串不能带有空格,还是要注意一下,一般还是要看下负载源代码给格式,保持一致就没问题了 OkHttpClient client = new OkHttpClient(); String jsonBody = "{\"cursor_score\":\"\",\"num\":18,\"refresh_type\":1,\"note_index\":32,\"unread_begin_note_id\":\"\",\"unread_end_note_id\":\"\",\"unread_note_count\":0,\"category\":\"homefeed.fashion_v3\",\"search_key\":\"\",\"need_num\":8,\"image_formats\":[\"jpg\",\"webp\",\"avif\"],\"need_filter_image\":false}"; // 注意这里是json格式 RequestBody requestBody = RequestBody.create(jsonBody,MediaType.parse("application/json; charset=utf-8")) Request request = new Request.Builder() .url("https://edith.xiaohongshu.com/api/sns/web/v1/homefeed") .post(requestBody) .header("authority", "edith.xiaohongshu.com") .header("accept", "application/json, text/plain, */*") .header("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6") .header("cache-control", "no-cache") .header("content-type", "application/json;charset=UTF-8") .header("cookie", "abRequestId=e3bdcd27-e1b5-5f53-89b5-7e2802d396e0; xsecappid=xhs-pc-web; a1=1913b4ae942vlubadspzq6n0dzv1sn1s16vme85li50000892240; webId=f51031180df1b91abeef5db1d71629e1; web_session=030037a184491809ffa8205bcf214a315d86ed; gid=yjyqD40iYWyfyjyqD40djKY24Jh17D0fMUuAh13KI8fuh628yMIyMd888YjJJ488KD82yj0y; webBuild=4.28.5; unread={%22ub%22:%2266986e9b0000000003027356%22%2C%22ue%22:%226699c440000000000d00fd0f%22%2C%22uc%22:23}; acw_tc=7c07ab06520fd63e04c63f0273004a3337c1cd30cc32b06c921ddff420388b1e; websectiga=3fff3a6f9f07284b62c0f2ebf91a3b10193175c06e4f71492b60e056edcdebb2; sec_poison_id=8ef6b86b-8ad1-4aa5-9ec4-e3309f2368b3" ) .header("origin", "https://www.xiaohongshu.com") .header("pragma", "no-cache") .header("referer", "https://www.xiaohongshu.com/") .header("sec-ch-ua", "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Microsoft Edge\";v=\"122\"") .header("sec-ch-ua-mobile", "?0") .header("sec-ch-ua-platform", "\"Windows\"") .header("sec-fetch-dest", "empty") .header("sec-fetch-mode", "cors") .header("sec-fetch-site", "same-site") .header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0") .header("x-b3-traceid", "1c3f0fbf2fac047b") .header("x-s", "XYW_eyJzaWduU3ZuIjoiNTMiLCJzaWduVHlwZSI6IngyIiwiYXBwSWQiOiJ4aHMtcGMtd2ViIiwic2lnblZlcnNpb24iOiIxIiwicGF5bG9hZCI6ImQ3ZTU0NWYyNTMzM2I2MWYxMjFlMmJmZGI4ZThhMGZkMDg2Mzg0NWY2ZTU3MzEyM2IzN2JmMjIyZDU5MGE4ZDg2NjQ3ODEyMzBkYWU5ODVjMjMzYzdiNDUxNjBkYmQ3NDdkZWY5ZDBmZDBhNWQ0YzAzMzE3OGRlNGYyYmQ3YTgyNGNkZTYxZDgzNDMxNmQ3OWQ1NTFjY2Q5YjJlYTEyZWM2YWNlMjM3ZjVjZjQzMDllZTg0OGU5NTQwZGEwY2NhN2IyZWJlNjMzODdhOGE3ZTAwNDM2Yzk2NmI5OWM4MWUzM2IxNDMyYjk0MDEyOGY3ZjcwMWU4Yjc2MTYyOTFiZDBmN2UxNDYxNWFhZjJiZDExMDBiYWRiNWM5NTFiZjc2N2M1ZWM5NDkxYWJiOTUzOGYwMzc5ZTY1YzgzOGQ1ZGVjODdkMjZlZmQzM2M5OGY0NGVlMjQzODlmNDE4YjUyNWY4ZmEyYzE0YjU4ZmZkOWZhMmYxZTcwMzhiNjQ1YTVlODhhOTBlYTRlYjRiZmM1NGJiMWIyODlkY2ExNDExYTk5In0=" ) .header("x-s-common", "2UQAPsHC+aIjqArjwjHjNsQhPsHCH0rjNsQhPaHCH0P1+UhhN/HjNsQhPjHCHS4kJfz647PjNsQhPUHCHdYiqUMIGUM78nHjNsQh+sHCH0c1P0W1+aHVHdWMH0ijP/DlP9HAPAG7PePhq/414ozIwB8VwgL7J74xJ/b7Jg8h4ebE+7WM2eGMPeZIPecl+0WFPsHVHdW9H0il+AHAP0qMP/PIweZINsQh+UHCHSY8pMRS2LkCGp4D4pLAndpQyfRk/Sz+yLleadkYp9zMpDYV4Mk/a/8QJf4EanS7ypSGcd4/pMbk/9St+BbH/gz0zFMF8eQnyLSk49S0Pfl1GflyJB+1/dmjP0zk/9SQ2rSk49S0zFGMGDqEybkea/8QJpp7/gknybkxpgkyyDDl/p4b2DRrpgSyzBTE/M4b2DRLyBkwzrrF/nkByDETpfMwzM83nDz02pSLy7Y82DDlnnksJrMoa/QwzMpE/dkiJbkLcgS82Dp7/dkVyrET/gSwzFShnnksJLEx8BT+pbrA/DzdPrMxagkwpB+E/nk8PbkLa/+w2SS7/Lz8+pSCyAmOzFEi/gkwybSCGA++zFFF/Lzz2bSCJBl+2flk/p4+2LMozgYyJpbhnDzb2SkTp/z+PSrFnfMtypkoLgSwPDM7/FzayDMCpfMwpMrF/nkbPDMxc/m+zb8TnSzp2Skg/g48PDDFnfM+2LMLa/++PDLA/FzaJrMLn/b+pFDU/nkByrMLpg4OzBz3ngk0PrEgpfS+ySb7/fMayrELLfTwzrEi/dkm+rRopgS8yf+h/0QBybSTL/zypFEk/nk8PrhUL/QwzrpCn/Qz2DRrLgY82SQx/fMayDRLc/mypBzTnDzm4MSL/fSyySLI/fM8+rhUpfT+zbrlnDzd+LEgaflyySpE/LzmPMDUpfYwJLFM/Lzz+LMrzgkw2D8ingkz2rECyBlyySk3/fk8+bSxL/++PSSC//QnyDhUz/zypb8knSzd2SSTLfT8yfqAnpz+PrMLcgk8prki//QnJpSgpfS+ySrInDz8+pkrGAp+yfVA/FznJbkT//++2SDFn/QyyMkTp/+wyDFF/LziyMSx87kypB+7/F4+2rExG7Y82f+hanhIOaHVHdWhH0ija/PhqDYD87+xJ7mdag8Sq9zn494QcUT6aLpPJLQy+nLApd4G/B4BprShLA+jqg4bqD8S8gYDPBp3Jf+m2DMBnnEl4BYQyrkSL98+zrTM4bQQPFTAnnRUpFYc4r4UGSGILeSg8DSkN9pgGA8SngbF2pbmqbmQPA4Sy9MaPpbPtApQy/8A8BE68p+fqpSHqg4VPdbF+LHIzBRQ2sTczFzkN7+n4BTQ2BzA2op7q0zl4BSQyopYaLLA8/+Pp0mQPM8LaLP78/mM4BIUcLzTqFl98Lz/a7+/LoqMaLp9q9Sn4rkOqgqhcdp78SmI8BpLzS4OagWFprSk4/8yLo4ULopF+LS9JBbPGf4AP7bF2rSh8gPlpd4HanTMJLS3agSSyf4AnaRgpB4S+9p/qgzSNFc7qFz0qBSI8nzSngQr4rSe+fprpdqUaLpwqM+l4Bl1Jb+M/fkn4rSh+nLlqgcAGfMm8p81wrlQzp+CanYb8aTmzDzQPFpcaFDhcDSkpA4yLo4+ag8oy0zA8g+8aLEA2b87LFSe+9pfw/8SPB8bwrSkcnL9p7Q/wob7pLSb+7Pl80mA+S4m8nSn4o4IyDRSpM874LS9ngm0Pe8S8bm7c7ZE+7+gpdqhagYQPDDAwBSAqg4y8M8FLokdwbb7GFESyfc68nSl49RQyrbALURmq9TBLSSQyURAPrM9q9TDz/mQyB4Ayp87zrSiz9zzqg4twopFwLDAP9LALoznanSwqM8n47YQzaRA8S8F/LEn4FkSqg4Bag8lzLShpjuUwgpfa9+d8gYM4e4QyBWhanDI8p+c49EUngkU/Bi7qM4AtM8QypmSqpmF/Mkc4F+Q40+SPMmF4aTr/fpn204ApD8L8LSi87PAJFRAypmFGgmM49bQ4f4SPop7yrSkN9pL8nSSagG68p+sN9p/qgzGagYNq7YM4MLF8pbFaLprtFS9+7+haLEA2rbdqAm0+7+DLo4kag8HpFS9aL80Lo4yaL+QLDSi87+n/ezV/fktq98l4ozQyoZUcdbFPLYQ87+/zepSPop7qBRc474Q4SkGanSc4B+c49blLo4ManSS8/mCq/FjNsQhwaHCN/DE+AcI+eH9PsIj2erIH0iU+dF=") .header("x-t", "1723275130800") .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); def res = response.body().string(); println res } 直接运行是没有问题的,然后就要看哪些参数是会影响请求的结果在请求头可以看到像 x-s x-s-common 等这一类的参数明显就是加密过的,而且一般情况也会作为必要参数,x-t很明显是个时间戳可以试一下将这几个参数注释掉运行,发现只有x-s的值不正确或者没有的情况下是拿不到请求结果的,现在也就明确了要找x-s的加密函数可以跟栈去找加密函数,找到在那两个函数或者文件内容由明文变成密文,或者直接搜索关键字勾选上正则匹配,只有7个匹配项可以一个个都看一下,不放心的话也可以都先打上断点,明显可以看到只有这里才有赋值语句,那就在这打上断点,看到调用了l(a && void 0 !== window._webmsxyw ? window._webmsxyw : encrypt_sign)(c, i) || {};三元表达式调用了window._webmsxyw函数,传参c,i在控制台调用这个函数现在加密函数已经差不多确定了进入到函数内部查看经过了大量混淆,这段代码想要扣下来还是比较麻烦的,基本上是没有一点可读性那就直接整个文件都拿下来,补环境,只要能跑通里面的window._webmsxyw就能拿到加密的值了新建一个包xhs,将复制下来的源代码粘贴到course.js文件中,为了更好的解耦,我们准备三个js文件,course存放源代码,env作为补环境的代码,xhs则写我们输入加密函数的代码跟逻辑然后在xhs文件里面引入require("./env")require("./sourse")注意一下要先引入环境,不然sourse会因为没有环境报错env使用代理看到哪些函数方法被调用了,却少什么补充什么就行了 function setProxy(proxyObjs) { for (let i = 0; i < proxyObjs.length; i++) { const handler = `{ get: function(target, property, receiver) { if (property!="Math" && property!="isNaN"){ if (target[property] && typeof target[property] !="string" && Object.keys(target[property]).length>3){ }else{ console.log("方法:", "get ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", target[property]);}} return target[property]; }, set: function(target, property, value, receiver) { console.log("方法:", "set ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]); return Reflect.set(...arguments); } }`; eval(`try { ${proxyObjs[i]}; ${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler}); } catch (e) { ${proxyObjs[i]} = {}; ${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler}); }`); } } setProxy(['window', 'document', ' navigator', 'screen', 'localStorage', 'location']) 补环境的代码env.js// 补环境 window window = global delete global delete Buffer // document document = { createElement: function(x){ if (x === "canvas"){ return { getContext: function(){} } } }, documentElement:{}, cookie:'abRequestId=e3bdcd27-e1b5-5f53-89b5-7e2802d396e0; xsecappid=xhs-pc-web; webId=f51031180df1b91abeef5db1d71629e1; web_session=030037a184491809ffa8205bcf214a315d86ed; gid=yjyqD40iYWyfyjyqD40djKY24Jh17D0fMUuAh13KI8fuh628yMIyMd888YjJJ488KD82yj0y; webBuild=4.28.5; unread={%22ub%22:%2266986e9b0000000003027356%22%2C%22ue%22:%226699c440000000000d00fd0f%22%2C%22uc%22:23}; acw_tc=7c07ab06520fd63e04c63f0273004a3337c1cd30cc32b06c921ddff420388b1e; websectiga= sec_poison_id=8ef6b86b-8ad1-4aa5-9ec4-e3309f2368b3' } // localStorage localStorage = { getItem: function (){ // return '{"signVersion":"1"}' return '{"validate":true,"commonPatch":["/fe_api/burdock/v2/note/post","/api/sns/web/v1/comment/post","/api/sns/web/v1/note/like","/api/sns/web/v1/note/collect","/api/sns/web/v1/user/follow","/api/sns/web/v1/feed","/api/sns/web/v1/login/activate","/api/sns/web/v1/note/metrics_report","/api/redcaptcha","/api/store/jpd/main","/phoenix/api/strategy/getAppStrategy"],"signUrl":"https://fe-video-qc.xhscdn.com/fe-platform/bccb34c4f2976c51b565494c0a760c42d962b25b.js","signVersion":"1","url":"https://fe-video-qc.xhscdn.com/fe-platform/a1c872577b980b890b0850cef89371b35125649a.js","reportUrl":"/api/sec/v1/shield/webprofile","desVersion":"2"}' } } // navigator navigator = { appCodeName: "Mozilla", appName: "Netscape", appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36' } xhs.jsrequire("./env") require("./sourse") const c = '/api/sns/web/v1/homefeed' const i = { "cursor_score": "", "num": 18, "refresh_type": 1, "note_index": 32, "unread_begin_note_id": "", "unread_end_note_id": "", "unread_note_count": 0, "category": "homefeed.fashion_v3", "search_key": "", "need_num": 8, "image_formats": [ "jpg", "webp", "avif" ], "need_filter_image": false } function getX(){ return window._webmsxyw(c,i) } console.log(getX()['X-s'])至此加密函数补环境就结束了{dotted startColor="#ff6c6c" endColor="#1989fa"/}不过还是有点小坑localStorage.getItem 直接返回一个空函数是会诱导生成错误的数据,我一开始就是返回的空函数,然后验证发现加密值是错的,因为使用了代理,可以看到最近的函数就是localStorage.getItem可以通过hook getItem函数拿到传参是多少localStorage.getItem_ = localStorage.getItem;localStorage.getItem = function(x){console.log(x);localStorage.getItem_(x);}再执行window._webmsxyw函数就能看到getItem传参有了参数,直接在控制台 localStorage.getItem("sdt_source_storage_key")拿到参数即可,现在就能正确拿到正确的加密值了
2024年08月11日
560 阅读
2 评论
4 点赞
1
...
9
10
11
...
22