美文网首页
Android SSH反向连接实践

Android SSH反向连接实践

作者: cg1991 | 来源:发表于2019-04-29 10:36 被阅读0次

    方案实现依靠以下几篇文章:
    http://blog.163.com/leekwen@126/blog/static/3316622920118144927681/
    http://www.cnblogs.com/whltingyu/p/4083448.html
    https://segmentfault.com/a/1190000002718360

    在android系统中移植ssh服务是基于实时维护移动端设备而提出来的需求,在客户的设备出现问题而研发人员无法处于现场时提出的解决方案。
    在实现这个方案之后可以通过终端执行adb指令,可以进入终端的文件系统删除或修改一些文件,以保证系统能够流畅运行。
    该方案实现思路如下:


    image

    解释一下,C端是PC端的一些ssh客户端,比如bitvise,SecureCRT等;B端是指后台服务器;A是指客户手上的机器,现在要实现的是C通过B连接A,处理A上面出现的问题。

    C端很好解决,下载一个SSH客户端就可以解决问题。B端是后台开发人员需要解决的问题,基本上安装一个ssh服务就可以解决了。A端是要重点解决的地方,涉及到了ssh服务的移植,包含移动端和服务端,另外还要移植ftp,开启服务之后保证服务的稳定性。

    • 第一步,移植ssh服务到Android系统,主要参考的文章是文章开头的第一篇文章,将其编译之后生成的目标文件分别是:dropbear,dropbearkey,scp,sftp-server,ssh,我们实际需要的文件是dropbear和sftp-server,ssh文件。获取到这两个文件之后,通过分析提取文章开头的第二篇文章中的步骤,不断摸索失败总结之后,最终只需要执行以下几行代码:
    private void initSSHFile(){
        MyLog.e(TAG, "init ssh file");
        String[]commands = {
            "rm-rf /data/dropbear/","mkdir /data/dropbear", "mkdir/data/dropbear/.ssh",
            
            "cp-rf /system/dss_host_key /data/dropbear/",//作为服务端生成的密钥
            
            "cp-rf /system/authorized_keys /data/dropbear/.ssh",//客户端生成的密钥pub文件
            
            "chmod-R 0744 /data/dropbear","mount -o rw,remount /",
            
            "echo\"root:x:0:0::/root:/system/bin/sh\" > /etc/passwd",
            
            "echo\"root::14531:0:99999:7:::\" > /etc/shadow",
            
            "echo\"root:x:0:\" > /etc/group",
            
            "echo\"root:!::\" > /etc/gshadow",
            
            "echo\"/system/bin/sh\" > /etc/shells",
            
            "echo\"PATH=\\\"/usr/bin:/usr/sbin:/bin:/sbin:/system/sbin:/system/bin:/system/xbin:/data/local/bin\\\"\"> /etc/profile",
            
            "echo\"export PATH\" >> /etc/profile",
            
            "mkdir/usr", "mkdir /usr/libexec",
            
            "cp-rf /system/bin/sftp-server /usr/libexec/",
            
            "rm-rf /system/priv-app/DLNARemoteService.apk",
            
            "mount-o ro,remount /"
        };
        ShellUtils.CommandResultresult = ShellUtils.execCommand(commands, true, true);
        MyLog.e(TAG,"error is "+ result.errorMsg);
        MyLog.e(TAG,"success is "+ result.successMsg);
    }
    

    方法中需要说明几个的地方是:

    1. dss_host_key是PC端ssh客户端生成的公钥文件,因为ssh是非对称加密,需要将公钥放到远端,私钥放到本地
    2. 需要从system cp到目标目录的几个文件都是需要提前放到升级包(update.zip)中,然后OTA升级。
    3. sftp-server文件在此处是为了PC客户端能够通过ftp打开A端的文件系统
      至此差不多将ssh服务移植到了Android系统了。
    • 接下来执行dropbear指令:
      dropbear-d /data/dropbear/dss_host_key -E -s –v
      在此过程中,B的端口对于A来说是未知的,需要A进程起来的时候主动向B请求ssh连接的地址和端口号,同时将本地开辟的端口通知B端。获取到B端的地址和端口之后就可以连接了:
      String remoteServerPort = "ssh -f -N -R" + portForA + ":localhost:localPort " + "-i /system/yourname.key"+ " remoteName@" + remoteIp;

    • 然后执行指令,在执行这个指令的过程中需要你输入一系列的确认信息,此处需要借助Java的IO接口做动态数据的写入和读取,如下:

    private voidexecuteReverseConnectServerCommand(String command){
        Log.e(TAG , "executeReverseConnectServerCommand:" + command);
        //ShellUtils.CommandResult reverseResult = ShellUtils.execCommand(commands, true , true);
        //Log.e(TAG , "fail result = " + reverseResult.errorMsg);
        //Log.e(TAG , "success result = " + reverseResult.successMsg);
        DataOutputStream os = null;
        Process process = null;
        BufferedReader successResult = null;
        BufferedReader errorResult = null;
        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            successResult = new      BufferedReader(newInputStreamReader(process.getInputStream()));
            errorResult = new BufferedReader(new  InputStreamReader(process.getErrorStream()));
            os.write((command + "\n").getBytes());
            os.flush();
            String s = null;
            StringBuilder successMsg = new StringBuilder();
            StringBuilder errorMsg = new StringBuilder();
            while ((s = errorResult.readLine())!= null) {
                MyLog.e(TAG , "error msg =" + s);
                if(!StringUtil.isEmpty(s)&& s.contains("password")){
                    MyLog.e(TAG ,"password msg = " + s);
                    os.write("yourpassward\n".getBytes());
                    os.flush();
                }else if(!StringUtil.isEmpty(s)&& s.contains("fingerprint")){
                    MyLog.e(TAG ,"fingerprint msg = " + s);
                    os.write("y\n".getBytes());
                    os.flush();
                }else if(!StringUtil.isEmpty(s)&& s.contains("connecting")){
                    MyLog.e(TAG ,"connecting msg = " + s);
                    os.write("y\n".getBytes());
                    os.flush();
                }
                errorMsg.append(s);
                s = null;
            }
            MyLog.e(TAG , "start exit");
            os.write("exit\n".getBytes());
            os.flush();
            int result = process.waitFor();
            MyLog.e(TAG , "result = " + result);
            MyLog.e(TAG , "success msg = " + successMsg.toString());
            MyLog.e(TAG , "error msg1 = " + errorMsg.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }catch(Exception e){
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (successResult != null) {
                    successResult.close();
                }
                if (errorResult != null) {
                    errorResult.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (process != null) {
                process.destroy();
                }
            }
        }
    }
    

    总共确认了三个信息,分别是密码,是否连接这个地址,是否连接。

    在确认完成之后接连接成功了,此时点击PC端的ftp按钮应该是可以弹出远端A的文件系统了,ssh在PC端连接成功之后就相当于是一个终端了,可以执行各种指令操作了。
    相关连接思路的实现参考文章开头的第三篇文章。
    需要注意的是整个过程的实现需要该app有root权限或是系统签名加android:sharedUserId="android.uid.system",不然权限不够执行ssh和dropbear相关指令的时候会失败。

    实现整个方案的过程中比较艰难的是:

    1. ssh服务和客户端的移植,研究在A端支持ftp服务
    2. 移植成功之后,将ssh服务跑起来并将来来回回的流程跑通
    3. 网上没有在android上做ssh反向连接的先例,但是还是一往无前的做了,并做出来了。
      微信公众号:Android部落格

    相关文章

      网友评论

          本文标题:Android SSH反向连接实践

          本文链接:https://www.haomeiwen.com/subject/fdarnqtx.html