最近老研究一些之前的東西,越來越懷舊了真是
下載源碼
要改造adb.exe從思路來講很簡單,把android源碼里面的adb.exe部分提取出來,改造再進行編譯不就可以了?是的。但是這一塊的源碼在哪個模塊呢?由哪些文件構成?我是不是要先下載所有的Android源碼才找得到?現在google的網站被封?我還得先...... 技術就是這樣,思路很簡單的事情操作起來可能并不簡單,需要大把的時間,這里給大家介紹一個資源
android源碼查找,可以根據文件名,類名,方法名的定義去查找到相應的文件(http://osxr.org/android/ident?)(Ver: 4.1.2_r2)
有了該網站,就可以不用下載整個android源碼了,但是我還是把一整套android源碼下下來了,整個過程太過艱辛,感嘆一下做技術人員不容易,和各種勢力進行抗爭啊真是。經過一番查找,下面為adb client所在的源碼目錄
到了這里,是不是要去找一些官方文檔,該目錄下的一些文檔說明來進行編譯,太糾結了,去github上找找吧,現在是共享時代
啰嗦了一堆,其實就是下載my_adb去進行改造,不要打我
對源碼進行改造
**源碼結構 **
//入口 adb.c
int main(int argc, char **argv)
{
#if ADB_HOST
adb_sysdeps_init();
adb_trace_init();
D("Handling commandline()\n");
return adb_commandline(argc - 1, argv + 1);
#else
if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
adb_device_banner = "recovery";
recovery_mode = 1;
}
start_device_log();
return adb_main(0, DEFAULT_ADB_PORT);
#endif
}
我們執行adb.exe時,會進入到if ture里面的分支,進入到adb_commondline函數
int adb_commandline(int argc, char **argv)
{
...
if(!strcmp(argv[0], "devices")) {
char *tmp;
snprintf(buf, sizeof buf, "host:%s", argv[0]);
tmp = adb_query(buf);
if(tmp) {
printf("List of devices attached \n");
printf("%s\n", tmp);
return 0;
} else {
return 1;
}
}
if(!strcmp(argv[0], "connect")) {
char *tmp;
if (argc != 2) {
fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
return 1;
}
snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
tmp = adb_query(buf);
if(tmp) {
printf("%s\n", tmp);
return 0;
} else {
return 1;
}
}
...
}
該函數里面的功能就是常規的解析命令行參數,然后執行對應的功能,上面的代碼展示出來的是常見的兩個功能,顯示設備列表和連接到某臺設備
OK,不往下分析了,先分析一下我們需要加的功能吧
需要改造的功能
通過上一篇文章的分析可以得出有兩個功能需要改進:adb.exe中無法傳送utf-8格式字符串的問題 和 增加傳送monkey指令的功能
adb.exe中無法傳送utf-8格式字符串的問題
該問題網上是有現成的解決方案的,思路上只需要在發送buffer的時候,把gbk轉成utf-8即可,adb daemon 是識別utf-8格式的,所以原生的adb傳送gbk格式,中文就會顯示不出來。
cmd默認字符編碼是gbk,而且vs2013里面默認的文件編碼也是gbk,如果把vs2013里面的源碼文件改成utf-8格式的,是否可以不更改adb的源碼,就可以用?我沒試過,有興趣的朋友可以測試一下,從原理上來講是可以的。
增加傳送monkey指令的功能
這個功能是需要和本地的adb server進行TCP交互的,還記得上一篇文章的圖嗎?
執行monkey命令流程流程是這樣的,一次連接可能會執行很多次命令
socket層(TCP傳輸層)
增加傳送monkey指令的功能
我們看一下socket層的代碼是否需要我們自己添加
char *adb_query(const char *service)
{
char buf[5];
unsigned n;
char *tmp;
int fd; //+
D("adb_query: %s\n", service);
//- int fd = adb_connect(service);
fd = adb_connect(service);
if(fd < 0) {
fprintf(stderr,"error: %s\n", __adb_error);
return 0;
}
if(readx(fd, buf, 4)) goto oops;
...
}
int adb_connect(const char *service)
{
// first query the adb server's version
int fd = _adb_connect("host:version");
if(fd == -2) {
fprintf(stderr, "* daemon not running. *\n");
return -1;
fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
__adb_server_port);
start_server:
...
}
adb client的命令有一部分是通過adb_query函數進行發送,里面會用到adb_connect,返回socket實例,再通過該socket讀取到返回的內容進行輸出
unsigned char* adb_monkey_query(const char *service, int mFd)
{
char buf[5];
unsigned n;
int fd; //+
unsigned char* outRel = NULL;
D("adb_query: %s\n", service);
//- int fd = adb_connect(service);
fd = _adb_monkey_connect(service,mFd,&outRel);
if(fd < 0) {
fprintf(stderr,"error: %s\n", __adb_error);
}
return outRel;
}
int adb_monkey_exec(const char *service, int mFd)
{
char buf[5];
unsigned n;
int fd; //+
unsigned char* outRel = NULL;
D("adb_query: %s\n", service);
//- int fd = adb_connect(service);
fd = _adb_monkey_connect(service,mFd,&outRel);
if (outRel!=NULL)
{
free(outRel);
}
if(fd < 0) {
fprintf(stderr,"error: %s\n", __adb_error);
return 0;
}
return 1;
}
int _adb_monkey_connect(const char *service, int mFd, unsigned char** outRel)
{
char tmp[5];
int len;
D("_adb_monkey_connect: %s\n", service);
len = strlen(service);
if((len < 1) || (len > 1024)) {
strcpy(__adb_error, "service name too long");
return -1;
}
if (mFd<=0)
{
mFd = socket_loopback_client(__adb_monkey_server_port, SOCK_STREAM);
}
if(mFd < 0) {
strcpy(__adb_error, "cannot connect to monkey port");
return -2;
}
if(writex(mFd, service, len)) {
strcpy(__adb_error, "write monkey cmd failure during connection");
adb_close(mFd);
return -1;
}
if(adb_monkey_status(mFd,outRel)) {
strcpy(__adb_error, "monkey cmd return err");
//adb_close(mFd);
return -1;
}
return mFd;
}
通過上面的代碼可以看出,我增加了adb_monkey_query adb_monkey_exec 和 adb_monkey_connect,其實query和exec都是用到了connect函數,只是一個有返回,一個沒有返回而已,底層adb_monkey_connect怎么實現的,其實和_adb_connect類似
int _adb_monkey_connect(const char *service, int mFd, unsigned char** outRel)
{
char tmp[5];
int len;
D("_adb_monkey_connect: %s\n", service);
len = strlen(service);
if((len < 1) || (len > 1024)) {
strcpy(__adb_error, "service name too long");
return -1;
}
if (mFd<=0)
{
mFd = socket_loopback_client(__adb_monkey_server_port, SOCK_STREAM);
}
if(mFd < 0) {
strcpy(__adb_error, "cannot connect to monkey port");
return -2;
}
if(writex(mFd, service, len)) {
strcpy(__adb_error, "write monkey cmd failure during connection");
adb_close(mFd);
return -1;
}
if(adb_monkey_status(mFd,outRel)) {
strcpy(__adb_error, "monkey cmd return err");
//adb_close(mFd);
return -1;
}
return mFd;
}
int adb_monkey_status(int fd,unsigned char** rel)
{
unsigned char tbuf[1];
int len,alllen=0;
int fail = 0;
*rel = NULL;
while(fd >= 0) {
len = adb_read(fd, tbuf, 1);
if (len == 0)
{//讀取出錯
fail = 1;
}
if(len == 0 || tbuf[0]=='\n') {
break;
}
if(len < 0) {
if(errno == EINTR) continue;
break;
}
alllen += len;
*rel = (unsigned char*)realloc(*rel,alllen+1);
memcpy(*rel+alllen-len,tbuf,len);
*(*rel+alllen) = 0;
}
if(fail) {
strcpy(__adb_error, "monkey fault (no status)");
return -1;
}
if(*rel != NULL && !memcmp(*rel, "OK", 2)) {
return 0;
}
return -1;
}
OK,整個monkey功能的socket層就算完了
應用層
adb.exe中無法傳送utf-8格式字符串的問題
直接上代碼,在執行命令的函數里面把buffer的編碼變掉就可以
int shell(char* inSerial, char* inParams, unsigned char** outRelStr)
{
int fd = 0;
char buf[10240];
unsigned char* rel = NULL;
char* inParamsUTF8;
transport_type ttype = kTransportAny;
int server_port = DEFAULT_ADB_PORT;
adb_set_transport(ttype, inSerial);
adb_set_tcp_specifics(server_port);
//這個函數就是關鍵的編碼轉換函數
GBK_to_UTF8(inParams,strlen(inParams),&inParamsUTF8);
ZeroMemory(buf,10240);
snprintf(buf,sizeof buf,"shell:%s",inParamsUTF8);
free(inParamsUTF8);
fd = adb_connect(buf); //+
if(fd >= 0) {
char buf[4096];
int len;
int alllen=0;
int first = 0;
while(fd >= 0) {
len = adb_read(fd, buf, 4096);
if(len == 0) {
break;
}
if(len < 0) {
if(errno == EINTR) continue;
break;
}
alllen+=len;
rel = (unsigned char*)realloc(rel,alllen+1);
memcpy(rel+alllen-len,buf,len);
rel[alllen] = 0;
//先做調試用,后期去掉
//fwrite(buf, 1, len, stdout);
//fflush(stdout);
}
* outRelStr = rel;
adb_close(fd);
return 1;
}
return 0;
}
GBK_to_UTF8函數的代碼不貼出來了,網上非常多
增加傳送monkey指令的功能
/*
初始化monkey的連接,我們采用連接一次成功后,可以無限發送命令的模式
連接deviceSocket并查詢版本信息
連接monkeySocket連接成功就行
返回:是否成功 1成功 0失敗
*/
int init(char* inSerial, int inMonkeyPort, int* outMonkeySocket)
{
transport_type ttype = kTransportAny;
int server_port = DEFAULT_ADB_PORT;
char *tmp;
unsigned char* mrel = NULL;
char buf[4096];
int fd = 0;
adb_trace_init();
adb_sysdeps_init();
//這里只是設置一個模式,沒有起實質性的變化
adb_set_transport(ttype, inSerial);
adb_set_tcp_specifics(server_port);
//打開Monkey的端口
snprintf(buf, sizeof buf, "host-serial:%s:forward:tcp:%d;tcp:%d",inSerial,inMonkeyPort,inMonkeyPort);
fd = adb_connect(buf);
if(fd >= 0) {
if (adb_status(fd))
{
adb_close(fd);
return 0;
}
read_finished(fd);
adb_close(fd);
} else {
fprintf(stderr,"error: %s\n", adb_error());
return 0;
}
//把手機端的Monkey端口設置為我們的端口
//這是一個bug貌似,非常低端... monkey是阻塞命令,如果連接成功之后直接close這樣monkey程序就會退出,所以睡5秒再說,官方的方法就是睡的5秒,如果有更好的辦法就太好了
set_monkey_port(inMonkeyPort);
snprintf(buf, sizeof buf, "shell:monkey --port %d", inMonkeyPort);
fd = adb_connect(buf);
printf("monkey --port %d done\r\n",inMonkeyPort);
if(fd >= 0)
{
Sleep(5000);
adb_close(fd);
}
else
{
return 0;
}
printf("monkey --port %d ok\r\n",inMonkeyPort);
//喚醒屏幕
printf("wake\n",buf);
*outMonkeySocket = _adb_monkey_connect( "wake\n",-1,&mrel);
if (mrel!=NULL)
{
free(mrel);
}
if(*outMonkeySocket < 0) {
fprintf(stderr,"error: %s\n", get_adb_error());
return 0;
}
return 1;
}
下面例舉幾個monkey的應用層命令
int wake(int inMonkeySocket)
{
return adb_monkey_exec("wake\n",inMonkeySocket);
}
int press(int inMonkeySocket, char* inKeyName, char* intPressType)
{
char buf[4096];
if (strncmp("down",intPressType,sizeof("down"))==0)
{
snprintf(buf, sizeof buf,"key down %s\n",inKeyName);
}
else if (strncmp("up",intPressType,sizeof("up"))==0)
{
snprintf(buf, sizeof buf,"key up %s\n",inKeyName);
}
else if (strncmp("downAndUp",intPressType,sizeof("downAndUp"))==0)
{
snprintf(buf, sizeof buf,"press %s\n",inKeyName);
}
return adb_monkey_exec(buf,inMonkeySocket);
}
OK,先分析思路,再搭建底層,最后建立應用層,整個adb.exe,或者adb.dll其他的,就可以操控整個android系統了,非常實用。下一講講啥呢?操控是可以,我要根據android設備的屏幕輸出判斷程序結果是否正確怎么辦?怎么獲取圖像?怎么辨別?