lamda Documentation

Android Full-Stack Device Control Platform: WebRTC/H.264 remote desktop, UI/OCR/image-matching automation, one-click MITM, built-in Frida, proxy/VPN/frp/P2P...

8,600 stars Python Python 1 file · ~32,268 tokens #adb#agents#ai#ai-agents#android#appium#automation#dynamic-analysis
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy lamda on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name lamda -p 53:53 --restart always firerpa/lamda:latest

File: tools/frps/README.en.md

FRPS service docker, save yourself the trouble of building it and you'll be up and running in a single command!

Note that these port forwarders are only recommended for Linux systems, so if your docker host is not Linux, please do not use them.

If you need to log in to the backend of frps from outside, you will also need to unblock 6119/tcp.

Execute the command

bash
docker run -it --rm --net host -e TOKEN=mypasswod -e BIND=127.0.0.1 -e PORTS=1000-5000 rev1si0n/frps
# TOKEN is the password, also the login password for the frps backend
# BIND is the bind interface The default is 127.0.0.1, do not set it to 0.0.0.0 unless you know what you are doing
# PORTS is the range of ports allowed to bind Default is 3000-5000

Note that some configuration and connection information will be output in the terminal after startup, find the ----- LAMDA CONFIG ----- segment
Copy the line fwd.xxxxxx=xxxx and write it to the /data/local/tmp/properties.local file on the device.
You can change the rport field there to the port you want, this port value must be in the PORTS range specified at boot time, the default 0 is randomly assigned.
Reboot the device and start lamda and lamda will automatically forward its own port to the server.

You can also access the frps backend page via 127.0.0.1:6119 (if the firewall 6119 is put through, you can also access it via http://服务器IP:6119)
The backend login user is lamda and the password is the TOKEN you provided when you started.


File: tools/frps/README.md

FRPS 服务 docker,省得你自己搭建,一条命令就可以跑起来

注意此类端口转发程序只建议在 Linux 类系统上使用,如果你的 docker 宿主机不是 Linux 类,请不要使用。

开始前请先放通防火墙 6009/tcp,如果需要从外网登录 frps 的后台,你还需要放通 6119/tcp。

执行命令

bash
docker run -it --rm --net host -e TOKEN=mypasswod -e BIND=127.0.0.1 -e PORTS=1000-5000 rev1si0n/frps
# TOKEN 为密码,同样为 frps 后台的登录密码
# BIND 为绑定接口 默认为 127.0.0.1,请不要设为 0.0.0.0 除非你知道自己在做什么
# PORTS 为允许绑定端口的范围 默认为 3000-5000

注意,启动后会在终端输出一些配置及连接信息,找到 ----- LAMDA CONFIG ----- 段
复制其中 fwd.xxxxx=xxxx 的行,并将其写入到设备上的 /data/local/tmp/properties.local 文件中,
你可以修改其中的 rport 字段为你想要的端口,这个端口值必须在启动时指定的 PORTS 范围中,默认 0 则为随机分配。
重启设备并启动 lamda,lamda 将会自动将自身端口转发到服务器上。

你还可以通过 127.0.0.1:6119 访问 frps 的后台页面(如果放通了防火墙 6119,你也可以通过 http://服务器IP:6119 访问)
后台登录用户名为 lamda,登录密码为你启动时提供的 TOKEN。


File: tools/globalmitm/README.en.md

An integrated docker image for analysing the traffic of applications that need to be accessed using an international proxy (You are in a regulated network).
Due to docker implementation issues on Windows/Mac, this image is used differently for Windows/Mac and Linux, please see separate sections.
If you are using a Mac M1, any of the following may not work for you, please use Windows or linux on an x86 architecture.

You may say, why don't I just use an upstream proxy: for some reason affecting DNS, you may not be able to access the real target site.

Preparing the proxy service

This image supports both normal mode (i.e. startmitm.py wrapped again) and foreign APP mode, for which you will need to prepare either of the following types of proxies first.
Currently proxies that require a login are not supported. Usually the client software you use (e.g. clash etc.) will create such a proxy port on your local machine (SOCKS5 or HTTP).
But it may only be bound to 127.0.0.1, set it to bind to any interface and get its binding port, then get your local LAN address.

Splice the proxy into the following link format IP:port and keep in mind its proxy type (SOCKS5/HTTP).

bash
# This is an example HTTP proxy address
192.168.0.2:7890
# This is an example SOCKS5 proxy address
192.168.0.2:1080
# Yes, they look the same, so read on

Now selectively look at the commands that correspond to your current system.

Linux system commands

foreign app mode

bash
# Use the HTTP proxy
docker run --rm -it --net host -e HTTP=192.168.0.2:7890 rev1si0n/mitm 192.168.x.x
# Use the SOCKS5 proxy
docker run --rm -it --net host -e SOCKS5=192.168.0.2:1080 rev1si0n/mitm 192.168.x.x

normal mode

bash
docker run --rm -it --net host rev1si0n/mitm 192.168.x.x

Now use your browser to open http://127.0.0.1:1234 or http://本机IP:1234 for man-in-the-middle.

Mac (Intel)/Windows system commands

For such systems, docker does not support native host mode, and the program will not know the IP address of the host.
So you need to prepare the local LAN IP in advance, i.e. the LANIP in the following parameters, assuming your LANIP is 192.168.0.2

Foreign APP mode

bash
# Use the SOCKS5 proxy
docker run -it --rm -p 53:53/udp -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 -e SOCKS5=192.168.0.2:1080 rev1si0n/mitm 192.168.x.x
# Use HTTP proxy
docker run -it --rm -p 53:53/udp -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 -e HTTP=192.168.0.2:7890 rev1si0n/mitm 192.168.x.x

normal mode

bash
docker run -it --rm -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 rev1si0n/mitm 192.168.x.x

Now use your browser to open http://127.0.0.1:1234 or http://MY_IP:1234 for the intermediary.

How to build the image

bash
### cd to the tools directory
docker build -t rev1si0n/mitm -f globalmitm/Dockerfile .

DNS2SOCKS

https://sourceforge.net/projects/dns2socks


File: tools/globalmitm/README.md

用于分析需要使用国际代理才能访问的应用的流量,是一个整合的 docker 镜像,
因为 Windows/Mac 上的 docker 实现问题,此镜像对于 Windows/Mac,Linux 有着不同的使用方法,请分别看不同章节。
如果你使用的是 Mac M1,下列任何方式可能都不适用于你,请使用 x86 架构的 Windows 或者 linux。

你可能会说,为什么我不直接使用上游代理:由于某些原因影响DNS,你可能无法访问到真正的目标站点。

准备代理服务

此镜像同时支持普通模式(即 startmitm.py 的再次封装)以及国外APP模式,对于国外APP,你需要先准备下面任一类型代理。
目前不支持需要登录的代理。通常,你用的客户端软件(例如 clash 等)会在你的本机创建这样的代理端口(SOCKS5 或者 HTTP)。
但是有可能它只绑定到了 127.0.0.1,将其设置为绑定任意接口并获知它的绑定端口,随后获取你的本机局域网地址。

拼接代理为以下链接格式 IP:端口,并牢记其代理类型(SOCKS5/HTTP)。

bash
# 这是一个示例 HTTP 代理地址
192.168.0.2:7890
# 这是一个示例 SOCKS5 代理地址
192.168.0.2:1080
# 是的,它们长得一样,请继续往下看

现在请选择性查看对应你当前系统的命令。

Linux 系命令

国外APP模式

bash
# 使用 HTTP 代理
docker run --rm -it --net host -e HTTP=192.168.0.2:7890 rev1si0n/mitm 192.168.x.x
# 使用 SOCKS5 代理
docker run --rm -it --net host -e SOCKS5=192.168.0.2:1080 rev1si0n/mitm 192.168.x.x

普通模式

bash
docker run --rm -it --net host rev1si0n/mitm 192.168.x.x

现在使用浏览器打开 http://127.0.0.1:1234 或者 http://本机IP:1234 即可进行中间人。

Mac (Intel)/Windows 系命令

对于此类系统,docker 并不支持原生 host 模式,程序也就无法获知宿主机的IP地址,
所以你需要提前准备本机局域网IP,即以下参数中的 LANIP,假设你的 LANIP 为 192.168.0.2

国外APP模式

bash
# 使用 SOCKS5 代理
docker run -it --rm -p 53:53/udp -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 -e SOCKS5=192.168.0.2:1080 rev1si0n/mitm 192.168.x.x
# 使用 HTTP 代理
docker run -it --rm -p 53:53/udp -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 -e HTTP=192.168.0.2:7890 rev1si0n/mitm 192.168.x.x

普通模式

bash
docker run -it --rm -p 8118:8118 -p 1234:1234 -e LANIP=192.168.0.2 rev1si0n/mitm 192.168.x.x

现在使用浏览器打开 http://127.0.0.1:1234 或者 http://本机IP:1234 即可进行中间人。

如何构建镜像

bash
# cd 到 tools 目录
docker build -t rev1si0n/mitm -f globalmitm/Dockerfile .

DNS2SOCKS

https://sourceforge.net/projects/dns2socks


File: tools/openvpn/README.en.md

Out-of-the-box OpenVPN service image

This image only ensures that the basic functionality is correct. If you are able to configure it yourself, it is recommended that you build it yourself or you can refer to the implementation of this image to do so. You will need to have a basic knowledge of Linux and Docker before using it. This image has been tested on Debian 9.

The default port for this service is 1190/UDP, so make sure you have allowed this rule in your firewall.

If your server is newly created, you may need to install Docker first, using the following command to install Docker

bash
apt update; apt upgrade -y; apt install -y curl
curl https://get.docker.com | sh
# In the test environment, the installation of docker failed with the following error
#E: Unable to locate package docker-compose-plugin
#E: Unable to locate package docker-scan-plugin
#E: Unable to locate package docker-scan-plugin
apt-get install -y --no-install-recommends docker-ce docker-ce-cli containerd.io

Preparation

You will also need to make the following changes on the server by executing the command

bash
echo net.ipv4.ip_forward=1 >>/etc/sysctl.conf
sysctl -p

If you have ufw** installed on your server, you also need to write the following in the first few lines of /etc/ufw/before.rules

where eth0 and the network segment are modified according to your actual server interface and configuration, note that they are added before *filter (if any)

bash
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 172.27.27.0/24 -o eth0 -j MASQUERADE
COMMIT

Then modify /etc/default/ufw

bash
DEFAULT_FORWARD_POLICY="ACCEPT"

Then execute the command

bash
ufw reload

If `ufw is not installed on your server**
Make sure that iptables' FORWARD defaults to ACCEPT, you can execute the following command

Note that you may need to reapply this rule after a server reboot, it is recommended that you install ufw

bash
iptables -P FORWARD ACCEPT

Initialising the configuration

Now, create a directory to store the service's configuration

bash
mkdir -p ~/lamda-openvpn-server

Immediately afterwards, start the initialization of the OpenVPN service

bash
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-server-new

Wait for the command to finish and you can now see the configuration of the service in the ~/lamda-openvpn-server directory
The configuration file is config.ovpn

You can open this file with an editor and it is recommended to change only the following fields

ini
# VPN network segment and mask
server 172.27.27.0 255.255.255.0
# VPN service port
port 1190

# or if you need a network interface on the server to be accessible by the VPN client
# You can also add a route, but note that you will only be able to access the IP of the current host in this segment
# If you need full client access to this network segment, you will need to make additional settings
push "route 192.168.68.0 255.255.255.0"

# Change 114 to the DNS server you need
push "dhcp-option DNS 114.114.114.114"

Create client connection credentials

Now that you've finished editing, start creating a client, myname can be changed to whatever you want, but don't duplicate it

bash
docker run --it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-new myname

Once created, use the following command to get the login credentials for this client

bash
# Note: The IP in the configuration is the current public IP that is automatically obtained, if it is not correct you will need to change it yourself
#
# Generate the ovpn configuration and redirect it to the file myname.ovpn, which can be used for APPs such as OpenVPN-Connect
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-profile ovpn myname >myname. ovpn
# Generate the OpenVPNProfile used by lamda, which can be used directly in lamda
# It contains a properties.local comment section where you can configure openvpn.*
# Copy it to /data/local/tmp/properties.local for automatic VPN connection
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-profile lamda myname

Revoke client credentials

If you need to revoke a client credential please execute the following command, you may need to restart the OpenVPN service after revocation

bash
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-revoke myname

Start the OpenVPN service

Now, start the OpenVPN service

Run in the foreground to see client connection logs directly for troubleshooting errors

bash
docker run --it --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run

Run in the background, it is recommended to start with this method after confirming

bash
docker run -d --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run

If that's correct, the individual functions are just right and you're ready to go.

Other references

Basic documentation: [https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/](https://openvpn.net/community-resources/reference -manual-for-openvpn-2-4/)

Routing setup: https://community.openvpn.net/openvpn/wiki/BridgingAndRouting


File: tools/openvpn/README.md

开箱即用的 OpenVPN 服务镜像

此镜像仅确保了基本功能无误,如果你自己有能力配置,建议自行搭建或者可以参考这个镜像的实现方法来完成。使用前,你需要有 Linux 以及 Docker 相关基础。此镜像在 Debian 9 上测试通过。

这里讲述的是如何在 Debian 9 上使用,通常情况下,这可以适用于其他系统例如 Ubuntu 以及 CentOS。该服务默认端口为 1190/UDP,请确保已在防火墙允许该规则。

如果你的服务器是全新创建的,你可能需要先安装 Docker,使用如下命令安装 Docker

bash
apt update; apt upgrade -y; apt install -y curl
curl https://get.docker.com | sh
# 在测试环境下,安装 docker 出现了失败的问题,报错为
#E: Unable to locate package docker-compose-plugin
#E: Unable to locate package docker-scan-plugin
# 此时执行如下命令即可
apt-get install -y --no-install-recommends docker-ce docker-ce-cli containerd.io

准备工作

你还需要在服务器上做出如下修改,执行命令

bash
echo net.ipv4.ip_forward=1 >>/etc/sysctl.conf
sysctl -p

如果你的服务器上安装了 ufw,还需在 /etc/ufw/before.rules 的前几行写入如下内容

这里的 eth0 和网段根据你的实际服务器接口和配置做修改,注意是加在 *filter 之前(如果有的话)

bash
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 172.27.27.0/24 -o eth0 -j MASQUERADE
COMMIT

然后修改 /etc/default/ufw

bash
DEFAULT_FORWARD_POLICY="ACCEPT"

随后执行命令

bash
ufw reload

如果你的服务器上没有安装 ufw
请确保 iptables 的 FORWARD 默认为 ACCEPT,你可以执行如下命令

注意服务器重启后你可能需要重新应用此规则,建议安装 ufw

bash
iptables -P FORWARD ACCEPT

初始化配置

现在,创建一个目录用来保存服务的配置

bash
mkdir -p ~/lamda-openvpn-server

紧接着,开始 OpenVPN 服务的初始化操作

bash
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-server-new

等待命令结束,现在,你可以在 ~/lamda-openvpn-server 目录下看到服务的配置
配置文件为 config.ovpn

你可以使用编辑器打开此文件,建议只修改下面几个字段

ini
# VPN 的网段以及掩码
server 172.27.27.0 255.255.255.0
# VPN 服务端口
port 1190

# 或者如果你需要服务器上某个网络接口可以被VPN客户端访问
# 你也可以增加一条路由,但是注意此时你也只能访问到当前主机在此网段的IP
# 如果需要客户端对此网段的完全访问,你还需进行额外设置
push "route 192.168.68.0 255.255.255.0"

# 修改 114 为你需要的 DNS 服务器
push "dhcp-option DNS 114.114.114.114"

创建客户端连接凭证

编辑完毕,现在开始创建一个客户端,myname 可以改为其他你想要的,但是不可重复

bash
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-new myname

创建完成后,使用如下命令来获取此客户端的登录凭证

bash
# 注意:配置中的 IP 是自动获取的当前公网IP,如果不对还需自行修改
#
# 生成 ovpn 配置,并重定向保存到文件 myname.ovpn,这个文件可以用于 OpenVPN-Connect 等 APP
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-profile ovpn myname >myname.ovpn
# 生成 lamda 使用的 OpenVPNProfile,可以直接在 lamda 中使用
# 其中包含一段 properties.local 注释部分,你可以将其中的 openvpn.* 配置
# 复制到 /data/local/tmp/properties.local 中以实现自动连接 VPN
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-profile lamda myname

撤销客户端凭证

如果需要撤销某个客户端凭证请执行以下命令,撤销后可能需重新启动 OpenVPN 服务

bash
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-revoke myname

启动 OpenVPN 服务

现在,启动 OpenVPN 服务

在前台运行,可以直接看到客户端连接日志,用于排查错误

bash
docker run -it --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run

在后台运行,确认无误后建议使用此方法启动

bash
docker run -d --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run

如果没错的话,各个功能都是刚刚好,你可以开始使用了。

其他参考

基础文档:https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/

路由设置:https://community.openvpn.net/openvpn/wiki/BridgingAndRouting


File: tools/socks5/README.en.md

Out-of-the-box socks5 proxy service images

Here lamda provides two methods of installing socks5, to avoid problems please read the following rules first

If you need to proxy the UDP protocol

Due to the nature of the socks5 udp proxy, this can make potholes as well as things more and more common, if you are sure you need to use UDP
One: your host system must be Linux and your firewall needs to be free of UDP ports `50000-55000
Two: your server network must not be NAT (FULL NAT is not an option, existing cloud servers are basically in NAT mode)

Why host Linux? Because on other systems docker may not be able to map such a large number of ports properly.
Secondly, the docker network mode on other systems will affect it.

If you don't know whether your server network is NAT or not, get the IP address of the default network interface by using a command such as ifconfig.
Then ping the address where you want to use the proxy. If the ping doesn't work, then your server is probably in NAT.

Of course, if the conditions are not met it doesn't mean that you can't use UDP, you can still set up gost yourself, the end of this article describes how to use gost to set up.

If you don't need to use UDP, then everything is very simple

Just use the following command

bash
docker run --it --rm -p 1080:1080 --name socks -e LOGIN=username -e PASSWORD=passwd rev1si0n/socks5

you can specify the outgoing network card if you need to

If you need to be able to specify an outgoing network card, that is, if your server or computer has more than one interface to the internet, take a home computer, you have two networks connected via cable and WIFI respectively
Then your computer may have two network interfaces wlan0 , eth0
and you need to specify which one is the proxy outgoing network.

Your computer or server must be a Linux system, if you want to use eth0 to get out of the network, you can start it with the following command

bash
docker run --it --rm --net host --name socks -e LOGIN=username -e PASSWORD=passwd -e DEV=eth0 rev1si0n/socks5

If you have met all the conditions for using UDP, use the following command

bash
docker run -it --rm --net host --name socks -e LOGIN=username -e PASSWORD=passwd rev1si0n/socks5

Using gost as a proxy

If you want to use UDP but can't meet the conditions for using UDP above, or can't use the specified outgoing interface, or don't want to install docker you can try gost.
Download the executable zip for your system from github.com/ginuerzh/gost/releases/.

bash
gost -L=socks5://username:passwd@:1080
# Note that all UDP port firewalls on the system are turned off.

that works.

Finally, just connect with the following code in lamda

python
profile = GproxyProfile()
profile.type = GproxyType.SOCKS5
#profile.udp_proxy = True or False
profile.host = "192.168.server IP"
profile.port = 1080
profile.login = "username"
profile.password = "passwd"
d.start_gproxy(profile)

File: tools/socks5/README.md

开箱即用的 socks5 代理服务镜像

在这里 lamda 提供了两种 socks5 的安装方法,为了避免问题,请先阅读以下规则

如果你需要代理UDP协议

由于socks5 udp代理的特性,这会让坑以及事情越来越多,如果你确定需要使用UDP
你必须遵循这些规则,其一:你的宿主机系统必须为 Linux,且防火墙需要放开 50000-55000 的UDP端口
其二:你的服务器网络必不能为 NAT(FULL NAT 也不行,现有的云服务器基本都是 NAT 模式)

为什么宿主机要是 Linux,因为在其他系统上 docker 可能无法正常映射如此大段的端口,
其次,其他系统上 docker 网络模式会影响。

如果你不知道你的服务器网络是不是 NAT,请通过 ifconfig 等命令获取默认网络接口的IP地址,
随后在你想要使用代理的地方 ping 这个地址,如果PING不通,那么你的服务器可能在 NAT 里。

当然,如果有条件不符合并不代表就不能用UDP,你仍然可以自行搭建 gost,文末介绍如何使用 gost 搭建。

如果不需要使用 UDP,那么一切都很简单

使用如下命令即可

bash
docker run -it --rm -p 1080:1080 --name socks -e LOGIN=username -e PASSWORD=passwd rev1si0n/socks5

如果你需要可以指定出网网卡

就是你的服务器或者电脑上有多个可以上网的接口,拿家用电脑来说,你通过网线和WIFI分别连接了两个网络
那么你的电脑可能就有两个网络接口 wlan0 , eth0
而你需要指定代理出网的那个网络。

你的电脑或者服务器必须为 Linux 系统,如果你想使用 eth0 出网,使用以下命令启动即可

bash
docker run -it --rm --net host --name socks -e LOGIN=username -e PASSWORD=passwd -e DEV=eth0 rev1si0n/socks5

如果你已满足使用UDP的各项条件,使用如下命令即可

bash
docker run -it --rm --net host --name socks -e LOGIN=username -e PASSWORD=passwd rev1si0n/socks5

使用 gost 来做代理

如果你想使用 UDP 但是无法满足上面使用UDP的条件,或者用不到指定出网接口,或者不想安装 docker 你可以尝试 gost。
从 github.com/ginuerzh/gost/releases/ 下载对应系统的可执行文件压缩包。

bash
gost -L=socks5://username:passwd@:1080
# 注意关闭系统上的所有UDP端口防火墙。

即可。

最后,在 lamda 中使用如下代码连接即可

python
profile = GproxyProfile()
profile.type        = GproxyType.SOCKS5
#profile.udp_proxy   = True 或者 False
profile.host        = "192.168.服务器IP"
profile.port        = 1080
profile.login       = "username"
profile.password    = "passwd"
d.start_gproxy(profile)

File: tools/README.en.md

These tools normally only work on linux/mac systems and are a package of common personal functions, not specifically designed to be Windows compatible but that doesn't mean lamda doesn't support them. If you are using Windows, scripts ending in .sh should not work properly.

Pre-requisites

Before you start, make sure that you have started the lamda server on your device.

Note: Some of the command documentation may be updated from time to time, so to avoid versioning issues, make sure you have the latest version of lamda and its Python libraries and dependencies installed on your device before continuing. Some functions require adb, so please ensure that you have the latest version of adb installed.

bash
# If you don't have it installed, please do your own search for how to install it, this is only basic advice
# Windows
https://developer.android.com/studio/releases/platform-tools
# Mac
brew install android-platform-tools
# brew cask install android-platform-tools
# Debian/Ubuntu
apt install adb

Clone the code locally

bash
git clone https://github.com/rev1si0n/lamda.git
# If you don't know how to use git or don't have it installed, download this file locally and unpack it
# https://github.com/rev1si0n/lamda/archive/refs/heads/master.zip

Go to the tools directory and install the required dependencies

bash
# Switch to the tools/ directory and execute
pip3 install -r requirements.txt

If you have enabled communication encryption (certificate) on the server side, you will need to set the environment variable CERTIFICATE before you can use it

bash
# For linux / Mac
export CERTIFICATE=/path/to/lamda.pem
# For Windows (paths must not contain spaces)
set CERTIFICATE=C:\Users\path\to\lamda.pem

If you have changed lamda's default port 65000, you will also need to set the environment variable before using it

bash
# For linux / Mac
export PORT=8123
# For Windows (path must not contain spaces)
set PORT=8123

192.168.1.2 is the example IP, please get the actual IP address of the device yourself.

cert.sh

Used to generate a certificate for transmission encryption

bash
bash cert.sh

If you need to generate a certificate for a specific CN

bash
bash cert.sh device1.example.com

The lamda.pem or device1.example.com.pem in the current directory will be the required certificate.
After the certificate has been applied on the server side, the client must also use the certificate for remote calls and ssh connections.
Once the certificate is applied, any operations performed through the client library will be encrypted and ssh will no longer use the default secret key.
The web control page will also require a login (you can find this login password in the last line of the certificate).

If the operating system is not convenient for using this tool, a default SSL certificate is already available for you in the current directory

bash
test.pem # The certificate used to encrypt client-server communication

cert.py

Used to generate a certificate for transport encryption, equivalent to cert.sh except that this script can also be run on Windows and is used in the same way as cert.sh.

Note: you may need to install the pyOpenSSL dependencies manually pip install pyOpenSSL

id_rsa

The default ssh private key used by lamda's built-in ssh, which is the same as the hard-coded private key in ssh.sh, scp.sh.

startmitm.py

Start the man-in-the-middle, this will fully automate the global man-in-the-middle on the device and you will be able to intercept http/s traffic for your application, including of course DNS requests (globally).
It is possible to apply and withdraw the man-in-the-middle automatically, and the device and network will be restored to its original state when you exit the script.

Tip: Sometimes it is annoying to have all this stuff on Windows.
So specifically for Windows there is startmitm.exe, a Windows command line program that can be downloaded and placed in the system PATH or current directory and executed from the command line.
It has the same arguments as startmitm.py. You can do this without installing Python and any associated environment, but you will only be able to do basic man-in-the-middle operations. You can find it in every release. If not, you can look at older releases.
For Linux/Mac users, if you want to generate/use this standalone executable, package it yourself using the pyinstaller startmitm.spec command
(requires pyinstaller to be installed).

If you need to intermediate the IAPP, go to globalmitm

First make sure that the current computer is on the same network segment as the device, 192.168.1.2 for the mobile device with lamda running.
Secondly, make sure you have verified on the command line that mitmproxy has been installed successfully (type mitmdump on the command line to verify this).

Of course, you can also easily mitm if the computer is not on the same network segment as the device or if it is a remote device, please read on.

Note: mitmweb is not suitable for long periods of multiple requests, the intercepted requests are stored in memory. So your memory will be eaten up if you use it for a long time (you can free it up from time to time by clicking on File->Clear All in the top left corner of the mitmweb interface).
For long man-in-the-middle operations, please use mitmdump instead, please find out how to use it yourself.

Execute

bash
# Note that the device IP must be first in the argument
python3 -u startmitm.py 192.168.1.2

That's all.

If you want to analyse with a LAN buddy

bash
python3 -u startmitm.py 192.168.1.2 --web-port 7890 --web-host 0.0.0.0
# Then, have your little one open http://你的IP地址:7890 in their browser and voila!

If you need to intercept application-specific traffic rather than global

bash
# where com.some.package is the application ID
python3 -u startmitm.py 192.168.1.2:com.some.package

That will work.

If you want to use a specific DNS, or in some cases you may have DNS resolution errors/unresolvable (which may occur on some native systems), you can do this

bash
# Use mainland China DNS
python3 -u startmitm.py 192.168.1.2 --nameserver 114.114.114.114

If additional arguments need to be passed to mitmproxy, such as -s, then execute

For information on how to write the http_flow_hook.py script, please refer to docs.mitmproxy.org/stable/addons-examples and mitmproxy/examples/contrib

bash
# So you can modify the request or response in real time by writing http_flow_hook.py
python3 -u startmitm.py 192.168.1.2 -s http_flow_hook.py

That's all.

The phone is not on the same network as the current computer, but you can physically touch the device, you can still do man-in-the-middle, but **you need to make sure the current device is connected to the computer via USB or adb connect and is ADB authorized.

bash
# localhost means using an adb device
# Only one adb device is currently connected
python3 -u startmitm.py localhost
# The computer is connected to multiple adb devices, you need to specify adb serial
# This serial can be found in the output of the command adb devices
python3 -u startmitm.py localhost --serial bfde362

This will work.

The phone is not under the same network as the current computer and does not have physical access to the device, but as long as you have access to the lamda port you can also do man-in-the-middle.
This is usually the case when you have forwarded lamda to a remote server using the built-in frp service, or when you have forwarded lamda's port 65000 somewhere on your own in some way (e.g. SSH, router port forwarding, etc. - be aware of security issues), in which case you and lamda
Only this port is available for direct communication, the other ports are not accessible to each other. In this case, the phone cannot access any of the local ports, and the local phone can only access the lamda port of the phone (or the phone has a public IP, but the local phone is on a non-interoperable intranet), so that you need to go through the following. (Note that OpenVPN network interworking is not a case of this)

In this case, it needs to be done in a slightly more cumbersome combination, and here's how to do it.

First, install your own adb public key onto the device using adb_pubkey.py or by calling the interface yourself (search for it in this document).

Now execute the following command

bash
adb kill-server
# If you are using the built-in frp or using ssh forwarding yourself
# where x.x.x.x is usually 127.0.0.1, please change this to suit the facts
# and 65000 here is not fixed, change it according to your actual destination forwarding port
adb connect x.x.x.x:65000

Finally, do the same as above via USB

bash
# localhost means the adb device is being used
# Only one adb device is currently connected
python3 -u startmitm.py localhost
# The computer is connected to multiple adb devices, you need to specify the adb serial
python3 -u startmitm.py localhost --serial x.x.x.x:65000

and that will do it.

Note: You may need to end the app completely and reopen it before the traffic data will be displayed.

Press CONTROL + C once to exit the script.

Forwarding to upstream agents

startmitm itself will also start mitmproxy as a proxy service, by default traffic is sent from the local NIC by mitmproxy, if you need traffic to go through an upstream proxy instead of the local machine, you can specify an upstream proxy using the following, only HTTP is supported as an upstream proxy.

DNS traffic will not go through the upstream proxy

bash
python3 -u startmitm.py 192.168.1.2 --mode upstream:http://example.com:8080
# Omitting the http:// start is fine
python3 -u startmitm.py 192.168.1.2 --mode upstream:example.com:8080

If the upstream proxy requires login authentication

bash
python3 -u startmitm.py 192.168.1.2 --mode upstream:example.com:8080 --upstream-auth USER:PASSWORD

These options are compatible with mitmproxy.

DNS man-in-the-middle (DNS+HTTP/S)

Intercepting DNS requests requires that mitmproxy version >= 9.0.0 (and Python >= 3.9) and that the script is run as administrator or root.
Some systems may have their own DNS service, so make sure that no other service is using port 53 before using this feature.

This option is different from -nameserver above, the -dns option refers to the dns intermediary

DNS broker, default upstream DNS server is 1.1.1.1

bash
python3 -u startmitm.py 192.168.1.2 --dns

Specify the upstream DNS as 114.114.114.114 (if in mainland China, we recommend using the following command to specify DNS)

bash
python3 -u startmitm.py 192.168.1.2 --dns 114.114.114.114

If the upstream DNS uses a non-standard port (e.g. 5353)

bash
python3 -u startmitm.py 192.168.1.2 --dns 192.168.0.100:5353

There are some changes to the method name definitions in the hook script, response() for normal http requests, and dns_response() for intercepting DNS.

python
def response(flow):
    print (flow, type(flow))

def dns_response(flow):
    print (flow, type(flow))

See the documentation for mitmproxy for details.

adb_pubkey.py

A script to install the local adb pubkey to lamda, otherwise the adb connection will show unauthorized.

bash
## install adb pubkey
python3 -u adb_pubkey.py install 192.168.1.2
# Uninstall adb pubkey
python3 -u adb_pubkey.py uninstall 192.168.1.2

After installation, execute

bash
adb kill-server
adb connect 192.168.1.2:65000
adb -s 192.168.1.2:65000 shell

to connect to the lamda adb.

ssh.sh

Connect to the shell terminal on the phone.

Execute

bash
bash ssh.sh 192.168.1.2

and you're done.

scp.sh

Use scp to copy the files on the device locally.

Copy the /sdcard/DCIM directory of 192.168.1.2 to the current directory

bash
bash scp.sh 192.168.1.2:/sdcard/DCIM .

Copy the local directory/file test/ to /sdcard/ on device 192.168.1.2

bash
bash scp.sh test/ 192.168.1.2:/sdcard

discover.py

List all devices online in the local network (some devices may not be listed in some network situations, please try several times)

bash
python3 discover.py

fridarpc.py

A simple wrapper for the fridarpc function.

This function requires you to be proficient in writing frida scripts. Please refer to the test-fridarpc.js file for the script used in the example, and note in particular that the return value of the function defined by rpc.exports in the frida script can only be string/list/json or any js that can be serialised by json. Assume that the device IP is 192.168.0.2.

Execute the following command to inject RPC into com.android.settings (watch for errors)

bash
python3 fridarpc.py -f test-fridarpc.js -a com.android.settings -d 192.168.0.2

Now that you have the interface out, you can simply request http://192.168.0.2:65000/fridarpc/myRpcName/getMyString?args=["A", "B"] to get the results of the methods within the script, and the link can be opened with a browser. GET, and the parameter list can also use multiple parameters at the same time.

Note that the arguments are provided in double quotes and we recommend using json.dumps(["A", "B"])

call with requests

python
import json
import requests
url = "http://192.168.0.2:65000/fridarpc/myRpcName/getMyString"
data = requests.post(url, data={"args": json.dumps(["A", "B"])}).json()
print (data["result"])

#* Status code 200 Everything is fine
#* Status code 410 Script needs to be re-injected or script is not injected (automatic re-injection is not currently supported)
#* Status code 500 Script or parameter exception
#* Status code 400 Parameter error

The format of the response result is fixed and can be opened in a browser for viewing.

emu-install.sh

Server-side installation script for mainstream emulators only, requires device to be WIFI adb enabled, needs to be downloaded in advance
The corresponding architecture server-side installation package to the current running directory.

bash
bash emu-install 192.168.1.2

statistics.sh

If you are unable to run LAMDA properly, you can use this script to generate an issue report by rebooting the device and ensuring that LAMDA has exited, pushing statistics.sh to /data/local/tmp using adb, and then executing it as root.

bash
# launch.sh is the path to the script that starts LAMDA
sh /data/local/tmp/statistics.sh /data/local/tmp/arm64-v8a/bin/launch.sh
# After execution, the file /sdcard/statistics.txt will be generated, just report this file

magisk

lamda's magisk module architecture

Various service scripts (Docker)

All images are for x86 platforms, you may have to make your own modifications and regenerate them for use on ARM processor Linux/Mac.

openvpn

Out-of-the-box OpenVPN service

globalmitm

Used to analyse traffic from foreign apps that require a proxy to connect

frps

Out-of-the-box frp port forwarding

socks5

Out-of-the-box socks5 proxy service


File: tools/README.md

这些工具正常情况下只能工作于 linux/mac 系统之上,为个人常用功能的封装,并未特意考虑兼容 Windows 但是这不代表 lamda 不支持。如果你使用的是 Windows,以.sh结尾的脚本应该无法正常工作。

前置条件

开始之前,请确保已经启动设备上的 lamda 服务端。

注意:部分命令文档可能具有时效性随时更新,为了避免版本问题,继续前请务必先在手机上安装最新版本的 lamda 以及其 Python 库和依赖库。部分功能需要使用 adb,请务必确保安装的 adb 是最新版本。

bash
# 如果没有安装,请 自行搜索 如何安装,这里提供的只是基础建议
# Windows
https://developer.android.com/studio/releases/platform-tools
# Mac
brew install android-platform-tools
#brew cask install android-platform-tools
# Debian/Ubuntu
apt install adb

克隆代码到本地

bash
git clone https://github.com/rev1si0n/lamda.git
# 如果不会使用或未安装 git,下载此文件到本地并解压
# https://github.com/rev1si0n/lamda/archive/refs/heads/master.zip

进入 tools 目录并安装所需的依赖包

bash
# 切换到 tools/ 目录并执行
pip3 install -r requirements.txt

如果你在服务端启用了通信加密(certificate),你需要在使用前设置环境变量 CERTIFICATE

bash
# 对于 linux / Mac
export CERTIFICATE=/path/to/lamda.pem
# 对于 Windows(路径不能包含空格)
set CERTIFICATE=C:\Users\path\to\lamda.pem

如果你修改了 lamda 的默认端口 65000,也需要在使用前设置环境变量

bash
# 对于 linux / Mac
export PORT=8123
# 对于 Windows(路径不能包含空格)
set PORT=8123

192.168.1.2 为示例IP,请自行获取设备的实际IP地址。

cert.sh

用来生成用于传输加密的证书

bash
bash cert.sh

如果你需要生成特定 CN 的证书

bash
bash cert.sh device1.example.com

即可,当前目录下的 lamda.pem 或者 device1.example.com.pem 即为所需的证书。
在服务端应用该证书后,客户端也必须使用此证书来进行远程调用以及 ssh 连接。
应用此证书后,将会加密任何通过 client 库进行的操作,ssh 将不再使用默认的秘钥。
同时,web 控制页面将需要登录(你可以在证书最后一行找到此登录密码),

如果操作系统不方便使用此工具,当前目录下已经为你准备好了一个默认的 SSL 证书

bash
test.pem    # 用于加密客户端与服务端通信的证书

cert.py

用来生成用于传输加密的证书,等效于 cert.sh 只不过这个脚本同时可以运行于 Windows,用法等同于 cert.sh。

注:你有可能需要手动安装 pyOpenSSL 依赖库 pip install pyOpenSSL

id_rsa

lamda 内置 ssh 使用的默认 ssh 私钥,此与 ssh.sh、scp.sh 中硬编码的私钥相同。

startmitm.py

启动中间人,这将会全自动的在设备上开启全局的中间人,你就可以截获应用的 http/s 流量,当然,也可以包括 DNS 请求(全局)。
它可以自动应用及撤销中间人,退出脚本后设备及网络也将恢复它原来的样子。

提示:有时在 Windows 上装这些东西是很烦的事情,
所以专门为 Windows 提供了 startmitm.exe,这是一个Windows 命令行程序,将其下载并放入系统 PATH 或者当前目录,在命令行执行即可。
它的参数与 startmitm.py 相同。这样你可以不用安装 Python 及任何相关环境,但是你也只能进行基本的中间人操作。你可以在每个版本的 release 中找到它。如果没有,可以翻看稍旧的 release。
对于 Linux/Mac 用户,如果你想生成/使用这种独立可执行程序,请自行使用 pyinstaller startmitm.spec 命令打包
(需要安装 pyinstaller)。

如果你需要对国际APP进行中间人,请转到 globalmitm

首先确保当前电脑与设备在同一个网段,192.168.1.2 为运行了 lamda 的手机设备。
其次,确保你已在命令行验证 mitmproxy 已安装成功(在命令行输入 mitmdump 进行验证)。

当然,电脑与设备不在同一网段或者是远程设备,你也可以轻松 mitm,请继续看下去。

注意:mitmweb 并不适合长时间多请求,其截获的请求均存储于内存之中。所以在长时间使用的情况下你的内存会被吃光(你可以不定期点击 mitmweb 界面的左上角 File->Clear All 来释放)。
对于长时间的中间人操作,请改用 mitmdump,请自行了解使用方法。

执行

bash
# 注意,设备IP 必须在参数第一位
python3 -u startmitm.py 192.168.1.2

即可。

如果你想和局域网的小伙伴一起分析

bash
python3 -u startmitm.py 192.168.1.2 --web-port 7890 --web-host 0.0.0.0
# 然后,让你的小伙伴用浏览器打开 http://你的IP地址:7890 即可

如果需要截获特定应用的流量而不是全局

bash
# 这里的 com.some.package 是应用的 ID
python3 -u startmitm.py 192.168.1.2:com.some.package

即可。

如果你想使用特定的DNS,或者一些情况下,你可能出现DNS解析错误/无法解析的情况(可能出现于一些原生的系统),可以这样做

bash
# 使用中国大陆 DNS
python3 -u startmitm.py 192.168.1.2 --nameserver 114.114.114.114

如果需要传递其他参数到 mitmproxy,例如 -s,则执行

关于如何编写 http_flow_hook.py 脚本,请参考 docs.mitmproxy.org/stable/addons-examples 以及 mitmproxy/examples/contrib

bash
# 这样你就可以通过编写 http_flow_hook.py 实时修改请求或者响应
python3 -u startmitm.py 192.168.1.2 -s http_flow_hook.py

即可。

手机与当前电脑不在同一网络下,但是你可以物理接触设备,你仍然可以进行中间人,但是需要确保当前设备已通过USB 或者 adb connect 接入电脑且已ADB授权。

bash
# localhost 代表使用 adb 设备
# 当前仅连接了一台 adb 设备
python3 -u startmitm.py localhost
# 电脑连接了多台 adb 设备,你需要指定 adb serial
# 这个 serial 请从命令 adb devices 的输出中寻找
python3 -u startmitm.py localhost --serial bfde362

即可。

手机与当前电脑不在同一网络下,也无法物理接触设备,但是只要你可以访问 lamda 的端口,你也可以进行中间人。
这种情况通常为:你使用了内置 frp 服务转发了 lamda 到远程服务器,或者你自行通过某种方式转发了 lamda 的 65000 端口到某个地方(例如 SSH、路由器端口转发等-注意安全性问题),这种情况下你和 lamda 之间
仅有这一个端口可以直接交流,其他端口是无法互相访问的。这种情况下,手机无法访问到本机的任何端口,本机也只能访问到手机的 lamda 端口(或者手机有公网IP,但本机在不互通的内网),这样需要通过以下方式来进行。(注意 OpenVPN 网络互通,并不属于这个情况)

这时,需要通过稍微繁琐的组合方式来进行,下面介绍如何操作。

首先,使用 adb_pubkey.py 或者自行调用接口将自身的 adb 公钥安装到设备上(请在本文档搜索)。

现在执行以下命令

bash
adb kill-server
# 如果你使用了内置 frp 或者自行使用了 ssh 转发,
# 这里的 x.x.x.x 通常为 127.0.0.1,请依据事实修改
# 而这里的 65000 也非固定,依据你实际设置的目的转发端口做修改
adb connect x.x.x.x:65000

最后,按照和上文 通过USB 一样的方法操作

bash
# localhost 代表使用 adb 设备
# 当前仅连接了一台 adb 设备
python3 -u startmitm.py localhost
# 电脑连接了多台 adb 设备,你需要指定 adb serial
python3 -u startmitm.py localhost --serial x.x.x.x:65000

即可。

注意:你可能需要完全结束APP并重新打开才会显示流量数据。

按下一次 CONTROL + C 退出脚本。

转发到上游代理

startmitm 本身也会启动 mitmproxy 作为代理服务,默认情况下流量都是 mitmproxy 从本机网卡发出的,如果你需要流量通过一个上游代理发出而不是本机,可以使用如下方式指定上游代理,仅支持 HTTP 作为上游代理。

DNS 流量不会经过上游代理

bash
python3 -u startmitm.py 192.168.1.2 --mode upstream:http://example.com:8080
# 省略掉 http:// 开头也是可以的
python3 -u startmitm.py 192.168.1.2 --mode upstream:example.com:8080

如果上游代理需要登录认证

bash
python3 -u startmitm.py 192.168.1.2 --mode upstream:example.com:8080 --upstream-auth USER:PASSWORD

这些选项是与 mitmproxy 兼容的。

DNS 中间人 (DNS+HTTP/S)

截获 DNS 请求需要确保 mitmproxy 的版本 >= 9.0.0(且 Python>=3.9),且需要以管理员或者root身份运行脚本。
部分系统上会存在自带的 DNS 服务,在使用该功能前请务必确保没有其他服务使用了 53 端口。

此选项与上方 --nameserver 意义不同,--dns 选项专指 dns 中间人

DNS 中间人,默认上游 DNS 服务器为 1.1.1.1

bash
python3 -u startmitm.py 192.168.1.2 --dns

指定上游 DNS 为 114.114.114.114(如果在中国大陆,建议使用如下命令指定DNS)

bash
python3 -u startmitm.py 192.168.1.2 --dns 114.114.114.114

如果上游 DNS 使用了非标准端口(例如 5353)

bash
python3 -u startmitm.py 192.168.1.2 --dns 192.168.0.100:5353

hook 脚本的方法名称定义有一些变化,正常 http 请求为 response(),截获 DNS 时需要使用 dns_response()。

python
def response(flow):
    print (flow, type(flow))

def dns_response(flow):
    print (flow, type(flow))

具体请查看 mitmproxy 的文档。

adb_pubkey.py

用于安装本机的 adb pubkey 到 lamda 的脚本,否则 adb 连接将会显示未授权。

bash
# 安装 adb pubkey
python3 -u adb_pubkey.py install 192.168.1.2
# 卸载 adb pubkey
python3 -u adb_pubkey.py uninstall 192.168.1.2

安装后,执行

bash
adb kill-server
adb connect 192.168.1.2:65000
adb -s 192.168.1.2:65000 shell

来连接 lamda adb。

ssh.sh

连接入手机上的 shell 终端。

执行

bash
bash ssh.sh 192.168.1.2

即可。

scp.sh

使用 scp 复制设备上的文件到本地。

将 192.168.1.2 的 /sdcard/DCIM 目录复制到当前目录

bash
bash scp.sh 192.168.1.2:/sdcard/DCIM .

将本地目录/文件 test/ 复制到设备 192.168.1.2 的 /sdcard/

bash
bash scp.sh test/ 192.168.1.2:/sdcard

discover.py

列出本地网络中所有在线的设备(部分网络情况下可能一些设备没有列出,请多次尝试)

bash
python3 discover.py

fridarpc.py

一个 fridarpc 功能的简单封装。

此功能需要你能熟练编写 frida 脚本。示例中使用的脚本请参照 test-fridarpc.js 文件,特别注意: frida 脚本中 rpc.exports 定义的函数返回值只能为 string/list/json 或者任意 js 中可以被 json 序列化的值。假设设备IP为 192.168.0.2。

执行以下命令注入 RPC 到 com.android.settings(注意查看是否有报错)

bash
python3 fridarpc.py -f test-fridarpc.js -a com.android.settings -d 192.168.0.2

现在已经将接口拿出来了,只需要请求 http://192.168.0.2:65000/fridarpc/myRpcName/getMyString?args=["A","B"] 即可得到脚本内方法的返回结果,链接也可以用浏览器打开,接口同时支持 POST 以及 GET,参数列表也可以同时使用多个参数。

注意参数的提供形式,是双引号,建议使用 json.dumps(["A", "B"])

用 requests 调用

python
import json
import requests
url = "http://192.168.0.2:65000/fridarpc/myRpcName/getMyString"
data = requests.post(url, data={"args": json.dumps(["A", "B"])}).json()
print (data["result"])

#* 状态码 200 一切正常
#* 状态码 410 需要重新注入脚本或者脚本未注入(目前不支持自动重新注入)
#* 状态码 500 脚本或参数异常
#* 状态码 400 参数错误

响应结果的格式是固定的,可在浏览器打开查看。

emu-install.sh

仅适用于主流模拟器的服务端安装脚本,需要设备开启 WIFI adb,需要提前下载
对应架构服务端安装包到当前运行目录。

bash
bash emu-install 192.168.1.2

statistics.sh

如果你无法正常运行 LAMDA,可以使用此脚本来生成 issue 报告,重启设备并且确保 LAMDA 已经退出,将 statistics.sh 使用 adb push 到 /data/local/tmp,随后使用 root 身份执行。

bash
# launch.sh 为启动 LAMDA 的脚本路径
sh /data/local/tmp/statistics.sh /data/local/tmp/arm64-v8a/bin/launch.sh
# 执行完毕后,会生成文件 /sdcard/statistics.txt,报告此文件即可

magisk

lamda 的 magisk 模块构架

各种服务脚本 (Docker)

所有镜像均为 x86 平台,在 ARM 处理器的 Linux/Mac 上使用你可能要自行做出修改并重新生成。

openvpn

开箱即用的 OpenVPN 服务

globalmitm

用于分析需要透过代理才能连接的国外APP的流量

frps

开箱即用的 frp 端口转发

socks5

开箱即用的 socks5 代理服务


File: README.en.md

English README is translated by deepl, we not guarantee it's the original meaning of the author, all readme is named README.en.md, if you still confused, please reach me at t.me/lamda_dev or by mail/gitter.

LAMDA is a helper framework for reverse and automation, designed to reduce the time and trivialities of security analysis and application testers by replacing a lot of manual work with programmatic interfaces, it is not a single feature framework. To give you an idea of its usefulness: do you install all kinds of proxies, plug-ins or point-and-click setups on your mobile phone to do your job? Do you have to operate your phone from a remote location that is thousands of miles away? Do you have the need to program your phone to control it? Are you still buying expensive IP switching, remote ADB debugging, RPA automation or even logcat logging services from some cloud phone vendors that you have to pay for? If so, then yes, all these problems can be solved with just one LAMDA. And, LAMDA is even more focused on distribution - in fact, you can manage devices scattered around the world in various network environments on a single public network server. Of course, LAMDA can do much more than that.

  • Zero dependency, just root is all you need
  • Stable production environment tested with over 500 devices under pressure
  • Easy root certificate setup via interface, with http/socks5 proxy for man-in-the-middle
  • Expose internal Java interface via frida as http api
  • near-commercial-grade software quality and stability, full ARM/X86 architecture
  • High security, support for interface and login authentication
  • Turns your device into a http web proxy
  • Partially compatible with uiautomator2
  • Device status/resource consumption readout
  • System configuration/property readout modification
  • UI layout review
  • WIFI ADB with built-in root privileges for wireless connect
  • Support for custom startup configuration
  • Support for emulators and real phones, cloud phones/headless development boards, redroid (ARM only)
  • Support for Android 6.0 (M, API 23) - 13 (T, API 33)
  • Support for WSA (Windows Subsystem for Android ™️)
  • Support for UDP protocol proxies (socks5 UDP mode)
  • Support for OpenVPN and coexistence with http proxy
  • Support for Magisk autostart service
  • Encapsulates a lot of common interfaces, only Python is required
  • Completely networked, no physical USB cable/USB hub, etc.
  • Large file uploads and downloads
  • Significantly reduces the threshold and time cost of mundane tasks
  • Get/replay the most recent Activity on the system
  • Send any application Activity
  • ssh login to device terminals
  • Connect to any device anywhere with LAMDA running
  • Run shell commands front and back, grant revocation of application privileges, etc.
  • Built-in http/socks5 proxy, can set proxy for global/specific applications
  • Built-in frida 15.x, IDA 7.5 server and other tools
  • Built-in crontab task service
  • Built-in Python 3.9 and some common modules
  • Built-in OpenVPN for global/non-global VPN
  • WIFI remote desktop (web)
  • WEB-side file upload and download
  • UI automation with interfaces to automate operations

One-click man-in-the-middle traffic analysis

With support for regular and international APP traffic analysis, DNS traffic analysis, and thanks to the mitmproxy flow hook, you have maximum control over any request, mitmproxy is feature rich enough to export curl commands or HTTPie commands for specific requests via its Export option. It is rich enough that you can export request-specific curl commands or HTTPie commands via its Export option, and the combination of analysis replay, interception modification, and functionality is a good substitute for any commercial/non-commercial software you have used. If you're still not sure what mitmproxy is and what it can do, be sure to look up the documentation first, as LAMDA will be using mitmproxy to present you with application requests.

This is done via globalmitm, startmitm.py in the tools/ directory, see the README in the same directory for how to use it.

Drag and Drop Uploads

Drag and drop uploads can be done directly from the remote desktop, with support for uploading entire directories up to a single 256MB file, which will always be uploaded to the /data/local/tmp directory.

Remote Desktop Connection

The interface can be operated at any time using your browser even when your phone is not with you, and with Python and related frida tools built in, it is your alternative online shell.

Automation through code

Directly through code point and click, it can replace most manual operations.

Interface layout review

You can instantly review the interface layout of your Android application for writing automation code from the remote desktop. You can enter the mode by clicking on the eye icon in the top right corner of the remote desktop.
Press CTRL + R to refresh the layout and click the eye icon again to exit.

Device Directory Index

You can browse the files on your device in your browser and you can also click on the file name to download the desired file.

If you wish to continue, please ensure that you have a rooted Android device with more than 2GB of RAM and more than 1GB of free storage or an Android emulator (the latest version of Nightmare, Thunderbolt emulator, or AVD [Android Studio Virtual Device] is recommended). Not fully supported by NetEase Mumu, Not supported by Tencent Handy Assistant, Blue Stack and Android internal virtualization such as VMOS). For real devices, we recommend devices running closest to the native system such as Google Series, One Plus, Android development boards, etc., or devices with only lightly modified systems. If you are using an OPPO/VIVO/Huawei/Xiaomi device and it does not run properly after trying, we recommend using an emulator instead.

For Cloud Mobile, AliCloud/Huawei Cloud Mobile is supported, X Finger, X MultiCloud, X Electric, X Cloud Rabbit, X Zixing, X MaCloud and any other brands are not supported.

Index

Disclaimer and Terms

In order to download and use the software LAMDA developed by rev1si0n (account github.com/rev1si0n) ("I"), you should read and comply with the User Agreement ("this Agreement") ("Agreement"). You must read and fully understand the terms and conditions, especially those that exclude or limit liability, and choose to accept or not accept them; unless you have read and accepted all of the terms and conditions of this Agreement, you are not authorized to download, install or use the Software and related services. By downloading, installing, using, obtaining an account, logging in, etc., you are deemed to have read and agreed to be bound by the above agreement; if you wish to access the Service, you (hereinafter referred to as "User") shall agree to all of the terms of this agreement and complete the application process as indicated on the page. You can find DISCLAIMER.TXT in the same directory as this document, or here DISCLAIMER.TXT. As it is not fully open source, in addition to the above terms:** You are authorised to reverse-analyse LAMDA itself for the purpose of malicious code**.

Please confirm that you have read and accepted all of the terms of this Agreement, otherwise you are not authorized to download, install or use the Software and related services.

Preamble

LAMDA is free software (freeware) developed by individuals, currently only the client and the protocol are open source, but I promise that it has nothing against you or superfluous, if you still have concerns, you can leave now or choose to pay for psychological comfort. Please respect each other and use it in accordance with the terms of use. Collaborative exchanges can be click here to send an email.

Why is it partially open source? Because LAMDA is both black and white and can easily be used by unscrupulous people to put the author at risk, so please respect the terms of use. It is recommended that you work with the code in the documentation and samples on a Linux or Mac system. Some of the functions require the use of tools in the tools/ directory, please refer to tools/README.en.md.

Special Note: Do not run on your own device, and when there is a possibility of using it on a public or untrusted network, make sure that you specify the PEM certificate at startup

Side effects after use

You may write lambda in your Python code as lamda, this is normal.

Feedback and suggestions

Because Android is widely used by various devices, it is not possible to guarantee 100% compatibility, and there may be various unknown situations such as running exceptions. The exceptions that occur include: restarting for no reason, app crashing frequently, touch failing or moving around for no reason, etc., frozen screen, etc. If you encounter this frequently, it is recommended to stop using it.
Click here Report a problem/suggestion, please describe in detail and include information such as model system.

Community discussion: t.me/lamda_dev | gitter.im/lamda-dev

Support the author

If you need to purchase a server, you can choose to purchase cloud services through the promotional link below.


Install LAMDA

There is a client side and a server side. The client side is mainly Python related libraries and interfaces, and the server side is the service that runs on the device/phone.

Precautions

Basic requirements

LAMDA ideally runs in an environment where you are freshly rooted (e.g. a new emulator, a ROM with its own permissions, Magisk just rooted), and before launching make sure that.

text
* Magisk Hide must be turned off
* frida-server must be turned off
* It is recommended to disable the Xposed/Magisk plugin
* Reboot the device after confirming

and will not enable any of the above entries marked as must after startup.

System settings

Check time zone time

Open System Settings, find Date & Time, check if the time is automatically set to China Standard Time or your location's time zone, check if the time is correct or within an acceptable margin of error, if not turn off Use network-provided time zone and Network time and manually set the time zone and time to your current location's time zone and time.

Turn off accessibility

Open System Settings, find Accessibility (in System or More Settings) and turn off or uninstall all applications that use accessibility (e.g. talkback, autojs, etc.).

Network settings

For real machines, you just need to make sure that your computer is on the same network as your phone.
For emulators, the emulator created by default does not normally interoperate with your local network. If you are using android x86 (VMWare-based Android virtual machine)
If you are using an android x86 (VMWare based Android virtual machine), please try setting the network mode to bridge in the virtual machine settings. For emulators such as Thunderbolt and Nightmare, you will need to follow the instructions in their settings to install the driver and enable bridge mode and then restart the emulator.
For Android Studio Virtual Device, there are no settings. If you need to connect to AVD, please execute adb forward tcp:65000 tcp:65000 first.
and use localhost (do not use 127.0.0.1) to connect.

Other settings

WSA (Windows Subsystem Android)

If you are using WSA (Windows Subsystem Android), make sure that the WSA version is not lower than 2210.40000 and that you are rooted, then open WSA Settings -> Subsystem Resources -> select Continuous and turn off Advanced Networking. Then restart the WSA subsystem.

AVD (Android Studio Virtual Device)

If you are using AVD (Android Studio Virtual Device), first extend the default storage size as follows.

bash
# Pixel_5_API_29 is the virtual machine ID, which can be listed using the command emulator -list-avds
# -partition-size Some of the newly created AVDs may only have a hundred megabytes of available storage space, change it to 2G here
emulator -avd Pixel_5_API_29 -partition-size 2048 -no-snapshot-load
# Use this command every time you start the virtual machine afterwards
#
# You may encounter a situation where the emulator command is not found.
# See this documentation for the location of this command https://developer.android.com/studio/run/emulator-commandline?hl=zh-cn and add it to the PATH variable
#
# If you are unable to complete the above command, manually click on Virtual Device Manager in Android Studio, create a new virtual machine, then find the corresponding virtual machine and click on the Edit button (a pen symbol) at the back.
# Click on Show Advanced Settings, find Storage -> Internal Storage and set it to at least 2GB.

Redroid (android in docker)

Note that currently only ARM (aarch64) based hosts are supported for Redroid, which you can check with the command uname -m.

If you are using Redroid (android in docker), take the officially recommended Ubuntu 20.04 as an example, first install the relevant modules for linux-modules-extra, (note that the following method may not be suitable for other Linux distributions, and if you are not familiar with Linux, we do not recommend that you continue with the following actions ).

bash
apt install linux-modules-extra-`uname -r`

Edit the file /etc/modules, copy the following names and insert them at the bottom of the file and restart the current host.

bash
# redroid modules
mac80211_hwsim

binder_linux
ashmem_linux

or every time the host reboots (note that if you don't use the above method then you have to do it every time you reboot)

bash
modprobe mac80211_hwsim
modprobe binder_linux devices="binder,hwbinder,vndbinder"
modprobe ashmem_linux

Finally use the following command to start, redroid_gpu_mode Please modify it as appropriate (note that this is different from the official command written).

bash
docker run -itd --rm --privileged --pull always -v /lib/modules:/lib/modules:ro -v ~/redroid:/data -p 127.0.0.1:5555:5555 -p 127.0.0.1:65001:65000 redroid/redroid:12.0.0-latest androidboot.redroid_gpu_mode=guest

The container 65000 is mapped here to the local 65001 because some of the tools need to be temporarily bound to port 65000.

Subsequently, you can access LAMDA on the host through http://127.0.0.1:65001.

Installing the client

Please use Python versions 3.6 - 3.10, Python 3.9 is recommended if possible

bash
pip3 install -U lamda
## That's it.
#
# If you need to use the built-in frida, be sure to install it using the following method
# You may need extranet access to install frida, otherwise you may be stuck for a long time (~10 minutes) until the installation fails
# Even if you have installed frida before, you should re-run the following command
pip3 install -U --force-reinstall 'lamda[full]'
# Please note that after completing the installation you will need to update any third party libraries that rely on frida using pip at the same time
# any third party libraries such as frida-tools objection etc. (if installed)
# Otherwise you may get undetectable exceptions in later use

After installation, run the command python3 -m lamda.client to check that it is installed correctly. If you get an error like the following

python
* AttributeError 'NoneType' object has no..
* TypeError: Couldn't build proto file..

This may be due to a conflict with another package installed that depends on protobuf. Please try the following command

bash
pip3 install -U --force-reinstall lamda

If you still have problems, create virtualenv to use it.

Installing the server

LAMDA installed by default does not have any authentication enabled, so others can access any content on your device, listen to your device or even access your network for further control. Please pay particular attention to the Enabling interface authentication section and be sure to use it on a network that you can trust.

Before installing, please select the appropriate architecture. You can get the current system architecture by using the adb shell command getprop ro.product.cpu.abi.
Normally, for current generation phones, you can just choose the arm64-v8a version, while for emulators such as Thunderbolt, you would choose either the 32 or 64 bit version of the Android system when creating a new emulator.
The 32-bit emulator system corresponds to x86 and the 64-bit corresponds to x86_64. Normally, the Thunderbolt emulator creates an x86 based Android 7.0 system by default.

LAMDA supports active reporting of device status, you can write an interface or use grafana to log device status, which includes information about the system, network, memory, CPU, disk, etc.

bash
# If you are not sure what this feature is please do not perform it and take care to replace the following link
echo "stat-report.url=http://example.com/report" >>/data/local/tmp/properties.local

This will cause LAMDA to POST device status information (JSON) to this link every minute after startup, which will not be listed here due to the number of fields.

Configuring automatic updates

LAMDA has a logic for automatic updates, but due to minute service interruptions, this is currently limited to internal use. Please download and install the latest version from github at your leisure at regular intervals.

If you really don't care if the service is unavailable by the minute when you update, writing the following configuration file before starting LAMDA will ensure that LAMDA is always up to date.

bash
# Go to the adb shell and execute
echo "upgrade.channel=latest" >> /data/local/tmp/properties.local

properties.local startup configuration

Before we start, it is important to introduce the properties.local file above.
properties.local is the LAMDA startup configuration file, usually stored on top of the device, which contains strings of type a=b.
By writing this file you can implement automatic connections to OpenVPN, proxies, port forwarding etc. when LAMDA starts.
LAMDA will look for this file and load it from /data/usr, /data/local/tmp, ${CFGDIR:-/data/local} at boot time.
You can place your properties.local configuration file in any one of these three locations.

In addition to properties.local, there is also a parameter -properties.remote that loads the configuration from a remote location, which allows LAMDA to download the configuration from an HTTP server at startup, see the section on starting LAMDA further on.

How to write the configuration is described in the individual functions.

possible errors with launch.sh and how to resolve them

bash
# If llllaamDaa started is displayed, the service is in daemon mode and you can exit the terminal

already running (already running, please do not start it more than once)
invalid TZ area (time zone is not set, set the time zone in the system time settings)
not run as root (not running as root)
unsupported sdk (running on an unsupported Android system)
abi not match   (wrong tar.gz package used)

Installation via Magisk

If your device uses Magisk, then you can install it in the easiest way possible and LAMDA can boot up. Make sure that Magisk version >= 20.4 and only supports installation in the Magisk App. For example, you want all LAMDAs flashed with this magisk module to have the interface authentication enabled, or you want them all to boot with the Magisk App**.
Or if you want them all to automatically connect to the proxy on boot, you can simply write properties.local or generate a PEM certificate and rename it to lamda.pem (see tools/ for instructions on how to use the tool).
Then open lamda-magisk-module.zip using compression software and drag it (lamda.pem or properties.local) into the common folder for automatic configuration at startup!

Now, download lamda-magisk-module.zip from the lamda/releases page and push it to /sdcard, open the Magisk App, click on Modules-> Install from local, select lamda-magisk-module.zip and wait for a while.

After a successful flash, please reboot the device. After rebooting, LAMDA should start itself on boot. However, to avoid possible crash problems, lamda will start after 30 seconds instead of immediately and you will have enough time to disable the LAMDA module (please wait 2 minutes after booting before connecting to use LAMDA). Once the installation is complete, you don't need to read the next paragraph on manual installation, just skip it.

Manual installation

The difference between the two methods is that some older devices may not be able to decompress the tar.gz suffix with the system's tar command, so *-install.sh is provided as a supplement, with a built-in busybox to decompress it. The device architecture obtained by getprop is known to be arm64-v8a, now connect the device to the current computer and make sure that the ADB is authorised and that you can switch root properly.

Method 1

From the release page lamda/releases
Download arm64-v8a.tar.gz-install.sh.

bash
# /data/local/tmp is the standard service installation path, but not mandatory
# You can put it in any folder with read/write access other than /sdcard
adb push arm64-v8a.tar.gz-install.sh /data/local/tmp
# Enter the adb shell
adb shell
# Type su to make sure you are root
su
# Switch to the directory
cd /data/local/tmp
# Execute the install script and start it (this will unpack and start the service)
sh arm64-v8a.tar.gz-install.sh
# Remove the installer
rm arm64-v8a.tar.gz-install.sh

Method 2

From the release page lamda/releases
Download arm64-v8a.tar.gz.

bash
# /data/local/tmp is the standard service installation path, but not mandatory
# You can put it in any folder with read/write access other than /sdcard
adb push arm64-v8a.tar.gz /data/local/tmp

When finished, go to the adb shell and unpack the files.

bash
# You should now be in the adb shell
cd /data/local/tmp
# Unpack the server file
# Note that the included tar command may not support the z option, so you may need to use busybox tar to unpack
# If your system does not come with the busybox command, download a version for the appropriate architecture from https://busybox.net/downloads/binaries/1.20.0
tar -xzf arm64-v8a.tar.gz
# Remove the installation package
rm arm64-v8a.tar.gz

Start the server

For method 1 installations, the service is started after installation, so you don't need to run the following command after installation** using this method, but it's fine to do it again as described below.
For either of the above installation methods, you will only ever need to do this on the first install, but the process of starting the service will need to be done every time the device reboots or after you manually shut down LAMDA, as LAMDA will not run itself.

Go to the adb shell and switch to su root and execute.

bash
# You should now be inside the adb shell, switching to the directory
cd /data/local/tmp
#
# Start the server
# Note that the arm64-v8a directory is named differently depending on the platform
# If you are using the x86 version, then the directory name is x86/ and you will need to change the command accordingly
sh arm64-v8a/bin/launch.sh
#
# If you want to enable encrypted transfers
# Use cert.sh/py in tools/ to generate the PEM certificate first
# Push it to the device e.g. /data/local/tmp/lamda.pem
# and set its owner and permissions to root and 600 (chown root:root lamda.pem; chmod 600 lamda.pem)
# and start it with the following command, lamda.pem must be an absolute path
sh arm64-v8a/bin/launch.sh --certificate=/data/local/tmp/lamda.pem
# This will encrypt any traffic generated through the LAMDA client
# but not the webui remote desktop traffic
# This will encrypt any traffic generated by the LAMDA client # but not webui remote desktop traffic.
# Load properties.local from a remote location
# Sometimes you may wish to load the startup configuration from a link, in which case you can upload properties.local to the server
# When LAMDA downloads this configuration, it will provide some information about the current device such as, unique device ID, device model, current version, etc.
# You can also write your own web service to distribute different startup configurations based on these device parameters
# It is recommended to use HTTPS links for added security, please ensure the device time is correct.
# Then start LAMDA as follows
sh arm64-v8a/bin/launch.sh --properties.remote=http://example.com/config/properties.local
# For static file services with Basic Auth enabled, the same username password is supported
sh arm64-v8a/bin/launch.sh --properties.remote=http://user:[email protected]/config/properties.local
# Hint: LAMDA will retry the request if it times out or returns a 50x status code.
# If it still fails 5 times in a row, LAMDA will abandon the attempt and continue to launch.
# You can of course customize the number of retries.
# Of course, it is possible to customise the number of retries but note that if the server continues to be unresponsive, LAMDA will also be stuck here forever
# When to set the retry count: the device may not have a network connection when it is first booted, if you want to start LAMDA at this point you can increase the value
sh arm64-v8a/bin/launch.sh --properties.remote=http://example.com/config/properties.local --properties.tries=30
# The number of seconds per round n of the retry mechanism will increase as the number of retries increases. So please set this value carefully.
#
# If you need LAMDA to listen to a specific port instead of 65000
# If you change it, make sure that all intranet devices are started on the same port
# Otherwise functions such as device discovery will not work properly
sh arm64-v8a/bin/launch.sh --port=8123
# Do not bind to a port below 1024

Wait for the exit, then close the terminal, and the service start is complete.

Note: There is a chance that the remote desktop will keep loading when starting up for the first time. When this happens, please first try to reboot the device and restart lamda.
If you experience a black screen/restart lag on the device several times during startup or use and other similar situations, it is recommended to stop using it.

Shutdown server

LAMDA is designed to run in the background 24/7 and it is not recommended to start and shut down frequently, if you do need to do so please make sure you shut down in one of the two ways below. To shut down the service using the interface please refer to the Shutdown and Restart section below, or you can use the following command as it may not be convenient to use the interface.

bash
kill -SIGUSR2 $(cat /data/usr/lamda.pid)

It may take up to ten seconds for the LAMDA service to exit completely, please do not execute this command more than once in a row.

Uninstalling server

LAMDA is very disciplined about its data planning and will never place random files on your system.
You can completely uninstall lamda with a few commands, before doing so, please follow the Shutdown LAMDA service above and wait at least 30 seconds to ensure that the service exits properly.

bash
# Delete LAMDA related directories
rm -rf /data/local/tmp/arm64-v8a /data/usr
# Reboot the device
reboot

Using LAMDA

Port 65000 on the device is the standard public port for this service and may need to be remembered, but in most cases you will not need to explicitly provide this port number.
You can also use the tools/ directory to list all devices and IPs on your network.
The following will always assume that the IP of the device is 192.168.0.2.

Remote Desktop

The remote desktop feature is designed for Chrome 95+ only, it does not support multiple access and is not guaranteed to be compatible with all browsers.

Open the link http://192.168.0.2:65000 in your browser to access the web remote desktop, where you can operate the device and execute commands through the root emulation terminal of the interface. If you specify the PEM certificate --certificate when starting the server, the remote desktop will require you to enter a password in order to continue accessing it, which you can find in the last line of the PEM certificate as a fixed 32-bit password.

You can also customise the video frame rate (fps), resolution scaling (res) and image quality of the Remote Desktop. Also, H.264 soft encoding is supported (in some cases less traffic and smoother, only with the latest version of Chrome). You can adjust this via the small gear in the top right corner of Remote Desktop, but please note that adjusting the above parameters will not necessarily have a positive effect, so please adjust according to the facts.

For a more user-friendly experience such as keyboard input, see the following section Wirelessly connecting to a WIFI ADB with built-in root privileges.
After completing adb connect to LAMDA, install Genymobile/scrcpy or barry-ran/QtScrcpy, see their documentation for details.

File Uploads

You can upload files/folders to the device by directly dragging files or directories to the terminal on the right from this page, multiple files or folders are supported at the same time, individual files must not exceed a maximum of 256MB, only up to 2k files can be uploaded at the same time, any files uploaded have permission 644, files will always be uploaded to the /data/local/tmp directory.

File downloads

LAMDA allows you to browse directories on your device and download files via your browser, simply open the link http://192.168.0.2:65000/fs/ in your browser (note the / at the end).

Mobile Proxy

There may be times when your app may experience errors under certain network conditions, or if you want to do some testing on the same network IP as your device.
LAMDA's tunnel2 feature, which allows you to use the device running LAMDA as an http network proxy, also inherits the power of LAMDA: you can be on nearly the same network as the device from anywhere. You can get a quick taste of this by using the curl command below, or you can set port 192.168.0.2 to 65000 as a proxy in Firefox Settings - Configure Proxy Manually and check Use this proxy for HTTPS too so that your Firefox will have the same outgoing IP as the device.

bash
# The default agent does not require any authentication, but when you start it with --certificate
# Then the login user name is lamda and the password is the same as the remote desktop login token
curl -x http://192.168.0.2:65000 https://httpbin.org/ip

Custom Proxy Configuration

If you want to use a mobile network (4G/5G) as a proxy outlet.

ini
# Append to properties.local configuration file
tunnel2.login=lamda
tunnel2.password=mypassword
#
# There are two configurable values for iface, i.e. wlan, rmnet, when the iface value is wlan
# will automatically detect the available wlan interfaces and select any one to make the request, when iface is rmnet
# will try to enable mobile data (even if WIFI is enabled) and send the request from the mobile network interface.
# When configured as rmnet/wlan but its interface has no network, the proxy will fail.
# When not configured, requests are made using the default network.
#tunnel2.iface=rmnet

If you want to be able to use the device as a proxy from any location, see the Making LAMDA connectable from any location section.

Connecting devices

Now, this will be accompanied by an introduction to the lamda library, before you start, make sure you have installed the client library correctly according to the Client installation section above.

It is recommended to go through the source code of the client in passing, not so much to understand it, but just so you can get an idea of what parameters are actually available.

python
from lamda.client import *

d = Device("192.168.0.2")
# If certificate is enabled on the server side please connect like this
d = Device("192.168.0.2", certificate="/path/to/lamda.pem")

Or, if you are familiar with uiautomator2, you can also use some of the automation functions via u2

python
import uiautomator2 as u2

# Note: Only about 70% of the major interfaces are compatible and the certificate option is not enabled on the server side
d = u2.connect("http://192.168.0.2:65000")

Or, just execute the command

bash
# Note that the DEVICE parameter is an IP, replace it with your own
python3 -m lamda.client -device 192.168.0.2
# You can then type the following directly into this shell

d in the following will always be assumed to be the d = Device("192.168.0.2") instance.

Let's start with a warm up

The following method will cause your phone to beep once, when there are a bunch of devices and you need to locate one of them, you can call this interface. (Requires the phone to be non-silent)

python
d.beep()

Setting up a system proxy

Only http and socks5 proxies are supported, IPv6 is not supported

Assuming you get a proxy from your service provider at http://123.123.123.123:8080
Just a few lines of code to allow tcp traffic on the device to pass through this proxy

python
profile = GproxyProfile()
profile.type = GproxyType.HTTP_CONNECT

# This option is optional if you need it or not
profile.drop_udp = True
profile.host = "123.123.123.123"
profile.port = 8080

d.start_gproxy(profile)

Detailed parameter configuration information

python
profile = GproxyProfile()
# socks5 proxy is GproxyType.SOCKS5
profile.type = GproxyType.HTTP_CONNECT
# If you need to redirect DNS queries to 114.114.114.114
# Note that this DNS is system global and all DNS issued by the system will be forwarded
# If you are co-locating with OpenVPN, do not set it to be the intranet DNS server for OpenVPN, as this may cause a complete disconnection
# Remove the nameserver configuration line and the system default DNS will be used
#
# Why this option is there: you can modify the dns domain resolution for some applications
profile.nameserver = "114.114.114.114"
profile.host = "Proxy server address"
profile.port = proxy server port

# If this proxy server requires login information (note: if it does not, comment out or delete the following two lines)
profile.password = "proxy server login password"
profile.login = "Proxy login user"

# socks5 mode supports udp proxies, but http proxies are not supported
# Since udp will not be proxied in most cases, it is a good idea to disable udp traffic
# Application/system UDP traffic will be blocked when drop_udp is True, default is False
profile.drop_udp = False

# Whether local traffic needs to *not* go through a proxy, if True, local traffic
# If True, local traffic such as 192.168.x.x 10.x.x.x etc. on the router's internal network will not go through the proxy, default is False
# Note: If udp_proxy is enabled, this option will not work for UDP traffic
profile.bypass_local_subnet = True

# Whether proxy udp traffic is required
# Note that the http proxy service does not support the proxy udp protocol, to enable this option you must use socks5 as a proxy server
# (GproxyType.SOCKS5), and the socks5 proxy server must be configured to enable udp proxy mode.
# A slightly more complex server configuration is required, to avoid pitfalls please refer to tools/socks5 for installation and configuration
# This setting will be ignored if you are using an http proxy or if the drop_udp option is True
profile.udp_proxy = False

# If you need to use a proxy only for a specific application (e.g. Android browser, remove these two lines if global)
app = d.application("com.android.browser")
profile.application.set(app)

# Cautions and tips.
# Running applications will not use the set proxy immediately after it is set
# because the tcp connection was established before the proxy was set
# So, you need to manually close the application and start it before it will establish a connection via the proxy
# That is, if you are doing man-in-the-middle traffic analysis, then after setting up the proxy
# You'll need to close the app and reopen it before you see the app's requests
#
# Note: Local DNS traffic will always not go through the proxy

# Start the proxy
d.start_gproxy(profile)
# Shut down the proxy
d.stop_gproxy()

Quickly setup a socks5 proxy

LAMDA provides an out-of-the-box socks5 proxy service docker with udp support in tools/, go to tools/socks5 for README.en.md.

Automatically connect to the proxy when LAMDA starts

Copy the following configuration and change the relevant configuration to your proxy information

txt
gproxy.enable=true
gproxy.type=http-connect
gproxy.host=123.333.333.333
gproxy.port=8080
gproxy.password=
gproxy.login=

Append or write this to properties.local and restart LAMDA.

Installing the man-in-the-middle certificate

It is recommended that you use or refer to the startmitm.py, globalmitm tool in the tools/ directory, which is already wrapped, and the interface described here.

Please make sure you have the certificates from fiddler, mitmproxy and for mitmproxy
For mitmproxy, the certificate given to you is in pem format as in the example below. For fiddler, it may be in crt format, so just provide the file path
as a parameter and you don't need to care about any conversion/filename issues.

To avoid wasting unnecessary time, we recommend using mitmproxy here.
If you are using Charles etc, I cannot guarantee that you will be able to set it up in one go.
As these applications are more complex to configure and you may need to understand the various proxy types in order to configure the SSL broker correctly.
If you must use it, it is recommended that you use Charles' socks5 as a proxy protocol.

Note: Android may not be supported 13

python
import os

# Splice the path to the mitmproxy-ca-cert.pem file
HOME = os.path.expanduser("~")
cert_path = os.path.join(HOME, ".mitmproxy", "mitmproxy-ca-cert.pem")
# Using mitmproxy as an example, install the certificate using the following code
d.install_ca_certificate(cert_path)

# Uninstall the certificate using the following code (it is not recommended to install and uninstall frequently if it does not change often)
d.uninstall_ca_certificate(cert_path)
# This certificate installation interface is generic, you can use it to install any certificate that your application requires you to install
# You can also use it to install certificates that Fiddler/Charles asks you to install
# Just provide the file path

Next, look at the Set up http/socks5 proxy section and just set the proxy to the address that the middleman application is listening to.
If no traffic is intercepted after following the process please attend the Special Note in the section on setting up proxies.

Setting up OpenVPN

This OpenVPN only supports login with credentials and can coexist with http/socks5 proxies.
Note that this feature only contains the main functionality of OpenVPN, other than the DNS configuration, no other configuration information pushed by the server can be applied at this time.
These include, but are not limited to, PAC proxy, http proxy configuration, etc. To save you the trouble of installing the service, the
LAMDA provides an out-of-the-box OpenVPN docker image which has scripts to generate the configuration below, please read on.

It is highly recommended to use the OpenVPN docker installation in tools and generate the following connection configuration.

python
profile = OpenVPNProfile()

# Whether to have a global VPN or not, False only routes server-side pushes to specific segments
profile.all_traffic = True
# Server-side open connection protocol (or OpenVPNProto.TCP)
profile.proto = OpenVPNProto.UDP
profile.host = "OpenVPN server address"
profile.port = OpenVPN server port
# Server-side channel encryption method
profile.cipher = OpenVPNCipher.AES_256_GCM

profile.tls_encryption = OpenVPNEncryption.TLS_CRYPT
profile.tls_key_direction = OpenVPNKeyDirection.KEY_DIRECTION_NONE
profile.tls_key = """
-----BEGIN OpenVPN Static key V1-----
tls key / tls auth
-----END OpenVPN Static key V1-----
"""

profile.ca = """
-----BEGIN CERTIFICATE-----
Server-side configured ca certificate
-----END CERTIFICATE-----
"""

profile.cert = """
-----BEGIN CERTIFICATE-----
Client certificate
-----END CERTIFICATE-----
"""

profile.key = """
-----BEGIN PRIVATE KEY-----
Client private key
-----END PRIVATE KEY-----
"""

# Start OpenVPN
d.start_openvpn(profile)
# Shut down OpenVPN
d.stop_openvpn()

Setup an OpenVPN service quickly

LAMDA provides an out-of-the-box OpenVPN docker in tools/, go to tools/openvpn for README.en.md.

Automatically connect to the VPN when LAMDA starts

You can use the command provided in tools/openvpn to generate the properties.local configuration, please do not write it yourself.

Connecting to the built-in FRIDA

No need to read this section for non-reverse work

Before and after starting this framework, please do not start any frida-server again on your own, otherwise you risk crashing your system. You only need to use the built-in frida via the following code.

  1. connect via the code
python
# when using LAMDA
device = d.frida
device.enumerate_processes()

is equivalent to

python
# Just an example, please try to connect using the above method
manager = frida.get_device_manager()
device = manager.add_remote_device("192.168.0.2:65000")
device.enumerate_processes()
  1. Use from the command line

For all official frida command line tools you just need to add the parameter -H 192.168.0.2:65000.

bash
frida -H 192.168.0.2:65000 -f com.android.settings
# If you specify the certificate option when starting the server, please note that you also need to add the --certificate parameter here e.g.
frida -H 192.168.0.2:65000 -f com.android.settings --certificate /path/to/lamda.pem

For objection and r0capture etc, these third party tools may not follow the command line usage of the native frida tools exactly, if you need to use these third party tools you need to make sure that LAMDA is started without the -certificate parameter (encrypted transfer), as these tools may not have a parameter to pass the parameter for PEM certificates.

bash
# objection example connection method (-N -h 192.168.0.2 -p 65000)
objection -N -h 192.168.0.2 -p 65000 -g com.android.settings explore
bash
# r0capture Example connection method (-H 192.168.0.2:65000)
python3 r0capture.py -H 192.168.0.2:65000 -f com.some.package
bash
# jnitrace example connection method (-R 192.168.0.2:65000)
jnitrace -R 192.168.0.2:65000 -l libc.so com.some.package
bash
# frida-dexdump sample connection method (-H 192.168.0.2:65000)
frida-dexdump -H 192.168.0.2:65000 -p PID

For other third party tools not mentioned please check their usage yourself.

Exposing Java interfaces using FRIDA

This feature requires you to be proficient in writing frida scripts.

Please go to the tools directory to see how to use it.

This feature requires you to be able to write frida scripts. Please refer to the test-fridarpc.js file for the script used in the example, and note in particular that the parameters and return values of the functions defined by rpc.exports in the frida script can only be int/float/string/list/jsdict or any js serialisable by JSON. Assume that the device IP is 192.168.0.2.

Execute the following command to inject RPC into com.android.settings (watch for errors), the relevant files are in the tools directory

bash
python3 fridarpc.py -f test-fridarpc.js -a com.android.settings -d 192.168.0.2

Now that the interface is exposed, you can simply request http://192.168.0.2:65000/fridarpc/myRpcName/getMyString?args=["A", "B"] to get the results of the methods within the script, the link can also be opened with a browser, the interface supports both POST and GET, the argument list can also use multiple arguments at the same time, an empty list means no arguments, note that the maximum **serialised string in the args argument here cannot exceed ** 32KB.

The two string parameters A, B in the link are the positional parameters of the method getMyString(paramA, paramB) in the injected script.

Note that the parameters are provided in double quotes, please not hand typed or string spliced this parameter, always use json.dumps(["A", "B"])

call with requests

python
import json
import requests
url = "http://192.168.0.2:65000/fridarpc/myRpcName/getMyString"
# Request api
res = requests.post(url, data={"args": json.dumps(["A","B"])})
print (res.status_code, res.json()["result"])

#* Status code 200 Everything is fine
#* Status code 410 Script needs to be re-injected or script not injected (automatic re-injection is not currently supported)
#* Status code 500 Script or parameter exception
#* Status code 400 Parameter error

The format of the response result is fixed and can be opened in a browser for viewing. Again, in conjunction with the following section, you will be able to use the interface directly on the public network.

Using the built-in timed tasks

There is a built-in cron service for executing timed tasks so that you can run scripts on the device at regular intervals and all rules will be executed as root.

This feature requires you to be able to write crontab rules, so if you are not familiar with crontab, please find out for yourself first.

Now, open the web console or a ssh/adb shell connected to your device and execute the command crontab -e, you will enter edit mode, press i in the English input mode, then write the rule and press ESC, SHIFT + :, type wq and press enter to save it. Due to the Android hibernation mechanism, timed tasks may not run at the time you expect after the screen is rested and you may need to set the device to always on.

Some example rules

text
@reboot     echo Executed when the framework is started
0 */1 * * * echo Execute every hour
* * * * *   echo Execute every minute
0 8 * * *   echo Execute at eight o'clock every day

Make LAMDA connectable from any location

Sometimes you may be in a situation where your phone is at home and you are not how to use it.
Before you start, you may need to prepare a public server. For security reasons, the most conservative configuration is used here, and it will be explained at the end how to do the functions described in full.

Because of the public server, lamda has a number of ways to do this, using OpenVPN for a more elegant implementation. The most convenient way is to use the frp. tools folder, which provides docker images of the service and which can be used to generate the following configuration information with a single click, you can go to tools to see how to use it.

This service uses the more mature port forwarding program fatedier/frp, please explore how to configure the server side in this project. Note: Do not bind the forwarded port to a public address. Make sure that your public server has all unnecessary ports closed.
Here you are given the easiest and safest configuration to start the server directly using the following command.

First start frps on your public server by executing the following command (note that you may also need to configure a firewall)

bash
frps --token lamda --bind_addr 0.0.0.0 --bind_port 6009 --proxy_bind_addr 127.0.0.1 --allow_ports 10000-15000

then write properties.local

Copy the following configuration and change the server address to your server's public IP

txt
fwd.host=server address
fwd.port=6009
fwd.rport=12345
fwd.token=lamda
fwd.protocol=tcp
fwd.enable=true

Append or write to properties.local, restart LAMDA and you're done.

How to use lamda with the above forwarding (needs to be used on the same public server where frps is deployed, as we have bound the forwarding port to 127.0.0.1)

python
from lamda.client import *
# Port is rport as above
d = Device("127.0.0.1", port=12345)
# The remote desktop can be accessed by opening a browser at http://127.0.0.1:12345
# Any remaining interface calls are implemented uniformly, no changes are needed

How to bulk forward without rewriting the rport configuration each time

If you need to set up multiple machines at once and don't care which port each machine is bound to
you can change the fwd.rport value in the above configuration to 0, which will cause your devices to be bound to a random port range of 10000-15000.
You can locate the corresponding port for device forwarding bindings by rotating the port range later.

I just want to be able to connect to the device from anywhere

Firstly, this is not recommended for security reasons, but if you do need to use this, it is recommended that you use OpenVPN to access the device on the same network segment as your computer.
If you are still prepared to use the frp method above for arbitrary access, first ensure that the LAMDA service is started with PEM certificates and change the --proxy_bind_addr 127.0.0.1 to --proxy_bind_addr 127.0.0.1 when starting the frps command.
to --proxy_bind_addr 0.0.0.0, which will cause port 12345 to bind directly to the public network. If you start lamda without the PEM certificate, anyone will be able to access it, which is very, very dangerous.
The second thing to note is that web remote desktop traffic is always http, and if someone is manning the middleman between you and the server communication, your login credentials could be stolen. Of course, this is not a problem if the web desktop is not used in the meantime.

Read and write system properties

set/read system properties

python
## Get the value of ro.secure
d.getprop("ro.secure")

# Set the value of ro.secure
d.setprop("ro.secure", "0")

Read and write system settings

Set/read Android system settings

python
settings = d.stub("Settings")

# If you are confused by strings such as screen_brightness below, please see the following documentation. Some constants
# may not be compatible in different versions of Android, as well as some vendors will have custom variables that need to be noted.

# https://developer.android.com/reference/android/provider/Settings.System
# https://developer.android.com/reference/android/provider/Settings.Secure
# https://developer.android.com/reference/android/provider/Settings.Global

# You can set the brightness of the system screen to manual using the following code
settings.put_system("screen_brightness_mode", "0")

# Example: Get and modify the screen brightness to 5 (0-255)
settings.get_system("screen_brightness")
settings.put_system("screen_brightness", "5")

# Example: Turn off developer options
settings.get_global("development_settings_enabled")
settings.put_global("development_settings_enabled", "0")

# Example
settings.get_secure("screensaver_enabled")
settings.put_secure("screensaver_enabled", "0)

Get device running status

python
status = d.stub("Status")

## Get the device boot time
status.get_boot_time()

# Get the device's disk usage
status.get_disk_usage(mountpoint="/data")

# Get battery information
status.get_battery_info()
# Get CPU usage
status.get_cpu_info()
# Get overall disk reads and writes
status.get_overall_disk_io_info()
# Get the user data disk reads and writes (userdata)
status.get_userdata_disk_io_info()
# Get the overall network send/receive situation
status.get_overall_net_io_info()
# Get the network traffic of the wlan0 interface
status.get_net_io_info("wlan0")
# Get memory usage
status.get_mem_info()

Execute commands on the device

Execute shell scripts/commands in the background of the device, in the foreground

python
## Execute foreground scripts (scripts with short execution times (within 0-10 seconds))
cmd = d.execute_script("whoami")
print (cmd.exitstatus)
print (cmd.stdout)
print (cmd.stderr)

# Execute background scripts (scripts that take a long time to execute)
# For background scripts, due to the possibility of users writing dead-loop scripts with infinite output that fill up memory, etc.
# The result of the execution is not known at the moment
ID = d.execute_background_script("sleep 100; exit 0;")
# Check if the background script has finished
d.is_background_script_finished(ID)
# Force the background script to end
d.kill_background_script(ID)

Make system debuggable

If you need to use dynamic analysis with JEB, IDA, etc., you may need to set this flag to do so, but of course it is built in and you can do this without having to permanently modify ro.debuggable.
But remember, you don't always need to call this interface, only use it if you see any articles/tutorials that tell you to modify ro.debuggable.

Note: After a successful call to this interface, the system will automatically reboot and you may still have to wait a while for the framework to resume as it did when you first started it!

use with caution, this feature may be unstable and may be removed at any time

python
debug = d.stub("Debug")

r = debug.set_debuggable()
print (r)

Start IDA Debugging Service

IDA 7.5 server is built in

python
debug = d.stub("Debug")

## Start IDA 32 server (port is customizable)
debug.start_ida(port=22032)
# Check if it is started
debug.is_ida_running()
# Shut down the IDA 32 server
debug.stop_ida()
# If you are debugging a 64-bit application, replace ida in the method name with ida64
# For example
debug.start_ida64(port=22064)
#debug.start_ida64(port=22064)
# If you need to customize the ida-server environment variable e.g. IDA_LIBC_PATH (also applies to start_ida)
debug.start_ida64(port=22064, IDA_LIBC_PATH="/apex/com.android.runtime/lib64/bionic/libc.so")
# Use start_ida when the target application you are debugging is 32-bit
# otherwise use start_ida64
# start_ida64 will be invalid when your device system is a 32-bit platform

Wireless connection built-in WIFI ADB

This ADB is not a full-featured adb, it only supports shell, pull, push, forward, reverse and other common functions.
With this feature you will be able to connect to the highest privileged adb without having to enable developer mode.

Note: jdwp debugging-related features are unique and conflict with the system's built-in features, so this adb is not currently supported.

python
# LAMDA's built-in adb service is completely independent of the adb service provided by the system itself
# So before you can use it, you need to install your adb public key on the device by calling the following interface manually
# Otherwise, connecting directly will show that you are not authorised (the secret key authorised in the system settings developer mode is not common to the built-in adb)
#
# adb_pubkey.py in the tools directory encapsulates the installation process for the following interface
# You can use this script to allow local connections with a single click, see its README, the following code is for illustrative purposes only
#
# The secret key file is located on your computer at ~/.android or C:\\Users\xxxx\.android and is called adbkey.pub
# If this file does not exist but the file adbkey exists, switch to that directory and execute the command
# adb pubkey adbkey >adbkey.pub to generate adbkey.pub
# adb pubkey
# Then use python code to stitch together the generated adbkey.pub path
import os
keypath = os.path.join("~", ".android", "adbkey.pub")
abs_keypath = os.path.expanduser(keypath)
print (abs_keypath)
#
# Then install this adbkey.pub to LAMDA
d.install_adb_pubkey(abs_keypath)
# This will allow you to connect to the built-in adb
# Connect to the device via the command adb connect 192.168.0.2:65000
# You can think of it exactly as a WIFI ADB
#
# Or if you need to remove the public key from the LAMDA built-in adb
d.uninstall_adb_pubkey(abs_keypath)

File manipulation

Upload files to or download files from a device (large files are supported)

python
## Download a file locally
d.download_file("/verity_key", "local file to write to")

# Download file to memory/opened file
from io import BytesIO
fd = BytesIO()
d.download_fd("/verity_key", fd)
print (fd.getvalue())

# Note that the file being written to must be opened in w+b mode
fd = open("Local file written to", "wb")
d.download_fd("/verity_key", fd)

# Upload a file to the device
d.upload_file("local_file_path.txt", "/data/local/tmp/upload_file_to_device.txt")

# Upload file from memory/opened file
from io import BytesIO
d.upload_fd(BytesIO(b "fileContent"), "/data/local/tmp/uploaded_file_to_device.txt")

# Note that the file must be opened using rb mode
fd = open("myfile.txt", "rb")
d.upload_fd(fd, "/data/local/tmp/upload_file_to_device.txt")

# Delete the file on the device
d.delete_file("/data/local/tmp/file.txt")

# Modify file permissions on the device
d.file_chmod("/data/local/tmp/files.txt", mode=0o777)

# Get information about the files on the device
d.file_stat("/data/local/tmp/files.txt")

Shutdown and restart

python
## Shutdown the system (equal to shutting down the computer)
d.shutdown()
# Reboot the system (equal to reboot)
d.reboot()

# Shut down the LAMDA service running on the device
d.exit()

Application Operations

List the IDs of all applications installed on the system

python
d.enumerate_all_pkg_names()

List all running applications on the device

python
d.enumerate_running_processes()

Get the applications currently in the foreground

python
d.current_application()

# Equivalent to
d.application(d.current_application().applicationId)

# Get the current foreground activity
d.current_application().activity

Launching, getting Activity

python
# import FLAG_ACTIVITY* constant definition
from lamda.const import *

# Get the 5 most recent activities in the system (up to 12)
activities = d.get_last_activities(count=5)
print (activities)

# You can just replay the last activity (note that not all activities can be replayed)
activity = activities[-1]
print (activity)
d.start_activity(**activity)

# Assemble activity information manually
# Only boolean, int, short, long, double, float and string types are supported for appending data
d.start_activity(action="***", category="***", component="***",
                 extras={"boolean": False, "int": 1, "string": "act", "float": 1.123},
                 flags=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TASK,
                 data="***", debug=False)

# Please refer to the documentation for the definition of flags
# https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_BROUGHT_TO_FRONT
# flags and debug parameters are not required, they are just one more possibility

# Call 10000 Customer Service
d.start_activity(action="android.intent.action.CALL", data="tel:10000")

# debug parameter stands for: whether to start the activity in debug mode
# If you know about Waiting for debugger, then it might be useful for you
# You can start an application in debug mode like this (your device or app needs to be debuggable)
la = d.application("com.android.settings").query_launch_activity()
d.start_activity(**la, debug=True)

# e.g. launch the settings APP (which of course is almost equivalent to launching the app directly)
d.start_activity(action="android.intent.action.MAIN",
                 category="android.intent.category.LAUNCHER",
                 component="com.android.settings/.Settings")

# For example, to access the certificate settings
d.start_activity(action="com.android.settings.TRUSTED_CREDENTIALS")

Grant/Revoke APP Permissions

Note that you should set permissions when the APP is not launched, calling it when the APP requests permissions will not have the effect of helping you click allow.

python
app = d.application("com.my.app")

# Import the PERMISSION_READ_PHONE_STATE constant (version > 3.90)
from lamda.const import *

# Get all permissions for the app
app.permissions()
# Grant READ_PHONE_STATE permissions
app.grant(PERMISSION_READ_PHONE_STATE, mode=GrantType.GRANT_ALLOW)
# Deny the READ_PHONE_STATE permission
app.grant(PERMISSION_READ_PHONE_STATE, mode=GrantType.GRANT_DENY)
# Check if permission has been granted
app.is_permission_granted(PERMISSION_READ_PHONE_STATE)
# Revoke a permission that has been granted
app.revoke(PERMISSION_READ_PHONE_STATE)

clear application cache, reset application

python
# Delete the application's cached data
app = d.application("com.my.app")
app.delete_cache()
# Reset the application data
app.reset_data()

start/stop the app

python
app = d.application("com.my.app")

# Start the application
app.start()
# Check if the application is running in the foreground
app.is_foreground()
# Close the app
app.stop()

Other

python
app = d.application("com.my.app")
# Get information about the application
app.info()

# Check if the application is installed
app.is_installed()
# Uninstall the application
app.uninstall()

# Query the app's launch Activity (entry activity)
app.query_launch_activity()

# Enable the application
app.enable()
# Disable the application
app.disable()

WIFI operation

Currently some of the functions of WIFI operation are not implemented due to possible device anomalies, only some of the implemented functions are described

python
wifi = d.stub("Wifi")

# Get wifi bssid,ssid ip and other related information
wifi.status()

# Get all bssid's in the blacklist
wifi.blacklist_get_all()

# Add the bssid to the blacklist (it will not be shown in the wifi list)
wifi.blacklist_add("3c:06:aa:8a:55:66")

# Clear all blacklists
wifi.blacklist_clear()

# Perform a wifi scan
wifi.scan()

# Get the results of a neighborhood wifi scan
wifi.scan_results()

# Get the mac address of the current wifi
wifi.get_mac_addr()

# Get wifi signal strength, link rate
wifi.signal_poll()

Basic UI Operations

Get device information

python
d.device_info()

screen-off/bright screen related

python
# Sleep
d.sleep()
# screen on
d.wake_up()
# Whether the screen is lit or not
d.is_screen_on()
# Whether the screen is locked or not
d.is_screen_locked()

clipboard

python
d.set_clipboard("Clipboard contents")

# Get clipboard contents (not supported on Android 10+)
d.get_clipboard()

physical keys

python
# This method works with the following 17 keys
# KEY_BACK
# KEY_CAMERA
# KEY_CENTER
# KEY_DELETE
# KEY_DOWN
# KEY_ENTER
# KEY_HOME
# KEY_LEFT
# KEY_MENU
# KEY_POWER
# KEY_RECENT
# KEY_RIGHT
# KEY_SEARCH
# KEY_UP
# KEY_VOLUME_DOWN
# KEY_VOLUME_MUTE
# KEY_VOLUME_UP
d.press_key(Keys.KEY_BACK)

# Also to allow more keys to be used, this method can be used
d.press_keycode(KeyCodes.KEYCODE_CALL)
# The KEYCODEs that can be used can be found in this document
# https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0

Screenshot

python
quality = 60 # screenshot quality, defaults to full quality
d.screenshot(quality).save("screenshot.png")
# Screenshot a specific area of the screen
# Definitions of Bound parameters top, left etc.

# top: count top pixels down from the top of the screen
# bottom: count down from the top of the screen to the bottom pixel
# left: count left pixels from the left side of the screen to the right
# right: count right pixels to the left of the screen

# top is always smaller than bottom and left is always smaller than right in normal circumstances
bound = Bound(top=50, bottom=80, left=50, right=80)
d.screenshot(quality, bound=bound).save("partial.png")

Click on a point on the screen

python
d.click(Point(x=100, y=100))

click on point A and drag it to point B

python
A = Point(x=100, y=100)
B = Point(x=500, y=500)

d.drag(A, B)

slide from point A to point B

python
A = Point(x=100, y=100)
B = Point(x=500, y=500)

d.swipe(A, B)

Slightly more complex multipoint swipe (nine-box unlock)

python
p1 = Point(x=100, y=100)
p2 = Point(x=500, y=500)
p3 = Point(x=200, y=200)

# Swipe from point P1 to point P2 and then to point P3, any point
d.swipe_points(p1, p2, p3)

open the notification bar/shortcut settings bar

python
d.open_notification()
d.open_quick_settings()

get page layout description XML

python
d.dump_window_hierarchy().getvalue()

wait for the interface layout to stop refreshing

python
# The unit is milliseconds, 5*1000 means 5 seconds
d.wait_for_idle(5*1000)

get last toast

python
d.get_last_toast()

Advanced UI Operations

Selector

To view the layout of the interface, first you need to open the web remote desktop of the device. You can then click on the eye icon in the top right hand corner of the remote desktop to access it, you will no longer be able to swipe the left hand side of the screen, you can click on the dotted box on the screen to view information about the corresponding element, you can use some of these properties as parameters for the Selector.
Clicking on the eye icon again will close the layout view. The layout view will not be refreshed as the page changes, it will always be the layout of the screen at the moment you press the shortcut key, if you need to refresh the layout please press the shortcut key CTRL+R manually.

Normally we would only use resourceId, clickable, text, description as parameters.
If the element has a normal resourceId, it will be used as Selector in preference, i.e.: Selector(resourceId="com.android.systemui:id/mobile_signal_single".
For no resourceId, its text will be used, i.e.: Selector(text="click to enter"), or more vaguely Selector(textContains="click")
Description is the same as text, but description is used less often.

Of course, Selector can take more than one parameter, and you can make other combinations, e.g. Selector(text="click through", clickable=True)

Note: it is rare to use Selector() directly; in most cases, d() is used.

All common match parameters.

text
textText              Exact match
textContains          text contains match
textStartsWith        textStarts
className             class name match
description           Description Exact match
descriptionContains   descriptionIncludes match
descriptionStartsWith descriptionStartsMatch
clickable             can be clicked
longClickable         can be long pressed
scrollable            scrollable
resourceId            Resource ID match

For the most part, you won't use Selector directly, but indirect use is ubiquitous.

Element manipulation

The above has all covered how to coordinate clicks on something as arbitrary as this, now it's on to how to manipulate fixed target elements. First, you need to know how to select the element.

python
# Select the element on the interface that contains the text App under test
element = d(textContains="APP under test")
# Of course, you don't have to assign to element this way, you can also just use d(textContains="APP under test")

OK, now you know how to get the element, but of course, it's not getting it at this point, it just means that you want to manipulate the element in the current interface, so here goes.

python
# We now assume that the app under test in the interface is the name of the app under test icon on the phone (the name below the icon).
element = d(textContains="APP under test")
# Whether or not the element exists
element.exists()
# Click on the element, if it does not exist an exception will be thrown
# COR_CENTER means click on the centre of the element, you can look at the COR_CENTER definition for other clickable positions
element.click(corner=Corner.COR_CENTER)

# Click on the element, no exception will be thrown if it doesn't exist
element.click_exists(corner=Corner.COR_CENTER)

# Long click on the element, not present throws an exception
element.long_click(corner=Corner.COR_CENTER)

# Get information about the element
element.info()

# Get the centroid of the element
element.info().bounds.center()

# Get the upper left point of the element
element.info().bounds.corner("top-left")

# Get the height of the element
element.info().bounds.height

# Get the width of the element
element.info().bounds.width

# Get the number of elements
element.count()

# Wait for an element to appear, up to 10 seconds
element.wait_for_exists(10*1000)

# Wait for elements to disappear, up to 10 seconds
element.wait_until_gone(10*1000)

# Get a screenshot of the element (not full screen, just the element)
# quality is the quality of the screenshot 1 - 100
element.screenshot(quality=60)

# Drag and drop this APP into the shopping folder (modify as appropriate)
element.drag_to(Selector(text="shopping"))

#########
# Find sibling or sub-level elements
#########
# Sometimes there are duplicate elements or elements with no obvious features that are difficult to locate
# In this case you can narrow the search by looking for sub/sib elements
# Sub-level elements, e.g. a chat login box with an input box that is a sub-level element of the login box
# sibling elements, e.g. a chat input box where the username and password boxes are siblings (in the normal case)
form = d(resourceId="login_form")
form.child(index=1)
# This will fetch the element under login_form with index 0
form.child(index=1).sibling()
# You can also do this to find the password retrieval button at the same level as login_form
# (you don't need to do this if you can already tell by the string, this is just a demonstration)
form.sibling(textContains="Retrieve password")
# They are an element in their own right, you can do anything with them as an element


############################
# Now the element changes its meaning and becomes a selected input box
############################

# Example: Enter the app under test in the search box on the OnePlus search app screen

# Note that you should not type directly into what appears to be an input box, it may not be entered
# Some input boxes need to be clicked once to get to the real input box, so use the resource ID of the real input box
element = d(resourceId="net.oneplus.launcher:id/search_all_apps")
element.set_text("App under test")

# Get the content of the input
element.get_text()

# Clear the text you just entered
element.clear_text_field()

# Combine with a click search to complete a human-like search operation.


# Sliding operations (sliding the list up and down to turn the page)
# Note that these operations do not guarantee precision, and none of the following methods normally require a selector.
# But you can add your own selectors as appropriate

# Slide up, step is adjustable, the more you do, the slower it will be, better for precision sliding
d().swipe(direction=Direction.DIR_UP, step=32)
# Other swipe directions.
#DIR_UP Swipe up
#DIR_LEFT Swipe left
#DIR_DOWN slide down
#DIR_RIGHT Swipe to the right

#########
#fling: flinging, the act of a normal person swiping the screen, faster
#########
# FLINGING from top down
d().fling_from_top_to_bottom()
# from bottom to top
d().fling_from_bottom_to_top()
# left to right
d().fling_from_left_to_right()
# right to left
d().fling_from_right_to_left()

# Other, keep sliding down/up left/right until it reaches the end
# Because it is not always possible to slide to the end or detect a slide to the end
# So the max_swipes parameter is required
d().fling_from_top_to_bottom_to_end(max_swipes=32)
d().fling_from_bottom_to_top_to_end(max_swipes=32)
d().fling_from_left_to_right_to_end(max_swipes=32)
d().fling_from_right_to_left_to_end(max_swipes=32)

#########
# scroll: more mechanical sliding
#########
step = 60
max_swipes = 32
# scroll from top to bottom step step
d().scroll_from_top_to_bottom(step)
# scroll from bottom to top step
d().scroll_from_bottom_to_top(step)
# Scroll from left to right step
d().scroll_from_left_to_right(step)
# slide from right to left step
d().scroll_from_right_to_left(step)

# Other, keep sliding down/up left/right until you reach the end
# Same as above fling description
d().scroll_from_top_to_bottom_to_end(max_swipes, step)
d().scroll_from_bottom_to_top_to_end(max_swipes, step)
d().scroll_from_left_to_right_to_end(max_swipes, step)
d().scroll_from_right_to_left_to_end(max_swipes, step)

Monitor

Monitors are used to listen for changes to the interface and perform set actions (clicking on elements or keystrokes) when conditions are met, this can have an impact on performance or when manual intervention is required so please use with caution, it is not enabled by default.

python
# Start the watcher loop
d.set_watcher_loop_enabled(True)

# Get if the watcher is started
d.get_watcher_loop_enabled()

# Remove all watchers applied to the system, it is recommended that this is done before each use to prevent the normal processing process from being affected by the previous task registration
d.remove_all_watchers()

# Get a list of all applied watchers in the system
d.get_applied_watchers()

# Remove a watcher completely
d.remove_watcher(name)

# Apply the watcher to the system (this watcher will take effect when watcher_loop is started)
d.set_watcher_enabled(name, True)
# Disable the application
d.set_watcher_enabled(name, False)

# Get whether this watcher is enabled or not
d.get_watcher_enabled(name)

watch how many times an element appears in the system interface

python
# Do some pre-test cleanup, though of course this is not required for every register
# Just to make sure the testing process is not interrupted
d.remove_all_watchers()
d.set_watcher_loop_enabled(True)

# The number of times the application watcher interface appears to be good
# The second argument is an array that can be given to multiple Selectors to indicate that the conditions are all met before they are logged
# But it is not recommended to have more than three
d.register_none_op_watcher("RecordElementAppearTimes", [Selector(textContains="good")])
d.set_watcher_enabled("RecordElementAppearTimes", True)

# ... Do the operations that satisfy the condition

# Get the number of times the record was appended
d.get_watcher_triggered_count("RecordElementAppearTimes")

# The number of times to reset the record
d.reset_watcher_triggered_count("RecordElementAppearTimes")

# Remove
d.remove_watcher("RecordElementAppearTimes")

click on an element when a matching element appears in the interface

python
# Do some pre-test cleanup, though of course this is not required for every register
# Just to make sure that the testing process is not interrupted
d.remove_all_watchers()
d.set_watcher_loop_enabled(True)

# Example is to automatically click agree when the user agreement appears after the app is launched
# The second parameter is an array that can be given to multiple Selectors to indicate that the conditions are all met before the click is made
# But it is not recommended to have more than three
d.register_click_target_selector_watcher("ClickAcceptWhenShowAggrement", [Selector(textContains="User Agreement")],
                                         Selector(textContains="Agree", clickable=True))
d.set_watcher_enabled("ClickAcceptWhenShowAggrement", True)

# ... Do the action that satisfies the condition

# Remove
d.remove_watcher("ClickAcceptWhenShowAggrement")

Click the physical button when the matching element appears in the interface

python
# Do some pre-test cleanup, though of course this is not required for every register
# Just to make sure that the testing process is not interrupted
d.remove_all_watchers()
d.set_watcher_loop_enabled(True)

# Example is to return to the start screen by pressing the HOME key when the Personal Centre is present in the interface
# The second argument is an array that can be given to multiple Selectors to indicate that the conditions are all met before the click is made
# But it is not recommended to have more than three
d.register_press_key_watcher("PressBackWhenHomePageShows", [Selector(textContains="Personal Centre")], Keys.KEY_HOME)
d.set_watcher_enabled("PressBackWhenHomePageShows", True)

# ... Do the actions that satisfy the condition

# Remove
d.remove_watcher("PressBackWhenHomePageShows")

Interface Lock

The basic functionality here allows you to lock the interface for use only by the current Device instance.

python
# # Get a lock, this will be automatically released after 60 seconds and other clients will have access to the lock, you can change this time
# However, if you change it too high because the exception script quits, you will almost never be able to connect to the device and you may need to do a reboot
# This acquire_lock interface can be reentered and is equivalent to _refresh_lock when reentered, it is recommended to call it only once
d._acquire_lock(releaseTime=60)
# Refresh the lock and set the lock expiry time to this leaseTime after each call
# Make periodic calls to keep the device locked
d._refresh_lock(leaseTime=60)
# Release the lock, other clients will have access to it
d._release_lock()

Using the internal terminal

The internal terminal is the terminal you connect to via the web remote desktop or ssh/internal adb, which has some commands and Python modules built in.
You can perform operations or run Python code directly in the terminal, or even perform self-control directly from within the terminal.

Now, assuming that you have opened the web remote desktop, you should already see a linux terminal on the page.

Execute the command cd to switch to your home directory (by default /data/usr), which is your workspace and where you can store your files. The terminal supports command completion but not parameter completion. You can also enter partial commands in the terminal and subsequently fill in the history of commands automatically with the up and down keys. Also, a number of command aliases are provided internally, these and their functions are listed below.

text
l         = command ls
ll        = command ls -l
la        = command ls -la
py        = the command python
..        = switch to parent directory
...       = switch to the parent directory of the parent directory
p         = switch to the previous directory
t         = switch to /data/local/tmp

Some other useful commands.

text
* python           (Python)
* strace           (syscall trace)
* ltrace           (libcall trace)
* curl             (cURL)
* fsmon            (file access monitoring)
* stunnel          (traffic encryption)
* redir            (port forwarding)
* scapy            (traffic analysis)
* iperf3           (network performance testing)
* nano             (file editor)
* vi               (file editor)
* ncdu             (find disk file occupancy)
* socat            (network tool)
* sqlite3          (reads SQLite databases, supports cipher)
* tcpdump          (traffic analysis)
* busybox          (command collection)
* MemDumper        (https://github.com/kp7742/MemDumper)
* frida            (frida-tools)
* frida-ps         (frida-tools)
* frida-trace      (frida-tools)
* frida-ls-devices (frida-tools)
* frida-discover   (frida-tools)
* frida-kill       (frida-tools)
* frida-apk        (frida-tools)
* frida-create     (frida-tools)
* frida-join       (frida-tools)
* and other basic linux commands

Python also has some built-in three-party libraries, note that additional libraries cannot be installed via the PIP.

text
* lamda            (itself)
* capstone         (disassembly engine)
* keystone_engine  (assembly engine)
* unicorn          (CPU emulation engine)
* lief             (binary program parsing)
* lxml             (xml/html parsing)
* redis            (redis client)
* tornado          (web framework)
* pyOpenSSL        (OpenSSL)
* requests         (requests)
* scapy            (Traffic analysis)
* frida            (frida)
* pyaxmlparser     (APK parse)
* xmltodict        (xml to dict)
* msgpack_python   (msgpack)

How to use these commands or libraries will not be covered here.

Tools and tutorials

A copy of the instructions for use is available in each of these folders.

One-click middleman

Open tools/README.en.md to view.

International proxies for man-in-the-middle

Open tools/README.en.md for viewing.

Installing the ADB public key

Open tools/README.en.md for viewing.

OpenVPN service

Open tools/README.en.md for viewing.

SOCKS5 service

Open tools/README.en.md for viewing.

Port Forwarding Service

Open tools/README.en.md for viewing.

Injecting Frida RPC scripts

Open tools/README.en.md to view.

Generate encrypted connection certificate

Open tools/README.en.md to view.

List intranet devices

Open tools/README.en.md to view.

If you still have questions, join the community discussion: t.me/lamda_dev | gitter.im/lamda-dev