lamda 3.157__tar.gz → 5.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {lamda-3.157 → lamda-5.0}/PKG-INFO +2 -2
- {lamda-3.157 → lamda-5.0}/README.md +212 -110
- {lamda-3.157 → lamda-5.0}/lamda/__init__.py +1 -1
- {lamda-3.157 → lamda-5.0}/lamda/client.py +215 -38
- {lamda-3.157 → lamda-5.0}/lamda/exceptions.py +3 -1
- {lamda-3.157 → lamda-5.0}/lamda/rpc/services.proto +17 -1
- lamda-5.0/lamda/rpc/storage.proto +13 -0
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/PKG-INFO +2 -2
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/SOURCES.txt +1 -0
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/requires.txt +5 -2
- {lamda-3.157 → lamda-5.0}/setup.py +6 -3
- {lamda-3.157 → lamda-5.0}/lamda/bcast.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/const.py +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/any.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/api.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/compiler/plugin.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/descriptor.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/duration.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/empty.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/field_mask.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/source_context.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/struct.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/timestamp.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/type.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/google/protobuf/wrappers.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/application.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/debug.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/file.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/policy.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/proxy.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/settings.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/shell.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/status.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/types.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/uiautomator.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/util.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/rpc/wifi.proto +0 -0
- {lamda-3.157 → lamda-5.0}/lamda/types.py +0 -0
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/dependency_links.txt +0 -0
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/not-zip-safe +0 -0
- {lamda-3.157 → lamda-5.0}/lamda.egg-info/top_level.txt +0 -0
- {lamda-3.157 → lamda-5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lamda
|
3
|
-
Version:
|
3
|
+
Version: 5.0
|
4
4
|
Summary: Android reverse engineering & automation framework
|
5
5
|
Home-page: https://github.com/rev1si0n/lamda
|
6
6
|
Author: rev1si0n
|
@@ -12,6 +12,6 @@ Classifier: Intended Audience :: Science/Research
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
13
13
|
Classifier: Operating System :: Android
|
14
14
|
Classifier: Topic :: Security
|
15
|
-
Requires-Python: >=3.6,<3.
|
15
|
+
Requires-Python: >=3.6,<3.12
|
16
16
|
Provides-Extra: next
|
17
17
|
Provides-Extra: full
|
@@ -8,6 +8,7 @@ LAMDA 是一个用于逆向及自动化的辅助框架,它设计为减少安
|
|
8
8
|
|
9
9
|
* 零依赖,只需 **root** 即可
|
10
10
|
* 前身通过超500台设备压力的稳定生产环境考验
|
11
|
+
* 可通过扩展模块使用完整的安卓内 Debian (12, bookworm) 环境
|
11
12
|
* 通过接口轻松设置根证书,配合 http/socks5 代理实现中间人
|
12
13
|
* 通过 frida 暴露内部 Java 接口(类 [virjar/sekiro](https://github.com/virjar/sekiro) 但基于 frida)
|
13
14
|
* 近乎商业级软件的质量和稳定性,ARM/X86全架构
|
@@ -34,6 +35,7 @@ LAMDA 是一个用于逆向及自动化的辅助框架,它设计为减少安
|
|
34
35
|
* 可使用 ssh 登录设备终端
|
35
36
|
* 只要有网即可连接任意地方运行了 LAMDA 的设备
|
36
37
|
* 前后台运行 shell 命令,授予撤销应用权限等
|
38
|
+
* 内置 Storage 用于存储设备变量
|
37
39
|
* 内置 http/socks5 代理,可设置系统/指定应用的代理
|
38
40
|
* 内置 frida 15.x, IDA 7.5 server 等工具
|
39
41
|
* 内置 crontab 定时任务
|
@@ -43,7 +45,10 @@ LAMDA 是一个用于逆向及自动化的辅助框架,它设计为减少安
|
|
43
45
|
* WEB 端文件上传下载
|
44
46
|
* UI自动化,通过接口实现自动化操作
|
45
47
|
|
46
|
-
|
48
|
+
如果觉得以下教程过于复杂看不懂,可以选择观看 [视频教程](https://lamda.run/tutorial/video)。
|
49
|
+
|
50
|
+
|
51
|
+

|
47
52
|
|
48
53
|
## 无视恶意软件对抗
|
49
54
|
|
@@ -53,7 +58,7 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
53
58
|
|
54
59
|
## 一键中间人流量分析
|
55
60
|
|
56
|
-
支持常规以及国际APP流量分析,DNS流量分析,得益于 [mitmproxy flow hook](https://docs.mitmproxy.org/stable/api/events.html),你可以对任何请求做到最大限度的掌控,mitmproxy
|
61
|
+
支持常规以及国际APP流量分析,DNS流量分析,得益于 [mitmproxy flow hook](https://docs.mitmproxy.org/stable/api/events.html),你可以对任何请求做到最大限度的掌控,mitmproxy 功能足够丰富,你可以使用 Python 脚本实时修改或者捕获应用的请求,也可以通过其 `Export` 选项导出特定请求的 `curl` 命令或者 `HTTPie` 命令,分析重放、拦截修改、功能组合足以替代你用过的任何此类商业/非商业软件。如果你仍不清楚 mitmproxy 是什么以及其具有的能力,请务必先查找相关文档,因为 LAMDA 将会使用 mitmproxy 为你展现应用请求。
|
57
62
|
|
58
63
|
通过 tools/ 目录下的 `globalmitm`,`startmitm.py` 实现,使用方法请看其同目录 README。
|
59
64
|
|
@@ -61,7 +66,7 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
61
66
|
|
62
67
|
## 拖拽上传
|
63
68
|
|
64
|
-
可直接在远程桌面拖拽上传,支持上传整个目录,最大支持单个 256MB 的文件,文件将始终被上传到 `/data/
|
69
|
+
可直接在远程桌面拖拽上传,支持上传整个目录,最大支持单个 256MB 的文件,文件将始终被上传到 `/data/usr/uploads` 目录下。
|
65
70
|
|
66
71
|

|
67
72
|
|
@@ -92,8 +97,6 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
92
97
|
|
93
98
|
如果你希望继续看下去,请确保:有一台已经 root 且运行内存大于 2GB,可用存储空间大于 1GB 的安卓设备或者安卓模拟器(推荐使用最新版**夜神**,**雷电**模拟器,或者 AVD [Android Studio Virtual Device])。**不完全支持** 网易 Mumu,**不支持**腾讯手游助手、蓝叠以及安卓内虚拟如 VMOS 等),对于真机,推荐运行最接近原生系统的设备如谷歌系、一加、安卓开发板等,或系统仅经过轻度改造的设备。如果你使用的是OPPO/VIVO/华为/小米的设备,经过尝试后无法正常运行,建议改用模拟器。
|
94
99
|
|
95
|
-
对于**云手机**,支持阿里云/华为云手机,不支持X手指、X多云、X电、X云兔、X子星、X马云及任何其他品牌。
|
96
|
-
|
97
100
|
<br>
|
98
101
|
|
99
102
|
# 目录
|
@@ -111,8 +114,6 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
111
114
|
- [安装服务端](#安装服务端)
|
112
115
|
- [通过 Magisk 安装](#通过-magisk-安装)
|
113
116
|
- [手动安装](#手动安装)
|
114
|
-
- [方式 1](#方式-1)
|
115
|
-
- [方式 2](#方式-2)
|
116
117
|
- [启动服务端](#启动服务端)
|
117
118
|
- [退出服务端](#退出服务端)
|
118
119
|
- [卸载服务端](#卸载服务端)
|
@@ -131,6 +132,7 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
131
132
|
- [使用 FRIDA 暴露 Java 接口](#使用-frida-暴露-java-接口)
|
132
133
|
- [使用内置的定时任务](#使用内置的定时任务)
|
133
134
|
- [使 LAMDA 可被任意地点连接](#使-lamda-可被任意地点连接)
|
135
|
+
- [读写内置键值存储器](#读写内置键值存储器)
|
134
136
|
- [读写系统属性](#读写系统属性)
|
135
137
|
- [读写系统设置](#读写系统设置)
|
136
138
|
- [获取设备运行状态](#获取设备运行状态)
|
@@ -146,6 +148,7 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
146
148
|
- [进阶UI操作](#进阶ui操作)
|
147
149
|
- [接口锁](#接口锁)
|
148
150
|
- [使用内置终端](#使用内置终端)
|
151
|
+
- [使用 Debian 环境扩展模块](#使用-debian-环境扩展模块)
|
149
152
|
- [工具及教程](#工具及教程)
|
150
153
|
- [一键中间人](#一键中间人)
|
151
154
|
- [国际代理进行中间人](#国际代理进行中间人)
|
@@ -159,15 +162,13 @@ MOMO (vvb2060) 是我们认为目前最强的ROOT特征检测软件,如 MOMO
|
|
159
162
|
|
160
163
|
# 免责声明及条款
|
161
164
|
|
162
|
-
为了下载使用由 rev1si0n (账号 github.com/rev1si0n)(以下简称“本人”)个人开发的软件 LAMDA ,您应当阅读并遵守《用户使用协议》(以下简称“本协议”)。请您务必审慎阅读、充分理解各条款内容,特别是免除或者限制责任的条款,并选择接受或不接受;除非您已阅读并接受本协议所有条款,否则您将无权下载、安装或使用本软件及相关服务。您的下载、安装、使用、获取账号、登录等行为即视为您已阅读并同意受到上述协议的约束;若您需要获得本服务,您(以下称"用户")应当同意本协议的全部条款并按照页面上的提示完成全部申请使用程序。您可以在本文档的相同目录找到 [DISCLAIMER.TXT](DISCLAIMER.TXT),或者点此 [免责声明](DISCLAIMER.TXT)
|
165
|
+
为了下载使用由 rev1si0n (账号 github.com/rev1si0n)(以下简称“本人”)个人开发的软件 LAMDA ,您应当阅读并遵守《用户使用协议》(以下简称“本协议”)。请您务必审慎阅读、充分理解各条款内容,特别是免除或者限制责任的条款,并选择接受或不接受;除非您已阅读并接受本协议所有条款,否则您将无权下载、安装或使用本软件及相关服务。您的下载、安装、使用、获取账号、登录等行为即视为您已阅读并同意受到上述协议的约束;若您需要获得本服务,您(以下称"用户")应当同意本协议的全部条款并按照页面上的提示完成全部申请使用程序。您可以在本文档的相同目录找到 [DISCLAIMER.TXT](DISCLAIMER.TXT),或者点此 [免责声明](DISCLAIMER.TXT) 查阅。此项目代码库中仅包含开源的客户端库、工具代码。因服务端程序以二进制方式发布,并未开源,所以除以上条款外:**授权您对 LAMDA SERVER 本身进行以恶意代码分析为目的的逆向**。
|
163
166
|
|
164
167
|
请确认您已阅读并接受本协议所有条款,否则您将无权下载、安装或使用本软件及相关服务。
|
165
168
|
|
166
169
|
# 前言
|
167
170
|
|
168
|
-
LAMDA 是个人开发的免费软件 (freeware)
|
169
|
-
|
170
|
-
为什么部分开源?因为 LAMDA 亦黑亦白,很容易被不法分子利用使作者处于危险之中,所以请尊重条款使用。建议在 Linux 或者 Mac 系统上操作文档及样例中的代码。部分功能需要配合 `tools/` 目录下的工具实现,如何使用请参照 [tools/README.md](tools/README.md)。
|
171
|
+
LAMDA 是个人开发的免费软件 (freeware),目前仅客户端库及工具代码是开源的,个人承诺 LAMDA 不会对您及您的设备有任何违规或多余的行为,如果仍有担心,您可以**立即离开**或者选择**付费**寻求心理安慰。互相尊重,使用请遵守使用条款。为什么部分开源?因为 LAMDA 亦黑亦白,很容易被不法分子利用使作者或者不明所以的用户处于危险之中,所以请尊重条款使用。建议在 Linux 或者 Mac 系统上操作文档及样例中的代码。部分功能需要配合 `tools/` 目录下的工具实现,如何使用请参照 [tools/README.md](tools/README.md)。
|
171
172
|
|
172
173
|
**特别注意**:**请勿在自用设备上运行,当有可能在公网或不信任的网络中使用时,务必确保在启动时指定了PEM证书**
|
173
174
|
|
@@ -180,7 +181,7 @@ LAMDA 是个人开发的免费软件 (freeware),目前仅客户端及协议是
|
|
180
181
|
因为安卓被各种设备广泛使用,无法保证百分百的兼容性,可能会有运行异常等各种未知情况,出现的异常情况包括:无故重启,APP经常崩溃,触摸失效或无故乱动等等,冻屏等情况。如果经常遇到,建议停止使用。
|
181
182
|
点此 [报告问题/建议](https://github.com/rev1si0n/lamda/issues/new),请详细描述并附上机型系统等信息。
|
182
183
|
|
183
|
-
社区讨论:[电报 t.me/lamda_dev](https://t.me/lamda_dev)
|
184
|
+
社区讨论:[电报 t.me/lamda_dev](https://t.me/lamda_dev)
|
184
185
|
|
185
186
|
> 顺便支持作者
|
186
187
|
|
@@ -204,7 +205,6 @@ LAMDA 最理想的运行环境是你刚刚 root(如:新建模拟器,自带
|
|
204
205
|
```
|
205
206
|
* 必须关闭 Magisk Hide
|
206
207
|
* 必须关闭 frida-server
|
207
|
-
* 建议关闭 Xposed/Magisk 插件
|
208
208
|
* 确认完毕重启设备
|
209
209
|
```
|
210
210
|
|
@@ -292,7 +292,7 @@ docker run -itd --rm --privileged --pull always -v /lib/modules:/lib/modules:ro
|
|
292
292
|
|
293
293
|
## 安装客户端
|
294
294
|
|
295
|
-
请使用 3.6 - 3.
|
295
|
+
请使用 3.6 - 3.11 版本的 Python,建议有条件使用 Python 3.9
|
296
296
|
|
297
297
|
```bash
|
298
298
|
pip3 install -U lamda
|
@@ -302,6 +302,8 @@ pip3 install -U lamda
|
|
302
302
|
# 你可能需要外网访问来安装 frida,否则可能会卡住许久(~10分钟)直至安装失败
|
303
303
|
# 即使之前安装过 frida,也应该重新执行以下命令
|
304
304
|
pip3 install -U --force-reinstall 'lamda[full]'
|
305
|
+
# 如果你安装的服务端是 7.0 (beta) 版本,请执行如下命令安装
|
306
|
+
pip3 install -U --force-reinstall 'lamda[next]'
|
305
307
|
# 请注意完成安装后,你需要同时使用 pip 更新任何依赖 frida
|
306
308
|
# 的第三方库例如 frida-tools objection 等(如果安装过的话)
|
307
309
|
# 否则后期使用可能会出现难以察觉的异常
|
@@ -325,7 +327,9 @@ pip3 install -U --force-reinstall lamda
|
|
325
327
|
|
326
328
|
## 安装服务端
|
327
329
|
|
328
|
-
**默认方式安装的 LAMDA
|
330
|
+
**默认方式安装的 LAMDA 没有开启任何认证,其他人可以访问设备上的任意内容,监听你的设备甚至接入设备网络进行进一步控制。请特别留意`启用接口认证`的部分,请务必在可以`信任的网络`内使用。并且请注意,`即使开启了接口认证`,任何`有权限登录远程桌面以及使用API`的人仍然对你的设备以及 LAMDA 本身有着完全的访问权限。**
|
331
|
+
|
332
|
+
**由于安全性原因,我们不建议将任何相关文件放在 `/data/local/*` 目录下。**
|
329
333
|
|
330
334
|
安装前,请先选择合适的架构,可以通过 adb shell 命令 `getprop ro.product.cpu.abi` 来获取当前的系统架构。
|
331
335
|
正常情况下,对于现时代的手机,可以直接选择 `arm64-v8a` 版本,而对于模拟器如雷电,你会在新建模拟器时选择32或64位版本的安卓系统,
|
@@ -334,8 +338,8 @@ pip3 install -U --force-reinstall lamda
|
|
334
338
|
LAMDA 支持设备状态主动上报,你可以编写接口或使用 grafana 来记录设备运行状况,其中包含了系统、网络、内存、CPU、磁盘等等信息。
|
335
339
|
|
336
340
|
```bash
|
337
|
-
#
|
338
|
-
echo "stat-report.url=http://example.com/report" >>/data/
|
341
|
+
# 如果不清楚这个功能是什么请不要执行,注意替换掉以下链接(需要 root 身份)
|
342
|
+
echo "stat-report.url=http://example.com/report" >>/data/properties.local
|
339
343
|
```
|
340
344
|
|
341
345
|
这样 LAMDA 会在启动后**每分钟**向此链接**POST**设备状态信息(JSON),由于字段较多,将不在此罗列。
|
@@ -347,8 +351,8 @@ LAMDA 存在一个自动更新的逻辑,但是由于存在分钟级的服务
|
|
347
351
|
如果你确实不在意更新时分钟级的服务不可用,启动 LAMDA 之前写入以下配置文件可以确保 LAMDA 始终为最新版本。
|
348
352
|
|
349
353
|
```bash
|
350
|
-
# 进入 adb shell
|
351
|
-
echo "upgrade.channel=latest" >>/data/
|
354
|
+
# 进入 adb shell 执行(需要 root 身份)
|
355
|
+
echo "upgrade.channel=latest" >>/data/properties.local
|
352
356
|
```
|
353
357
|
|
354
358
|
> properties.local 启动配置
|
@@ -356,7 +360,7 @@ echo "upgrade.channel=latest" >>/data/local/tmp/properties.local
|
|
356
360
|
在开始前,有必要介绍一下上面的 `properties.local` 文件,
|
357
361
|
properties.local 为 LAMDA 的启动配置文件,通常存储于设备之上,其中包含了 `a=b` 类型的字符串,
|
358
362
|
通过编写此文件,你可以实现在 LAMDA 启动时自动连接到 OpenVPN、代理、端口转发等。
|
359
|
-
LAMDA 在启动时,会从 `/data/usr
|
363
|
+
LAMDA 在启动时,会从 `/data`, `/data/usr` 查找该文件并载入(usr 目录在 LAMDA 首次启动前并不存在,所以你可能需要手动创建)。
|
360
364
|
你可以在以上三个位置任意一个放置你的 properties.local 配置文件。
|
361
365
|
|
362
366
|
除了 `properties.local`,还有一个从加载远端配置的参数 `--properties.remote`,它可以让 LAMDA 在启动时从HTTP服务器下载配置,请继续看往启动 LAMDA 的章节。
|
@@ -387,77 +391,57 @@ abi not match (使用了错误的 tar.gz 包)
|
|
387
391
|
|
388
392
|
### 手动安装
|
389
393
|
|
390
|
-
|
391
|
-
|
392
|
-
#### 方式 1
|
394
|
+
由于部分老旧设备可能无法通过系统的 `tar` 命令来解压 tar.gz 后缀的文件,所以提供了 `busybox` 用来作为补充,你可能需要同时下载提供的 busybox。现在已知 getprop 获得的设备架构为 `arm64-v8a`,现在将设备连接到当前电脑并确保已授权 ADB、可以正常切换 root。
|
393
395
|
|
394
396
|
从 `release` 页面 [lamda/releases](https://github.com/rev1si0n/lamda/releases)
|
395
|
-
下载 `arm64-v8a.tar.gz-
|
397
|
+
下载 `lamda-server-arm64-v8a.tar.gz` 以及 `busybox-arm64-v8a`。
|
396
398
|
|
397
399
|
```bash
|
398
|
-
# /data/local/tmp
|
399
|
-
|
400
|
-
adb push arm64-v8a
|
401
|
-
# 进入 adb shell
|
402
|
-
adb shell
|
403
|
-
# 输入 su 确保为 root 身份
|
404
|
-
su
|
405
|
-
# 切换到目录
|
406
|
-
cd /data/local/tmp
|
407
|
-
# 执行安装脚本并启动(这将解包并启动服务)
|
408
|
-
sh arm64-v8a.tar.gz-install.sh
|
409
|
-
# 删除安装包
|
410
|
-
rm arm64-v8a.tar.gz-install.sh
|
411
|
-
```
|
412
|
-
|
413
|
-
#### 方式 2
|
414
|
-
|
415
|
-
从 `release` 页面 [lamda/releases](https://github.com/rev1si0n/lamda/releases)
|
416
|
-
下载 `arm64-v8a.tar.gz`。
|
417
|
-
|
418
|
-
```bash
|
419
|
-
# /data/local/tmp 是标准服务安装路径,但并不是强制要求
|
420
|
-
# 你可以放到除了 /sdcard 之外任何具备可读写权限的文件夹
|
421
|
-
adb push arm64-v8a.tar.gz /data/local/tmp
|
400
|
+
# 将文件临时推送到 /data/local/tmp
|
401
|
+
adb push lamda-server-arm64-v8a.tar.gz /data/local/tmp
|
402
|
+
adb push busybox-arm64-v8a /data/local/tmp
|
422
403
|
```
|
423
404
|
|
424
405
|
完成后,进入 `adb shell`,解包文件:
|
425
406
|
|
426
407
|
```bash
|
427
408
|
# 你现在应该在 adb shell 内
|
428
|
-
|
409
|
+
# 使用此种方式,服务端程序将被安装到 /data
|
410
|
+
# 确保切换为 root 身份
|
411
|
+
su
|
412
|
+
# 确保上传的 busybox 可执行
|
413
|
+
chmod 755 /data/local/tmp/busybox-arm64-v8a
|
414
|
+
|
415
|
+
cd /data
|
429
416
|
# 解包服务端文件
|
430
|
-
|
431
|
-
#
|
432
|
-
|
433
|
-
#
|
434
|
-
rm arm64-v8a.tar.gz
|
417
|
+
/data/local/tmp/busybox-arm64-v8a tar -xzf /data/local/tmp/lamda-server-arm64-v8a.tar.gz
|
418
|
+
# 服务将被解压到 /data/server 目录下
|
419
|
+
|
420
|
+
# 删除安装包以及 busybox
|
421
|
+
rm /data/local/tmp/lamda-server-arm64-v8a.tar.gz
|
422
|
+
rm /data/local/tmp/busybox-arm64-v8a
|
435
423
|
```
|
436
424
|
|
437
425
|
## 启动服务端
|
438
426
|
|
439
|
-
|
440
|
-
对于上面任意一种安装方法,你永远只需要在首次安装时操作,但是**启动服务**的过程则需要在每次 设备重启 或者 你手动关闭 LAMDA 后执行,因为 LAMDA 不会自己运行。
|
427
|
+
使用 Magisk 安装后的 LAMDA 会在开机时自动启动,你只需要在首次安装后重启一次设备即可。而对于手动安装的 LAMDA,在每次**设备重启**或者**手动退出服务**后你都需要重新执行以下命令来启动 LAMDA SERVER。
|
441
428
|
|
442
429
|
进入 adb shell,并切换为 `su` root 身份,执行:
|
443
430
|
|
444
431
|
```bash
|
445
|
-
#
|
446
|
-
|
447
|
-
|
432
|
+
# 确保为 root 身份
|
433
|
+
# 你现在应该在 adb shell 内
|
434
|
+
su
|
448
435
|
# 启动服务端
|
449
|
-
|
450
|
-
# 如果你使用的是 x86 版本,那么这个目录名则是 x86/,你需要对命令做相应修改
|
451
|
-
sh arm64-v8a/bin/launch.sh
|
436
|
+
sh /data/server/bin/launch.sh
|
452
437
|
#
|
453
438
|
# 如果你想要启用加密传输
|
454
|
-
# 请先使用 tools/ 中的 cert.
|
455
|
-
# 将其push到设备例如 /data/
|
439
|
+
# 请先使用 tools/ 中的 cert.py 来生成 PEM 证书
|
440
|
+
# 将其push到设备例如 /data/lamda.pem
|
456
441
|
# 并将其属主及权限设置为 root 以及 600 (chown root:root lamda.pem; chmod 600 lamda.pem)
|
457
442
|
# 并使用以下命令启动,lamda.pem 必须为绝对路径
|
458
|
-
sh
|
459
|
-
# 这将加密任何通过 LAMDA
|
460
|
-
# 但不包括 webui 远程桌面的流量
|
443
|
+
sh /data/server/bin/launch.sh --certificate=/data/lamda.pem
|
444
|
+
# 这将加密任何通过 LAMDA 产生的通信流量
|
461
445
|
#
|
462
446
|
# 从远端加载 properties.local
|
463
447
|
# 有时候你可能希望从链接加载启动配置,这时你可以将 properties.local 上传到服务器
|
@@ -465,21 +449,21 @@ sh arm64-v8a/bin/launch.sh --certificate=/data/local/tmp/lamda.pem
|
|
465
449
|
# 你也可以自行编写web服务来根据这些设备参数分发不同的启动配置
|
466
450
|
# 建议使用 HTTPS 链接增加安全性,请确保设备时间正确。
|
467
451
|
# 随后使用如下方式启动 LAMDA
|
468
|
-
sh
|
452
|
+
sh /data/server/bin/launch.sh --properties.remote=http://example.com/config/properties.local
|
469
453
|
# 对于开启了 Basic Auth 的静态文件服务,同样支持提供用户名密码
|
470
|
-
sh
|
454
|
+
sh /data/server/bin/launch.sh --properties.remote=http://user:password@example.com/config/properties.local
|
471
455
|
# 提示:LAMDA 会在超时或者返回 50x 状态码时重试请求,
|
472
456
|
# 如果连续 5 次仍然失败,LAMDA 会放弃尝试并继续启动。
|
473
457
|
#
|
474
458
|
# 当然,可以自定义重试次数但是注意,如果服务器持续无响应,LAMDA 也将永远卡在这里
|
475
459
|
# 什么时候需要设置重试次数:刚开机时设备可能并没有网络连接,如果你要在这时启动 LAMDA 你可以增大该值
|
476
|
-
sh
|
460
|
+
sh /data/server/bin/launch.sh --properties.remote=http://example.com/config/properties.local --properties.tries=30
|
477
461
|
# 重试机制的每轮等待秒数n会随着重试次数的增加而增加。所以请谨慎设置该值。
|
478
462
|
#
|
479
463
|
# 如果你需要 LAMDA 监听到特定端口而不是 65000
|
480
464
|
# 如果修改,请确保所有内网设备均以相同端口启动
|
481
465
|
# 否则设备发现等功能无法正常工作
|
482
|
-
sh
|
466
|
+
sh /data/server/bin/launch.sh --port=8123
|
483
467
|
# 请不要绑定 1024 以下的端口
|
484
468
|
```
|
485
469
|
|
@@ -505,7 +489,7 @@ LAMDA 对于自身数据的规划非常规范,绝对不会在你的系统中
|
|
505
489
|
|
506
490
|
```bash
|
507
491
|
# 删除 LAMDA 相关目录
|
508
|
-
rm -rf /data/
|
492
|
+
rm -rf /data/server /data/usr
|
509
493
|
# 重启设备
|
510
494
|
reboot
|
511
495
|
```
|
@@ -520,7 +504,7 @@ reboot
|
|
520
504
|
|
521
505
|
远程桌面功能仅为 Chrome 95+ 设计,不支持多人访问,不保证兼容所有浏览器,如遇功能不正常请使用 Chrome。
|
522
506
|
|
523
|
-
在浏览器中打开链接 `http://192.168.0.2:65000` 可进入 web 远程桌面,你可以在此操作设备以及通过该界面的root模拟终端执行命令。如果启动服务端时指定了PEM证书 `--certificate
|
507
|
+
在浏览器中打开链接 `http://192.168.0.2:65000` 可进入 web 远程桌面,你可以在此操作设备以及通过该界面的root模拟终端执行命令。如果启动服务端时指定了PEM证书 `--certificate`,远程桌面将需要你输入密码才能继续访问,并且你需要将 `http://` 改为 `https://` 使用 HTTPS 的方式访问,你可以使用文本编辑器在PEM证书第一行找到这个固定密码。
|
524
508
|
|
525
509
|
你也可以自定义远程桌面的 视频帧率(fps)、分辨率缩放比例(res)以及图像质量(quality)。同时,支持 H.264 软编码(部分情况下使用流量更少更流畅,仅支持最新版 Chrome 浏览器)。你可以通过远程桌面右上角的小齿轮进行调整,但是请注意,调整以上参数并不一定会产生正向效果,请依据事实调整。
|
526
510
|
|
@@ -529,7 +513,7 @@ reboot
|
|
529
513
|
|
530
514
|
## 文件上传
|
531
515
|
|
532
|
-
你可以在此页面直接**拖动文件或目录到右侧终端**上来上传文件/文件夹到设备,支持同时拖动多个文件或文件夹,单个文件最大不得超过 256MB,最多只支持同时上传 2k 个文件,上传的任何文件权限均为 644,文件将始终上传到 `/data/
|
516
|
+
你可以在此页面直接**拖动文件或目录到右侧终端**上来上传文件/文件夹到设备,支持同时拖动多个文件或文件夹,单个文件最大不得超过 256MB,最多只支持同时上传 2k 个文件,上传的任何文件权限均为 644,文件将始终上传到 `/data/usr/uploads` 目录下。
|
533
517
|
|
534
518
|
## 文件下载
|
535
519
|
|
@@ -543,6 +527,7 @@ LAMDA 的 tunnel2 功能,支持你将运行 LAMDA 的设备作为 http 网络
|
|
543
527
|
```bash
|
544
528
|
# 默认代理无需任何认证,但是当你使用了 --certificate 启动时
|
545
529
|
# 那么登录用户名为: lamda,密码与远程桌面登录令牌 (token) 相同
|
530
|
+
# 建议使用自定义配置 tunnel2.password 自行设置密码
|
546
531
|
curl -x http://192.168.0.2:65000 https://httpbin.org/ip
|
547
532
|
```
|
548
533
|
|
@@ -809,10 +794,10 @@ device = d.frida
|
|
809
794
|
device.enumerate_processes()
|
810
795
|
```
|
811
796
|
|
812
|
-
|
797
|
+
等效原生代码
|
813
798
|
|
814
799
|
```python
|
815
|
-
#
|
800
|
+
# 仅做示例,为了通用性,请务必使用上述方法
|
816
801
|
manager = frida.get_device_manager()
|
817
802
|
device = manager.add_remote_device("192.168.0.2:65000")
|
818
803
|
device.enumerate_processes()
|
@@ -825,33 +810,10 @@ device.enumerate_processes()
|
|
825
810
|
```bash
|
826
811
|
frida -H 192.168.0.2:65000 -f com.android.settings
|
827
812
|
# 如果你在服务端启动时指定了 certificate 选项,请注意也需要在此加入 --certificate 参数例如
|
828
|
-
|
813
|
+
# 且需要提供 token,这个 token 可以在 lamda.pem 的最后一行找到
|
814
|
+
frida -H 192.168.0.2:65000 -f com.android.settings --certificate /path/to/lamda.pem --token f141bce852f70730506f995991450adb
|
829
815
|
```
|
830
816
|
|
831
|
-
对于 objection 以及 r0capture 等,这些第三方工具可能并不会完全遵循原生 frida 工具的命令行用法,如果你需要使用这些第三方工具,需要确保 LAMDA 启动时**没有使用** `--certificate` 参数(加密传输),因为这些工具可能并没有可以传递PEM证书的参数。
|
832
|
-
|
833
|
-
```bash
|
834
|
-
# objection 示例连接方法 (-N -h 192.168.0.2 -p 65000)
|
835
|
-
objection -N -h 192.168.0.2 -p 65000 -g com.android.settings explore
|
836
|
-
```
|
837
|
-
|
838
|
-
```bash
|
839
|
-
# r0capture 示例连接方法 (-H 192.168.0.2:65000)
|
840
|
-
python3 r0capture.py -H 192.168.0.2:65000 -f com.some.package
|
841
|
-
```
|
842
|
-
|
843
|
-
```bash
|
844
|
-
# jnitrace 示例连接方法 (-R 192.168.0.2:65000)
|
845
|
-
jnitrace -R 192.168.0.2:65000 -l libc.so com.some.package
|
846
|
-
```
|
847
|
-
|
848
|
-
```bash
|
849
|
-
# frida-dexdump 示例连接方法 (-H 192.168.0.2:65000)
|
850
|
-
frida-dexdump -H 192.168.0.2:65000 -p PID
|
851
|
-
```
|
852
|
-
|
853
|
-
其他未提及的第三方工具请自行查看其使用方法。
|
854
|
-
|
855
817
|
## 使用 FRIDA 暴露 Java 接口
|
856
818
|
|
857
819
|
这个功能类似于 [virjar/sekiro](https://github.com/virjar/sekiro),关于它的用途请参考 virjar 大佬的
|
@@ -859,7 +821,7 @@ frida-dexdump -H 192.168.0.2:65000 -p PID
|
|
859
821
|
|
860
822
|
> 请转到 tools 目录查看使用方法。
|
861
823
|
|
862
|
-
此功能需要你能熟练编写 frida 脚本。示例中使用的脚本请参照 test-fridarpc.js 文件,特别注意: frida 脚本中 rpc.exports 定义的函数参数以及返回值只能为 int/float/string/list/
|
824
|
+
此功能需要你能熟练编写 frida 脚本。示例中使用的脚本请参照 test-fridarpc.js 文件,特别注意: frida 脚本中 rpc.exports 定义的函数参数以及返回值只能为 int/float/string/list/map 或者任意 js 中**可以被 JSON序列化**的值。假设设备IP为 192.168.0.2。
|
863
825
|
|
864
826
|
> 执行以下命令注入 RPC 到 com.android.settings(注意查看是否有报错),下面的相关文件在 tools 目录
|
865
827
|
|
@@ -867,7 +829,7 @@ frida-dexdump -H 192.168.0.2:65000 -p PID
|
|
867
829
|
python3 fridarpc.py -f test-fridarpc.js -a com.android.settings -d 192.168.0.2
|
868
830
|
```
|
869
831
|
|
870
|
-
现在已经将接口暴露出来了,只需要请求 `http://192.168.0.2:65000/fridarpc/myRpcName/getMyString?args=["A","B"]` 即可得到脚本内方法的返回结果,链接也可以用浏览器打开,接口同时支持 POST 以及 GET,参数列表也可以同时使用多个参数,空列表代表无参数,注意这里的 args 参数序列化后的字符串最长**不能超过** `32KB
|
832
|
+
现在已经将接口暴露出来了,只需要请求 `http://192.168.0.2:65000/fridarpc/myRpcName/getMyString?args=["A","B"]` 即可得到脚本内方法的返回结果,链接也可以用浏览器打开,接口同时支持 POST 以及 GET,参数列表也可以同时使用多个参数,空列表代表无参数,注意这里的 args 参数序列化后的字符串最长**不能超过** `32KB`(在使用了 --certificate 的情况下,链接需要改为 https 方式)。
|
871
833
|
|
872
834
|
链接中的两个字符串参数 "A", "B" 即为注入的脚本中的方法 `getMyString(paramA, paramB)` 的位置参数。
|
873
835
|
|
@@ -920,6 +882,7 @@ print (res.status_code, res.json()["result"])
|
|
920
882
|
> 首先在你的公网服务器上执行以下命令启动 frps(注意你可能还需要配置防火墙)
|
921
883
|
|
922
884
|
```bash
|
885
|
+
# frps 版本需要 > v0.45.0
|
923
886
|
frps --token lamda --bind_addr 0.0.0.0 --bind_port 6009 --proxy_bind_addr 127.0.0.1 --allow_ports 10000-15000
|
924
887
|
```
|
925
888
|
|
@@ -961,6 +924,80 @@ d = Device("127.0.0.1", port=12345)
|
|
961
924
|
改为 `--proxy_bind_addr 0.0.0.0`,这将导致 12345 端口直接绑定到公网。如果你未使用PEM证书启动 lamda,任何人都将可以访问,这是**非常非常危险**的。
|
962
925
|
其次需要注意,web 远程桌面的流量始终都是 http 的,如果有人在你和服务器通信之间进行中间人,你的登录凭证可能会被窃取。当然,如果此期间不用 web 桌面将不存在这个问题。
|
963
926
|
|
927
|
+
## 读写内置键值存储器
|
928
|
+
|
929
|
+
> Storage 是 LAMDA 内置的键值存储,它具有持久性,即使 LAMDA 重启,你依然可以在下次 LAMDA 启动时读取这些变量。
|
930
|
+
> 该 Storage 让你可以在设备中持久化存储信息以供不同的 client API 进程读取。
|
931
|
+
|
932
|
+
Storage 的总容量为 128MB,请勿用来存储大量数据。
|
933
|
+
|
934
|
+
```python
|
935
|
+
# 获取一个 Storage 对象
|
936
|
+
storage = d.stub("Storage")
|
937
|
+
|
938
|
+
# 清空 Storage 中的所有信息(包括容器)
|
939
|
+
storage.clear()
|
940
|
+
|
941
|
+
# 清除名为 container_name 的容器中存储的所有键值
|
942
|
+
storage.remove("container_name")
|
943
|
+
|
944
|
+
# 获取一个键值容器对象
|
945
|
+
container = storage.use("container_name")
|
946
|
+
|
947
|
+
# 存储在 Storage 中的 key_name 和 container_name 是安全的
|
948
|
+
# 无法通过任何方式读取原始字符串,你必须完整知道容器名称以及 key 才能从容器中读取数据
|
949
|
+
# 如果还需要安全的存储值,比如,当该设备会被其他人使用,但是你不想存储的配置被其他人读取,
|
950
|
+
# 你可以像以下示例,提供加解密方法,这样即使 LAMDA 被非法访问
|
951
|
+
# 非预期的访问者也无法解密容器中存储的任何明文信息
|
952
|
+
from lamda.client import FernetCryptor
|
953
|
+
# 获取键值容器对象,对该容器的读写均通过 FernetCryptor 加解密
|
954
|
+
container = storage.use("container_name", cryptor=FernetCryptor, key="this_is_password")
|
955
|
+
|
956
|
+
# 当然,你也可以自己编写加解密流程
|
957
|
+
from lamda.client import BaseCryptor
|
958
|
+
class MyCryptor(BaseCryptor):
|
959
|
+
def __init__(self, cryptor_arg=0):
|
960
|
+
# 这里写入你的加解密初始化过程
|
961
|
+
def encrypt(self, data):
|
962
|
+
# 这里写入你的加密过程
|
963
|
+
return data
|
964
|
+
def decrypt(self, data):
|
965
|
+
# 这里写入你的解密过程
|
966
|
+
return data
|
967
|
+
# 获取键值容器对象,对该容器的读写均通过 MyCryptor 加解密
|
968
|
+
container = storage.use("container_name", cryptor=MyCryptor, cryptor_arg=999)
|
969
|
+
|
970
|
+
|
971
|
+
# 获取 key_name 的值(如果不存在,则返回 None)
|
972
|
+
container.get("key_name")
|
973
|
+
|
974
|
+
# 获取 key_name 的生存时间(-2 为该键不存在,-1 为无限生存时间)
|
975
|
+
# 其他正整数则为该 key 的剩余存活秒数
|
976
|
+
container.ttl("key_name")
|
977
|
+
|
978
|
+
# 设置 key_name 的值为 "value",并且 10 秒后自动删除
|
979
|
+
container.setex("key_name", "value", 10)
|
980
|
+
|
981
|
+
# 设置 key_name 的生存时间为 60 秒
|
982
|
+
# 60 秒后,该键值将自动被删除
|
983
|
+
container.expire("key_name", 60)
|
984
|
+
|
985
|
+
# 仅当 key_name 不存在时设置该键值
|
986
|
+
container.setnx("key_name", "value")
|
987
|
+
|
988
|
+
# 设置 key_name 的值为 "value"
|
989
|
+
# 其中,值支持任何 msgpack 可序列化的变量
|
990
|
+
container.set("key_name", [1, 2, 3])
|
991
|
+
container.set("key_name", {"john": "due"})
|
992
|
+
container.set("key_name", b"value")
|
993
|
+
container.set("key_name", "value")
|
994
|
+
|
995
|
+
# 检查 key_name 是否存在于容器中
|
996
|
+
container.exists("key_name")
|
997
|
+
# 删除 key_name
|
998
|
+
container.delete("key_name")
|
999
|
+
```
|
1000
|
+
|
964
1001
|
|
965
1002
|
## 读写系统属性
|
966
1003
|
|
@@ -1146,24 +1183,24 @@ fd = open("写入到的本地文件", "wb")
|
|
1146
1183
|
d.download_fd("/verity_key", fd)
|
1147
1184
|
|
1148
1185
|
# 上传文件到设备
|
1149
|
-
d.upload_file("本地文件路径.txt", "/data/
|
1186
|
+
d.upload_file("本地文件路径.txt", "/data/usr/上传到设备上的文件.txt")
|
1150
1187
|
|
1151
1188
|
# 从 内存/已打开的文件 上传文件
|
1152
1189
|
from io import BytesIO
|
1153
|
-
d.upload_fd(BytesIO(b"fileContent"), "/data/
|
1190
|
+
d.upload_fd(BytesIO(b"fileContent"), "/data/usr/上传到设备上的文件.txt")
|
1154
1191
|
|
1155
1192
|
# 注意必须使用 rb 模式打开文件
|
1156
1193
|
fd = open("myfile.txt", "rb")
|
1157
|
-
d.upload_fd(fd, "/data/
|
1194
|
+
d.upload_fd(fd, "/data/usr/上传到设备上的文件.txt")
|
1158
1195
|
|
1159
1196
|
# 删除设备上的文件
|
1160
|
-
d.delete_file("/data/
|
1197
|
+
d.delete_file("/data/usr/文件.txt")
|
1161
1198
|
|
1162
1199
|
# 修改设备上的文件权限
|
1163
|
-
d.file_chmod("/data/
|
1200
|
+
d.file_chmod("/data/usr/文件.txt", mode=0o777)
|
1164
1201
|
|
1165
1202
|
# 获取设备上文件的信息
|
1166
|
-
d.file_stat("/data/
|
1203
|
+
d.file_stat("/data/usr/文件.txt")
|
1167
1204
|
```
|
1168
1205
|
|
1169
1206
|
## 关机重启
|
@@ -1856,6 +1893,71 @@ t = 切换到 /data/local/tmp
|
|
1856
1893
|
这里不会介绍如何使用这些命令或库。
|
1857
1894
|
|
1858
1895
|
|
1896
|
+
## 使用 Debian 环境扩展模块
|
1897
|
+
|
1898
|
+
LAMDA 可以通过一个模块创建可在安卓内使用的完整 Debian 环境,你可以使用 apt 安装软件以及进行代码编译,同样,你可以在此环境中自行编译及使用 bpf 相关程序。你可以在 release 页面中找到 `lamda-mod-debian-arm64-v8a.tar.gz`(请根据你的机器架构下载对应的安装包)。
|
1899
|
+
然后通过远程桌面或者 内置 adb 等方式,将 lamda-mod-debian-arm64-v8a.tar.gz 上传到设备,随后进行如下安装操作。
|
1900
|
+
|
1901
|
+
> 注:该 debian 环境只包含基础的软件包,你需要使用 apt 自行安装 git、python3 等常用命令。
|
1902
|
+
|
1903
|
+
```bash
|
1904
|
+
# 切换到用户模块目录
|
1905
|
+
cd /data/usr/modules
|
1906
|
+
# 假设,该文件被你上传到了 /data/local/tmp
|
1907
|
+
tar -xzf /data/local/tmp/lamda-mod-debian-arm64-v8a.tar.gz
|
1908
|
+
# 解包完成
|
1909
|
+
```
|
1910
|
+
|
1911
|
+
解包完成后,当前目录下将会存在一个 `debian` 目录,这时,你已经完成了基本的安装,下面介绍如何进入系统。
|
1912
|
+
|
1913
|
+
```bash
|
1914
|
+
# 首先我们需要获知绝对目录,在以上情况下,绝对目录为 /data/usr/modules/debian
|
1915
|
+
# 注意:每个根(debian 根系统)同时只支持一个终端实例使用
|
1916
|
+
# 执行以下命令进入 debian interactive shell
|
1917
|
+
debian /bin/bash
|
1918
|
+
# 执行一次 id 命令
|
1919
|
+
debian /bin/bash -c id
|
1920
|
+
#
|
1921
|
+
# 如果你并没有将模块安装于 /data/usr/modules,则需要指定模块位置
|
1922
|
+
debian --root /path/to/debian /bin/bash
|
1923
|
+
```
|
1924
|
+
|
1925
|
+
下面介绍进阶使用方法,你可以使用此 debian 环境自行建立一个 ssh 服务器,或者在此 debian 环境中运行 Python 脚本。
|
1926
|
+
由于每个独立的 debian 环境只支持一个终端实例使用,我们建议用 ssh 的方式,这样,你可以接入多个 shell 到此 debian 环境。
|
1927
|
+
什么是 `只支持一个终端实例使用`?就是当你执行 `debian /bin/bash` 后并保持使用状态,如果你在其他地方继续执行此命令,
|
1928
|
+
该命令将会返回错误,使你无法再次进入此根系统,除非你将之前的 `debian /bin/bash` 退出。
|
1929
|
+
|
1930
|
+
现在,我们介绍如何在此 debian 环境中运行一个 ssh 服务以及安装基础的 Python 环境。
|
1931
|
+
|
1932
|
+
```bash
|
1933
|
+
# 执行命令进入 debian shell
|
1934
|
+
debian /bin/bash
|
1935
|
+
# 现在,你应该在 debian shell 中,执行以下命令
|
1936
|
+
root@localhost: apt update
|
1937
|
+
root@localhost: apt install -y openssh-server procps python3 python3-pip python3-dev
|
1938
|
+
root@localhost: echo 'PermitRootLogin yes' >>/etc/ssh/sshd_config
|
1939
|
+
root@localhost: echo 'StrictModes no' >>/etc/ssh/sshd_config
|
1940
|
+
root@localhost: mkdir -p /run/sshd
|
1941
|
+
root@localhost: # 修改 root 密码
|
1942
|
+
root@localhost: echo root:lamda|chpasswd
|
1943
|
+
root@localhost: # 退出 debian 环境
|
1944
|
+
root@localhost: exit
|
1945
|
+
# 现在,你已经进入了 lamda 的 shell 环境,执行以下命令来启动 debian 环境中的 ssh 服务
|
1946
|
+
debian /usr/sbin/sshd -D -e
|
1947
|
+
```
|
1948
|
+
|
1949
|
+
如果你想此 debian ssh 环境随 lamda 一同启动,请执行 `crontab -e`,并在其中写下如下规则并重启即可。
|
1950
|
+
|
1951
|
+
```bash
|
1952
|
+
@reboot debian /usr/sbin/sshd -D -e >/data/usr/sshd.log 2>&1
|
1953
|
+
```
|
1954
|
+
|
1955
|
+
现在,获取该设备的 IP 地址,随后在你的电脑上执行如下命令并输入密码 lamda 即可登录该 debian shell
|
1956
|
+
|
1957
|
+
```bash
|
1958
|
+
ssh root@192.168.x.x (手机IP)
|
1959
|
+
```
|
1960
|
+
|
1859
1961
|
# 工具及教程
|
1860
1962
|
|
1861
1963
|
其中的每个文件夹下都有一份使用说明。
|
@@ -1897,4 +1999,4 @@ t = 切换到 /data/local/tmp
|
|
1897
1999
|
打开 [tools/README.md](tools/README.md) 查看。
|
1898
2000
|
|
1899
2001
|
|
1900
|
-
如果仍有疑问,请加入社区讨论:[电报 t.me/lamda_dev](https://t.me/lamda_dev)
|
2002
|
+
如果仍有疑问,请加入社区讨论:[电报 t.me/lamda_dev](https://t.me/lamda_dev)
|
@@ -10,18 +10,26 @@ import copy
|
|
10
10
|
import time
|
11
11
|
import uuid
|
12
12
|
import json
|
13
|
+
import base64
|
14
|
+
import hashlib
|
13
15
|
import platform
|
14
16
|
import warnings
|
15
17
|
import builtins
|
16
18
|
import logging
|
19
|
+
import msgpack
|
20
|
+
# fix protobuf>=4.0/win32, #10158
|
21
|
+
if sys.platform == "win32":
|
22
|
+
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
|
17
23
|
import grpc
|
18
24
|
|
25
|
+
import pem as Pem
|
19
26
|
import collections.abc
|
20
|
-
# fix pyreadline
|
27
|
+
# fix pyreadline, py310, Windows
|
21
28
|
collections.Callable = collections.abc.Callable
|
22
29
|
|
23
30
|
from urllib.parse import quote
|
24
31
|
from collections import defaultdict
|
32
|
+
from cryptography.fernet import Fernet
|
25
33
|
from os.path import basename, dirname, expanduser, join as joinpath
|
26
34
|
from google.protobuf.json_format import MessageToDict, MessageToJson
|
27
35
|
from grpc_interceptor import ClientInterceptor
|
@@ -56,6 +64,8 @@ __all__ = [
|
|
56
64
|
"Keys",
|
57
65
|
"KeyCode",
|
58
66
|
"KeyCodes",
|
67
|
+
"BaseCryptor",
|
68
|
+
"FernetCryptor",
|
59
69
|
"OpenVPNAuth",
|
60
70
|
"OpenVPNEncryption",
|
61
71
|
"OpenVPNKeyDirection",
|
@@ -229,11 +239,39 @@ def to_dict(prot):
|
|
229
239
|
return json.loads(r)
|
230
240
|
|
231
241
|
|
242
|
+
class BaseCryptor(object):
|
243
|
+
def __str__(self):
|
244
|
+
return "{}".format(self.__class__.__name__)
|
245
|
+
__repr__ = __str__
|
246
|
+
def encrypt(self, data):
|
247
|
+
return data
|
248
|
+
def decrypt(self, data):
|
249
|
+
return data
|
250
|
+
|
251
|
+
|
232
252
|
class BaseServiceStub(object):
|
253
|
+
def __str__(self):
|
254
|
+
return "{}".format(self.__class__.__name__)
|
255
|
+
__repr__ = __str__
|
233
256
|
def __init__(self, stub):
|
234
257
|
self.stub = stub
|
235
258
|
|
236
259
|
|
260
|
+
class FernetCryptor(BaseCryptor):
|
261
|
+
def __init__(self, key=None):
|
262
|
+
key = self._get_key(key)
|
263
|
+
self.encoder = Fernet(key)
|
264
|
+
def encrypt(self, data):
|
265
|
+
return self.encoder.encrypt(data)
|
266
|
+
def decrypt(self, data):
|
267
|
+
return self.encoder.decrypt(data)
|
268
|
+
def _get_key(self, key):
|
269
|
+
key = (key or "").encode()
|
270
|
+
key = hashlib.sha256(key).digest()
|
271
|
+
key = base64.b64encode(key)
|
272
|
+
return key
|
273
|
+
|
274
|
+
|
237
275
|
class ClientLoggingInterceptor(ClientInterceptor):
|
238
276
|
def truncate_string(self, s):
|
239
277
|
return "{:.1024}...".format(s) if len(s) > 1024 else s
|
@@ -251,18 +289,14 @@ class ClientLoggingInterceptor(ClientInterceptor):
|
|
251
289
|
|
252
290
|
|
253
291
|
class ClientSessionMetadataInterceptor(ClientInterceptor):
|
254
|
-
def
|
255
|
-
|
256
|
-
|
292
|
+
def __init__(self, session):
|
293
|
+
super(ClientSessionMetadataInterceptor, self).__init__()
|
294
|
+
self.session = session
|
257
295
|
def intercept(self, function, request, details):
|
258
|
-
"""
|
259
|
-
在每次远程调用加上本实例的ID用于实现锁功能
|
260
|
-
"""
|
261
296
|
metadata = {}
|
262
297
|
metadata["version"] = __version__
|
263
|
-
metadata["instance"] = self.
|
298
|
+
metadata["instance"] = self.session
|
264
299
|
metadata["hostname"] = quote(platform.node())
|
265
|
-
metadata["python_branch"] = platform.python_branch()
|
266
300
|
details = details._replace(metadata=metadata.items())
|
267
301
|
res = function(request, details)
|
268
302
|
return res
|
@@ -286,7 +320,7 @@ class GrpcRemoteExceptionInterceptor(ClientInterceptor):
|
|
286
320
|
return clazz(*args)
|
287
321
|
|
288
322
|
def raise_remote_exception(self, res):
|
289
|
-
metadata = dict(res.initial_metadata())
|
323
|
+
metadata = dict(res.initial_metadata() or [])
|
290
324
|
exception = metadata.get("exception", None)
|
291
325
|
if exception != None:
|
292
326
|
raise self.remote_exception(exception)
|
@@ -371,8 +405,6 @@ class ObjectUiAutomatorOpStub:
|
|
371
405
|
r = self.stub.selectorClickExists(req)
|
372
406
|
return r.value
|
373
407
|
def click_exist(self, *args, **kwargs):
|
374
|
-
# deprecated
|
375
|
-
warnings.warn("use d(..).click_exists() instead", DeprecationWarning)
|
376
408
|
return self.click_exists(*args, **kwargs)
|
377
409
|
def long_click(self, corner=Corner.COR_CENTER):
|
378
410
|
"""
|
@@ -390,8 +422,6 @@ class ObjectUiAutomatorOpStub:
|
|
390
422
|
r = self.stub.selectorExists(req)
|
391
423
|
return r.value
|
392
424
|
def exist(self, *args, **kwargs):
|
393
|
-
# deprecated
|
394
|
-
warnings.warn("use d(..).exists() instead", DeprecationWarning)
|
395
425
|
return self.exists(*args, **kwargs)
|
396
426
|
def info(self):
|
397
427
|
"""
|
@@ -837,7 +867,7 @@ class UiAutomatorStub(BaseServiceStub):
|
|
837
867
|
return ObjectUiAutomatorOpStub(self.stub, kwargs)
|
838
868
|
|
839
869
|
|
840
|
-
class
|
870
|
+
class ApplicationOpStub:
|
841
871
|
def __init__(self, stub, applicationId):
|
842
872
|
"""
|
843
873
|
Application 子接口,用来模拟出实例的意味
|
@@ -845,7 +875,8 @@ class ObjectApplicationOpStub:
|
|
845
875
|
self.applicationId = applicationId
|
846
876
|
self.stub = stub
|
847
877
|
def __str__(self):
|
848
|
-
return "
|
878
|
+
return "{}:{}".format(self.stub.__class__.__name__,
|
879
|
+
self.applicationId)
|
849
880
|
__repr__ = __str__
|
850
881
|
def is_foreground(self):
|
851
882
|
"""
|
@@ -907,6 +938,8 @@ class ObjectApplicationOpStub:
|
|
907
938
|
req = protos.ApplicationRequest(name=self.applicationId)
|
908
939
|
r = self.stub.resetApplicationData(req)
|
909
940
|
return r.value
|
941
|
+
def reset(self):
|
942
|
+
return self.reset_data()
|
910
943
|
def start(self):
|
911
944
|
"""
|
912
945
|
启动应用
|
@@ -1018,7 +1051,118 @@ class ApplicationStub(BaseServiceStub):
|
|
1018
1051
|
r = self.stub.startActivity(req)
|
1019
1052
|
return r.value
|
1020
1053
|
def __call__(self, applicationId):
|
1021
|
-
return
|
1054
|
+
return ApplicationOpStub(self.stub, applicationId)
|
1055
|
+
|
1056
|
+
|
1057
|
+
class StorageOpStub:
|
1058
|
+
def __str__(self):
|
1059
|
+
return "{}:{}".format(self.stub.__class__.__name__,
|
1060
|
+
self.name)
|
1061
|
+
__repr__ = __str__
|
1062
|
+
# 用于容器值序列化的方法
|
1063
|
+
def _decrypt(self, data):
|
1064
|
+
return self.cryptor.decrypt(data)
|
1065
|
+
def _encrypt(self, data):
|
1066
|
+
return self.cryptor.encrypt(data)
|
1067
|
+
def _unpack(self, value):
|
1068
|
+
return msgpack.loads(self._decrypt(value))
|
1069
|
+
def _pack(self, value):
|
1070
|
+
return self._encrypt(msgpack.dumps(value))
|
1071
|
+
# 注意:此接口可能并不是跨语言通用
|
1072
|
+
def __init__(self, stub, name, cryptor=None):
|
1073
|
+
self.cryptor = cryptor
|
1074
|
+
self.name = name
|
1075
|
+
self.stub = stub
|
1076
|
+
def delete(self, key):
|
1077
|
+
"""
|
1078
|
+
删除一个 KEY
|
1079
|
+
"""
|
1080
|
+
req = protos.StorageRequest(key=key)
|
1081
|
+
req.container = self.name
|
1082
|
+
res = self.stub.delete(req)
|
1083
|
+
return res.value
|
1084
|
+
def exists(self, key):
|
1085
|
+
"""
|
1086
|
+
检查一个 KEY 是否存在
|
1087
|
+
"""
|
1088
|
+
req = protos.StorageRequest(key=key)
|
1089
|
+
req.container = self.name
|
1090
|
+
res = self.stub.exists(req)
|
1091
|
+
return res.value
|
1092
|
+
def get(self, key, default=None):
|
1093
|
+
"""
|
1094
|
+
获取 KEY 对应的键值
|
1095
|
+
"""
|
1096
|
+
req = protos.StorageRequest(key=key)
|
1097
|
+
req.container = self.name
|
1098
|
+
val = self.stub.get(req).value
|
1099
|
+
res = self._unpack(val) if val else default
|
1100
|
+
return res
|
1101
|
+
def set(self, key, value):
|
1102
|
+
"""
|
1103
|
+
设置 KEY 对应的键值
|
1104
|
+
"""
|
1105
|
+
value = self._pack(value)
|
1106
|
+
req = protos.StorageRequest(key=key, value=value)
|
1107
|
+
req.container = self.name
|
1108
|
+
res = self.stub.set(req)
|
1109
|
+
return res.value
|
1110
|
+
def setex(self, key, value, ttl):
|
1111
|
+
"""
|
1112
|
+
设置 KEY 对应的键值,该 KEY 在 TTL 秒后自动删除
|
1113
|
+
"""
|
1114
|
+
value = self._pack(value)
|
1115
|
+
req = protos.StorageRequest(key=key, value=value)
|
1116
|
+
req.container = self.name
|
1117
|
+
req.ttl = ttl
|
1118
|
+
res = self.stub.setex(req)
|
1119
|
+
return res.value
|
1120
|
+
def setnx(self, key, value):
|
1121
|
+
"""
|
1122
|
+
设置 KEY 对应的键值 (仅当该键不存在时)
|
1123
|
+
"""
|
1124
|
+
value = self._pack(value)
|
1125
|
+
req = protos.StorageRequest(key=key, value=value)
|
1126
|
+
req.container = self.name
|
1127
|
+
res = self.stub.setnx(req)
|
1128
|
+
return res.value
|
1129
|
+
def expire(self, key, ttl):
|
1130
|
+
"""
|
1131
|
+
设置 KEY 在 TTL 秒后过期
|
1132
|
+
"""
|
1133
|
+
req = protos.StorageRequest(key=key, ttl=ttl)
|
1134
|
+
req.container = self.name
|
1135
|
+
res = self.stub.expire(req)
|
1136
|
+
return res.value
|
1137
|
+
def ttl(self, key):
|
1138
|
+
"""
|
1139
|
+
获取 KEY 的 TTL (过期时间)
|
1140
|
+
"""
|
1141
|
+
req = protos.StorageRequest(key=key)
|
1142
|
+
req.container = self.name
|
1143
|
+
res = self.stub.ttl(req)
|
1144
|
+
return res.value
|
1145
|
+
|
1146
|
+
|
1147
|
+
class StorageStub(BaseServiceStub):
|
1148
|
+
def clear(self):
|
1149
|
+
"""
|
1150
|
+
删除所有 Storage 容器
|
1151
|
+
"""
|
1152
|
+
r = self.stub.clearAll(protos.Empty())
|
1153
|
+
return r.value
|
1154
|
+
def use(self, name, cryptor=BaseCryptor, **kwargs):
|
1155
|
+
"""
|
1156
|
+
使用一个 Storage 容器
|
1157
|
+
"""
|
1158
|
+
return StorageOpStub(self.stub, name, cryptor(**kwargs))
|
1159
|
+
def remove(self, name):
|
1160
|
+
"""
|
1161
|
+
删除一个 Storage 容器
|
1162
|
+
"""
|
1163
|
+
req = protos.String(value=name)
|
1164
|
+
r = self.stub.clearContainer(req)
|
1165
|
+
return r.value
|
1022
1166
|
|
1023
1167
|
|
1024
1168
|
class UtilStub(BaseServiceStub):
|
@@ -1075,11 +1219,12 @@ class UtilStub(BaseServiceStub):
|
|
1075
1219
|
"""
|
1076
1220
|
r = self.stub.shutdown(protos.Empty())
|
1077
1221
|
return r.value
|
1078
|
-
def reload(self):
|
1222
|
+
def reload(self, clean=False):
|
1079
1223
|
"""
|
1080
1224
|
重载设备上运行的服务端
|
1081
1225
|
"""
|
1082
|
-
|
1226
|
+
req = protos.Boolean(value=clean)
|
1227
|
+
r = self.stub.reload(req)
|
1083
1228
|
return r.value
|
1084
1229
|
def exit(self):
|
1085
1230
|
"""
|
@@ -1500,6 +1645,18 @@ class LockStub(BaseServiceStub):
|
|
1500
1645
|
req = protos.Integer(value=leaseTime)
|
1501
1646
|
r = self.stub.acquireLock(req)
|
1502
1647
|
return r.value
|
1648
|
+
def get_locking_session(self):
|
1649
|
+
"""
|
1650
|
+
获取当前占有设备锁的会话ID
|
1651
|
+
"""
|
1652
|
+
r = self.stub.getLockingSession(protos.Empty())
|
1653
|
+
return r.value
|
1654
|
+
def get_session_token(self):
|
1655
|
+
"""
|
1656
|
+
获取当前占有设备锁的会话的令牌
|
1657
|
+
"""
|
1658
|
+
r = self.stub.getSessionToken(protos.Empty())
|
1659
|
+
return r.value
|
1503
1660
|
def refresh_lock(self, leaseTime=60):
|
1504
1661
|
"""
|
1505
1662
|
刷新用于控制设备的锁,应该在定时任务每60s内调用以保持会话
|
@@ -1606,50 +1763,65 @@ class WifiStub(BaseServiceStub):
|
|
1606
1763
|
|
1607
1764
|
class Device(object):
|
1608
1765
|
def __init__(self, host, port=65000,
|
1609
|
-
certificate=None
|
1766
|
+
certificate=None,
|
1767
|
+
session=None):
|
1610
1768
|
self.certificate = certificate
|
1611
1769
|
self.server = "{0}:{1}".format(host, port)
|
1612
1770
|
if certificate is not None:
|
1613
1771
|
with open(certificate, "rb") as fd:
|
1614
|
-
|
1615
|
-
creds = grpc.ssl_channel_credentials(
|
1616
|
-
|
1772
|
+
key, crt, ca = self._parse_certdata(fd.read())
|
1773
|
+
creds = grpc.ssl_channel_credentials(root_certificates=ca,
|
1774
|
+
certificate_chain=crt,
|
1775
|
+
private_key=key)
|
1776
|
+
self._chan = grpc.secure_channel(self.server, creds,
|
1617
1777
|
options=(("grpc.ssl_target_name_override",
|
1618
|
-
self.
|
1778
|
+
self._parse_cname(crt)),
|
1619
1779
|
("grpc.enable_http_proxy",
|
1620
|
-
|
1780
|
+
0)))
|
1621
1781
|
else:
|
1622
|
-
|
1623
|
-
|
1782
|
+
self._chan = grpc.insecure_channel(self.server)
|
1783
|
+
session = session or uuid.uuid4().hex
|
1784
|
+
interceptors = [ClientSessionMetadataInterceptor(session),
|
1624
1785
|
GrpcRemoteExceptionInterceptor(),
|
1625
1786
|
ClientLoggingInterceptor()]
|
1626
|
-
self.
|
1787
|
+
self.channel = grpc.intercept_channel(self._chan,
|
1627
1788
|
*interceptors)
|
1789
|
+
self.session = session
|
1628
1790
|
@property
|
1629
1791
|
def frida(self):
|
1630
1792
|
if _frida_dma is None:
|
1631
1793
|
raise ModuleNotFoundError("frida")
|
1794
|
+
kwargs = {}
|
1632
1795
|
if self.certificate is not None:
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1796
|
+
kwargs["certificate"] = self.certificate
|
1797
|
+
if self._get_session_token():
|
1798
|
+
kwargs["token"] = self._get_session_token()
|
1799
|
+
device = _frida_dma.add_remote_device(self.server,
|
1800
|
+
**kwargs)
|
1637
1801
|
return device
|
1638
1802
|
def __str__(self):
|
1639
1803
|
return "Device@{}".format(self.server)
|
1640
1804
|
__repr__ = __str__
|
1641
|
-
def
|
1642
|
-
|
1805
|
+
def _parse_certdata(self, data):
|
1806
|
+
key, crt, ca = Pem.parse(data)
|
1807
|
+
ca = ca.as_bytes()
|
1808
|
+
crt = crt.as_bytes()
|
1809
|
+
key = key.as_bytes()
|
1810
|
+
return key, crt, ca
|
1811
|
+
def _parse_cname(self, crt):
|
1812
|
+
_, _, der = pem.unarmor(crt)
|
1643
1813
|
subject = x509.Certificate.load(der).subject
|
1644
1814
|
return subject.native["common_name"]
|
1645
1815
|
def _get_service_stub(self, module):
|
1646
1816
|
stub = getattr(services, "{0}Stub".format(module))
|
1647
|
-
return stub(self.
|
1817
|
+
return stub(self.channel)
|
1648
1818
|
def stub(self, module):
|
1649
1819
|
modu = sys.modules[__name__]
|
1650
1820
|
stub = self._get_service_stub(module)
|
1651
1821
|
wrap = getattr(modu, "{0}Stub".format(module))
|
1652
|
-
|
1822
|
+
inst = getattr(self, module, wrap(stub))
|
1823
|
+
self.__setattr__(module, inst)
|
1824
|
+
return inst
|
1653
1825
|
# 快速调用: File
|
1654
1826
|
def download_fd(self, fpath, fd):
|
1655
1827
|
return self.stub("File").download_fd(fpath, fd)
|
@@ -1695,8 +1867,8 @@ class Device(object):
|
|
1695
1867
|
return self.stub("Util").shutdown()
|
1696
1868
|
def exit(self):
|
1697
1869
|
return self.stub("Util").exit()
|
1698
|
-
def reload(self):
|
1699
|
-
return self.stub("Util").reload()
|
1870
|
+
def reload(self, clean=False):
|
1871
|
+
return self.stub("Util").reload(clean)
|
1700
1872
|
def beep(self):
|
1701
1873
|
return self.stub("Util").beep()
|
1702
1874
|
def setprop(self, name, value):
|
@@ -1815,6 +1987,7 @@ class Device(object):
|
|
1815
1987
|
return self.stub("UiAutomator").device_info()
|
1816
1988
|
def __call__(self, **kwargs):
|
1817
1989
|
return self.stub("UiAutomator")(**kwargs)
|
1990
|
+
# 日志打印
|
1818
1991
|
def setup_log_format(self):
|
1819
1992
|
logging.basicConfig(format=FORMAT)
|
1820
1993
|
def set_debug_log_enabled(self, enable):
|
@@ -1822,6 +1995,10 @@ class Device(object):
|
|
1822
1995
|
logger.setLevel(level)
|
1823
1996
|
return enable
|
1824
1997
|
# 接口锁定
|
1998
|
+
def _get_session_token(self):
|
1999
|
+
return self.stub("Lock").get_session_token()
|
2000
|
+
def _get_locking_session(self):
|
2001
|
+
return self.stub("Lock").get_locking_session()
|
1825
2002
|
def _acquire_lock(self, leaseTime=60):
|
1826
2003
|
return self.stub("Lock").acquire_lock(leaseTime)
|
1827
2004
|
def _refresh_lock(self, leaseTime=60):
|
@@ -36,9 +36,11 @@ class StaleObjectException(Exception):
|
|
36
36
|
""" Exception """
|
37
37
|
class StartupActivityNotFound(Exception):
|
38
38
|
""" Exception """
|
39
|
+
class StorageOutOfMemory(Exception):
|
40
|
+
""" Exception """
|
39
41
|
class UiAutomatorException(Exception):
|
40
42
|
""" Exception """
|
41
43
|
class UiObjectNotFoundException(Exception):
|
42
44
|
""" Exception """
|
43
45
|
class UnHandledException(Exception):
|
44
|
-
""" Exception """
|
46
|
+
""" Exception """
|
@@ -18,6 +18,7 @@ import public "settings.proto";
|
|
18
18
|
import public "status.proto";
|
19
19
|
import public "application.proto";
|
20
20
|
import public "uiautomator.proto";
|
21
|
+
import public "storage.proto";
|
21
22
|
import public "proxy.proto";
|
22
23
|
import public "file.proto";
|
23
24
|
import public "wifi.proto";
|
@@ -220,7 +221,7 @@ service Util {
|
|
220
221
|
rpc installCACertificate(CertifiRequest) returns (Boolean) {}
|
221
222
|
rpc uninstallCACertificate(CertifiRequest) returns (Boolean) {}
|
222
223
|
rpc shutdown(google.protobuf.Empty) returns (Boolean) {}
|
223
|
-
rpc reload(
|
224
|
+
rpc reload(Boolean) returns (Boolean) {}
|
224
225
|
rpc exit(google.protobuf.Empty) returns (Boolean) {}
|
225
226
|
rpc setProp(SetPropRequest) returns (Boolean) {}
|
226
227
|
rpc getProp(String) returns (String) {}
|
@@ -236,6 +237,21 @@ service File {
|
|
236
237
|
|
237
238
|
service Lock {
|
238
239
|
rpc releaseLock(google.protobuf.Empty) returns (Boolean) {}
|
240
|
+
rpc getLockingSession(google.protobuf.Empty) returns (String) {}
|
241
|
+
rpc getSessionToken(google.protobuf.Empty) returns (String) {}
|
239
242
|
rpc acquireLock(Integer) returns (Boolean) {}
|
240
243
|
rpc refreshLock(Integer) returns (Boolean) {}
|
244
|
+
}
|
245
|
+
|
246
|
+
service Storage {
|
247
|
+
rpc clearAll(StorageRequest) returns (Boolean) {}
|
248
|
+
rpc clearContainer(StorageRequest) returns (Boolean) {}
|
249
|
+
rpc exists(StorageRequest) returns (Boolean) {}
|
250
|
+
rpc delete(StorageRequest) returns (Boolean) {}
|
251
|
+
rpc expire(StorageRequest) returns (Boolean) {}
|
252
|
+
rpc ttl(StorageRequest) returns (Integer) {}
|
253
|
+
rpc set(StorageRequest) returns (Boolean) {}
|
254
|
+
rpc setnx(StorageRequest) returns (Boolean) {}
|
255
|
+
rpc setex(StorageRequest) returns (Boolean) {}
|
256
|
+
rpc get(StorageRequest) returns (Bytes) {}
|
241
257
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// Copyright 2022 rev1si0n (ihaven0emmail@gmail.com). All rights reserved.
|
2
|
+
//
|
3
|
+
// Distributed under MIT license.
|
4
|
+
// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
5
|
+
syntax = "proto3";
|
6
|
+
package lamda.rpc;
|
7
|
+
|
8
|
+
message StorageRequest {
|
9
|
+
string container = 1;
|
10
|
+
string key = 2;
|
11
|
+
bytes value = 3;
|
12
|
+
uint32 ttl = 4;
|
13
|
+
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lamda
|
3
|
-
Version:
|
3
|
+
Version: 5.0
|
4
4
|
Summary: Android reverse engineering & automation framework
|
5
5
|
Home-page: https://github.com/rev1si0n/lamda
|
6
6
|
Author: rev1si0n
|
@@ -12,6 +12,6 @@ Classifier: Intended Audience :: Science/Research
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
13
13
|
Classifier: Operating System :: Android
|
14
14
|
Classifier: Topic :: Security
|
15
|
-
Requires-Python: >=3.6,<3.
|
15
|
+
Requires-Python: >=3.6,<3.12
|
16
16
|
Provides-Extra: next
|
17
17
|
Provides-Extra: full
|
@@ -1,7 +1,10 @@
|
|
1
|
-
grpcio-tools<1.
|
1
|
+
grpcio-tools<1.60.0,>=1.35.0
|
2
2
|
grpc-interceptor<0.15.0,>=0.13.0
|
3
|
-
grpcio<1.
|
3
|
+
grpcio<1.60.0,>=1.35.0
|
4
|
+
cryptography>=35.0.0
|
5
|
+
msgpack>=1.0.0
|
4
6
|
asn1crypto<2,>=1.0.0
|
7
|
+
pem==23.1.0
|
5
8
|
|
6
9
|
[:sys_platform == "win32"]
|
7
10
|
pyreadline==2.1
|
@@ -10,7 +10,7 @@ setuptools.setup(
|
|
10
10
|
description = "Android reverse engineering & automation framework",
|
11
11
|
url = "https://github.com/rev1si0n/lamda",
|
12
12
|
author = "rev1si0n",
|
13
|
-
python_requires = ">=3.6,<3.
|
13
|
+
python_requires = ">=3.6,<3.12",
|
14
14
|
zip_safe = False,
|
15
15
|
extras_require = {
|
16
16
|
"next": ["frida>=16.0.0,<17.0.0"],
|
@@ -20,10 +20,13 @@ setuptools.setup(
|
|
20
20
|
],
|
21
21
|
},
|
22
22
|
install_requires= [
|
23
|
-
"grpcio-tools>=1.35.0,<1.
|
23
|
+
"grpcio-tools>=1.35.0,<1.60.0",
|
24
24
|
"grpc-interceptor>=0.13.0,<0.15.0",
|
25
|
-
"grpcio>=1.35.0,<1.
|
25
|
+
"grpcio>=1.35.0,<1.60.0",
|
26
|
+
"cryptography>=35.0.0",
|
27
|
+
"msgpack>=1.0.0",
|
26
28
|
"asn1crypto>=1.0.0,<2",
|
29
|
+
"pem==23.1.0",
|
27
30
|
],
|
28
31
|
classifiers = [
|
29
32
|
"Environment :: Console",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|