Skip to content

Commit

Permalink
3.0.48
Browse files Browse the repository at this point in the history
  • Loading branch information
rev1si0n committed Nov 25, 2022
1 parent f9cfa69 commit 10fe303
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 80 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
3.0.48
-----------------------
* 免安装的 windows startmitm 命令
* 支持从内存上传/下载文件到内存
* 添加 screenshot() 别名

3.0.47
-----------------------
* 简化 globalmitm,支持 HTTP,SOCKS5 代理
Expand Down
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@

为了下载使用由 rev1si0n (账号 github.com/rev1si0n)(以下简称“本人”)个人开发的软件 lamda ,您应当阅读并遵守《用户使用协议》(以下简称“本协议”)。请您务必审慎阅读、充分理解各条款内容,特别是免除或者限制责任的条款,并选择接受或不接受;除非您已阅读并接受本协议所有条款,否则您将无权下载、安装或使用本软件及相关服务。您的下载、安装、使用、获取账号、登录等行为即视为您已阅读并同意受到上述协议的约束;若您需要获得本服务,您(以下称"用户")应当同意本协议的全部条款并按照页面上的提示完成全部申请使用程序。您可以在本文档的相同目录找到 [DISCLAIMER.TXT](DISCLAIMER.TXT),或者点此 [免责声明](DISCLAIMER.TXT) 查阅。

lamda 本身不会侵入其他应用,仅提供自身及第三方程序的能力给用户自行选择,所有操作均为用户的主观行为。

## 前言

lamda 是个人开发的免费软件 (freeware),目前仅客户端及协议是开源的,但个人承诺它没有任何对您违规或多余的行为,如果仍有担心,您可以**立即离开**或者选择**付费**寻求心理安慰。互相尊重,使用请遵守使用条款。合作交流请在 [ISSUE](https://github.com/rev1si0n/lamda/issues/new) 中留下联系方式。
Expand Down Expand Up @@ -142,7 +144,7 @@ lamda 是个人开发的免费软件 (freeware),目前仅客户端及协议是
如果你在安卓设备上启动 lamda 后,设备多次出现无故重启或者出现应用崩溃的情况,
请在启动 lamda **之前**,在当前 shell 执行命令
```bash
export lamda_crash_system=1
export crashed=1
```
这种情况多数是由 frida 导致的,这将禁用掉 frida 一些功能,也意味着你将无法正常使用内置的 frida。
但是将保证你可以使用大部分其他的内置接口。
Expand Down Expand Up @@ -857,21 +859,20 @@ status.get_mem_info()
> 在设备后台,前台执行 shell 脚本/命令

```python
shell = d.stub("Shell")
# 执行前台脚本(执行时间短的脚本)
cmd = shell.execute_script("pwd")
# 执行前台脚本(执行时间短(0-10秒内)的脚本)
cmd = d.execute_script("whoami")
print (cmd.exitstatus)
print (cmd.stdout)
print (cmd.stderr)

# 执行后台脚本(执行时间长的脚本)
# 对于后台脚本,因考虑可能用户写出死循环脚本无限输出导致内存占满等问题
# 暂时无法获知其执行结果
ID = shell.execute_background_script("sleep 100; exit 0;")
ID = d.execute_background_script("sleep 100; exit 0;")
# 检查后台脚本是否结束
shell.is_background_script_finished(ID)
d.is_background_script_finished(ID)
# 强制结束后台脚本
shell.kill_background_script(ID)
d.kill_background_script(ID)
```

## 使系统可调试
Expand Down Expand Up @@ -965,13 +966,29 @@ d.is_android_debug_bridge_running()

```python
# 下载文件到本地
d.download_file("/data/local/tmp/设备上的文件.txt", "写入到的本地文件.txt")
d.download_file("/verity_key", "写入到的本地文件")

# 下载文件到 内存/已打开的文件
from io import BytesIO
fd = BytesIO()
d.download_fd("/verity_key", fd)
print (fd.getvalue())

# 注意必须使用 w+b 模式打开被写入文件
fd = open("写入到的本地文件", "wb")
d.download_fd("/verity_key", fd)

# 上传文件到设备
# 会自动修改远程的文件权限信息为本地文件权限信息
# 即本地文件权限为 0755,那么上传到设备上的文件也为此权限
d.upload_file("本地文件路径.txt", "/data/local/tmp/上传到设备上的文件.txt")

# 从 内存/已打开的文件 上传文件
from io import BytesIO
d.upload_fd(BytesIO(b"fileContent"), "/data/local/tmp/上传到设备上的文件.txt")

# 注意必须使用 rb 模式打开文件
fd = open("myfile.txt", "rb")
d.upload_fd(fd, "/data/local/tmp/上传到设备上的文件.txt")

# 删除设备上的文件
d.delete_file("/data/local/tmp/文件.txt")

Expand Down
2 changes: 1 addition & 1 deletion lamda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
__version__ = "3.0"
__build__ = 47
__build__ = 48
132 changes: 70 additions & 62 deletions lamda/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
import os
import io
import re
import sys
import time
Expand Down Expand Up @@ -281,7 +282,7 @@ def raise_remote_exception(self, res):
raise self.remote_exception(exception)


class ObjectUiAutomatorOpStubWrapper:
class ObjectUiAutomatorOpStub:
def __init__(self, stub, selector):
"""
UiAutomator 子接口,用来模拟出实例的意味
Expand All @@ -302,6 +303,8 @@ def take_screenshot(self, quality=100):
quality=quality)
r = self.stub.selectorTakeScreenshot(req)
return BytesIO(r.value)
def screenshot(self, quality=100):
return self.take_screenshot(quality=quality)
def get_text(self):
"""
获取选择器选中输入控件中的文本
Expand Down Expand Up @@ -563,9 +566,9 @@ def scroll_from_right_to_left_to_end(self, max_swipes, step):
return self._scroll_to_end(max_swipes, step, is_vertical=False)


class UiAutomatorStubWrapper(BaseServiceStub):
class UiAutomatorStub(BaseServiceStub):
def __init__(self, *args, **kwargs):
super(UiAutomatorStubWrapper, self).__init__(*args, **kwargs)
super(UiAutomatorStub, self).__init__(*args, **kwargs)
self.watchers = defaultdict(dict)
def device_info(self):
"""
Expand Down Expand Up @@ -783,6 +786,8 @@ def take_screenshot(self, quality, bound=None):
bound=bound)
r = self.stub.takeScreenshot(req)
return BytesIO(r.value)
def screenshot(self, quality, bound=None):
return self.take_screenshot(quality, bound=bound)
def dump_window_hierarchy(self):
"""
获取屏幕界面布局 XML 文档
Expand All @@ -796,10 +801,10 @@ def wait_for_idle(self, timeout):
r = self.stub.waitForIdle(protos.Integer(value=timeout))
return r.value
def __call__(self, **kwargs):
return ObjectUiAutomatorOpStubWrapper(self.stub, kwargs)
return ObjectUiAutomatorOpStub(self.stub, kwargs)


class ObjectApplicationOpStubWrapper:
class ObjectApplicationOpStub:
def __init__(self, stub, applicationId):
"""
Application 子接口,用来模拟出实例的意味
Expand Down Expand Up @@ -941,7 +946,7 @@ def is_installed(self):
return r.value


class ApplicationStubWrapper(BaseServiceStub):
class ApplicationStub(BaseServiceStub):
def current_application(self):
"""
获取当前处于前台的应用的信息
Expand Down Expand Up @@ -973,10 +978,10 @@ def start_activity(self, **activity):
r = self.stub.startActivity(req)
return r.value
def __call__(self, applicationId):
return ObjectApplicationOpStubWrapper(self.stub, applicationId)
return ObjectApplicationOpStub(self.stub, applicationId)


class UtilStubWrapper(BaseServiceStub):
class UtilStub(BaseServiceStub):
def _get_file_content(self, certfile):
with open(certfile, "rb") as fd:
return fd.read()
Expand Down Expand Up @@ -1064,7 +1069,7 @@ def getprop(self, name):
return r.value


class DebugStubWrapper(BaseServiceStub):
class DebugStub(BaseServiceStub):
def _read_pubkey(self, pubkey):
with open(pubkey, "rb") as fd:
return fd.read()
Expand Down Expand Up @@ -1150,7 +1155,7 @@ def stop_ida64(self):
return r.value


class SettingsStubWrapper(BaseServiceStub):
class SettingsStub(BaseServiceStub):
def _put(self, group, name, value):
req = protos.SettingsRequest(group=group, name=name,
value=value)
Expand Down Expand Up @@ -1192,7 +1197,7 @@ def put_secure(self, name, value):
return self._put(Group.GROUP_SECURE, name, value)


class ShellStubWrapper(BaseServiceStub):
class ShellStub(BaseServiceStub):
def execute_script(self, script, alias=None):
"""
前台执行一段脚本(支持标准的多行脚本)
Expand Down Expand Up @@ -1223,7 +1228,7 @@ def kill_background_script(self, tid):
return r.value


class StatusStubWrapper(BaseServiceStub):
class StatusStub(BaseServiceStub):
def get_boot_time(self):
"""
获取设备启动时间 Unix 时间戳
Expand Down Expand Up @@ -1282,7 +1287,7 @@ def get_mem_info(self):
return r


class ProxyStubWrapper(BaseServiceStub):
class ProxyStub(BaseServiceStub):
def is_openvpn_running(self):
"""
检查 OPENVPN 是否正在运行
Expand Down Expand Up @@ -1323,7 +1328,7 @@ def stop_gproxy(self):
return r.value


class SelinuxPolicyStubWrapper(BaseServiceStub):
class SelinuxPolicyStub(BaseServiceStub):
def policy_set_allow(self, source, target, tclass, action):
"""
selinux allow
Expand Down Expand Up @@ -1382,47 +1387,36 @@ def policy_create_domain(self, name):
return r.value


class FileStubWrapper(BaseServiceStub):
def _file_stream_read(self, fpath, chunksize):
with open(fpath, "rb") as fd:
for chunk in iter(lambda: fd.read(chunksize), bytes()):
yield chunk
def _file_streaming_send(self, fpath, dest, chunksize):
class FileStub(BaseServiceStub):
def _fd_stream_read(self, fd, chunksize):
for chunk in iter(lambda: fd.read(chunksize), bytes()):
yield chunk
def _fd_streaming_send(self, fd, dest, chunksize):
yield protos.FileRequest(path=dest)
for chunk in self._file_stream_read(fpath, chunksize):
for chunk in self._fd_stream_read(fd, chunksize):
yield protos.FileRequest(payload=chunk)
def _file_streaming_recv(self, fpath, iterator):
with open(fpath, "wb") as fd:
for chunk in iterator:
fd.write(chunk.payload)
def download_file(self, fpath, dest):
"""
下载设备上的文件到本地, dest: 下载到本地的路径
"""
if os.path.isdir(dest):
dest = joinpath(dest, basename(fpath))
st = self.file_stat(fpath)
def _fd_streaming_recv(self, fd, iterator):
for chunk in iterator:
fd.write(chunk.payload)
def download_fd(self, fpath, fd):
req = protos.FileRequest(path=fpath)
iterator = self.stub.downloadFile(req)
self._file_streaming_recv(dest, iterator)
mode = st.st_mode & 0o777
os.chmod(dest, mode)
self._fd_streaming_recv(fd, iterator)
st = self.file_stat(fpath)
return st
def upload_file(self, fpath, dest):
"""
上传本地文件到设备中, dest: 上传在设备的路径
"""
chunksize = 1024*1024
if not os.path.isfile(fpath):
raise OSError("%s is not a file" % fpath)
if not os.access(fpath, os.R_OK):
raise OSError("%s is not readable" % fpath)
streaming = self._file_streaming_send(fpath, dest,
def upload_fd(self, fd, dest):
chunksize = 1024*1024*1
streaming = self._fd_streaming_send(fd, dest,
chunksize)
self.stub.uploadFile(streaming)
mode = os.stat(fpath).st_mode & 0o777
st = self.file_chmod(dest, mode)
st = self.file_stat(dest)
return st
def download_file(self, fpath, dest):
with io.open(dest, mode="wb") as fd:
return self.download_fd(fpath, fd)
def upload_file(self, fpath, dest):
with io.open(fpath, mode="rb") as fd:
return self.upload_fd(fd, dest)
def delete_file(self, fpath):
"""
删除设备上的文件
Expand All @@ -1446,7 +1440,7 @@ def file_stat(self, fpath):
return r


class LockStubWrapper(BaseServiceStub):
class LockStub(BaseServiceStub):
def acquire_lock(self, leaseTime=60):
"""
获取用于控制设备的锁,成功返回 true,被占用则会引发异常提示
Expand All @@ -1469,7 +1463,7 @@ def release_lock(self):
return r.value


class WifiStubWrapper(BaseServiceStub):
class WifiStub(BaseServiceStub):
def status(self):
"""
获取当前已连接 WIFI 的信息
Expand Down Expand Up @@ -1592,25 +1586,23 @@ def frida(self):
def __str__(self):
return "Device@{}".format(self.server)
__repr__ = __str__
def _get_proto_stub(self, module):
stub = getattr(services, "{0}Stub".format(module))
return stub
def _ssl_common_name(self, cer):
_, _, der = pem.unarmor(cer)
subject = x509.Certificate.load(der).subject
return subject.native["common_name"]
def _initialize_service_stub(self, module):
stub = self._get_proto_stub(module)
stub = getattr(self, module, stub(self.chann))
setattr(self, module, stub)
def _get_service_stub(self, module):
stub = getattr(services, "{0}Stub".format(module))
return stub(self.chann)
def stub(self, module):
self._initialize_service_stub(module)
name = "{}_classInstance".format(module)
wrapper = globals()["{}StubWrapper".format(module)]
wraped = getattr(self, name, wrapper(getattr(self, module)))
setattr(self, name, wraped)
return wraped
modu = sys.modules[__name__]
stub = self._get_service_stub(module)
wrap = getattr(modu, "{0}Stub".format(module))
return wrap(stub)
# 快速调用: File
def download_fd(self, fpath, fd):
return self.stub("File").download_fd(fpath, fd)
def upload_fd(self, fd, dest):
return self.stub("File").upload_fd(fd, dest)
def download_file(self, fpath, dest):
return self.stub("File").download_file(fpath, dest)
def upload_file(self, fpath, dest):
Expand Down Expand Up @@ -1681,6 +1673,15 @@ def stop_openvpn(self):
return self.stub("Proxy").stop_openvpn()
def stop_gproxy(self):
return self.stub("Proxy").stop_gproxy()
# 快速调用: Shell
def execute_script(self, script, alias=None):
return self.stub("Shell").execute_script(script, alias=alias)
def execute_background_script(self, script, alias=None):
return self.stub("Shell").execute_background_script(script, alias=alias)
def is_background_script_finished(self, tid):
return self.stub("Shell").is_background_script_finished(tid)
def kill_background_script(self, tid):
return self.stub("Shell").kill_background_script(tid)
# 快速调用: UiAutomator
def click(self, point):
return self.stub("UiAutomator").click(point)
Expand Down Expand Up @@ -1716,6 +1717,8 @@ def press_keycode(self, code):
return self.stub("UiAutomator").press_keycode(code)
def take_screenshot(self, quality=100, bound=None):
return self.stub("UiAutomator").take_screenshot(quality, bound=bound)
def screenshot(self, quality=100, bound=None):
return self.stub("UiAutomator").screenshot(quality, bound=bound)
def dump_window_hierarchy(self):
return self.stub("UiAutomator").dump_window_hierarchy()
def wait_for_idle(self, timeout):
Expand Down Expand Up @@ -1766,6 +1769,11 @@ def _refresh_lock(self, leaseTime=60):
return self.stub("Lock").refresh_lock(leaseTime)
def _release_lock(self):
return self.stub("Lock").release_lock()
def __enter__(self):
self._acquire_lock(leaseTime=sys.maxsize)
return self
def __exit__(self, type, value, traceback):
self._release_lock()


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 10fe303

Please sign in to comment.