• Android NDK学习之第一个实例---端口扫描


    为什么要写一个端口扫描的程序,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;//=='' } (*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=" "; 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! "); // 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 "); 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 ",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 ", 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;//==''
        }
        (*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 
    " , errno , strerror(errno));
            //exit(0);
            return;
        }
        else
        {
            LOGD("Socket created.
    ");
        }
    
        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 
    ");
    //        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 
    " , 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 
    " , 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 
    " , errno , strerror(errno));
            //exit(0);
            return;
        }
        /*
        LOGD("Starting sniffer thread...
    ");
        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 
    " , errno , strerror(errno));
            //exit(0);
            return;
        }
    */
        LOGD("Starting to send syn packets
    ");
    
        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 
    " , 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...
    ");
        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
    ");
            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
    ");
                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
    " , 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);
    }
    
    
    
    
    
  • 相关阅读:
    手动编译安装nginx
    centoos 安装hadoop集群
    block中如何避免循环引用
    正则表达式
    iOS开发ARC内存管理
    block的内部实现
    Block存储区域
    block的语法
    Collection(数组、字典、集合)
    block捕获自动变量和对象
  • 原文地址:https://www.cnblogs.com/makefile/p/3970371.html
Copyright © 2020-2023  润新知