记一次远程CMD开发过程
开发初衷:
有些同学电脑老是要出问题,但又不是什么大问题,通常几句cmd就能搞定。之前解决方案有2:一是远程演示,我口述别人操作;一是我写个cmd脚本,但毕竟不在本机不好调试。(吐槽一下常用的远程控制实在是难用至极)
解决:远程cmd,能够实时的反馈执行结果,带宽占用少
软件环境:
windows10, Microsoft Visual C# 2010 Express, 资料都是百度的!
设计以及有些我觉得该思考的问题:
思考:
1、终端应该做成能在所有能联网的设备上跑,而不是内网版
2、假设终端只在内网上运行,那么一个终端需要知道另一个终端的ip和端口(可通过广播,但麻烦了)
3、做成广域网版使用什么技术(主要指消息是通过转发还是打洞),最后选择转发,因为打洞我还不是很会 -_-''
4、终端应该区分控制端和被控制端?不用,软件内部功能区分就行了;如果终端分开那么服务器端还要做识别,就设计麻烦了
5、先就这么多吧...
大概是这么设计的:
1、服务器开启后可接受2个客户端连接,不区分【控制端】或【被控制端】,只做消息转发(提高效率);只有当两端都连上后才能传输消息,当只有一端存在时该端发送消息会收到服务器提示 "另一端不在线"
2、客户端进入时选择是【控制端】还是【被控制端】,连上后提示另一端是否连接上服务器
3、【控制端】向【被控制端】发送控制命令,【控制端】将收到的消息(期待收到的回显)显示
4、【被控制端】等待命令,收到后执行并将回显显示于该端,最后将回显发送回【控制端】
实现:
const int PORT = ; //Server PORT
const string HOST = "127.0.0.1"; //Server IP
static TcpClient tcp = new TcpClient();
static bool isAlive = false; //连接标识
static bool s=false; //控制端标识,需要传入参数[u999]进入 //包中第一字节为0x04解析为聊天消息,否则为控制消息;别问为什么用0x04,问就是缘分 static void Main(string[] args)
{
//控制端判断
if(args.Length!=)
if(string.Compare(args[],"u999")==) //控制端
{
Console.Write("Welcome, controller.");
s=true;
}
else
Console.WriteLine("Hello loser."); //想爆破密码的?
try{
tcp.Connect(HOST, PORT); //连接阻塞
isAlive = true;
Console.WriteLine("-------已连接=" + tcp.Client.RemoteEndPoint + "------");
}catch(Exception e){
Console.WriteLine(e.Message+"\n任意键退出...");
Console.ReadKey();
return;
} if(!s) //被控制端
{
//发送线程
ThreadStart ts=new ThreadStart(sendToCtrl);
Thread th=new Thread(ts);
th.Start(); Process p = new Process();
p.StartInfo.FileName = "cmd.exe"; //程序名
p.StartInfo.UseShellExecute = false; //不使用程序外壳
p.StartInfo.RedirectStandardInput = true; //重定向输入
p.StartInfo.RedirectStandardOutput = true; //重定向输出
p.StartInfo.RedirectStandardError = true; //错误
p.StartInfo.CreateNoWindow = true; //创建窗口
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //订阅输出
p.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); //错误输出
p.Start();
p.BeginOutputReadLine(); //程序开始后开始订阅
p.BeginErrorReadLine(); byte[] recvBuf = new byte[];
string command = "";
int recvNum;
while(!p.HasExited) //循环执行
{
recvNum = ;
try{
recvNum = tcp.Client.Receive(recvBuf); //服务等待阻塞
}catch(Exception){ //断开连接
break;
}
if(recvNum == ) //主动断开:空消息
break;
if(recvBuf[]!=0x04) //控制消息
{
command = Encoding.UTF8.GetString(recvBuf, , recvNum); //接收远程命令
p.StandardInput.WriteLine(command); //执行
}
else
Console.WriteLine(Encoding.UTF8.GetString(recvBuf,,recvNum-));
Thread.Sleep(); //检测exit命令,while循环条件检查
}
Console.WriteLine("-------End.------\n任意键退出...");
//先关程序,再关连接
p.Close();
p = null;
tcp.Client.Close();
tcp.Close();
tcp=null;
}
else //控制端
{
//接收线程
ThreadStart ts = new ThreadStart(recvMsg);
Thread th = new Thread(ts);
th.Start(); string input = "";
while(true) //循环发送
{
//这里也输入的是什么消息,反正都当控制消息吧(区分要解析输入)
input = Console.ReadLine();
if(isAlive)
tcp.Client.Send(Encoding.UTF8.GetBytes(input+'\n'));
else
break;
Thread.Sleep();
}
}
Console.ReadKey(); //这一句没什么卵用但我不想删
} //控制端回显消息
static void recvMsg()
{
byte[] recvBuf = new byte[];
int recvNum;
while(true) //循环接收
{
recvNum = ;
try{
recvNum = tcp.Client.Receive(recvBuf);
}catch(Exception){
break;
}
if(recvNum == )
break;
//以上为TCP断开判断 //以下为什么要分开写呢?因为合起来麻烦了...
if(recvBuf[]!=0x04) //被控端回显
Console.Write(Encoding.UTF8.GetString(recvBuf,, recvNum));
else //聊天消息
Console.WriteLine(Encoding.UTF8.GetString(recvBuf,, recvNum-));
}
tcp.Client.Close();
tcp.Close();
isAlive = false;
Console.WriteLine("-------Server down------");
} //被控端消息发送
static string output = "";
static void sendToCtrl()
{
int outputFlag = ; //被控端超时发送标记
string outputNow = "";
byte[] sendBytesT = null, sendBytes=null;
while(true) //循环检测缓冲池
{
Thread.Sleep();
if(!isAlive || output.Length == ) //未连接 或 无缓存数据
continue;
if(output.Length > || outputFlag > ) //缓存满4k 或 超时传送150ms
{
outputNow = output;
output = ""; //这段可能有点low, 望大佬告知该怎么写
sendBytesT = Encoding.UTF8.GetBytes(outputNow);
sendBytes = new byte[sendBytesT.Length + ];
sendBytes[] = 0x04;
sendBytesT.CopyTo(sendBytes, ); try{
tcp.Client.Send(sendBytes);
}catch(Exception){
break;
}
outputFlag = ;
continue;
}
++outputFlag;
}
} //被控端输出入池
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if(!string.IsNullOrEmpty(e.Data))
{
Console.WriteLine(e.Data);
output += e.Data + '\n';
}
客户端代码
//服务器端不区分控制端被控制端,只作数据转发
const int PORT = ; //Server PORT
static TcpClient[] tcp=new TcpClient[];
static int alive=; //二进制位标识该终端是否在线=00|01|10|11
static void Main()
{
//消息线程
ParameterizedThreadStart ts=new ParameterizedThreadStart(user); TcpListener listen=new TcpListener(IPAddress.Any,PORT);
TcpClient tcpT=null;
while(true) //循环监听
{
if(alive==) //两个终端==11
{
Thread.Sleep();
continue;
}
listen.Start();
Console.WriteLine("Listen="+PORT);
tcpT=listen.AcceptTcpClient(); //监听阻塞
Console.WriteLine("Accept="+tcpT.Client.RemoteEndPoint);
if(alive<) //==00|01
{
tcp[]=tcpT;
alive|=;
Thread th=new Thread(ts);
th.Start();
}
else //==10,不可能==11(当==11时两个线程都在消息循环中)
{
tcp[]=tcpT;
alive|=;
Thread th=new Thread(ts);
th.Start();
}
listen.Stop();
}
Console.ReadKey();
} //处理一个终端,传入本段代码控制的tcp序号==0|1
static void user(object index0)
{
int index=(int)index0;
if(index!= && index!=) //非法验证
return;
byte[] sendBytesT=Encoding.UTF8.GetBytes(alive==?"Another is connected.\n":"Only you, wait.\n");
byte[] sendBytes=new byte[sendBytesT.Length+];
sendBytes[]=0x04;
sendBytesT.CopyTo(sendBytes,);
tcp[index].Client.Send(sendBytes); byte[] recvBuf = new byte[];
int recvNum;
while(true) //接收[转发]消息
{
recvNum=;
try{
recvNum = tcp[index].Client.Receive(recvBuf);
}catch(Exception){
break;
}
if(recvNum == )
break;
//以上为TCP断开判断 被控制端断开 //Console.WriteLine(recvNum+","+Encoding.UTF8.GetString(recvBuf,0,recvNum));
if(alive==) //两端在线
{
byte[] sendBuf=new byte[recvNum];
Array.Copy(recvBuf,sendBuf,recvNum);
tcp[index==?:].Client.Send(sendBuf);
}
else //一端不在线
{
sendBytesT=Encoding.UTF8.GetBytes("Another is disconnected.Ctrl+C to EXIT.\n");
sendBytes=new byte[sendBytesT.Length+];
sendBytes[]=0x04;
sendBytesT.CopyTo(sendBytes,);
tcp[index].Client.Send(sendBytes);
}
}
tcp[index].Client.Close();
tcp[index].Close();
tcp[index]=null;
alive&=index==?:;
}
服务器代码
一些解释:
1、服务器只允许两个终端连接应该能满足一般使用场景,至少能解决开始叙述的问题
2、客户端开始那个密码区分控制端的东西,其实没多大用,编译出来的exe我用16进制打开都能找到 "密码",希望会密码学的大神不要喷,我确实不大会
3、【被控制端】那段调用cmd的代码网上不少,略有借鉴...
4、【被控制端】消息回显为什么不直接发送回【控制端呢】?因为【被控制端】每输出一行就会产生一个输出重定向,如果每一个重定向都直接使用网络传送,那么将导致网络负荷过大(我猜的),所以根据网络原理我优化了发送界定为:150ms的超时传送 或 4k的超限传送
5、服务器端可以做成多终端连接,但消息转发/群发会麻烦,可改进用于一个终端控制多个终端,考虑使用特征编号之类的识别控制群
6、消息前加不可打印字符最开始是由于:测试开启两个【被控制端】时,会造成消息循环发送,当区分控制消息和显示消息后,显示消息就不会执行并循环控制
7、之后测试开启两个【控制端】,【控制端】之间可以互相发送消息,并且都不会当成控制消息(因为该流程里没有执行这个步骤),所以只能分类处理一下显示出来;估计...大概相当于个能聊天的东西了吧
8、最后我觉得数据转发应该有更牛逼的办法,比如服务器直接把网络流量重定向之类的;说实话我觉得我写的服务器这种接收再转发——实在是low爆了,真的希望大神能帮我改进下
记一次远程CMD开发过程的更多相关文章
- 记一个社交APP的开发过程——基础架构选型(转自一位大哥)
记一个社交APP的开发过程——基础架构选型 目录[-] 基本产品形态 技术选型 最近两周在忙于开发一个社交App,因为之前做过一点儿社交方面的东西,就被拉去做API后端了,一个人头一次完整的去搭这么一 ...
- 记一次解决cmd中执行java提示"找不到或无法加载主类"的问题
今天遇到一个问题:在cmd命令行中,用javac编译java文件可以成功,但是用java执行却提示“找不到或无法加载主类”.现将该问题的原因以及解决办法记录一下. 先理解一下系统变量path和clas ...
- 10.17小作业 基于TCP开发一款远程CMD程序
基于TCP开发一款远程CMD程序 客户端连接服务器后,可以向服务器发送命令 服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端 注意: 执行系统指令使用subprocess ...
- 远程cmd操作
<<PSTools.zip>><<Install_PowerCmd.exe>><<cmder_mini.zip>><< ...
- python练习-Socket实现远程cmd命令
需求:基于tcp的套接字实现远程执行命令的操作 代码示例: # 编辑者:闫龙 #Client端部分 import socket #导入骚凯特模块 CmdObj = socket.socket(sock ...
- 匿名管道 远程cmd
管道是单向的,传送数据的方向是固定的,所以互相通信需要两个管道. STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.dwFlags = STARTF ...
- C语言Socket-模拟远程CMD(客户端向服务器发送命令,服务器执行该命令)
服务端(server) #include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.li ...
- 记一次使用cmd执行java文件遇到的坑...包括“使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题”
今天写了一个java文件,类似聊天软件的东西.在eclipse里输入输出显得没感觉,于是乎就准备在cmd里输入和显示输出.如下图,我准备运行的是ChatDemo.class文件.路径是:D:\work ...
- Springcloud踩坑记---使用feignclient远程调用服务404
公司项目进行微服务改造,由之前的dubbo改用SpringCloud,微服务之间通过FeignClient进行调用,今天在测试的时候,eureka注册中心有相应的服务,但feignclient就是无法 ...
随机推荐
- es6基本语法,vue基本语法
一.es6基本语法 0.es6参考网站 http://es6.ruanyifeng.com/#README 1.let 和 const (1)const特点: 只在局部作用域起作用 不存在变量提升 不 ...
- RSA der加密 p12解密以及配合AES使用详解
在前面的文章中我有说过AES和RSA这两种加密方式,正好在前段时间再项目中有使用到,在这里再把这两种加密方式综合在一起写一下,具体到他们的使用,以及RSA各种加密文件的生成. 一: RSA各种加密相关 ...
- PWN 菜鸡入门之 shellcode编写 及exploid-db用法示例
下面我将参考其他资料来一步步示范shellcode的几种编写方式 0x01 系统调用 通过系统调用execve函数返回shell C语言实现: #include<unistd.h> #in ...
- 【简易bat脚本】启动java程序
前置条件:path中添加了JAVAHOME配置了java环境变量 1.新建txt文本文件 2.粘贴以下内容 @echo off set path=%path%;.;java -classpath &q ...
- centOS7.3 6忘记密码/修改root密码
RedHat最近升级了centos linux操作系统,更新为centos7,更新幅度之大,连红帽官方的认证RHCE也进行了升级,认证必须使用rhel7,可见红帽官方对centos7的重视程度. 最新 ...
- python数据库-安装问题总结(48)
一.ERROR1698(28000):Access denied for user root@localhost错误 我的操作系统是ubuntu: 我的MySQL版本是: 安装完成后,登录mysql的 ...
- Python之Pandas库学习(二):数据读写
1. I/O API工具 读取函数 写入函数 read_csv to_csv read_excel to_excel read_hdf to_hdf read_sql to_sql read_json ...
- Codeforces Gym100962J:Jimi Hendrix(树型DP)
http://codeforces.com/gym/100962/attachments 题意:有一个n个节点的字母树,给出n-1条边的信息,代表边上有一个字母,然后给出长度为m的字符串,问是否能在这 ...
- CSU 1508:地图的四着色(DFS+剪枝)
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1508 题意:地图中四联通的块是一个国家,A和B每个人可以涂两种颜色,且B不能涂超过5次,相邻的国家 ...
- Hackbar再次更新后的破解思路 v2.2.6
不得不说在日常测试和渗透测试中hackbar这一插件给我带来了很大的便利 Hackbar在2.1.3之后的版本就开始收费了虽说价格不是很贵,但我们还是本着学习研究的心态来看看怎么绕过收费验证. 谷歌: ...