Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android NDK學習之第一個實例---端口掃描

Android NDK學習之第一個實例---端口掃描

編輯:關於Android編程

為什麼要寫一個端口掃描的程序,Java來寫不是很方便嗎? 因為我也沒有想到什麼例子能夠方便的來練習。於是想到以前找到的端口掃描的C代碼,於是想用他們來練習。 掃描服務端端口的方式有許多種,最簡單的就是直接去連接該端口,復雜一些的就是看SYN的應答。其他方式就不說了。 下面的portScan.c位於jni目錄下:(原本可在linux下運行,修改部分代碼,使之能夠返回結果給Java層的調用。) #include<stdio.h> #include<stdlib.h> #include<jni.h> #include <android/log.h>   #include<sys/socket.h> #include<netdb.h> #include<string.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<fcntl.h> #include<time.h> #include<sys/types.h> #define TIMEOUT 5 //由於把socket設置為非阻塞,使用select函數,觀察其                 //5秒後的是否連接成功,連接不成功則認為其端口沒有開放     #define  LOG_TAG    "System.out" #define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)   struct servenet{     char * s_name;     char** s_aliases;     int    s_port;     char*  s_proto; };   //可復用工具方法,其實也不用寫的這麼復雜,因為JNI提供了一個轉換函數,你可以自己去查,但是可以了解c代碼調用Java方法的過程。 //返回char數組的首地址 //將java的String對象類型的數據轉換為C語言的char數組類型 char* Jstring2Cstr(JNIEnv* env,jstring jstr) {     char * rtn=NULL;     jclass clsString=(*env)->FindClass(env,"java/lang/String");     jstring strEncode=(*env)->NewStringUTF(env,"GB2312");     //下一句不知道為什麼不能被保存,有沖突!!!!!!!//     jmethodID mid=(*env)->GetMethodID(env,clsString,"getBytes","(Ljava/lang/String;)[B");     //第三個參數為java中的方法的簽名,可以通過java -s 加上包名.類名 查看     jbyteArray jba=(jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strEncode);     //相當於執行了String的getBytes(“GB2312”)方法     jsize len=(*env)->GetArrayLength(env,jba);     jbyte* jbt=(*env)->GetByteArrayElements(env,jba,JNI_FALSE);//獲取元素     if(len > 0){         rtn=(char*)malloc(len+1);         memcpy(rtn,jbt,len);//將jbyte*復制到rtn中去         rtn[len]=0;//=='\0'     }     (*env)->ReleaseByteArrayElements(env,jba,jbt,0);     //jbyte是新開辟的空間,現在不用了所以要釋放,0表示釋放jba指向的全部內存空間     return rtn; } jintArray startScan(JNIEnv*,char* argv,jint start_port,jint end_port); //下面是主函數,返回int數組 jintArray Java_com_linux_portscaner_MainActivity_scan(JNIEnv*env,jobject clazz,             jstring argv,jint start_port,jint end_port){     LOGD("%s","in c before start");     char *host_argv=Jstring2Cstr(env,argv);     jintArray rtn=startScan(env,host_argv,start_port,end_port);     //return (**env).NewStringUTF(env,"hello from native c");     return rtn; }   //int main(int argc,char** argv) //參數是目標機的IP jintArray startScan(JNIEnv*env,char* argv,jint start_port,jint end_port) {     //int port[65535]={0};index=0;//返回開放的端口號組成的數組     //char* rtn="\n";     struct sockaddr_in server;     int ret;     int len;     int count=0;//number of ports that are open      int scanport;int end_p=(int)end_port;//將jint轉換為int型     jint portsOpen[20]={0};     jintArray jArray=(*env)->NewIntArray(env,20);//int a[20]       int sockfd;     int flag;//標示是ip還是主機名     struct hostent *host;//存放主機信息     fd_set rset;     fd_set wset;         struct servenet *sp; //    if(argc<2) //    { //        fprintf(stderr,"Please enter the server's IP or hostname!\n"); //        exit(1); //    }    //    if((host=gethostbyname("www.baidu.com"))==NULL)     if((host=gethostbyname(argv))==NULL)         flag=0;     else flag=1;//domain name       for(scanport=(int)start_port;scanport<=end_p;scanport++)     {             if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0)))         {         //perror("can not create socket\n");             LOGD("%s","can not create socket");             return;             //exit(1);         }           memset(&server,0,sizeof(struct sockaddr_in));         server.sin_family = AF_INET;         if(!flag){             server.sin_addr.s_addr = inet_addr(argv);         }else             server.sin_addr=*((struct in_addr*)host->h_addr);         server.sin_port = htons(scanport);           int flag2 = fcntl(sockfd, F_GETFL,0);         fcntl(sockfd,F_SETFL, flag2|O_NONBLOCK);         struct timeval tm;         tm.tv_sec = TIMEOUT;         tm.tv_usec = 0;           //connect為非阻塞,連接不成功立即返回-1         if (!connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))){              sp=getservbyport(htons(scanport),"tcp");             //printf("tcp port %d is open:%s\n ",scanport,sp->s_name);              //close(sockfd);              //return (**env).NewStringUTF(env,"open:"+scanport);              LOGD("open1:%d",scanport);                            portsOpen[count++]=scanport;              //rtn=(**env).NewStringUTF(env,strcat(rtn,&scanport));                 }//假如連接不成功,則運行select,直到超時         else {             FD_ZERO(&rset);             FD_ZERO(&wset);             FD_SET(sockfd, &rset);             FD_SET(sockfd, &wset);             int error; //錯誤代碼             int len = sizeof(error);             //5秒後查看socket的狀態變化             if (select(sockfd+1,&rset,&wset,NULL,&tm)>0){                 getsockopt(sockfd, SOL_SOCKET, SO_ERROR,&error, &len );                 if(error == 0)                     //printf("Port %d is opened\n", scanport);                     //close(sockfd);                     //return (**env).NewStringUTF(env,"open2:"+scanport);                 LOGD("open2:%d",scanport);                 portsOpen[count++]=scanport;                 //rtn=(**env).NewStringUTF(env,strcat(rtn,&scanport));             }           }         close(sockfd);       }     //return rtn;     (*env)->SetIntArrayRegion(env,jArray, 0, 19, portsOpen);//將portsOpen中的值復制到jintArray中去,數組copy     return jArray; }   //void Java_com_example_hellondk_MainActivity_scan(JNIEnv*env,jobject clazz){ //    LOGD("%s","in c before start"); //    startScan(); //} 復制代碼 程序需要在有網絡的條件下運行,故需在manifest中加入internet權限。   startScan函數需要兩個參數:端口號的大小范圍,來自用戶界面的用戶輸入,Java 代碼如下:   復制代碼 package com.linux.portscaner;   import java.io.IOException;   import android.app.Activity; import android.content.Context; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.TextView;   public class MainActivity extends Activity {       private static TextView tv1=null;     private EditText ipOrHost,startPort,endPort;     //本地方法     public native int[] scan(String argv,int start_port,int end_port);     static{         System.loadLibrary("portScan");     }     static Handler UIupdater=new Handler(){         @Override         public void handleMessage(Message msg) {             //String buffer=(String)msg.obj;             int []ports=(int[])msg.obj;             StringBuilder sb=new StringBuilder();             sb.append("open:");             for(int i=0;i<ports.length;i++){                 if(ports[i]!=0)                     sb.append(ports[i]).append("  ");             }             tv1.setText(sb.toString());         }     };     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);         tv1=(TextView)findViewById(R.id.tv1);         ipOrHost=(EditText)findViewById(R.id.ipOrHost);         startPort=(EditText)findViewById(R.id.startPort);         endPort=(EditText)findViewById(R.id.endPort);         /*try {             Runtime.getRuntime().exec("su");         } catch (IOException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }*/     }       public void scanRemote(View v){         ConnectivityManager cm = (ConnectivityManager)                    getSystemService(Context.CONNECTIVITY_SERVICE);         boolean hasNet=cm.getActiveNetworkInfo().isAvailable();         if(hasNet){             tv1.setText("正在掃描,請耐心等待。。。");             Thread t=new Thread(new Runnable() {                 @Override                 public void run() {                     int[] openedPort=scan(ipOrHost.getText().toString(),                             Integer.parseInt(startPort.getText().toString()),                             Integer.parseInt(endPort.getText().toString()));                     MainActivity.UIupdater.obtainMessage(0, openedPort).sendToTarget();                 }             });             t.start();         }         else {             tv1.setText("請檢查網絡連接");             return;         }                  //tv1.setText("open:"+openedPort);     }     //A/libc(4643): Fatal signal 11 (SIGSEGV) at 0x00004522 (code=1),     //thread 4685 (Thread-305)       public void scanLocal(View v){         new Thread(new Runnable() {             @Override             public void run() {                 int[] openedPort=scan("10.0.0.2",                         Integer.parseInt(startPort.getText().toString()),                         Integer.parseInt(endPort.getText().toString()));//"localhost"                 MainActivity.UIupdater.obtainMessage(0, openedPort).sendToTarget();             }         }).start();     }     @Override     public boolean onCreateOptionsMenu(Menu menu) {         // Inflate the menu; this adds items to the action bar if it is present.         getMenuInflater().inflate(R.menu.main, menu);         return true;     }   } 復制代碼 布局文件就不列出來了,程序運行後可以嘗試這掃描百度等,但是速度異常的慢,你可以試試掃描某網站的79~90端口。   下面貼上一個syn掃描的linux程序,但是要想讓它能工作在android上比上邊的要費勁。因為它用到了raw_socket需要root權限。   但是NDK的lib不能夠獲得root權限,只有shell能獲得。你可以寫一個助手程序,讓你的linux程序運行在shell中,接收端口號等命令行參數,   將掃描結果存儲到文件或數據庫或與java端建立socket連接或助手程序創建原始套接字後建立UNIX本地套接字傳遞文件描述符給要進行端口掃描的NDK的Native code。   但是自己學的很膚淺,未能進一步實現。下面的代碼實驗證明是不成功的,直接調用su程序並不能獲得root權限。另外有一點需要注意,新開了一條線程專門用來接收包,   沒有加入對時間和數量的控制。join來等待線程執行結束,但是線程中有個循環。   復制代碼 復制代碼 #include<jni.h> #include <android/log.h>   #include<stdio.h> //printf #include<string.h> //memset #include<stdlib.h> //for exit(0); #include<sys/socket.h> #include<errno.h> //For errno - the error number #include<pthread.h> #include<netdb.h>    //hostend #include<arpa/inet.h> #include<netinet/tcp.h>    //Provides declarations for tcp header #include<netinet/ip.h>    //Provides declarations for ip header #include <unistd.h> //fork #define  LOG_TAG    "System.out" #define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)   void * receive_ack( void *ptr ); int process_packet(unsigned char* , int); unsigned short csum(unsigned short * , int ); char * hostname_to_ip(char * ); int get_local_ip (char *); jintArray startScan(JNIEnv*env,char* argv,jint start_port,jint end_port); struct pseudo_header    //needed for checksum calculation {     unsigned int source_address;     unsigned int dest_address;     unsigned char placeholder;     unsigned char protocol;     unsigned short tcp_length;       struct tcphdr tcp; };   struct in_addr dest_ip;   //可復用工具方法 //返回char數組的首地址 //將java的String對象類型的數據轉換為C語言的char數組類型 char* Jstring2Cstr(JNIEnv* env,jstring jstr) {     char * rtn=NULL;     jclass clsString=(*env)->FindClass(env,"java/lang/String");     jstring strEncode=(*env)->NewStringUTF(env,"GB2312");     //下一句不知道為什麼不能被保存,有沖突!!!!!!!//     jmethodID mid=(*env)->GetMethodID(env,clsString,"getBytes","(Ljava/lang/String;)[B");     //第三個參數為java中的方法的簽名,可以通過java -s 加上包名.類名 查看     jbyteArray jba=(jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strEncode);     //相當於執行了String的getBytes(“GB2312”)方法     jsize len=(*env)->GetArrayLength(env,jba);     jbyte* jbt=(*env)->GetByteArrayElements(env,jba,JNI_FALSE);//獲取元素     if(len > 0){         rtn=(char*)malloc(len+1);         memcpy(rtn,jbt,len);//將jbyte*復制到rtn中去         rtn[len]=0;//=='\0'     }     (*env)->ReleaseByteArrayElements(env,jba,jbt,0);     //jbyte是新開辟的空間,現在不用了所以要釋放,0表示釋放jba指向的全部內存空間     return rtn; } jintArray Java_com_linux_portscaner_MainActivity_scan(JNIEnv*env,jobject clazz,             jstring argv,jint start_port,jint end_port){     LOGD("%s","portScan.c before start");     char *host_argv=Jstring2Cstr(env,argv);     jintArray rtn=startScan(env,host_argv,start_port,end_port);     //return (**env).NewStringUTF(env,"hello from native c");     return rtn; } //參數是目標機的IP jintArray startScan(JNIEnv*env,char* argv,jint start_port,jint end_port) {     system("su");     //Create a raw socket     int s = socket (AF_INET, SOCK_RAW , IPPROTO_TCP);     if(s < 0)     {         LOGD("Error creating socket. Error number : %d . Error message : %s \n" , errno , strerror(errno));         //exit(0);         return;     }     else     {         LOGD("Socket created.\n");     }       jint portsOpen[20]={0};     jintArray jArray=(*env)->NewIntArray(env,20);//int a[20]     //Datagram to represent the packet     char datagram[4096];       //IP header     struct iphdr *iph = (struct iphdr *) datagram;       //TCP header     struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));       struct sockaddr_in  dest;     struct pseudo_header psh;       char *target =argv; //argv[1];   //    if(argc < 2) //    { //        printf("Please specify a hostname \n"); //        exit(1); //    }       if( inet_addr( target ) != -1)     {         dest_ip.s_addr = inet_addr( target );     }     else     {         char *ip = hostname_to_ip(target);         if(ip != NULL)         {             LOGD("%s resolved to %s \n" , target , ip);             //Convert domain name to IP             dest_ip.s_addr = inet_addr( hostname_to_ip(target) );         }         else         {             LOGD("Unable to resolve hostname : %s" , target);             //exit(1);             return;         }     }       int source_port = 43591;     char source_ip[20];     get_local_ip( source_ip );       LOGD("Local source IP is %s \n" , source_ip);       memset (datagram, 0, 4096);    /* zero out the buffer */       //Fill in the IP Header     iph->ihl = 5;     iph->version = 4;     iph->tos = 0;     iph->tot_len = sizeof (struct ip) + sizeof (struct tcphdr);     iph->id = htons (54321);    //Id of this packet     iph->frag_off = htons(16384);     iph->ttl = 64;     iph->protocol = IPPROTO_TCP;     iph->check = 0;        //Set to 0 before calculating checksum     iph->saddr = inet_addr ( source_ip );    //Spoof the source ip address     iph->daddr = dest_ip.s_addr;       iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);       //TCP Header     tcph->source = htons ( source_port );     tcph->dest = htons (80);     tcph->seq = htonl(1105024978);     tcph->ack_seq = 0;     tcph->doff = sizeof(struct tcphdr) / 4;        //Size of tcp header     tcph->fin=0;     tcph->syn=1;     tcph->rst=0;     tcph->psh=0;     tcph->ack=0;     tcph->urg=0;     tcph->window = htons ( 14600 );    // maximum allowed window size     tcph->check = 0; //if you set a checksum to zero, your kernel's IP stack should fill in the correct checksum during transmission     tcph->urg_ptr = 0;       //IP_HDRINCL to tell the kernel that headers are included in the packet     int one = 1;     const int *val = &one;       if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)     {         LOGD ("Error setting IP_HDRINCL. Error number : %d . Error message : %s \n" , errno , strerror(errno));         //exit(0);         return;     }     /*     LOGD("Starting sniffer thread...\n");     char *message1 = "Thread 1";     int  iret1;     pthread_t sniffer_thread;       if( pthread_create( &sniffer_thread , NULL ,  receive_ack , (void*) message1) < 0)     {         LOGD ("Could not create sniffer thread. Error number : %d . Error message : %s \n" , errno , strerror(errno));         //exit(0);         return;     } */     LOGD("Starting to send syn packets\n");       int port;     dest.sin_family = AF_INET;     dest.sin_addr.s_addr = dest_ip.s_addr;     for(port = start_port ; port <= end_port ; port++)  //set the port     {         tcph->dest = htons ( port );         tcph->check = 0;    // if you set a checksum to zero, your kernel's IP stack should fill in the correct checksum during transmission           psh.source_address = inet_addr( source_ip );         psh.dest_address = dest.sin_addr.s_addr;         psh.placeholder = 0;         psh.protocol = IPPROTO_TCP;         psh.tcp_length = htons( sizeof(struct tcphdr) );           memcpy(&psh.tcp , tcph , sizeof (struct tcphdr));           tcph->check = csum( (unsigned short*) &psh , sizeof (struct pseudo_header));           //Send the packet         if ( sendto (s, datagram , sizeof(struct iphdr) + sizeof(struct tcphdr) , 0 , (struct sockaddr *) &dest, sizeof (dest)) < 0)         {             LOGD ("Error sending syn packet. Error number : %d . Error message : %s \n" , errno , strerror(errno));             //exit(0);             return;         }     }   //    pthread_join( sniffer_thread , NULL);   //    printf("%d" , iret1); //    pthread_kill(sniffer_thread,0);     //return 0;     pid_t pid=fork();     if(pid==-1){         LOGD("Can't fork process!");         return;     }     if(!pid){//child         start_sniffer();     }     return NULL; }   /*     Method to sniff incoming packets and look for Ack replies */ void * receive_ack( void *ptr ) {     //Start the sniffer thing     start_sniffer(); }   int start_sniffer() {     int sock_raw;     //int count=0;//count the number of open ports     int saddr_size , data_size;     struct sockaddr saddr;       unsigned char *buffer = (unsigned char *)malloc(65536); //It's Big!       LOGD("Sniffer initialising...\n");     fflush(stdout);       //Create a raw socket that shall sniff     sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);       if(sock_raw < 0)     {         LOGD("Socket Error\n");         fflush(stdout);         return 1;     }       saddr_size = sizeof saddr;       while(1)     {         //Receive a packet         data_size = recvfrom(sock_raw , buffer , 65536 , 0 , &saddr , &saddr_size);           if(data_size <0 )         {             LOGD("Recvfrom error , failed to get packets\n");             fflush(stdout);             return 1;         }           //Now process the packet         //portsOpen[count++]=         process_packet(buffer , data_size);     }       close(sock_raw);     LOGD("Sniffer finished.");     fflush(stdout);     return 0; }   int process_packet(unsigned char* buffer, int size) {     //Get the IP Header part of this packet     struct iphdr *iph = (struct iphdr*)buffer;     struct sockaddr_in source,dest;     unsigned short iphdrlen;       if(iph->protocol == 6)     {         struct iphdr *iph = (struct iphdr *)buffer;         iphdrlen = iph->ihl*4;           struct tcphdr *tcph=(struct tcphdr*)(buffer + iphdrlen);           memset(&source, 0, sizeof(source));         source.sin_addr.s_addr = iph->saddr;           memset(&dest, 0, sizeof(dest));         dest.sin_addr.s_addr = iph->daddr;           if(tcph->syn == 1 && tcph->ack == 1 && source.sin_addr.s_addr == dest_ip.s_addr )         {             LOGD("Port %d open\n" , ntohs(tcph->source));             //portsOpen[count++]=ntohs(tcph->source);             fflush(stdout);             return ntohs(tcph->source);         }     } }   /*  Checksums - IP and TCP  */ unsigned short csum(unsigned short *ptr,int nbytes) {     register long sum;     unsigned short oddbyte;     register short answer;       sum=0;     while(nbytes>1) {         sum+=*ptr++;         nbytes-=2;     }     if(nbytes==1) {         oddbyte=0;         *((u_char*)&oddbyte)=*(u_char*)ptr;         sum+=oddbyte;     }       sum = (sum>>16)+(sum & 0xffff);     sum = sum + (sum>>16);     answer=(short)~sum;       return(answer); }   /*     Get ip from domain name  */ char* hostname_to_ip(char * hostname) {     struct hostent *he;     struct in_addr **addr_list;     int i;       if ( (he = gethostbyname( hostname ) ) == NULL)     {         // get the host info         herror("gethostbyname");         return NULL;     }       addr_list = (struct in_addr **) he->h_addr_list;       for(i = 0; addr_list[i] != NULL; i++)     {         //Return the first one;         return inet_ntoa(*addr_list[i]) ;     }       return NULL; }   /*  Get source IP of system , like 192.168.0.6   */   int get_local_ip ( char * buffer) {     int sock = socket ( AF_INET, SOCK_DGRAM, 0);       const char* kGoogleDnsIp = "8.8.8.8";     int dns_port = 53;       struct sockaddr_in serv;       memset( &serv, 0, sizeof(serv) );     serv.sin_family = AF_INET;     serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);     serv.sin_port = htons( dns_port );       int err = connect( sock , (const struct sockaddr*) &serv , sizeof(serv) );       struct sockaddr_in name;     socklen_t namelen = sizeof(name);     err = getsockname(sock, (struct sockaddr*) &name, &namelen);       const char *p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100);       close(sock); }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved