lamda 7.90__tar.gz → 8.5__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {lamda-7.90 → lamda-8.5}/PKG-INFO +1 -1
- lamda-8.5/README.md +46 -0
- {lamda-7.90 → lamda-8.5}/lamda/__init__.py +1 -1
- {lamda-7.90 → lamda-8.5}/lamda/client.py +72 -61
- {lamda-7.90 → lamda-8.5}/lamda/exceptions.py +2 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/application.proto +4 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/services.proto +3 -3
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/PKG-INFO +1 -1
- lamda-7.90/README.md +0 -43
- {lamda-7.90 → lamda-8.5}/lamda/bcast.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/const.py +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/any.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/api.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/compiler/plugin.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/descriptor.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/duration.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/empty.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/field_mask.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/source_context.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/struct.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/timestamp.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/type.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/google/protobuf/wrappers.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/debug.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/file.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/policy.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/proxy.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/settings.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/shell.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/status.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/storage.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/types.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/uiautomator.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/util.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/rpc/wifi.proto +0 -0
- {lamda-7.90 → lamda-8.5}/lamda/types.py +0 -0
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/SOURCES.txt +0 -0
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/dependency_links.txt +0 -0
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/not-zip-safe +0 -0
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/requires.txt +0 -0
- {lamda-7.90 → lamda-8.5}/lamda.egg-info/top_level.txt +0 -0
- {lamda-7.90 → lamda-8.5}/setup.cfg +0 -0
- {lamda-7.90 → lamda-8.5}/setup.py +0 -0
lamda-8.5/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
<p align="center">
|
2
|
+
<img src="image/logo.svg" alt="FIRERPA" width="345">
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p align="center">安卓 RPA 机器人框架,下一代移动端数据自动化机器人</p>
|
6
|
+
|
7
|
+
<p align="center">
|
8
|
+
<img src="https://img.shields.io/badge/python-3.6+-blue.svg?logo=python&labelColor=yellow" />
|
9
|
+
<img src="https://img.shields.io/badge/android-6.0--15-blue.svg?logo=android&labelColor=white" />
|
10
|
+
<img src="https://img.shields.io/badge/root%20require-red.svg?logo=android&labelColor=black" />
|
11
|
+
<img src="https://img.shields.io/github/downloads/rev1si0n/lamda/total" />
|
12
|
+
<img src="https://img.shields.io/github/v/release/rev1si0n/lamda" />
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<p align="center"><a href="https://device-farm.com/doc/index.html">使用文档</a> | <a href="https://t.me/lamda_dev">TELEGRAM</a> | <a href="https://qm.qq.com/q/zDaX2a594I">QQ 群组</a> | <a href="https://device-farm.com/doc/版本历史.html">更新历史</a></p>
|
16
|
+
|
17
|
+
智能机崛起,传统网页端的普及度也开始显著减弱,数据与应用正加速向移动端转移。越来越多的人选择通过智能手机和平板等移动设备来获取信息和服务。随着移动设备的普及,用户享受到更便捷访问体验,传统的网页内容模式面临重新审视。与此同时,数据采集的技术也亟需适应这一趋势。过去,许多数据采集工具专注于网页内容,在移动端环境中,尤其是在移动端封闭的黑盒中,现今的常规技术也面临着新的挑战,FIRERPA 创造了可能,将这一切变得更加简单快捷。
|
18
|
+
|
19
|
+
FIRERPA 是**安卓领域的集大成者**,设计为减少**安全分析**及**应用测试**或**自动化**工作的时间和琐碎问题,经过超 500 台设备集群的生产环境考验,稳定应用于多个大型系统,包括自动化取证,云平台,数据采集,合规分析等,完全商业级软件的质量和稳定性,仅需 root 权限即可正常运行。具备 **ARM/X86** 全架构,安卓 **6.0-15** 的广泛兼容性([16 KB page size](https://developer.android.com/guide/practices/page-sizes) 的安卓 15 适配中),支持 **模拟器**、**真机**、**云手机**、 **WSA**(Windows Subsystem for Android™️)、**RK开发板** (所有 ARM 架构)以及 **Redroid** 等大多数运行安卓系统的设备。为**移动端 RPA 和数据自动化**提供稳定的解决方案,以**编程化**的**接口**替代大量手动操作,**易部署**,没有那些复杂花哨不跨平台的安装流程,你所需要的能力他大概率能做到并且做的更好。他并不是一个单一功能的框架,他是集 Appium、uiautomator **自动化**的超集同时具备**逆向**领域如 **Hook** **抓包** **证书安装** **组网** **API跟踪** **手机自控** 等等各种能力的框架。当然,FIRERPA 可以做到的远不止于此,你可以阅读使用文档尽情探索他的所有能力。
|
20
|
+
|
21
|
+
|
22
|
+
<h3><p align="center">适用广泛的服务架构设计</p></h3>
|
23
|
+
|
24
|
+
<p align="center">FIRERPA 不同于任何其他同类或者子集软件,在设计之初即考虑到了各种可能的应用环境,可在非侵入的情况下运行在当下绝大部分的安卓设备内,即插即用,没有任何外部依赖,无需任何多余设置,原生即是为大型的业务场景设计,易于系统化的管理及更新。而此时,其他具备相似功能的软件还在经历掉线、不稳定、兼容性差、无人维护、管理难度大等各种问题。</p>
|
25
|
+
|
26
|
+
<h3><p align="center">丰富编程接口为业务赋能</p></h3>
|
27
|
+
|
28
|
+
<p align="center">提供多达 160 条编程 API 接口,让你可以对安卓设备进行无微不至的管理和操作,提供了包括命令执行,系统设置,系统状态,应用相关,自动化控制,代理以及文件等十几个大类的接口。同时提供了封装完整的 Python 库让你可以快速上手使用。规范及稳定的服务和接口让你可以无忧的通过代码控制每一个细节,甚至可以让其和 AI 进行互联,让大模型自动编写以及执行任务。</p>
|
29
|
+
|
30
|
+
<h3><p align="center">简洁易用的远程桌面</p></h3>
|
31
|
+
|
32
|
+
<p align="center">
|
33
|
+
<img src="image/demo.gif" alt="动图演示" width="95%">
|
34
|
+
</p>
|
35
|
+
|
36
|
+
<h3><p align="center">一键中间人流量分析及更多其他功能</p></h3>
|
37
|
+
|
38
|
+
支持常规以及国际APP流量分析,DNS流量分析,得益于 [mitmproxy flow hook](https://docs.mitmproxy.org/stable/api/events.html),你可以对任何请求做到最大限度的掌控,mitmproxy 功能足够丰富,你可以使用 Python 脚本实时修改或者捕获应用的请求,也可以通过其 `Export` 选项导出特定请求的 `curl` 命令或者 `HTTPie` 命令,分析重放、拦截修改、功能组合足以替代你用过的任何此类商业/非商业软件。如果你仍不清楚 mitmproxy 是什么以及其具有的能力,请务必先查找相关文档,因为 FIRERPA 将会使用 mitmproxy 为你展现应用请求。
|
39
|
+
|
40
|
+
<p align="center">
|
41
|
+
<img src="image/mitm.gif" alt="动图演示" width="95%">
|
42
|
+
</p>
|
43
|
+
|
44
|
+
<p align="center">
|
45
|
+
当然,FIRERPA 提供的能力不止于这些,他是你强有力的设备控制及管理工具,感兴趣请转到 使用文档。
|
46
|
+
</p>
|
@@ -88,6 +88,7 @@ __all__ = [
|
|
88
88
|
"Orientation",
|
89
89
|
"OpenVPNProfile",
|
90
90
|
"GproxyProfile",
|
91
|
+
"TouchBuilder",
|
91
92
|
"Selector",
|
92
93
|
"TouchWait",
|
93
94
|
"TouchMove",
|
@@ -256,6 +257,7 @@ TouchSequence.load = classmethod(touchSequenceLoad)
|
|
256
257
|
TouchSequence.save = touchSequenceSave
|
257
258
|
TouchSequence.appendAction = touchSequenceAppendAction
|
258
259
|
TouchSequence.appendDown = touchSequenceAppendDown
|
260
|
+
TouchSequence.appendMove = touchSequenceAppendMove
|
259
261
|
TouchSequence.appendWait = touchSequenceAppendWait
|
260
262
|
TouchSequence.appendUp = touchSequenceAppendUp
|
261
263
|
|
@@ -302,9 +304,6 @@ class CustomOcrBackend(object):
|
|
302
304
|
|
303
305
|
|
304
306
|
class BaseCryptor(object):
|
305
|
-
def __str__(self):
|
306
|
-
return "{}".format(self.__class__.__name__)
|
307
|
-
__repr__ = __str__
|
308
307
|
def encrypt(self, data):
|
309
308
|
return data
|
310
309
|
def decrypt(self, data):
|
@@ -312,9 +311,6 @@ class BaseCryptor(object):
|
|
312
311
|
|
313
312
|
|
314
313
|
class BaseServiceStub(object):
|
315
|
-
def __str__(self):
|
316
|
-
return "{}".format(self.__class__.__name__)
|
317
|
-
__repr__ = __str__
|
318
314
|
def __init__(self, stub):
|
319
315
|
self.stub = stub
|
320
316
|
|
@@ -334,6 +330,29 @@ class FernetCryptor(BaseCryptor):
|
|
334
330
|
return key
|
335
331
|
|
336
332
|
|
333
|
+
class TouchBuilder(object):
|
334
|
+
def __init__(self):
|
335
|
+
self.s = TouchSequence()
|
336
|
+
def down(self, x, y, pressure=50, track=0):
|
337
|
+
self.s.appendDown(tid=track, x=x, y=y,
|
338
|
+
pressure=pressure)
|
339
|
+
return self
|
340
|
+
def move(self, x, y, pressure=50, track=0):
|
341
|
+
self.s.appendMove(tid=track, x=x, y=y,
|
342
|
+
pressure=pressure)
|
343
|
+
return self
|
344
|
+
def up(self, track=0):
|
345
|
+
self.s.appendUp(tid=track)
|
346
|
+
return self
|
347
|
+
def wait(self, mills):
|
348
|
+
self.s.appendWait(wait=mills)
|
349
|
+
return self
|
350
|
+
def build(self):
|
351
|
+
sequence = TouchSequence()
|
352
|
+
sequence.CopyFrom(self.s)
|
353
|
+
return sequence
|
354
|
+
|
355
|
+
|
337
356
|
class ClientLoggingInterceptor(ClientInterceptor):
|
338
357
|
def truncate_string(self, s):
|
339
358
|
return "{:.1024}...".format(s) if len(s) > 1024 else s
|
@@ -467,8 +486,6 @@ class ObjectUiAutomatorOpStub:
|
|
467
486
|
corner=corner)
|
468
487
|
r = self.stub.selectorClickExists(req)
|
469
488
|
return r.value
|
470
|
-
def click_exist(self, *args, **kwargs):
|
471
|
-
return self.click_exists(*args, **kwargs)
|
472
489
|
def long_click(self, corner=Corner.COR_CENTER):
|
473
490
|
"""
|
474
491
|
长按选择器选中的控件
|
@@ -484,26 +501,12 @@ class ObjectUiAutomatorOpStub:
|
|
484
501
|
req = protos.SelectorOnlyRequest(selector=self.selector)
|
485
502
|
r = self.stub.selectorExists(req)
|
486
503
|
return r.value
|
487
|
-
def exist(self, *args, **kwargs):
|
488
|
-
return self.exists(*args, **kwargs)
|
489
504
|
def info(self):
|
490
505
|
"""
|
491
506
|
获取选择器选中控件的信息
|
492
507
|
"""
|
493
508
|
req = protos.SelectorOnlyRequest(selector=self.selector)
|
494
509
|
return self.stub.selectorObjInfo(req)
|
495
|
-
def info_of_all_instances(self):
|
496
|
-
"""
|
497
|
-
获取选择器选中的所有控件的信息
|
498
|
-
"""
|
499
|
-
req = protos.SelectorOnlyRequest(selector=self.selector)
|
500
|
-
r = self.stub.selectorObjInfoOfAllInstances(req)
|
501
|
-
return r.objects
|
502
|
-
def all_instances(self):
|
503
|
-
"""
|
504
|
-
获取选择器选中的所有元素控件
|
505
|
-
"""
|
506
|
-
return list(self)
|
507
510
|
def _new_object(self, **kwargs):
|
508
511
|
selector = copy.deepcopy(self._selector)
|
509
512
|
selector.update(**kwargs)
|
@@ -559,6 +562,11 @@ class ObjectUiAutomatorOpStub:
|
|
559
562
|
return self._new_object(index=idx)
|
560
563
|
def instance(self, idx):
|
561
564
|
return self._new_object(instance=idx)
|
565
|
+
def get(self, idx):
|
566
|
+
"""
|
567
|
+
获取匹配的第 N 个索引的元素
|
568
|
+
"""
|
569
|
+
return self.instance(idx)
|
562
570
|
def __iter__(self):
|
563
571
|
"""
|
564
572
|
遍历所有符合选择器条件的元素实例
|
@@ -1042,16 +1050,21 @@ class AppScriptRpcInterface(object):
|
|
1042
1050
|
self.application = application
|
1043
1051
|
self.stub = stub
|
1044
1052
|
self.name = name
|
1053
|
+
def __str__(self):
|
1054
|
+
return "{}:Script:{}".format(self.application,
|
1055
|
+
self.name)
|
1056
|
+
__repr__ = __str__
|
1045
1057
|
def __call__(self, *args):
|
1046
1058
|
call_args = dict()
|
1047
1059
|
call_args["method"] = self.name
|
1048
1060
|
call_args["args"] = args
|
1049
1061
|
req = HookRpcRequest()
|
1050
1062
|
req.package = self.application.applicationId
|
1063
|
+
req.user = self.application.user
|
1051
1064
|
req.callinfo = json.dumps(call_args)
|
1052
1065
|
result = self.stub.callScript(req)
|
1053
|
-
|
1054
|
-
return
|
1066
|
+
data = json.loads(result.callresult)
|
1067
|
+
return data
|
1055
1068
|
|
1056
1069
|
|
1057
1070
|
class ApplicationOpStub:
|
@@ -1063,8 +1076,8 @@ class ApplicationOpStub:
|
|
1063
1076
|
self.applicationId = applicationId
|
1064
1077
|
self.stub = stub
|
1065
1078
|
def __str__(self):
|
1066
|
-
return "{}
|
1067
|
-
|
1079
|
+
return "Application:{}@{}".format(self.applicationId,
|
1080
|
+
self.user)
|
1068
1081
|
__repr__ = __str__
|
1069
1082
|
def is_foreground(self):
|
1070
1083
|
"""
|
@@ -1118,7 +1131,7 @@ class ApplicationOpStub:
|
|
1118
1131
|
req.user = self.user
|
1119
1132
|
r = self.stub.isPermissionGranted(req)
|
1120
1133
|
return r.value
|
1121
|
-
def
|
1134
|
+
def clear_cache(self):
|
1122
1135
|
"""
|
1123
1136
|
清空应用的缓存数据(非数据仅缓存)
|
1124
1137
|
"""
|
@@ -1126,7 +1139,7 @@ class ApplicationOpStub:
|
|
1126
1139
|
req.user = self.user
|
1127
1140
|
r = self.stub.deleteApplicationCache(req)
|
1128
1141
|
return r.value
|
1129
|
-
def
|
1142
|
+
def reset(self):
|
1130
1143
|
"""
|
1131
1144
|
清空应用的所有数据
|
1132
1145
|
"""
|
@@ -1134,8 +1147,6 @@ class ApplicationOpStub:
|
|
1134
1147
|
req.user = self.user
|
1135
1148
|
r = self.stub.resetApplicationData(req)
|
1136
1149
|
return r.value
|
1137
|
-
def reset(self):
|
1138
|
-
return self.reset_data()
|
1139
1150
|
def start(self):
|
1140
1151
|
"""
|
1141
1152
|
启动应用
|
@@ -1226,27 +1237,34 @@ class ApplicationOpStub:
|
|
1226
1237
|
req.spawn = spawn
|
1227
1238
|
req.destination = emit
|
1228
1239
|
req.encode = encode
|
1240
|
+
req.user = self.user
|
1229
1241
|
r = self.stub.attachScript(req)
|
1230
1242
|
return r.value
|
1231
1243
|
def detach_script(self):
|
1232
1244
|
"""
|
1233
1245
|
移除注入应用的 Hook 脚本
|
1234
1246
|
"""
|
1235
|
-
req = protos.
|
1247
|
+
req = protos.HookRequest()
|
1248
|
+
req.package = self.applicationId
|
1249
|
+
req.user = self.user
|
1236
1250
|
r = self.stub.detachScript(req)
|
1237
1251
|
return r.value
|
1238
1252
|
def is_attached_script(self):
|
1239
1253
|
"""
|
1240
1254
|
检查使用在此应用注入了 Hook 脚本
|
1241
1255
|
"""
|
1242
|
-
req = protos.
|
1256
|
+
req = protos.HookRequest()
|
1257
|
+
req.package = self.applicationId
|
1258
|
+
req.user = self.user
|
1243
1259
|
r = self.stub.isScriptAttached(req)
|
1244
1260
|
return r.value
|
1245
1261
|
def is_script_alive(self):
|
1246
1262
|
"""
|
1247
1263
|
检查应用中的 Hook 脚本是否正常
|
1248
1264
|
"""
|
1249
|
-
req = protos.
|
1265
|
+
req = protos.HookRequest()
|
1266
|
+
req.package = self.applicationId
|
1267
|
+
req.user = self.user
|
1250
1268
|
r = self.stub.isScriptAlive(req)
|
1251
1269
|
return r.value
|
1252
1270
|
def __getattr__(self, name):
|
@@ -1263,7 +1281,7 @@ class ApplicationStub(BaseServiceStub):
|
|
1263
1281
|
获取当前处于前台的应用的信息
|
1264
1282
|
"""
|
1265
1283
|
top = self.stub.currentApplication(protos.Empty())
|
1266
|
-
app = self.__call__(top.packageName)
|
1284
|
+
app = self.__call__(top.packageName, user=top.user)
|
1267
1285
|
app.activity = top.activity
|
1268
1286
|
return app
|
1269
1287
|
def get_application_by_name(self, name, user=0):
|
@@ -1305,6 +1323,7 @@ class ApplicationStub(BaseServiceStub):
|
|
1305
1323
|
安装设备上的 apk 文件(注意此路径为设备上的 apk 路径)
|
1306
1324
|
"""
|
1307
1325
|
req = protos.ApplicationRequest(path=fpath)
|
1326
|
+
req.user = self.user
|
1308
1327
|
r = self.stub.installFromLocalFile(req)
|
1309
1328
|
return r
|
1310
1329
|
def __call__(self, applicationId, user=0):
|
@@ -1312,10 +1331,6 @@ class ApplicationStub(BaseServiceStub):
|
|
1312
1331
|
|
1313
1332
|
|
1314
1333
|
class StorageOpStub:
|
1315
|
-
def __str__(self):
|
1316
|
-
return "{}:{}".format(self.stub.__class__.__name__,
|
1317
|
-
self.name)
|
1318
|
-
__repr__ = __str__
|
1319
1334
|
# 用于容器值序列化的方法
|
1320
1335
|
def _decrypt(self, data):
|
1321
1336
|
return self.cryptor.decrypt(data)
|
@@ -1734,20 +1749,20 @@ class ProxyStub(BaseServiceStub):
|
|
1734
1749
|
|
1735
1750
|
|
1736
1751
|
class SelinuxPolicyStub(BaseServiceStub):
|
1737
|
-
def
|
1752
|
+
def allow(self, source, target, tclass, action):
|
1738
1753
|
"""
|
1739
1754
|
selinux allow
|
1740
1755
|
"""
|
1741
1756
|
req = protos.SelinuxPolicyRequest(source=source, target=target,
|
1742
|
-
|
1757
|
+
tclass=tclass, action=action)
|
1743
1758
|
r = self.stub.policySetAllow(req)
|
1744
1759
|
return r.value
|
1745
|
-
def
|
1760
|
+
def disallow(self, source, target, tclass, action):
|
1746
1761
|
"""
|
1747
1762
|
selinux disallow
|
1748
1763
|
"""
|
1749
1764
|
req = protos.SelinuxPolicyRequest(source=source, target=target,
|
1750
|
-
|
1765
|
+
tclass=tclass, action=action)
|
1751
1766
|
r = self.stub.policySetDisallow(req)
|
1752
1767
|
return r.value
|
1753
1768
|
def get_enforce(self):
|
@@ -1763,27 +1778,27 @@ class SelinuxPolicyStub(BaseServiceStub):
|
|
1763
1778
|
req = protos.Boolean(value=enforced)
|
1764
1779
|
r = self.stub.setEnforce(req)
|
1765
1780
|
return r.value
|
1766
|
-
def
|
1781
|
+
def enabled(self):
|
1767
1782
|
"""
|
1768
1783
|
获取设备上的 selinux 是否已经启用
|
1769
1784
|
"""
|
1770
1785
|
r = self.stub.isEnabled(protos.Empty())
|
1771
1786
|
return r.value
|
1772
|
-
def
|
1787
|
+
def enforce(self, name):
|
1773
1788
|
"""
|
1774
1789
|
设置一个域为 enforce
|
1775
1790
|
"""
|
1776
1791
|
req = protos.String(value=name)
|
1777
1792
|
r = self.stub.policySetEnforce(req)
|
1778
1793
|
return r.value
|
1779
|
-
def
|
1794
|
+
def permissive(self, name):
|
1780
1795
|
"""
|
1781
1796
|
设置一个域为 permissive
|
1782
1797
|
"""
|
1783
1798
|
req = protos.String(value=name)
|
1784
1799
|
r = self.stub.policySetPermissive(req)
|
1785
1800
|
return r.value
|
1786
|
-
def
|
1801
|
+
def create_domain(self, name):
|
1787
1802
|
"""
|
1788
1803
|
新建一个 selinux 域
|
1789
1804
|
"""
|
@@ -2022,11 +2037,6 @@ class OcrOperator(object):
|
|
2022
2037
|
OCR - 检查元素是否存在
|
2023
2038
|
"""
|
2024
2039
|
return bool(self.find_target_item())
|
2025
|
-
def exist(self):
|
2026
|
-
"""
|
2027
|
-
OCR - 检查元素是否存在
|
2028
|
-
"""
|
2029
|
-
return self.exists()
|
2030
2040
|
def click(self):
|
2031
2041
|
"""
|
2032
2042
|
OCR - 点击元素(不存在则报错)
|
@@ -2037,11 +2047,6 @@ class OcrOperator(object):
|
|
2037
2047
|
OCR - 点击元素(不存在将不会产生异常)
|
2038
2048
|
"""
|
2039
2049
|
return self.find_cb(self._click, False)
|
2040
|
-
def click_exist(self):
|
2041
|
-
"""
|
2042
|
-
OCR - 点击元素(不存在将不会产生异常)
|
2043
|
-
"""
|
2044
|
-
return self.click_exists()
|
2045
2050
|
def screenshot(self, quality=100):
|
2046
2051
|
"""
|
2047
2052
|
OCR - 对元素进行截图
|
@@ -2123,6 +2128,15 @@ class Device(object):
|
|
2123
2128
|
policy["maxBackoff"] = "15s"
|
2124
2129
|
config = json.dumps(dict(methodConfig=[{"name": [{}],
|
2125
2130
|
"retryPolicy": policy,}]))
|
2131
|
+
option = dict()
|
2132
|
+
option["grpc.max_send_message_length"] = 64*1024*1024
|
2133
|
+
option["grpc.max_receive_message_length"] = 128*1024*1024
|
2134
|
+
option["grpc.keepalive_time_ms"] = 30*1000
|
2135
|
+
option["grpc.keepalive_timeout_ms"] = 15*1000
|
2136
|
+
option["grpc.keepalive_permit_without_calls"] = True
|
2137
|
+
option["grpc.max_pings_without_data"] = 0
|
2138
|
+
option["grpc.service_config"] = config
|
2139
|
+
option["grpc.enable_http_proxy"] = 0
|
2126
2140
|
if certificate is not None:
|
2127
2141
|
with open(certificate, "rb") as fd:
|
2128
2142
|
key, crt, ca = self._parse_certdata(fd.read())
|
@@ -2132,13 +2146,10 @@ class Device(object):
|
|
2132
2146
|
self._chan = grpc.secure_channel(self.server, creds,
|
2133
2147
|
options=(("grpc.ssl_target_name_override",
|
2134
2148
|
self._parse_cname(crt)),
|
2135
|
-
(
|
2136
|
-
("grpc.enable_http_proxy",
|
2137
|
-
0)))
|
2149
|
+
*tuple(option.items()),))
|
2138
2150
|
else:
|
2139
2151
|
self._chan = grpc.insecure_channel(self.server,
|
2140
|
-
options=((
|
2141
|
-
("grpc.enable_http_proxy", 0))
|
2152
|
+
options=(*tuple(option.items()),)
|
2142
2153
|
)
|
2143
2154
|
session = session or uuid.uuid4().hex
|
2144
2155
|
interceptors = [ClientSessionMetadataInterceptor(session),
|
@@ -22,6 +22,8 @@ class InvalidAndroidPackage(Exception):
|
|
22
22
|
""" Exception """
|
23
23
|
class InvalidArgumentError(Exception):
|
24
24
|
""" Exception """
|
25
|
+
class InvalidOperationError(Exception):
|
26
|
+
""" Exception """
|
25
27
|
class InvalidRootCertificate(Exception):
|
26
28
|
""" Exception """
|
27
29
|
class MethodNotFoundException(Exception):
|
@@ -54,6 +54,7 @@ message ApplicationActivityInfo {
|
|
54
54
|
int64 flags = 7;
|
55
55
|
bool debug = 8;
|
56
56
|
string data = 9;
|
57
|
+
uint32 user = 10;
|
57
58
|
}
|
58
59
|
|
59
60
|
message ApplicationActivityInfoList {
|
@@ -77,6 +78,7 @@ message ApplicationInfo {
|
|
77
78
|
uint32 versionCode = 10;
|
78
79
|
string versionName = 11;
|
79
80
|
string activity = 12;
|
81
|
+
uint32 user = 13;
|
80
82
|
}
|
81
83
|
|
82
84
|
message ApplicationProcess {
|
@@ -102,11 +104,13 @@ message HookRequest {
|
|
102
104
|
DataEncode encode = 5;
|
103
105
|
uint32 standup = 6;
|
104
106
|
bool spawn = 7;
|
107
|
+
uint32 user = 8;
|
105
108
|
}
|
106
109
|
|
107
110
|
message HookRpcRequest {
|
108
111
|
string package = 1;
|
109
112
|
string callinfo = 2;
|
113
|
+
uint32 user = 3;
|
110
114
|
}
|
111
115
|
|
112
116
|
message HookRpcResponse {
|
@@ -52,10 +52,10 @@ service Application {
|
|
52
52
|
rpc getIdentifierByLabel(String) returns (String) {}
|
53
53
|
|
54
54
|
rpc callScript(HookRpcRequest) returns (HookRpcResponse) {}
|
55
|
-
rpc isScriptAlive(
|
56
|
-
rpc isScriptAttached(
|
55
|
+
rpc isScriptAlive(HookRequest) returns (Boolean) {}
|
56
|
+
rpc isScriptAttached(HookRequest) returns (Boolean) {}
|
57
57
|
rpc attachScript(HookRequest) returns (Boolean) {}
|
58
|
-
rpc detachScript(
|
58
|
+
rpc detachScript(HookRequest) returns (Boolean) {}
|
59
59
|
}
|
60
60
|
|
61
61
|
service Debug {
|
lamda-7.90/README.md
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
<p align="center">
|
2
|
-
<img src="image/logo.svg" alt="LAMDA" width="345">
|
3
|
-
</p>
|
4
|
-
|
5
|
-
<p align="center">安卓 RPA 机器人框架,下一代移动端数据自动化机器人</p>
|
6
|
-
|
7
|
-
<p align="center">
|
8
|
-
<img src="https://img.shields.io/badge/python-3.6+-blue.svg?logo=python&labelColor=yellow" />
|
9
|
-
<img src="https://img.shields.io/badge/android-6.0+-blue.svg?logo=android&labelColor=white" />
|
10
|
-
<img src="https://img.shields.io/badge/root%20require-red.svg?logo=android&labelColor=black" />
|
11
|
-
<img src="https://img.shields.io/github/downloads/rev1si0n/lamda/total" />
|
12
|
-
<img src="https://img.shields.io/github/v/release/rev1si0n/lamda" />
|
13
|
-
</p>
|
14
|
-
|
15
|
-
<p align="center"><a href="https://device-farm.com/doc/index.html">使用文档</a> | <a href="https://t.me/lamda_dev">TELEGRAM</a> | <a href="https://lamda.run/join/qq">QQ 群组</a> | <a href="https://github.com/rev1si0n/lamda/blob/HEAD/CHANGELOG.txt">更新历史</a></p>
|
16
|
-
|
17
|
-
智能机的崛起,传统网页端的普及度也开始显著减弱,数据与应用正加速向移动端转移。越来越多的人选择通过智能手机和平板等移动设备来获取信息和服务。随着移动设备的普及,用户享受到更便捷访问体验,传统的网页内容模式面临重新审视。与此同时,数据采集的技术也亟需适应这一趋势。过去,许多数据采集工具专注于网页内容,在移动端环境中,尤其是在移动端封闭的黑盒中,现今的常规采集技术也面临着新的挑战。LAMDA 的诞生,为这一切创造了可能。
|
18
|
-
|
19
|
-
LAMDA 是**安卓领域的集大成者**,设计为减少**安全分析**及**应用测试**或**自动化**工作的时间和琐碎问题,经过超 500 台设备的生产环境考验,稳定应用于多个大型系统,包括自动化取证,云平台,数据采集,合规分析等,具备商业级软件的质量和稳定性,仅需 root 权限即可正常运行。具备 ARM/X86 全架构,安卓 6.0-14 的广泛兼容性,支持 **模拟器**、**真机**、**云手机**、 **WSA**(Windows Subsystem for Android™️)、**RK开发板** (所有 ARM 架构)以及 **Redroid** 等大多数运行安卓系统的设备。为**移动端 RPA 数据采集**提供稳定的解决方案,以**编程化**的**接口**替代大量手动操作,**易部署**,没有那些复杂花哨不跨平台的安装流程,你所需要的能力他大概率能做到并且做的更好。他并不是一个单一功能的框架,他是集 Appium、uiautomator **自动化**的超集同时具备**逆向**领域如 **Hook** **抓包** **证书安装** **组网** **API跟踪** **手机自控** 等等各种能力的框架。当然,LAMDA 可以做到的远不止于此,你可以阅读使用文档尽情探索他的所有能力。
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
<h3><p align="center">丰富的设备编程接口</p></h3>
|
24
|
-
|
25
|
-
<p align="center">提供多达 160 条编程 API 接口,让你可以对安卓设备进行无微不至的管理和操作,提供了包括命令执行,系统设置,系统状态,应用相关,自动化相关,代理以及文件等十几个大类的接口。同时提供了封装完整的 Python 库让你可以快速上手使用。</p>
|
26
|
-
|
27
|
-
<h3><p align="center">简洁易用的远程桌面</p></h3>
|
28
|
-
|
29
|
-
<p align="center">
|
30
|
-
<img src="image/demo.gif" alt="动图演示" width="95%">
|
31
|
-
</p>
|
32
|
-
|
33
|
-
<h3><p align="center">一键中间人流量分析及更多其他功能</p></h3>
|
34
|
-
|
35
|
-
支持常规以及国际APP流量分析,DNS流量分析,得益于 [mitmproxy flow hook](https://docs.mitmproxy.org/stable/api/events.html),你可以对任何请求做到最大限度的掌控,mitmproxy 功能足够丰富,你可以使用 Python 脚本实时修改或者捕获应用的请求,也可以通过其 `Export` 选项导出特定请求的 `curl` 命令或者 `HTTPie` 命令,分析重放、拦截修改、功能组合足以替代你用过的任何此类商业/非商业软件。如果你仍不清楚 mitmproxy 是什么以及其具有的能力,请务必先查找相关文档,因为 LAMDA 将会使用 mitmproxy 为你展现应用请求。
|
36
|
-
|
37
|
-
<p align="center">
|
38
|
-
<img src="image/mitm.gif" alt="动图演示" width="95%">
|
39
|
-
</p>
|
40
|
-
|
41
|
-
<p align="center">
|
42
|
-
当然,LAMDA 提供的能力不止于这些,由于篇幅较长将不在此罗列,他是你强有力的设备控制及管理工具,如果你感兴趣,请转到 使用文档。
|
43
|
-
</p>
|
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
|
File without changes
|
File without changes
|
File without changes
|