学习Python渗透第7天:编写远程控制程序(中)

使用Python执行系统命令

我们在图形化的界面中,基本上都是通过鼠标和键盘的方式来进行大部分的操作,其实大部分操作也可以通过命令行的方式来完成,目前Windows和Linux都提供了可以完成各种命令行的操作。虽然说命令行的操作不如图形化的操作这么的方便,但是将图形化操作转化为网络数据包时,产生的流量非常大,因此远程控制的时候通常选择没有图形化界面的命令行控制方式。

使用subprocess()模块执行系统命令:

subprocess模块中主要包含三个用来创建子进程的函数,subprocess.call(),subprocess.run(),subprocess.Popen(),其中subprocess.call()和subprocess.run()都是通过对subprocess.Popen()函数的封装来实现高级函数,下面我们来分别查看这几个函数的使用方法。

(1)subprocess.call(args,*,stdin=None,stdout=None,stderr=None,shell=False)

这里最为重要的参数就是args,它既可以是一个字符串,也可以是一个包含程序参数的列表,用来指明要执行的命令,使用这个参数我们可以在python中执行对应的系统命令,如果是列表类型,那么第一个参数通常是可执行文件的路径

stdin,stdout,stderr分别表示标准输入,标准输出,错误句柄,他们可以是管道,文件描述符或文件对象,默认值是None,表示从父进程继承。

shell=False参数会让subprocess.call()接收字符串类型的变量作为命令,并调用shell去执行这个字符串,当shell=False时,subprocess.call()函数值接收数组变量作为命令,并将数组的第一个元素作为命令,剩下的全部作为命令的参数。

如果子进程不需要进行交互操作,我们就可以使用该函数类创建子进程,下面我们使用这个函数来启动操作系统。这里我们创建了一个子进程来打开记事本

import subprocess

child=subprocess.call('notepad.exe')

然后我们对这个子进程进行打印,这里的输出信息表示退出信息,如果退出成功表示为0,不成功表示为非0

print(child)

(2)subprocess.run(args,*,stdin=None,input=None,stdout=None,stderr=None,shell=False,timeout=None,check=False)

这里面的args,stdin,stdout,stderr和shell是和subprocess.call()是相同的,其余的参数下面进行解释:

input:用于传递输入给命令的字节字符串。如果提供了 input 参数,则 stdin 参数会被忽略。默认为 None。

timeout:设置命令的超时时间,超过该时间命令将被终止。默认为 None,表示没有超时限制。

check: 如果为 True,当命令返回非零退出代码时,会抛出 CalledProcessError 异常。默认为 False。

例如给出一个使用subprocess.run()函数切换到C盘的系统命令“cd c:"的程序,并输出了subprocess.run()函数的返回类型,值,returncode和stdout

import subprocess

res = subprocess.run(("cd","c:"),stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
#这里参数使用元组传递,元组的第一个作为命令,元组后面的作为命令参数,如果你想用一个字符串来表示命令和参数也可以,但是建议使用元组,这样不容易出错
#这里subprocess.PIPE是将子进程的标准输出和标准错误在父进程中重定向到一个管道中,然后res会对其进行捕获
print(type(res))
print(res)
print('code:',res.returncode,'stdout:',res.stdout)
#res.returncode是返回码,res.stdout这个就是刚才捕获的在管道里面的标准输出
(3)subprocess.Popen()

上面两个函数都是基于subprocess.Popen()函数的封装,如果你希望能够按照自己的想法来使用一些功能时,classPopen类就成了一个最好的选择,这个类的格式如下。

classPopen(args,bufsize=0,executable=None,stdin=None,stdout=None,stderr=None,preexec_fn=None,close_fds=False,shell=False.cwd=None,env=None,universal_newlines=False,startupinfo=None,creationflags=0)

这里最重要的参数就是args,args参数既可以是一个字符串,也可以时一个包含命令和参数的列表,例如使用记事本打开test.txt文件:

subprocess.Popen(["notepade.exe","test.txt"])

另外需要注意的是,Popen对象被创建后,主进程不会自动等子进程完成,编写一个python程序,代码如下:

import subprocess
child = subprocess.Popen(["ping","www.baidu.com"])
print('parent process')
#这里在执行子进程ping百度的时候会同时看到parent process被打印出来,因为主程序不会等子进程结束再开始

如果需要等待子进程结束再开始主进程,那么我们需要用到wait()函数

import subprocess
child = subprocess.Popen(["ping","www.baidu.com"])
child.wait()
print('parent subprocess')

我们已经掌握 了subprocess模块的基本用法,接下来就利用这个模块编写一个执行系统命令的函数:

import subprocess

def run_commend(commend):
    commend=commend.rstrip()
    try:
        child = subprocess.run(commend,shell=True)
        #这里shell=True表示传入的参数是一个字符串,如果是False那就表示以列表传递
    except:
        child = "can not execute the command"
    return child
excute = "dir d:"#这个表示列出D盘的内容
output = run_commend(excute)

总结来说,subprocess.call() 是最简单的,subprocess.run() 是更高级和推荐的方法,而 subprocess.Popen() 则提供了最大的灵活性。根据你的需求,选择合适的方法来执行外部命令。

使用套接字模块实现客户端与服务端通信的程序:

下面来编写客户端的程序,客户端可以将接收到的命令发送给服务端

import socket
str_msg=input("请输入要发送的信息")
s1=socket.socket()
#创建一个套接字对象
s1.connect(("127.0.0.1",2345))
#进行连接
str_msg=str_msg.encode(encode='gbk')
#对传输数据使用encode处理,因为python3不再支持str类型传输,需要转换为bytes类型
s1.send(str_msg)
print(str(s1.recv(1024)))
#接收一个长度为1024的字符串
s1.close()
#关闭连接

然后再编写一个服务器端的程序,来接收客户端发来的数据:

import socket
import subprocess
def run_command(command):
    command=command.rstrip()#rstrip用来删除字符串末尾指定字符,默认为空格
    print(command)
    try:
        child=subprocess.run(command,shell=True)
    except:
        child='can not execute the command'
    return child
s2 = socket.socket()
s2.bind(("127.0.0.1",2345))
s2.listen(5)
str="hello world"
while 1:
    conn,address = s2.accept()
    print('a new connect from',address)
    conn.send(str.encode(encoding='gbk'))
    data=conn.recv(1024)
    data = bytes.decode(data)
    print('the command is '+data)
    output = run_command(data)
conn.close()

这时候当你在客户端输入dir d:你就会得到

持续写入命令进行控制:

执行前面的服务器端和客户端之后,可以发现虽然能成功执行命令,但是执行了我们写入的命令之后,两个程序都退出了,如果希望能在客户端得到一个持续控制的命令行,还需要再客户端和服务器端程序中各自加入一个循环,这样一个程序中就出现了循环的嵌套,变得十分复杂,为了让程序变得更为简洁,这里我们可以考虑使用另一个专门用来处理网络通信的模块socketserver,这是标准库中的一个高级模块,它可以简化客户端跟服务端的程序。

创建和使用socketserver模块的流程如下:

(1)创建一个请求处理的类,这个类继承BasRequestsHandler类,并且要重写父类的handle()方法。

(2)使用Ip地址,端口和第一步创建的类来实例化TCPSever

(3)使用server.server_forver()函数处理多个请求,持续循环运行

(4)关闭连接,使用server_close()函数

在handle()方法里包含四个方法。

init()方法:初始化控制设置,初始化连接套接字,地址,处理实例等信息

handle()方法:定义如何处理每一个连接

setup()方法:在handle()方法之前执行,一般用作设置默认之外的连接配置

finish()方法:在handle()方法之后执行

使用socketserver模块之后的代码如下:

import socketserver
import subprocess
class MyTCPHandler(socketserver.BaseRequestHandler):
#定义一个名为MyTCPHandler的类,继承自socketserver.BaseRequestHandler
    def handle(self):
    #重写父类里的handle方法
        try:
            while 1:
                self.data=self.request.recv(1024)
                #self.request是socketserver.BaseRequestHandler中的一个属性,它表示与客户端建立的连接套接字对象,通过这个套接字对象可以进行数据的收发
                #self.request.recv是调用套接字对象的recv方法来接收客户端发送的数据,最多接收1024字节
                print(self.data)
                print("{} send:".format(self.client_address),self.data)
                #self.client_address是socketserver.BaseRequestHandler类中的一个属性,表示客户端的地址信息,通常是一个元组,包含客户端的IP地址和端口号
                command=self.data.decode()
                command=command.rstrip()
                #rstrip()去除字符串末尾的空格
                child=subprocess.run(command,shell=True)
                if not self.data:
                    print('connection lost')
                    break
                self.request.sendall(self.data.upper())
        except:
            print(self.client_address,"连接断开")
        finally:
            self.request.close()
    def setup(self):
        print('before handle,连接建立',self.client_address)
    def finish(self):
        print('finish run after handle')
if __name__ =='__main__':
    HOST,PORT='127.0.0.1',9999
    server=socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    #这一行代码表示创建一个TCP服务器对象,参数HOST,PORT指定了服务器的主机地址和端口号,MyTCPHandler是刚刚自定义的类。
    server.serve_forever()
    #调用server_forever()方法,用于启动TCP服务器并使其一直运行,直到显示的停止服务器或发生错误
        

修改过后的客户端是这样的:

import socket
client =socket.socket()
client.connect(('127.0.0.1',9999))
while 1:
    cmd=input("请输入命令,想退出请按quit").strip()
    if len(cmd)==0:
        continue
    if cmd=="quit":
        break
    client.send(cmd.encode())
    cmd_res=client.recv(1024)
    print(cmd_res.decode())
client.close()

好了,今天的学习分享到此为止,如果有什么不对的地方欢迎大家的指正,谢谢大家的观看。

  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值