通常我们在Android应用中执行某个命令时会使用“Runtime.getRuntime().exec("命令路径")”这种方式,但是当我们执行抓包操作时,使用这条命令无论如何都不行,通过下面代码打印结果发现,该命令一定要在root权限下才能执行。
BufferedReader brW = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((str = brW.readLine()) != null)
Log.d("cwmp", "w:"+str);
但是我们的Android设备(包括机顶盒、手机等)通常并没有root过,apk的最高权限也只是system权限,这该怎么解决?首先我们要知道,方法总比问题多,在Android设备的/system/bin路径下,我们会看到很多二进制文件,这些二进制文件可以获得root权限。因此,我们可以通过C语言来实现抓包功能,通过NDK把该C代码交叉编译成二进制文件置于/system/bin路径下,并赋予其root权限,此时,这个二进制文件就具备了抓包能力了。现在问题又来了,我们现在是想通过apk去调用这个抓包指定,抓包完成后又该怎么通知apk呢?其实,Android可以通过socket使底层与framework层进行通信,具体请参考Android中使用socket使底层和framework通信的实现方法。
接下来我们将贴出关键实现代码。
1、编写socket服务端代码fstiService.cpp,生成可执行脚本fstiService
#define SOCKET_NAME "fstiService" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "itv_assistance", __VA_ARGS__) #include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include pthread_t thread[2]; char s_time[10]; //抓包时间子串 char s_command[256]; //抓包指令子串 //抓包指令:system("/system/bin/tcpdump -v -w /sdcard/te.pcap"); //获取进程tcpdump的进程号 int getPid() { //for Linux C //FILE *fp = popen("ps -e | grep 'tcpdump' | awk '{print $1}'", "r"); //for Android(ARM) //FILE *fp = popen("ps | grep 'tcpdump'", "r"); FILE *fp = popen("ps | grep 'tcpdump'", "r"); char buff[1024] = { 0 }; while (NULL != fgets(buff, sizeof(buff), fp)) ; //取消换行符(10) buff[strlen(buff) - 1] = ' '; pclose(fp); char dst[5] = { 0 }; char *p = buff; char *q = dst; //每一行进程信息的第二个字符串为进程号 while (*p != ' ') p++; while (*p == ' ') p++; while (*p != ' ') *(q++) = *(p++); *(q++) = ' '; return atoi(dst); } //截取子串(抓包时间(秒):抓包命令) void substring(char *time, char *command, char *src) { char *p = src; char *q = time; char *s = command; while (*p != '/') *(q++) = *(p++); *(q++) = ' '; //如果Tcpdump命令已添加环境变量,则添加下行代码 //否则删除下一行代码,client传递的参数格式必须为: num/tcpdump所在路径 p++; while (*p) *(s++) = *(p++); *(s++) = ' '; } //抓包线程 void *thread1(void *arg) { system(s_command); } void *thread2(void *arg) { int i_time = atoi(s_time); int begin = time((time_t*) NULL); while (1) { if (time((time_t*) NULL) - begin < i_time) { //printf("当前时间(s):%ldn", time((time_t*)NULL)); continue; } else { int n = kill(getPid(), SIGKILL); LOGD("the kill process result is n=%d", n); break; } } return 0; } //创建子线程 void thread_create() { int temp; memset(&thread, 0, sizeof(thread)); if ((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) LOGD("create tcpdump thread failure"); else LOGD("create tcpdump thread success"); if ((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) LOGD("create count thread failure"); else LOGD("create count thread success"); } void thread_wait() { if (thread[0] != 0) { pthread_join(thread[0], NULL); LOGD("tcpdump thread has terminated"); } if (thread[1] != 0) { //pthread_join(thread[1], NULL); printf("counter thread has terminated"); } } int main() { int connect_number = 6; int fdListen = -1, new_fd = -1; int ret; struct sockaddr_un peeraddr; socklen_t socklen = sizeof(peeraddr); int numbytes; char buff[256]; //这一步很关键,就是获取init.rc中配置的名为 "fstiService" 的socket //获取已绑定的socket,返回-1为错误情况 fdListen = android_get_control_socket(SOCKET_NAME); if (fdListen < 0) { LOGD("failed to get socket '" SOCKET_NAME "' errno %d", errno); exit(-1); } ret = listen(fdListen, connect_number); LOGD("listen result %d", ret); if (ret < 0) { perror("listen"); exit(-1); } new_fd = accept(fdListen, (struct sockaddr *) &peeraddr, &socklen); LOGD("accept_fd %d", new_fd); if (new_fd < 0) { LOGD("%d", errno); perror("accept error"); exit(-1); } //循环等待Socket客户端发来消息 while (1) { if ((numbytes = recv(new_fd, buff, sizeof(buff), 0)) == -1) { LOGD("%d", errno); perror("recv"); continue; } LOGD("the parameter received from socket client is %s", buff); if(strcmp(buff, "exit") != 0){ substring(s_time, s_command, buff); thread_create(); thread_wait(); } char result[10] = "successp"; int sendR = send(new_fd, result, strlen(result), 0); //apk退出后,buff中仍然缓存之前的调用命令,此时会额外再执行一次抓包,固下面代用重写buff中数据 strcpy(buff, "exit"); if (sendR == -1) { perror("send"); close(new_fd); exit(0); } } close(new_fd); close(fdListen); return 0; }
2、配置init.rc文件,添加如下配置
service fstiService /system/bin/fstiService socket fstiService stream 777 system system class main
此处配置了一个名为“fstiService”的服务,Android设备开机会自动启动并运行/system/bin/fstiService这个脚本文件。服务端代码完成后,我们需要将其编译成可执行脚本fstiService,Android.mk内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #指定该模块在所有版本下都编译 LOCAL_MODULE_TAGS :=optional LOCAL_MODULE := fstiService LOCAL_SRC_FILES := fstiService.cpp LOCAL_LDLIBS := -llog #编译成动态库 #include $(BUILD_SHARED_LIBRARY) #编译成可执行文件 include $(BUILD_EXECUTABLE)
3、Android客户端代码
public class SocketClient {
private final String SOCKET_NAME = "fstiService";
private LocalSocket client = null;
private LocalSocketAddress address = null;
private boolean isConnected = false;
private int connectTime = 1;
public SocketClient(){
client = new LocalSocket();
//A socket in the Android reserved namespace in /dev/socket.
//only the init process may create a socket here
address = new LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
new ConnectSocketThread().start();
}
public String sendMsg(String msg){
if(!isConnected)
return "Connect failure";
try{
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream());
out.println(msg);
out.flush();
return in.readLine();
}catch(IOException e){
e.printStackTrace();
}
return "Nothing Return";
}
private class ConnectSocketThread extends Thread{
@Override
public void run() {
while(!isConnected && connectTime <= 3){
try{
sleep(1000);
Log.d("itv_assistance", "Try to connect socket; ConnectTime: "+connectTime);
client.connect(address);
isConnected = true;
}catch(Exception e){
connectTime++;
isConnected = false;
Log.d("itv_assistance", "Connect Failure");
}
}
}
}
public void closeSocket(){
try{
client.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
至此,基于非root的Android设备上的抓包实现方法就完成了,接下来就是编译系统进行测试了,这步我没有亲自去做,而是把fstiService脚本及init.rc配置文件的操作交给合作厂商来做了,apk是我们自己做的,经测试一切OK。
点击下载源码:http://xiazai.jb51.net/201611/yuanma/AndroidFstiService(jb51.net)
以上所述是小编给大家介绍的Android设备上非root的抓包实现方法(Tcpdump方法),实现一个模拟后台数据登入的效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对考高分网网站的支持!



