小程序安全防护-微信网关&小程序加固突破(下)

小程序安全防护-微信网关&小程序加固突破(下)

image

写在前面

以下都是情节模拟,并非具体情况,请细加甄别

在上次对某克的小程序测试后,我得出了目前微信对小程序的防护措施(加固+网关)的方式,无法拦截掉本地动态调试的判断,但不知道是业务催得紧还是老板不死心还是微信又推产品了,总之,老板让我去上海跑个小差,参加一下微信的线下公开课,和他们好好讨论一下这个问题……

image

事前准备

纵使在公开课上对来会嘉宾介绍的非常有利,下会后当我当面提出 DevTools 本地动态调试的方法时,商务解释不了推给产品,产品解释不了提问经理,经理找来研发同学,肯定了我的说法,即:

  1. 目前微信网关更倾向于流量监测和风控行为的评估,请求在客户端封装后发往微信网关,再由网关转发至原服务端,这点与认知相同;

  2. 小程序加固目前针对的个体是.wxpkg的包,方法和我们认知的混淆、加壳一致(三级防护增大30-40%体积,五级防护增加三倍体积),无法动态调试指的是获得这个包后无法在本地微信开发工具中调试;

  3. 关于前端通过DevTools进行调试得到js中参数值和代码逻辑的防护,与他们的技术沟通过表示只能在js中加入禁止调试的代码(但也讨论了可以绕过)

即使这样还无法让老板信服,很简单,如果这几点是真的,那就把某特这个被微信誉为2025年上半年最佳小程序安全防护实践的项目弄下来试试

image

使用工具

  • 微信 PC 客户端最新版本

  • WMPFDebugger

  • Cheat Engine(CE)

  • 等等(市面上真的不少)

“受害者”入场

  • 小程序:某特

开始动手

某特

书接上回,某特的小程序要求微信版本不低于4.1.1(后来知道是不低于最新版本微信),但实际上这个限制可以绕过,甚至可以不绕过

你可以通过 Cheat Engine(CE),去搜寻低版本的地址,如 3.8.0.33 的十六进制为 63080021,找到对应地址后改成 64010224(4.1.2.36的十六进制),即可绕过版本限制,版本转换

image

然后你会发现提示你需要在移动端打开……但这个绕过也很容易,其实这个就是类似 wx.getSystemInfo() 这种接口获取设备信息,获取到字段为 windows,就跳转到限制页面了,你都可以打开 Debugger 了,在跳转前查到调用接口,修改参数值就可以了

image

image

可以看到确实是做了混淆的,但是可以被动态调试,即使混淆也能看到传参过程和内容的,只要肯有耐心,但这里我就不继续了,我的目的已经达到了,我又不是灰产= =

image

写在后面

纵使这番,老板也就消停了一会,两三周后又来问我,能不能和微信信用分关联啥的了,我只能说,安全没有否决业务阻拦生产的权利(至少在这家公司没有),只能识别到风险给足建议了,只是看(老板你)能不能说服业务了

image

小程序安全防护-微信网关&小程序加固突破(上)

小程序安全防护-微信网关&小程序加固突破(上)

image

写在前面

以下都是情节模拟,并非具体情况,请细加甄别

在25年中,业务以“专门下载 APP 太慢啦,我们要小程序操作”为由,施压产研,希望要求把开门等业务重要逻辑重新放回小程序,于是老板找我评估一下安全性

拜托,即使我是23年才来,我也是半接手把开门从小程序“入土”的,现在又要改回去,开什么玩笑!

“我也理解你,所以微信那边给出了一个方案,说是接微信网关和小程序加固就可以保证安全性,你看看”

然后我就被拉入了相关群组,也就意味着去测试微信网关等一系列小程序安全措施了

image

事前准备

其实之前在推业务下线小程序的时候,我就做过测试,论证了为什么小程序的相关实现不安全,因为从根本来说,小程序可以被理解为是微信内置浏览器渲染出的前端而已,是可以被动态调试的,一如浏览器 F12 后 Debugger

当时我还研究过 .wxapkg 的逆向拆解,还原代码之类的,可以参考:

拼图碎片:反编译小程序、数据加密解密、sign值绕过

但其实完全没必要这么麻烦……

使用工具

话说回来,明明企业微信自己都带 DevTools = =

“受害者”入场

就来两个已经接入和采用方案的小程序试试呗

image

  • 小程序:某克、某特

开始动手

某特

先假装不敌,败下阵来

image

某特的小程序要求微信版本不低于4.1.1(后来知道是不低于最新版本微信),但实际上这个限制可以绕过,先不浪费时间,没事,还有下篇呢

某克

完全不是对手,技术力感觉不如我这家公司= =

主要采集了三个请求和响应点:商品分类、个人主页、个人主页-地址簿

商品分类

image

image

可以明显地看到请求头和请求体是明文的,没有用特殊协议,也没有进行加密

个人主页

image

image

在 events 中可以看到本地客户端信息和与 api.nike.com.cn 的关联信息

个人主页-地址簿

image

image

作为用户隐私信息之一的地址信息,耐克是用 GET 请求,通过 upmid 去分配用户存储路径返回的

隐藏请求

image

image

在测试的过程中,仅发现了“sa?project=MiniProgram”下的 Payload 类似加密,但解析过后发现,仅仅是 Base64 编码后 URL 编码了一下,不算加密,也不是微信自身的协议形式:

image

image

动态调试

以 upmid 为着手点,动态调试得到用户地址的获取过程:

image

写在后面

说实话,某克测下来给我感觉完全不行啊,好像都没什么防护措施,后续也向微信那边再次确认了,他们的措辞是“可能是因为接入的是已经很久前的版本了巴拉巴拉”,那么就请看下篇吧

image

PC端WeChat逆向注入

PC端WeChat逆向注入

image

写在前面

首先是好久都没写原创的技术博客了,上一篇还是去海参崴的旅行回记:滨海远东之后(After Vladivostok)

起笔这篇文章的时候,实际上已经是25年12月,但事情大概是25年5月发生的事情,也就是其实我只咕了一年不到!(理直气壮x

之所以有时间来写写这一年来遇到的技术趣事,是因为一方面到年底了,一方面我又要开始漂泊了(为什么我要说又),细节的原因大家有兴趣可以看看 食用指南 下面的每年记事,我大概会把这几篇文章更完后一并更新之

言归正传,所有的这种如题所示需求都是公司需要,以下都是情节模拟,并非具体情况,请细加甄别

在24年底其实就有 TL 找我,说目前我们的私域运营效率比较低,还要用批量的实机统一调度操作,问我能不能破解企业微信的私有协议,让后端能直接修改数据传参,调用接口,回复用户、发朋友圈等……

image

呵,我当然不会答应,只是“好好好,我有时间看看”的说法,可要知道这是在法律边缘疯狂试探的,当然,后面还有老板要我去爬竞品公司数据啥的,只能说这个公司简直了= =

总而言之

总而言之,虽然我推辞了,但到半年后这件事情再被捞起来的时候,我就推不掉了,我只能想办法实现而又不让它上线,也就是拿出达成目标的证据但给出不能这么做的足够理由

前置准备

我在做前期信息搜集的时候,参考了:基于wireshark对网页版微信抓包和ios微信抓包分析微信3.9.8.25机器人(Hook注入)搭建教程文档 还有一众相关文章

前者让我抛弃了直接破解加密协议的想法,且先不说能否实现,也太麻烦了

后者让我转向了另一个实现方向:我能不能 Hook 微信,通过这样的方式进行一些功能调用和传参修改呢?

image

摸索开始了——

在查阅了大量前人的资料和看到无数的卖技术贩子后,我大概了解了目前做这一块的“灰产技术”大概的执行方式:利用低版本的 PC 端微信,对其进行功能调用内存地址的发掘,然后通过外置函数模拟接口数据,再写入微信进行接口请求篡改或直接执行

image

定制DLL

其实现在所有主流的 PC 微信 Hook 几乎都来自一个项目:https://github.com/ttttupup/wxhelper

我也就借用这个项目的示例,高概括的向大家说一下步骤是如何进行的,如何实现的:

  1. 用 Cheat Engine(CE)去找微信触发接口的回调地址,先在固定对话框(文件传输助手)发送特定的语句,然后在 CE 中寻找相应语句所在地址;
  2. 然后用 x64DBG 或者 OD 在地址处下断点,断点后一步步跟进到调用函数,或者直接用 IDA Pro 或 Ghidra 对关键地址进行搜索后,一步步向上找调用函数;

image

  1. 再结合这个关键函数,让 AI 帮忙分析一下,写一下带汇编语句的 C 代码,作为一个业务功能(给特定对象发消息)的回调(Call),然后再编译成一个动态连结库(.DLL),供外部拓展使用(http)

image

接口调用

如何运用编写出的 DLL 以及如何撰写接口的调用,示例在 wxhelper 的 client.py 中都有,这里只展示部分代码:

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
>import requests
>import json


>def check_login():
"""
0.检查是否登录
:return:
"""
url = "127.0.0.1:19088/api/?type=0"
payload = {}
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


>def user_info():
"""
登录用户信息
:return:
"""
url = "127.0.0.1:19088/api/?type=8"
payload = {}
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)


>def send_text():
"""
发送文本
:return:
"""
url = "127.0.0.1:19088/api/?type=2"
payload = json.dumps({
"wxid": "filehelper",
"msg": "123"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
......

最终实现

当前面两步实现后,就可以进行简单的功能调用了,以下是获取个人信息以及发送消息的实际情况:

image

image

后续推辞

当然,这只是我给“私域优化建议”的一部分,对于这部分,我表示:实现了,但不能做,有封号风险

为什么不能用这种方式?一方面,这种方式需要一个人固定维护这份 .DLL 文件,每个版本的微信对应调用函数的映射地址都是会变化的,在企微强更较频繁的环境下难以如此费精力的去再做一遍(我不负责!);另一方面,在思考企微协议相关的利用与分析这段时间里,我在许多群组里都看到了因为使用这种方式不恰当导致企业账号被封禁的情况,从流量请求来看,这部分从本地发出的请求很容易被识别,属于腾讯自己灰产的一部分

image

如此辩解就好,也算是完成了任务,也没走上违法犯罪的道路,可喜可贺,可喜可贺

参考引用

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

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

ttttupup-wxhelper

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

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

如何使用Apk-Medit对APK进行内存搜索和数据修改

如何使用Apk-Medit对APK进行内存搜索和数据修改

本文转自 Alpha_h4ck 并作补充

写在前面的话

内存修改往往是在游戏中实现作弊的最简单方法,而且内存也是我们在安全测试过程中必须要去面对和检测的一个内容。虽然现在社区也已经有类似GameGuardian这种的工具了,但现在还没有针对非Root设备的类似工具。Apk-Medit这款工具是一个针对可调式APK的内存搜索和数据修改工具,该工具专为移动端游戏安全测试而设计,我们可以在非Root设备(无需NDK)中使用该工具。

工具安装

首先,我们需要访问该项目的【GitHub Releases页面】来下载该项目源码。下载完成之后,我们需要将代码拷贝至目标安卓设备的/data/local/tmp/目录下:

1
2
3
$ adb push medit /data/local/tmp/medit

medit: 1 file pushed. 29.0 MB/s (3135769 bytes in 0.103s)

代码构建

我们可以使用make命令来完成代码的构建,这里要求使用Go编译器。代码构建完成之后,使用adb连接设备,它将会把构建好的代码推送至目标Android设备的/data/local/tmp/目录下:

1
2
3
4
5
6
7
$ make

GOOS=linux GOARCH=arm64 GOARM=7 go build -o medit

/bin/sh -c "adb push medit /data/local/tmp/medit"

medit: 1 file pushed. 23.7 MB/s (3131205 bytes in 0.126s)

工具命令

搜索

在内存中搜索特定的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> find 999982

Search UTF-8 String...

Target Value: 999982([57 57 57 57 56 50])

Found: 0!

------------------------

Search Word...

parsing 999982: value out of range

------------------------

Search Double Word...

Target Value: 999982([46 66 15 0])

Found: 1!

Address: 0xe7021f70

我们还可以指定目标数据类型,比如说字符串、dword和qword等等:

1
2
3
4
5
6
7
8
9
> find dword 999996

Search Double Word...

Target Value: 999996([60 66 15 0])

Found: 1!

Address: 0xe7021f70

过滤

我们可以对搜索结果进行过滤,并匹配当前的搜索值:

1
2
3
4
5
6
7
8
9
> filter 993881

Check previous results of searching dword...

Target Value: 993881([89 42 15 0])

Found: 1!

Address: 0xe7021f70

数据修改

我们可以直接修改目标地址的数据值:

1
2
3
> patch 10

Successfully patched!

ps命令

寻找目标进程,如果只有一个的话,我们可以使用ps命令来自动指定:

1
2
3
4
5
> ps

Package: jp.aktsk.tap1000000, PID: 4398

Target PID has been set to 4398.

绑定进程

如果目标PID是通过ps命令设置的,我们就可以跟目标进程进行绑定,并通过ptrace来终止App内的所有进程:

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
45
46
47
> attach

Target PID: 4398

Attached TID: 4398

Attached TID: 4405

Attached TID: 4407

Attached TID: 4408

Attached TID: 4410

Attached TID: 4411

Attached TID: 4412

Attached TID: 4413

Attached TID: 4414

Attached TID: 4415

Attached TID: 4418

Attached TID: 4420

Attached TID: 4424

Attached TID: 4429

Attached TID: 4430

Attached TID: 4436

Attached TID: 4437

Attached TID: 4438

Attached TID: 4439

Attached TID: 4440

Attached TID: 4441

Attached TID: 4442

如果目标PID没有设置的话,我们就需要在命令行中专门指定了:

1
> attach <pid>

解绑进程

解绑已绑定的进程:

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
45
> detach

Detached TID: 4398

Detached TID: 4405

Detached TID: 4407

Detached TID: 4408

Detached TID: 4410

Detached TID: 4411

Detached TID: 4412

Detached TID: 4413

Detached TID: 4414

Detached TID: 4415

Detached TID: 4418

Detached TID: 4420

Detached TID: 4424

Detached TID: 4429

Detached TID: 4430

Detached TID: 4436

Detached TID: 4437

Detached TID: 4438

Detached TID: 4439

Detached TID: 4440

Detached TID: 4441

Detached TID: 4442

导出

显示内存导出数据:

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
> dump 0xf0aee000 0xf0aee300

Address range: 0xf0aee000 - 0xf0aee300

----------------------------------------------

00000000 34 32 20 61 6e 73 77 65 72 20 28 74 6f 20 6c 69 |42 answer (to li|

00000010 66 65 20 74 68 65 20 75 6e 69 76 65 72 73 65 20 |fe the universe |

00000020 65 74 63 7c 33 29 0a 33 31 34 20 70 69 0a 31 30 |etc|3).314 pi.10|

00000030 30 33 20 61 75 64 69 74 64 20 28 61 76 63 7c 33 |03 auditd (avc|3|

00000040 29 0a 31 30 30 34 20 63 68 61 74 74 79 20 28 64 |).1004 chatty (d|

00000050 72 6f 70 70 65 64 7c 33 29 0a 31 30 30 35 20 74 |ropped|3).1005 t|

00000060 61 67 5f 64 65 66 20 28 74 61 67 7c 31 29 2c 28 |ag_def (tag|1),(|

00000070 6e 61 6d 65 7c 33 29 2c 28 66 6f 72 6d 61 74 7c |name|3),(format||

00000080 33 29 0a 31 30 30 36 20 6c 69 62 6c 6f 67 20 28 |3).1006 liblog (|

00000090 64 72 6f 70 70 65 64 7c 31 29 0a 32 37 31 38 20 |dropped|1).2718 |

000000a0 65 0a 32 37 31 39 20 63 6f 6e 66 69 67 75 72 61 |e.2719 configura|

000000b0 74 69 6f 6e 5f 63 68 61 6e 67 65 64 20 28 63 6f |tion_changed (co|

000000c0 6e 66 69 67 20 6d 61 73 6b 7c 31 7c 35 29 0a 32 |nfig mask|1|5).2|

000000d0 37 32 30 20 73 79 6e 63 20 28 69 64 7c 33 29 2c |720 sync (id|3),|

000000e0 28 65 76 65 6e 74 7c 31 7c 35 29 2c 28 73 6f 75 |(event|1|5),(sou|

000000f0 72 63 65 7c 31 7c 35 29 2c 28 61 63 63 6f 75 6e |rce|1|5),(accoun|

退出

如需退出该工具,可以使用exit命令或按下Ctrl+D:

1
2
3
> exit

Bye!

工具测试

我们可以使用make命令来运行测试代码:

1
$ make test

工具使用Demo

image

image

【实用教程】记Simplehook+Lspatch+Shizuku 免root使用教程 番茄为例

【实用教程】记Simplehook+Lspatch+Shizuku 免root使用教程 番茄为例

本文转自 w1234567890 并作补充

发现论坛好多小伙伴有了simplehook配置却不知道怎么用,而且搜了一下论坛也没有免root用hook的简洁教程,今天就来以番茄为例子写一个教程,希望对大家有帮助。

注:此教程以番茄为例子,面向不方便真机root,虚拟机不会弄root,lsp的小伙伴,全程免root,可以在真机丝滑使用。 需要root的有大把教程,基本和免root大同小异,我结尾也附了一些截图就不详细说了,看不懂的搜一下。

目录


受害者

需要工具

教程开始

受害者:

需要工具:

simplehook配置 在下面

番茄(5.9.3.32)点我下载

Simplehook(normal版) 点我下载

Lspatch 点我下载

Shizuku 点我下载

教程开始:

注意,因为我看到一个番茄的hook配置,所以此教程才以番茄为例子,其他软件也可以,别较真

一.安装以上四个软件

二.Shizuku配置激活

1.下载安装

2.按官方教程配对启动(推荐用无线配对)

点我查看官方教程

3.成功后看最上面显示shizuku正在运行即可

4.点管理授权应用,勾选Lspatch

三.Lspatch配置教程

1.打开LSPatch,看到shizuku服务可用

2.点管理页点击+号,找到番茄安装包或者安装到手机直接搜索

3.然后选本地模式修补,签名选用lv2然后点击开始修补

ps:注意看提示打开番茄,需要先打开LSPatch,不然会闪退

4.如图最后一行路径(我记得刚开始有弹窗让你选修补路径)就是安装包所在,安装即可

5.重进LSPatch,再次进入管理页,就会看到番茄

6.长按番茄,然后选择模块作用域,把下载的simplehook勾选上就行了

四.Simplehook配置

1.点首页右下角加号

2.复制如下番茄(测试软件)配置(可能论坛问题,分享的代码配置有朋友说格式错误,所以用附件分享)

1
>[{"packageName":"com.dragon.read","appName":"番茄免费小说","versionName":"5.9.3.32","description":"","configs":"[{"mode":2,"className":"com.dragon.read.widget.m","methodName":"a","params":"*","enable":false},{"mode":2,"className":"com.dragon.read.reader.ad.readflow","methodName":"*","params":"*","enable":false},{"mode":1,"className":"com.dragon.read.user.model.VipInfoModel","methodName":"<init>","params":"java.lang.String,java.lang.String,java.lang.String,Z,Z,I,Z,com.dragon.read.rpc.model.VipSubType","resultValues":"1893211199s,1s,1s,true,true,,true,"}]","id":3}]

点我下载 提取码:pBVj

(注:实际上可以不管这些配置,框架自带的足够用了)

3.点导入配置

4.就会出现番茄图标

5.长按点启动即可

注:填写完simplehook配置之后,需要清除番茄数据,然后再重新打开。如果发现未生效,重复操作即可。

ps:番茄验签好像有问题,我昨天测试的时候一切正常,今天弄又提示不安全了,用虚拟机又可以了。不过没事免root教程就这样,大家可以换软件测试。

————

真机测试图(图一昨日成功图二今日失败):

虚拟机测试和配置(一切正常,会员功能可用):

注:此篇文章是原作者怕被查水表,所以把图片都隐去了,但是工具使用都是没问题的,确实是在应用未加固或加固能力不强时可用的

非root环境下frida持久化的两种方式及脚本

非root环境下frida持久化的两种方式及脚本

本文转自 八重嘤 并作补充

frida是一个非常好用的hook框架,但使用中有两个问题,一是非root手机使用挺麻烦的,二是frida相较于其他HOOK框架没那么持久。网上的持久化大多基于xposed、刷ROM或者是virtualapp,前面两个是比较重量级,不够轻便。虚拟化技术本身就自带风险,很容易被检测到。

在Android端,网上教程里大部分都是使用frida server来进行hook,其实还有一种使用方法为 frida gadget,此方法需要将frida-gadget.so注入到apk中,纯手动的话过于麻烦,所以这里实现两个脚本,分别用修改smali、修改so来注入目标。

我使用的frida-gadget版本为14.2.18。有其他版本的需求,需要替换tools下的so文件

方法一 调试apk中含有so

此方法相对简单。原理来自于古早的静态注入方式:Android平台感染ELF文件实现模块注入

而这种注入方式有工具可以快速实现:How to use frida on a non-rooted device

该方法优点在于可以让gadget是第一个启动的,缺点是没有so的apk不能用。

1.效果

首先运行注入脚本,获得注入且重签名后的apk。直接安装。

image

将frida_script.js push 到/data/local/tmp。frida_script.js为你的hook代码:

1
2
3
4
Java.perform(function () {
var Log = Java.use("android.util.Log");
Log.e("frida-OOOK", "Have fun!");
});//android 不要使用console.log

打开app即可看到效果,app每次启动都会成功的打印LOG:

image

不想使用持久化(本地js脚本),也可以通过电脑连接:

image

不使用持久化,就不必添加config文件,所以脚本执行不需要执行-persistence,执行下面的就可以:

1
python LIEFInjectFrida.py apkfile  outdir  libnative-lib.so  -apksign

2.代码

工具详细代码:https://github.com/nszdhd1/UtilScript/blob/main/LIEFInjectFrida.py

运行脚本记得安装lief(pip install lief)

其实关键代码就几行:

1
2
3
4
5
6
for soname in injectsolist: #遍历apk中指定SO有哪几种架构,并添加gadget.so为依赖库。
if soname.find("x86") != -1:
continue
so = lief.parse(os.getcwd()+"\\"+soname)
so.add_library("libfrida-gadget.so")
so.write(soname+"gadget.so")

方法二 apk中没有so

在实际情况下,并不是所有的apk都有so。没有so,方法一便没有用武之地了。

此方法呢,是通过修改smali,调用System.loadLibrary来加载so。该原理更简单,但是有一个弊端就是时机不够靠前,没有办法hook Activity 启动之前的代码。

手动修改太麻烦,还是写一个脚本自动化注入。

此方法优点是原理简单,缺点是脚本实现麻烦,容易写bug

1. 效果

首先运行注入脚本,获得注入且重签名后的apk。直接安装。

image

image

frida_script.js代码同上,同样也可以使用电脑连接:

image

2. 代码

工具详细代码:https://github.com/nszdhd1/UtilScript/blob/main/SmaliInjectFrida.py

关键代码:

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
45
46
47
48
49
50
51
52
53
def get_launchable_activity_aapt(self): #通过aapt找到apk的启动activity
aapt_path = os.path.join(self.toolPath, 'aapt.exe')
cmd = '%s dump badging "%s" ' % (aapt_path, self.apkpath)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out,err = p.communicate()
cmd_output = out.decode('utf-8').split('\r')
for line in cmd_output:
#正则,pattern.search正常,pattern.match就会有问题=-=懒得解决了
pattern = re.compile("launchable-activity: name='(\S+)'")
match = pattern.search(line)
if match:
# print match.group()[27:-1]
return match.group()[27:-1]

def injectso(self):
target_activity = self.get_launchable_activity_aapt()
for dex in self.dexList:
print(dex)
if self.dexDecompile(dex):
smali_path = os.path.join(self.decompileDir,target_activity.replace('.','\\'))+".smali"
print(smali_path)
with open(smali_path, 'r') as fp:
lines = fp.readlines()
has_clinit = False
start = 0
for i in range(len(lines)):
#start是获取smali中,可以添加代码的位置
if lines[i].find(".source") != -1:
start = i
#找到初始化代码
if lines[i].find(".method static constructor <clinit>()V") != -1:
if lines[i + 3].find(".line") != -1:
code_line = lines[i + 3][-3:]
lines.insert(i + 3, "%s%s\r" % (lines[i + 3][0:-3], str(int(code_line) - 2)))
print("%s%s" % (lines[i + 3][0:-3], str(int(code_line) - 2)))
#添加相关代码
lines.insert(i + 4, "const-string v0, \"frida-gadget\"\r")
lines.insert(i + 5, "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r")
has_clinit = True
break
#如果碰上本身没有clinit函数的apk,就需要自己添加
if not has_clinit:
lines.insert(start + 1, ".method static constructor <clinit>()V\r")
lines.insert(start + 2, ".registers 1\r")
lines.insert(start + 3, ".line 10\r")
lines.insert(start + 4, "const-string v0, \"frida-gadget\"\r")
lines.insert(start + 5, "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r")
lines.insert(start + 6, "return-void\r")
lines.insert(start + 7, ".end method\r")

with open(smali_path, "w") as fp:
fp.writelines(lines)
self.dexCompile(dex)

Frida 持久化检测特征

我因为方便,将frida js 放在了/data/local/tmp下,如果直接放在app的沙箱下,这就是一个稳定的hook框架了。

既然做了持久化,就要从防御者角度看看哪些方面可以检测到应用被注入了。

首先,当然是内存中会有frida-gadget.so。但这个so可以被重命名(我可以命名为常见的模块,比如libBugly.so),所以检测/proc/pid/maps下是否有frida-gadget并不准确。因为frida有一个config文件,是持久化必须存在的。所以检测libs下是否有lib*.so和lib*.config.so是一种较为可行的方法。但是,如果你不使用持久化,或者去github上找到frida的源码修改gaget.vala(ps.这一点是合理的猜想,还未验证过),就可以让防御者检测不到。

1
2
3
4
5
6
7
8
9
10
11
#gaget.vala 代码片段
if ANDROID
if (!FileUtils.test (config_path, FileTest.EXISTS)) {
var ext_index = config_path.last_index_of_char ('.');
if (ext_index != -1) {
config_path = config_path[0:ext_index] + ".config.so";#修改这里,就可以检测不到。需要保持后缀不变(例如改成symbols.so)
} else {
config_path = config_path + ".config.so";
}
}
#endif

除去端口检测这种几乎没什么用的,还有一种比较可行的是内存扫描,扫描内存中是否有LIBFRIDA_GADGET关键词,具体实现网上有教程我就不介绍了。