0x00 前言
前几天在使用Android模拟器的时候,发现无法连接PPTP类型的VPN服务器,报如下的错误:
I/mtpd (30035): Creating PPPoX socket
F/mtpd (30035): Socket() Address family not supported by protocol
对应的代码如下:
static int create_pppox()
{
int pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OPNS);
log_print(INFO, "Creating PPPoX socket");
if (pppox == -1) {
log_print(FATAL, "Socket() %s", strerror(errno));
exit(SYSTEM_ERROR);
} else {
struct sockaddr_pppopns address = {
.sa_family = AF_PPPOX,
.sa_protocol = PX_PROTO_OPNS,
.tcp_socket = the_socket,
.local = local,
.remote = remote,
};
if (connect(pppox, (struct sockaddr *)&address, sizeof(address))) {
log_print(FATAL, "Connect() %s", strerror(errno));
exit(SYSTEM_ERROR);
}
}
return pppox;
}
错误是在第7行报出来的,也就是说:在创建AF_PPPOX类型的socket时失败了。
网上说是因为kernel不支持的原因,需要重新编译kernel。
0x01 编译3.10的内核
查看模拟器中使用的内核版本:
Linux version 3.10.0+ (jinqian@jinqian.mtv.corp.google.com) (gcc version 4.9 20150123 (prerelease) (GCC) ) #448 SMP PREEMPT Mon Feb 29 13:49:38 PST 2016
将网上搜到的编译方法汇总如下(我只编译x86版本,不需要交叉编译):
git clone https://android.googlesource.com/kernel/goldfish.git
cd goldfish
git branch -a
返回如下结果:
remotes/origin/HEAD -> origin/master
remotes/origin/android-3.10
remotes/origin/android-3.18
remotes/origin/android-goldfish-2.6.29
remotes/origin/android-goldfish-3.10
remotes/origin/android-goldfish-3.10-k-dev
remotes/origin/android-goldfish-3.10-l-mr1-dev
remotes/origin/android-goldfish-3.10-m-dev
remotes/origin/android-goldfish-3.10-n-dev
remotes/origin/android-goldfish-3.18
remotes/origin/android-goldfish-3.18-dev
remotes/origin/android-goldfish-3.4
remotes/origin/android-goldfish-3.4-l-mr1-dev
remotes/origin/android-goldfish-4.4-dev
remotes/origin/heads/for/android-goldfish-3.18-dev
remotes/origin/linux-goldfish-3.0-wip
remotes/origin/master
选择android-goldfish-3.10分支
git checkout android-goldfish-3.10
android源码库的路径prebuilts/qemu-kernel/build-kernel.sh是一个内核编译脚本。执行如下命令可以直接编译内核:
prebuilts/qemu-kernel/build-kernel.sh --arch=x86 --config=i386_ranchu --out=/tmp/
如果报以下错误, 请在命令行后面加上: –cross=/usr/bin/
It looks like x86_64-linux-android-gcc is not in your path ! Aborting.
也可以改成android源码中提供的gcc路径前缀
编译完成后会在/tmp目录下生成kernel-qemu文件,将其替换掉模拟器镜像文件目录中的kernel-ranchu文件,重启模拟器即可;或是在启动模拟器的命令行中添加-kernel /tmp/kernel-qemu,启动也可以。
0x02 解决编译的内核无法启动模拟器问题
使用编译的内核启动模拟器后,发现会一直黑屏,无法进入系统。
加入-show-kernel参数后看到启动时一直报:
init: untracked pid 16860 killed by signal 9
查看logcat看到如下crash信息:
I/DEBUG ( 1312): pid: 1389, tid: 1389, name: surfaceflinger >>> /system/bin/surfaceflinger <<<
I/DEBUG ( 1312): signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
I/DEBUG ( 1312): eax 00000000 ebx 0000056d ecx 0000056d edx 00000006
I/DEBUG ( 1312): esi b3056c48 edi 00000002
I/DEBUG ( 1312): xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007b
I/DEBUG ( 1312): eip b2f75686 ebp 0000056d esp bff6d410 flags 00000286
I/DEBUG ( 1312):
I/DEBUG ( 1312): backtrace:
I/DEBUG ( 1312): #00 pc 00074686 /system/lib/libc.so (tgkill+22)
I/DEBUG ( 1312): #01 pc 0002217b /system/lib/libc.so (pthread_kill+155)
I/DEBUG ( 1312): #02 pc 000239f4 /system/lib/libc.so (raise+36)
I/DEBUG ( 1312): #03 pc 0001bdf4 /system/lib/libc.so (abort+84)
I/DEBUG ( 1312): #04 pc 000314e5 /system/lib/libsurfaceflinger.so
I/DEBUG ( 1312): #05 pc 000201d7 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::init()+199)
I/DEBUG ( 1312): #06 pc 00000b51 /system/bin/surfaceflinger
I/DEBUG ( 1312): #07 pc 00012f94 /system/lib/libc.so (__libc_init+100)
I/DEBUG ( 1312): #08 pc 00000cd6 /system/bin/surfaceflinger
原来是surfaceflinger一直crash,导致看不到启动动画。
网上搜了一气,也没有找到原因。切换到3.10的其它几个分支也不行,最好的情况的是在关闭opengl的情况下进入系统了,但是界面显示明显和正常情况不一样。
在山穷水尽之时,看到了http://blog.csdn.net/ayu_ag/article/details/51741679这篇文章,里面提供了根据提交记录查找编译时的版本号的方法。使用和sdk编译时使用的版本,应该不会有问题吧!
现在的模拟器使用的都是ranchu内核,android源码是在7.0的时候才开始提供的,路径是:prebuilts/qemu-kernel/x86/ranchu/kernel-qemu
提交记录中也没有看到有用的信息。
后来把 https://android.googlesource.com/platform/prebuilts/qemu-kernel 项目拉下来,在git log中找到了如下信息:
commit e6e975ca2f2e57dced55759ab40b2851c144a67d
Author: Jin Qian <jinqian@google.com>
Date: Mon Oct 31 14:28:45 2016 -0700
Upgrade emulator kernels
kernel-n-dev-android-goldfish-3.18-arm - build: 3421005
kernel-n-dev-android-goldfish-3.10-mips64 - build: 3420631
kernel-n-dev-android-goldfish-3.10-arm - build: 3420641
kernel-n-dev-android-goldfish-3.18-arm64 - build: 3421004
kernel-n-dev-android-goldfish-3.10-mips - build: 3420642
kernel-n-dev-android-goldfish-3.10-arm64 - build: 3420640
kernel-n-dev-android-goldfish-3.10-x86_64-qemu1 - build: 3420638
kernel-n-dev-android-goldfish-3.4-x86 - build: 3407988
kernel-n-dev-android-goldfish-3.10-x86_64 - build: 3420647
kernel-n-dev-android-goldfish-3.18-mips - build: 3421003
kernel-n-dev-android-goldfish-3.18-x86_64 - build: 3421007
kernel-n-dev-android-goldfish-3.4-mips - build: 3407999
kernel-n-dev-android-goldfish-3.4-arm - build: 3408013
kernel-n-dev-android-goldfish-3.10-x86 - build: 3420649
kernel-n-dev-android-goldfish-3.18-x86 - build: 3421006
kernel-n-dev-android-goldfish-3.18-mips64 - build: 3421008
Upgrade 3.4 kernel images to 1cc37bf
1cc37bf MIPS: Attach goldfish machine reset and power-off routines
Upgrade 3.10 kernel images to fd6659e
fd6659e Merge branch 'android-3.10' into android-goldfish-3.10
13f4ded BACKPORT: LogiHID: hid-lg4ff: tech G29 wheel patches
288f516 android: binder: use copy_from_user_preempt_disabled
...
0e7ad12 BACKPORT: ALSA: usb-audio: Fix double-free in error paths after snd_usb_add_audio_stream() call
5982d2e BACKPORT: ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk()
b581331 pstore: drop pmsg bounce buffer
Upgrade 3.18 kernel images to 82a0dee
82a0dee Merge branch 'android-3.18' into android-goldfish-3.18
74b2c7a android: binder: support for file-descriptor arrays.
53b1701 android: binder: support for scatter-gather.
...
cd6ae1f Revert "UPSTREAM: ARM: 8494/1: mm: Enable PXN when running non-LPAE kernel on LPAE processor The VMSA field of MMFR0 (bottom 4 bits) is incremented for each added feature. PXN is supported if the value is >= 4 and LPAE is supported if it is >= 5."
7447458 UPSTREAM: perf: Fix race in swevent hash
5753ff2 ANDROID: dm: Fix symbol exports for dm target callbacks
Change-Id: I5a635595233199939145487df580c6f519c24335
里面的“fd6659e”就是我们需要的版本号
git checkout fd6659e
再执行编译,就可以正常运行模拟器了。
0x03 如何编译内核模块
在正常编译出内核前,我还尝试了编译ko模块来绕过编译完整内核的问题。这里一并记录一下。
build-kernel.sh脚本事实上会先根据arch/x86/configs/i386_ranchu_defconfig文件生成.config文件。
要编译出ko,需要在i386_ranchu_defconfig中加入以下行:
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IPV6=y
然后将需要编译成模块的配置改为m,如:
CONFIG_PPP=m
CONFIG_PPP_BSDCOMP=m
CONFIG_PPP_DEFLATE=m
CONFIG_PPP_MPPE=m
CONFIG_PPPOLAC=m
CONFIG_PPPOPNS=m
执行命令:
make i386_ranchu_defconfig
export ARCH=x86
make -j16
编译出来的内核文件路径是:arch/x86/boot/bzImage
而且,期望的ko文件也编译出来了。
drivers/net/slip/slhc.ko
drivers/net/ppp/ppp_mppe.ko
drivers/net/ppp/bsd_comp.ko
drivers/net/ppp/ppp_generic.ko
drivers/net/ppp/pppolac.ko
drivers/net/ppp/pppox.ko
drivers/net/ppp/pppopns.ko
drivers/net/ppp/ppp_deflate.ko
但是在测试的时候,发现ko中依赖到了net/core/net_namespace.c中的一个函数,而这个函数是通过CONFIG_NET_NS开关控制的。sdk提供的kernel里应该关闭了这个开关,因此,这些ko事实上是不能加载进去的。
而且,从6.0开始,sdk提供的kernel已经不再支持动态加载ko,估计是担心有安全风险。
0x04 如何支持PPTP
使用默认参数编译出来的内核,已经是支持ppp的了。但是测试发现,依然不能连上VPN服务器。原因是PPTP中使用GRE协议作为数据通道协议,该协议与TCP、UDP是同一层的,而模拟器实现的NAT只支持TCP和UDP协议,想要支持的话,必须要修改模拟器源码。
由于模拟器不像普通NAT网络里会有多台机器,而且一般来说,同时只会有一个进程在发起VPN连接,所以,可以不进行数据包的转换,直接透传。