How do I run "adb shell" commands in a terminal emulator locally on an Android device?

user2571203 picture user2571203 · Oct 29, 2017 · Viewed 45.9k times · Source

From a shell on my PC, I can run adb shell cmd package list packages, and get a list of all installed packages. I would like to run this and similar commands locally on my Android phone (Nexus 6P) in a terminal emulator (currently using Termux).

If I open the same shell with /system/bin/sh, and then try to run /system/bin/cmd package list packages, nothing happens (no errors, just outputs nothing and reloads the prompt).

If I run /system/bin/cmd -l the list of options appears as expected. $PATH and $LD_LIBRARY_PATH are the same in both environments. One major difference is that echo $USER returns "shell" from adb shell, but returns my local username from /system/bin/sh launched from Termux.

Is there any way to replicate the behavior of commands run from adb shell in a terminal emulator locally on Android?

Edit: My device is rooted, and I am OK with root only solutions.

Answer

Six picture Six · Dec 4, 2017

I don't have a rooted Nougat device handy, but something like the following may be a close enough approximation to adb shell (assuming you are using SuperSU):

env -i USER=shell "$(PATH=/system/xbin:/system/bin:/su/bin:/sbin:/magisk/.core/bin which su)" shell --context u:r:shell:s0 --shell /system/bin/sh --command COMMAND

I (very briefly) tested it from Termux on a rooted Marshmallow device.

To elaborate:

  • the -i flag is used to start with an empty environment
  • USER=shell isn't specifically required, but for some reason su refuses to run with a completely empty environment
  • $(PATH=/system/xbin:/system/bin:/su/bin:/sbin:/magisk/.core/bin which su) points to the full path of the su binary on your device and can be hardcoded if you prefer
  • shell instructs the su binary to login as the shell user (the same as adb shell)
  • --context u:r:shell:s0 sets the appropriate SELinux context
  • --shell /system/bin/sh instructs SuperSU to use the system shell rather than it's own sush shell

Another option would be to actually run adb from the device, connecting to itself over TCP. If you need some functionality that is only available via adb (e.g. in my case it was adb forward) then this may be your only option. Unfortunately this isn't particularly convenient.

I wasn't able to find success with any publicly available adb binaries, so I build it myself with a few minor changes. You can see the sources I used and the changes I made at https://github.com/shakalaca/fastboot-adb-android and https://github.com/brbsix/fastboot-adb-android, respectively.

Once you have adb installed, here's an abbreviated list of commands I used to connect to the device:

# Add iptables rules to block external connections to port 9999'
su root iptables -N adbd
su root iptables -A adbd -i lo -p tcp -m tcp --dport 9999 -j ACCEPT
su root iptables -A adbd -p tcp -m tcp --dport 9999 -j DROP
su root iptables -A INPUT -j adbd

# Necessary in order to display authorization prompt
su shell setprop ro.debuggable 1

su shell setprop service.adb.tcp.port 9999

su root start adbd

adb connect 127.0.0.1:9999

adb wait-for-local-device

To shut down:

adb kill-server
su root stop adbd
su shell setprop ro.debuggable 0
su shell setprop service.adb.tcp.port 0
su root iptables -D INPUT -j adbd
su root iptables -F adbd
su root iptables -X adbd