关于ICS/JB下命令行启动程序segfault的问题

其实很多人研究过这个问题了,只是最近我碰到这次原因有些不同……
症状是在命令行执行命令碰到segmentation fault,logcat里有:

W/dalvikvm(12364): Exception Ljava/lang/NullPointerException; thrown while initializing Ljava/lang/System;
W/dalvikvm(12364): Exception Ljava/lang/ExceptionInInitializerError; thrown while initializing Ljava/lang/ClassLoader$SystemClassLoader;
....
I/dalvikvm(12364): java.lang.ExceptionInInitializerError:
I/dalvikvm(12364):     at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:125)
I/dalvikvm(12364):     at dalvik.system.NativeStart.main(Native Method)
I/dalvikvm(12364): Caused by:
I/dalvikvm(12364): java.lang.ExceptionInInitializerError:
I/dalvikvm(12364):     at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:100)
....
I/dalvikvm(12364): Caused by:
I/dalvikvm(12364): java.lang.NullPointerException:
I/dalvikvm(12364):     at java.util.Hashtable.put(Hashtable.java:365)
I/dalvikvm(12364):     at java.lang.System.initSystemProperties(System.java:286)
I/dalvikvm(12364):     at java.lang.System.getProperties(System.java:260)
....

原因大致上就是LD_LIBRARY_PATH里少了东西。ICS以后的系统,启动Android那套VM需要LD_LIBRARY_PATH里必须要有一些路径,默认是/vendor/lib和/system/lib,试下来至少要/system/lib,否则会找不到库,然后…… 不清楚之后发生了什么,反正最后就导致上面那个错误……

但是默认情况是有的,啥时候没有呢?其实这次碰见bug是因为,我为了弄到root把shell(/system/bin/mksh) setuid了…… 然后在shell里就一直有问题……
对于setuid的程序,启动时effective uid与uid不符,动态链接器便会调用linker_secure_env(),把LD_LIBRARY_PATH从环境里踢掉。ICS之后的系统又必须要这个值VM才能启动,于是就挂了……
其实这是个古老的安全机制,否则你LD_LIBRARY_PATH指到别的地方启动setuid程序,就能让他加载你的库,然后以root权限调用到你的库里去,那自然想干啥都行……

把sh的权限弄对之后就没事了……

Xoom更新4.1.1

之前听说Android 4.1.1在Xoom上公开测试了,昨儿听说已经OTA了,于是去系统设置里检查更新,没有……
去网上看,说可以清除Google Service Framework的数据再试。试了一次,果然刷出来一个更新,4.1.1的。Google这是故意的么……
下完之后,更新,刚过百分之十几,就成了个倒了的Android的样子,上面还有个红色三角。

根据经验,反正是升级败了。重启,去/cache,发现recovery目录里有last_log。看最后说啥验证失败。

E: failed to verify whole-file signature
E: signature verification failed
Installation aborted

我一开始以为是更新包验证败了,但是弄到电脑上,能正常解开……(更新包就在/cache里,一个zip) 那估计和上次一样,因为这是个补丁包,所以要先验证源文件正确,才能打补丁,而某些文件可能被改过了,在Root或者别的时候。

这时候我想起来,上次见到过一个叫OTA RootKeeper的东西,可以在更新后保留Root,就去装了一个,打开之后,选保留Root以及暂时unroot。
然后重启到EOS Recovery,尝试装那个zip,果然有apply_patch_check失败的,首先是/system/bin/gzip。进去一看,貌似被改成了到busybox的链接…… 大约是哪个busybox安装器干的好事。从网上下了一个4.0.4的完整镜像,从里面扒出gzip换掉系统里那个符号链接,再试,这次换成了ip。重复操作,搞定了ping和toolbox之后,这次是boot分区校验和挂了…… 重启到fastboot,刷那个4.0.4镜像里的,再更新,还是不行……

想想大概是4.0.4那个镜像的boot改过了,去下载的地方,发现是root过的镜像…… 搜了一圈,就是找不到没root过的4.0.4 boot分区。
网上的恢复办法都是刷回3.x然后一路更新上来,好像很麻烦…… 我想,可以弄来3.x的boot.img和之后所有的补丁,一路打上来,弄到一个4.0.4的boot.img,再刷进去……

在xda的http://forum.xda-developers.com/showthread.php?t=1597609这个帖子里有个网站,里面有3.0.1的原始镜像和所有升级包。把镜像里的boot.img和升级包里的所有boot.img.p扒出来,然后就是咋打补丁的问题了。
我记得android里有applypatch,于是去android source里面找,果然有这个东西。尝试编译一份主机可用的,居然成功了,命令如下:
在applypatch目录里

gcc utils.c imgpatch.c bspatch.c freecache.c bsdiff.c main.c applypatch.c -o applypatch -I../../../system/core/include/ -I../ ../minelf/Retouch.c -lbz2 -lz ../../bootloader/legacy/libc/sha.c ../mtdutils/mtdutils.c

然后跑,发现命令格式挺简单,就是

applypatch 源文件 目标文件 目标sha1 目标大小 源文件sha1:补丁

话说居然要目标sha1…… 用来验证是不是打对了么……
sha1嘛,可以从各个补丁包里的updater-script脚本里发现,就是脚本里调apply_patch()的参数。

用这个办法搞定4.0.4的boot.img,确定sha1没问题后用fastboot刷了进去。然后再更新,终于成功了……
再开OTA RootKeeper,恢复Root,于是Root也有了…… 真方便……

关于4.1的感想:貌似是流畅了点,另外可以玩Google Now了~

在FreeBSD上装linux skype 4

折腾了一个晚上好歹是搞出来了……
起因是把linux_base更新到了linux_base-c6,也就是CentOS 6。这是个相当新的版本,各种rpm找起来也比较容易。
但是问题是,ports里其他linux包基本都是老的,要么是fc4的,要么是f10的…… 反正用不了。于是统统删掉。但是这样linux程序基本就跑不了,缺很多包……
于是就自己搞rpm来装。但是rpm这回又不好使,试了ports里rpm3/4/5貌似都不行。
怀疑是因为rpm调了cpio,而FreeBSD 8-STABLE里的cpio貌似还不支持xz? 因为CentOS那些新包是xz压缩的,于是就解不了。
后来干脆不管rpm了,搞个脚本:

if [ ! -f $1 ]; then
        wget http://mirror.yandex.ru/centos/6.2/os/i386/Packages/$1
fi
rpm2cpio $1 | unxz > ~/tmp
cd /compat/linux
sudo cpio -idv < ~/tmp

加上包文件名,就能自动下载安装…… 坏处是没有rpm帮着记录包信息了…… 以后删起来会很麻烦…… 再说吧……

首先搞下来skype 4.0.0.7的动态链接版本,解压后尝试运行,一堆链接失败…… 拿ldd看一眼,然后逐个去rpmfind找包名,用那个脚本装…… 装了几十个包,好歹差不多可以跑了。主要是一堆libX*,qt,dbus,… 但是没声,也不能输入中文。

首先是声的问题,选项里声音设备连OSS都没。额外装了alsa-plugins-oss,还是没有OSS设备。尝试装alsa-plugins-pulseaudio,pulse设备倒是出来了,但是启用就出问题…… 研究了下,貌似alsa要配置,改了asound.conf,引入oss配置文件,再进去,好歹声音正常了。

然后是中文。本来xim配fcitx应该就可以,但是不知道为啥不行,切不出来。
于是我想看看有啥问题,尝试写了个LD_PRELOAD用的hook XOpenIM的库。native的BSD程序测试没啥问题,找个Linux机子编译,那儿测了也没问题,但是弄回来,就没有反应…… 为了确认,我又hook了XOpenDisplay,结果这次更奇怪,貌似会陷入无限递归一类的情况,反正LD_PRELOAD不成,xim也没法调。

那就转而用fcitx的qt im module。csslayer说fcitx的im module是用dbus通信的。试了一下linux的dbus-monitor和qdbus,貌似都连不上。根据ktrace,貌似是linux程序在bsd下连unix socket会有问题?…… 于是转而研究能不能用tcp socket。改了dbus配置,让它也监听tcp端口。然后dbus-launch出来个shell,确认环境变量之后,linux的qdbus还是不行…… 这回貌似是dbus那边的问题?反正看来dbus服务器自己只能用unix socket才能好好工作?…… 最后参考dbus源代码,改了之前写的一个tcp转发程序,搞了个dbus专用tcp socket转发unix socket的程序。dbus的第一个包带有认证信息,是用sendmsg发的,还要特殊处理…… 这个日后再说吧,反正搞出这个程序之后,篡改DBUS的环境变量,把环境变量DBUS_SESSION_BUS_ADDRESS里的unix:path=/var/tmp/dbus-zGrz0qEmd9改成tcp:host=localhost,port=xxxx,再启动linux的qdbus…… 好歹是连上了。

搞定dbus之后,尝试搞输入法模块本身。貌似centos上根本没有fcitx的包,于是搞了fc15/17的,反正应该关系很近嘛…… 结果就是不出来,右键菜单里没有fcitx这个。后来问csslayer,他说可能是qt版本问题,说插件编译环境qt版本要比当前qt低才行。centos的qt是4.6.2,然后抓了几个fc15/16/17的fcitx-qt4下来,发现都依赖4.7或者4.8+(用strings看,能看见version=4.8.0之类……)。先尝试篡改一把,用二进制编辑器改成4.6.2,倒是能载入,但是会有找不到的qt的符号…… 换个办法,去找centos更新的qt4,倒是找到了,在http://pkgs.org/centos-6-rhel-6/russian-fedora-free-updates-i386/有qt4 4.8.0。搞上这个qt4,再找个依赖qt4 4.8.0的fcitx-qt4…… 还是有问题,不过这次是linux的fcitx-utils版本太老…… 更新之后,又说我本机fcitx太老…… 更新本机fcitx之后…… 终于可以输入中文了!……
之后搞了个脚本自动篡改dbus地址,启动转发……

#!/bin/sh
 
export QT_IM_MODULE=fcitx
/home/henryhu/src/tcp2unix &
 
DBUS_ORIG=$DBUS_SESSION_BUS_ADDRESS
DBUS_NEW=`echo -n $DBUS_ORIG | sed -e 's/unix:path.*,/tcp:host=localhost,port=8888,/'`
export DBUS_SESSION_BUS_ADDRESS=$DBUS_NEW
 
/home/henryhu/soft/skype-4.0.0.7/skype

于是终于可以好好用skype了……

APK相关工具

昨天搞的时候很混乱,今天整理了一下,搞了个图:
APK (de)compile
具体可以参见wiki:
APK (de)compile

大致上有很多办法可以改一个apk,总的来说有两个办法,一个是搞成 Dalvik VM 的汇编,然后改。一个是先从 Dalvik VM 的字节码搞成 JVM 的字节码,然后搞成 JVM 的汇编,甚至搞成 Java source code,然后改。改完了再倒回去,重新签名就是新apk了……

另,Dia 真好用……