在 Android 手机上的使用 stunnel (不需要 root )

☆ Android stunnel

https://github.com/comp500/SSLSocks.git 这个项目可以使用 android 版本的 stunnel, 其实就是调用 https://www.stunnel.org/downloads/stunnel-5.57-android.zip

具体配置和使用命令行差距不大,参考:
https://github.com/comp500/SSLSocks/blob/master/README.md
https://hamy.io/post/0011/how-to-run-stunnel-on-your-android-device/

☆ 设置全局代理

  1. 在 wifi 连接的情况

打开wifi 列表 -> 长按连接的 wifi -> 点击修改 -> 高级选项 -> 填写代理相关信息

  1. 使用 adb shell 执行命令
1
adb shell settings put global http_proxy 192.168.xx.xxx:8888
  1. Android Proxy Toggle

https://github.com/theappbusiness/android-proxy-toggle.git

要正常使用 app 需要用 adb shell 连接上设置相应的权限:

1
adb shell pm grant com.kinandcarta.create.proxytoggle android.permission.WRITE_SECURE_SETTINGS

OnePlus 7T 升级到 Android 12 后,执行上面的命令将出错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
OnePlus7T:/ $ pm grant com.kinandcarta.create.proxytoggle android.permission.WRITE_SECURE_SETTINGS

Exception occurred while executing 'grant':
java.lang.SecurityException: grantRuntimePermission: Neither user 2000 nor current process has android.permission.GRANT_RUNTIME_PERMISSIONS.
at android.app.ContextImpl.enforce(ContextImpl.java:2187)
at android.app.ContextImpl.enforceCallingOrSelfPermission(ContextImpl.java:2215)
at com.android.server.pm.permission.PermissionManagerService.grantRuntimePermissionInternal(PermissionManagerService.java:1477)
at com.android.server.pm.permission.PermissionManagerService.grantRuntimePermission(PermissionManagerService.java:1459)
at android.permission.PermissionManager.grantRuntimePermission(PermissionManager.java:378)
at com.android.server.pm.PackageManagerShellCommand.runGrantRevokePermission(PackageManagerShellCommand.java:2419)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:260)
at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
at android.os.ShellCommand.exec(ShellCommand.java:38)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:25948)
at android.os.Binder.shellCommand(Binder.java:970)
at android.os.Binder.onTransact(Binder.java:854)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4818)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:8987)
at android.os.Binder.execTransactInternal(Binder.java:1226)
at android.os.Binder.execTransact(Binder.java:1163)

少数派的文章 在 ColorOS 上免 root 玩机,请先打开这个开关 中指出需要在开发者选项中
开启 禁止权限监控

开启后可以正常执行 pm grant 命令,执行成功后可以使用 dumpsys package <package name> 列出所有的权限。

1
2
3
4
5
6
7
dumpsys package com.kinandcarta.create.proxytoggle | grep permission

requested permissions:
android.permission.WRITE_SECURE_SETTINGS
android.permission.WRITE_SETTINGS
install permissions:
android.permission.WRITE_SECURE_SETTINGS: granted=true

恢复无代理

如果遇上一些异常情况,比如错误卸载了 Android Proxy Toggle, 可以使用下面命令去掉 Android 全局代理。

1
2
3
adb shell settings delete global http_proxy
adb shell settings delete global global_http_proxy_host
adb shell settings delete global global_http_proxy_port

关于恶意 Android 软件的那些事

近年来,Android手机平台上的恶意App呈不断上升的趋势,根据G DATA的数据仅2015年 第一季度发现了440267 种新的安卓恶意软件,也就是说,全球范围内每18秒就有一个新的恶意软件被发现。而天朝由于Google被封的原因,更是使恶意软件的传播更加猖獗。Google Play store 为了保证Android用户的安全做了大量的努力,和国内的一些第三方应用市场相比安全一些的。

重打包是Android App主流的传播方式之一,和以前单独的恶意App不同,重打包的软件 在合法正常的App中插入恶意代码,迷惑性极强,一般用户觉察不出什么不同。而重打包选取 的App也大多是非常流行的App,如愤怒的小鸟之类,这些都在地下市场隐秘的进行着。重打包 App然后上传第三方App应用市场,第三方市场把关不严格的话,这些插入了恶意代码的App 就可以下载安装了。

除了重打包短信,彩信也是Android 恶意App传播的主要方式,和以前QQ的消息尾巴类似发一些奇怪的话,后面加上短链接。短链接就是经过压缩的链接,没法一些看出原始的链接,点击后会自动下载恶意App。臭名昭著的“相册”就是通过这种方式传播,短信的内容是:小明,你还记得这些照片吗? t.cn/xyz1 注意这里的名字小明是正确的,相册偷取了大量的手机通讯录,按照通讯录的名字来发送,迷惑性大大增强了。

还有值得大家注意的是,一定要警惕申请设备管理员权限的App,设备管理员可以做很多高权限的事,比如修改设备密码,擦掉数据等操作,Android手机的勒索软件就是修改了设备的密码,禁止手机主人进入手机,而进行勒索的,最近这类软件的数目增加很快,大家一定要注意。

上面说了很多Android恶意软件相关的内容,也给大家一些安全建议,提高了安全意识也就不容易中招了。

  1. 尽量在Google play 或者官方网站下载APP,不要第三方应用市场随意下载
  2. 不要随意点击不明链接
  3. 对于申请设备管理员的App,保持高度警惕

最后教大家一招急救,许多Android手机也有安全模式,安全模式只加载出厂应用,进入了安全模式,删除恶意软件,可能可以挽救你的手机 :)

在虚拟机中调试android 手机的方法

由于Google的源码是在ubuntu下编译的,Google官方提供了较为详细的编译说明,所以使用 了ubuntu 14.04 进行编译,编译完成后有一个问题,如何进行源码调试。(源代码在虚拟机中)

使用 virutalbox Extension Pack

首先的想法是直接使用中virutalBox 的USB 把手机给连到guest,折腾了一下比较麻烦, 首先需要安装virutalbox 的 Extension Pack,下载地址

http://download.virtualbox.org/virtualbox/5.1.18/Oracle_VM_VirtualBox_Extension_Pack-5.1.18-114002.vbox-extpack

管理->全局设定->扩展,选择下载的扩展包,安装。接下来这步很关键,上次就是这里没有搞定。

1
sudo usermod -a -G vboxusers <username>

执行命令后需要注销,重新登录。

VirtualBox 虚拟机设置 -> usb 添加需要接入 guest 的 usb 设备。

1
2
3
4
5
> lsudo lsusb  
Bus 001 Device 002: ID 18d1:4ee7 Google Inc.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 80ee:0021 VirtualBox USB Tablet
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

使用端口映射

后面想到adb 其实是通过tcp 和Android设备通讯, 所以只要做端口映射打个通道,就可以解决在virutalBox的虚拟机里调试手机的问题。

1
tcp        0      0 127.0.0.1:5037          0.0.0.0:*               LISTEN      26706/adb 

在guest里执行下面两条命令

1
2
autossh -nNL1234:localhost:1234  10.0.2.2 
autossh -nNL5037:localhost:5037 10.0.2.2

其中 10.0.2.2 是host的ip,5037 是adb 默认监听的端口,1234 是gdbserver 监听的端 口,利用ssh在host和guest 打个通道。

在host执行adb forward 命令

1
adb forward tcp:1234 tcp:1234 

这条命令在host和手机上建立了一条通道。

这样就搞定了adb的连接问题。执行这三行命令后在ubuntu下, adb devices 可以看到下面的输出, 问题得到解决。

1
2
3
4
> adb devices  
List of devices attached 01b00af93dc59e163 device

gdbclient system_server :1234 4475

输出正常,可以开始调试了。

非root Android 设备用gdbserver进行native 调试的方法

没有root的设备,要使用gdbserver 调试app 会遇到权限问题。(emulator 没有问题)

1
2
3
1|shell@mako:/data/local/tmp $ ./gdbserver :1234 --attach 16907
Cannot attach to lwp 16907: Operation not permitted (1)
Exiting

Android 系统提供了一个run-as 命令来暂时切换用户,但是这个命令有限制,必须是app 打开了debuggable才行,否则会报 Package xx is not debuggable 的错误。
http://android.googlesource.com/platform/system/core.git/+/master/run-as/run-as.c 的注释来看,主要的作用有两个:

  • 可以查看自己开发的应用的数据
  • 可以使用gdb_server 进行native 的debug

我们的需求是第2个,我们希望可以使用gdb_server 来调试 app

1
2
3
4
5
6
shell@mako:/ $ run-as
Usage: run-as []

shell@mako:/ $
shell@mako:/ $ run-as system_server /data/tmp/gdbserver --attach 596 :1234
run-as: Package 'system_server' is unknown

翻看源码,发现有下面 代码:

1
2
3
4
5
6
7
8
/* reject system packages */
if (userAppId < AID_APP) {
panic("Package '%s' is not an application\n", pkgname);
}
/* reject any non-debuggable package */
if (!info.isDebuggable) {
panic("Package '%s' is not debuggable\n", pkgname);
}

限制比较严格,调试系统app估计是没什么戏,root了应该就没有问题了。但是调试一般的app 还是没有问题的,用apktool 将 AndroidManifest.xml 的 debuggable 设置为true,重新 打包就可以进行native 的 debug 了。下面以CVE-2014-7911的POC为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.heen.CVE_2014_7911">

<application
android:allowBackup="true"
android:debuggable="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

这个app 正好 android:debuggable=”true” 不用修改了,在模拟器上安装这个app。搭建 gdb调试环境, 分下面几个步骤走:

(1) 创建几个目录

1
2
3
mkdir ~/Android
mkidr ~/Android/system_lib
mkidr ~/Android/vendor_lib

(2) 将Android 设置上的lib下载到本地

1
2
3
4
5
6
7
8
9
10
11
12
13
cd ~/Android/system_lib/
adb pull /system/lib

cd ~/Android/vendor_lib/
adb pull /vendor/lib

cd ~/Android

# 在64位系统 /system/bin/app_process32 和 /system/bin/app_process64
adb pull /system/bin/app_process

cd ~/Android
adb pull /system/bin/linker

(3) 上传gdbserver

1
adb push $NDK_PATH/prebuilt/android-arm/gdbserver/gdbserver /data/local/tmp/gdbserver

环境基本搭建好了,测试一下 run-as 命令

1
2
3
4
5
> adb shell ps

...
u0_a86 16907 174 900568 38564 ffffffff 00000000 S com.heen.CVE_2014_7911
...
1
2
> adb shell run-as com.heen.CVE_2014_7911 id
uid=10086(u0_a86) gid=10086(u0_a86) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:untrusted_app:s

已经切换过来了,uid 变了,挂上gdbserver

1
2
3
4
> adb shell run-as com.heen.CVE_2014_7911 /data/local/tmp/gdbserver :123 --attach 16907

Can't open socket: Permission denied.
Exiting

报了另外一个错误,还是不行,google 一翻发现有debug-pipe 参数,尝试了一下

1
2
3
> adb shell run-as com.heen.CVE_2014_7911 /data/local/tmp/gdbserver +debug-pipe --attach 16907
Attached; pid = 16907
Listening on Unix socket debug-pipe

恩,现在没有报错了,执行一下端口转发。

1
2
adb forward tcp:5039 localfilesystem:/data/data/com.heen.CVE_2014_7911/debug-pipe

OK, 已经可以调试了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
> $NDK_PATH/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gdb ~/Android/app_process 
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-linux-android".
For bug reporting instructions, please see:
...
Reading symbols from /home/henices/Android/app_process...(no debugging symbols found)...done.
(gdb) target remote :5039
Remote debugging using :5039
warning: Could not load shared library symbols for 100 libraries, e.g. /system/bin/linker.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0x4013a73c in ?? ()

(gdb) info proc
process 16907
cmdline = 'com.heen.CVE_2014_7911'
cwd = '/'
exe = '/system/bin/app_process'

(gdb) set solib-search-path ~/Android:~/Android/system_lib/:~/Android/vendor_lib/
(gdb) info sharedlibrary
0x400f3a60 0x400fe79c Yes (*) /home/henices/Android/linker
0x40126070 0x401566ec Yes (*) /home/henices/Android/system_lib/libc.so
0x40174828 0x401749c8 Yes (*) /home/henices/Android/system_lib/libstdc++.so
0x401798f0 0x4018c478 Yes (*) /home/henices/Android/system_lib/libm.so
0x40114f50 0x40116490 Yes (*) /home/henices/Android/system_lib/liblog.so
0x4010c38c 0x40110988 Yes (*) /home/henices/Android/system_lib/libcutils.so
0x401acb1c 0x401af20c Yes (*) /home/henices/Android/system_lib/libgccdemangle.so
0x401a81d0 0x401a94ac Yes (*) /home/henices/Android/system_lib/libcorkscrew.so
0x4019b780 0x401a1f24 Yes (*) /home/henices/Android/system_lib/libutils.so
0x401cbc50 0x401d5ba4 Yes (*) /home/henices/Android/system_lib/libbinder.so
0x402955f0 0x4029585c Yes (*) /home/henices/Android/system_lib/libhardware.so
0x402925d0 0x40292834 Yes (*) /home/henices/Android/system_lib/libmemtrack.so
0x402bbbf0 0x402cb80c Yes (*) /home/henices/Android/system_lib/libz.so
0x402a4240 0x402b23fc Yes (*) /home/henices/Android/system_lib/libandroidfw.so
0x402d6774 0x402e53a0 Yes (*) /home/henices/Android/system_lib/libexpat.so
0x403083a8 0x4031e684 Yes (*) /home/henices/Android/system_lib/libstlport.so

Android boot.img 格式分析

首先需要搞到boot.img,网络上流传的方法是通过/proc/mtd 在获取需要的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> ./adb shell
> su
> cat /proc/mtd

dev: size erasesize name
mtd0: 00700000 00020000 "boot"
mtd1: 07c20000 00020000 "cache"
mtd2: 00700000 00020000 "recovery"
mtd3: 00140000 00020000 "splash"
mtd4: 00700000 00020000 "FOTA_STO"
mtd5: 09e80000 00020000 "system"
mtd6: 0a4e0000 00020000 "userdata"
mtd7: 00080000 00020000 "misc"
mtd8: 00180000 00020000 "persist"

但是在Nexus4, Android 4.4.4 已经找不到这个文件了

1
2
3
> cat /proc/mtd

cat: /proc/mtd: No such file or directory

但是还是有方法可以获取到需要的信息。

1
2
3
4
5
6
> mount

.....
/dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ro,seclabel,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/cache /cache ext4 rw,seclabel,nosuid,nodev,noatime,data=ordered 0 0
.....

可以看到大致上面的输出(上面的输出省略了部分内容),从输出中可以看出 /dev/block/platform/msm_sdcc.1/by-name
是一个比较有意思的路径,有by-name 的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
> ls /dev/block/platform/msm_sdcc.1/by-name/

lrwxrwxrwx root root 2014-06-30 20:50 DDR -> /dev/block/mmcblk0p24
lrwxrwxrwx root root 2014-06-30 20:50 aboot -> /dev/block/mmcblk0p12
lrwxrwxrwx root root 2014-06-30 20:50 abootb -> /dev/block/mmcblk0p15
lrwxrwxrwx root root 2014-06-30 20:50 boot -> /dev/block/mmcblk0p6
lrwxrwxrwx root root 2014-06-30 20:50 cache -> /dev/block/mmcblk0p22
lrwxrwxrwx root root 2014-06-30 20:50 grow -> /dev/block/mmcblk0p25
lrwxrwxrwx root root 2014-06-30 20:50 m9kefs1 -> /dev/block/mmcblk0p8
lrwxrwxrwx root root 2014-06-30 20:50 m9kefs2 -> /dev/block/mmcblk0p9
lrwxrwxrwx root root 2014-06-30 20:50 m9kefs3 -> /dev/block/mmcblk0p10
lrwxrwxrwx root root 2014-06-30 20:50 metadata -> /dev/block/mmcblk0p18
lrwxrwxrwx root root 2014-06-30 20:50 misc -> /dev/block/mmcblk0p19
lrwxrwxrwx root root 2014-06-30 20:50 modem -> /dev/block/mmcblk0p1
lrwxrwxrwx root root 2014-06-30 20:50 persist -> /dev/block/mmcblk0p20
lrwxrwxrwx root root 2014-06-30 20:50 recovery -> /dev/block/mmcblk0p7
lrwxrwxrwx root root 2014-06-30 20:50 rpm -> /dev/block/mmcblk0p11
lrwxrwxrwx root root 2014-06-30 20:50 rpmb -> /dev/block/mmcblk0p16
lrwxrwxrwx root root 2014-06-30 20:50 sbl1 -> /dev/block/mmcblk0p2
lrwxrwxrwx root root 2014-06-30 20:50 sbl2 -> /dev/block/mmcblk0p3
lrwxrwxrwx root root 2014-06-30 20:50 sbl2b -> /dev/block/mmcblk0p13
lrwxrwxrwx root root 2014-06-30 20:50 sbl3 -> /dev/block/mmcblk0p4
lrwxrwxrwx root root 2014-06-30 20:50 sbl3b -> /dev/block/mmcblk0p14
lrwxrwxrwx root root 2014-06-30 20:50 system -> /dev/block/mmcblk0p21
lrwxrwxrwx root root 2014-06-30 20:50 tz -> /dev/block/mmcblk0p5
lrwxrwxrwx root root 2014-06-30 20:50 tzb -> /dev/block/mmcblk0p17
lrwxrwxrwx root root 2014-06-30 20:50 userdata -> /dev/block/mmcblk0p23

哈哈,一清二楚。

1
2
3
4
5
6
7
> dd if=/dev/block/mmcblk0p6 of=/sdcard/boot.img bs=4096
5632+0 records in
5632+0 records out
23068672 bytes transferred in 2.398 secs (9619963 bytes/sec)

>./adb pull /sdcard/boot.img /tmp/boot.img
4270 KB/s (23068672 bytes in 5.275s)

boot.img 主要由两部分组成,kernel 和 ramdisk,下面使用010editor来写一个模版,解析boot.img。从Android的源码可以知道boot.img的结构 app/aboot/bootimg.h

1
2
3
4
5
6
7
8
9
10
11
+-----------------+ 
| boot header | 1 page
+-----------------+
| kernel | n pages
+-----------------+
| ramdisk | m pages
+-----------------+
| second stage | o pages
+-----------------+
| device tree | p pages
+-----------------+
  boot header 的结构定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned dt_size; /* device_tree in bytes */
unsigned unused; /* future expansion: should be 0 */
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */

unsigned char cmdline[BOOT_ARGS_SIZE];
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
};

010editor 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512

typedef struct boot_img_hdr
{
char magic[BOOT_MAGIC_SIZE];
uint kernel_size; /* size in bytes */
uint kernel_addr; /* physical load addr */
uint ramdisk_size; /* size in bytes */
uint ramdisk_addr; /* physical load addr */
uint second_size; /* size in bytes */
uint second_addr; /* physical load addr */
uint tags_addr; /* physical addr for kernel tags */
uint page_size; /* flash page size we assume */
uint dt_size; /* device_tree in bytes */
uint unused; /* future expansion: should be 0 */
ubyte name[BOOT_NAME_SIZE]; /* asciiz product name */

ubyte cmdline[BOOT_ARGS_SIZE];
uint id[8]; /* timestamp / checksum / sha1 / etc */
}BOOT_IMG_HDR;

typedef struct boot_hdr {
BOOT_IMG_HDR header;
ubyte unused[header.page_size - sizeof(header)];
}HEADER;

LittleEndian();
HEADER header_section;

local uint page_size = header_section.header.page_size;
local uint kernel_size = header_section.header.kernel_size;
local uint ramdisk_size = header_section.header.ramdisk_size;
local uint second_size = header_section.header.second_size;

Printf("page_size: %x\nkernel_size: %x\nramdisk_size: %x\nsecond_size: %x\n", page_size, kernel_size, ramdisk_size, second_size);

local uint n = (kernel_size + page_size - 1) / page_size;
local uint m = (ramdisk_size + page_size - 1) / page_size;
local uint o = (second_size + page_size - 1) / page_size;

typedef struct kernel {
ubyte kernel_data[kernel_size];
ubyte unused[n * page_size - kernel_size];
}KERNEL;

KERNEL kernel_section;

typedef struct ramdisk {
ubyte ramdisk_data[ramdisk_size];
ubyte unused[m * page_size - ramdisk_size];
}RAMDISK;

RAMDISK ramdisk_section;

typedef struct second {
ubyte second_data[second_size];
ubyte unused[o * page_size - second_size];

}SECOND;

SECOND second_section;