• Python灰帽编程——初识Python下(函数与文件)


    1. 函数

    需求:随机密码生成器。

    逻辑上讲,函数就是一个功能;代码上讲,多条Python 语句的集合,语句块。

    1.1 函数介绍

    1.1.1 函数基本概念

    逻辑上讲,函数就是一个功能;代码上讲,函数就是多条Python语句的集合,语句块。

    函数是对程序逻辑进行结构化或过程化的一种编程方法,将整块代码巧妙地隔离成易于管理的小块。把重复代码放到函数中而不是进行大量的拷贝,这样既能节省空间,也有助于保持一致性;通常函数都是用于实现某一种功能。

    1.1.2 创建函数

    Python 中函数是用==def== 语句来创建的,语法如下:

    def function_name(args):
        """
        定义一个函数
        """
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5

    标题行由def 关键字,函数的名字,以及参数的集合(如果有的话)组成。

    def 子句(函数体)的部分包括: - (可选)体现为字符串的函数文档(函数说明,以及帮助等信息) - (必需)函数体

    1.1.3 调用函数

    同大多数语言相同,Python 用()调用函数:

    • 如果没有加圆括号,只是对函数的引用;
    # 01 - 函数的定义和调用.py
    
    def test():			# 定义函数,函数名为test
        print("This is function test")
    
    test()				# 调用函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    函数调用过程:

    • 函数被调用时,会单独开辟一段私有的内存空间。
    • 函数运行结束的之后,会回到调用函数的位置,继续向下执行。
    # 02 - 函数的调用过程.py
    
    def a():
        print("This is function a")
    
    def b():
        print("Function b start...")
        a()
        print("Function b stop!")
    
    b()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.1.4 前向引用

    先定义,后调用。

    >>> test()
    Traceback (most recent call last):
      File "", line 1, in <module>
    NameError: name 'test' is not defined
    >>> def test():
    ...     print("This is function test")
    ... 
    >>> test()
    This is function test
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.1.5 函数返回值

    多数情况下,函数并不直接打印数据,而是向调用者返回一个值:

    • 函数的返回值使用return 关键字
    • 没有return 的话,函数默认返回None
    # 03 - 函数的返回值.py
    
    def add():
        x = 10
        y = 3
        sum = x + y
        # print(sum)
        return sum
    
    print(add() + 7)			# 如果函数没有return的话只是输出不能参与运算,因为函数自身没有值。加上return就是将返回值变成函数自己。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.2 函数参数

    1.2.1 定义参数

    类似于数学上的代数,用字母表示数。参数分为:

    参数类型说明
    形式参数函数定义时,紧跟在函数名后圆括号内的参数被称为形式参数,简称形参。 由于它不是实际存在变量,所以又称虚拟变量。
    实际参数函数调用时,函数名后面括号中的参数(可以是一个表达式) 称为“实际参数”,简称实参。
    # 04 - 形参和实参.py
    
    def add(x, y):
        sum = x + y
        return sum
    
    print(add(10, 3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:实参要与形参一一对应。

    1.2.2 默认参数

    默认参数,不向该参数传入值也是允许的

    # 05 - 默认参数.py
    
    def add(x = 0, y = 0):
        sum = x + y
        return sum
    
    print(add())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.2.3 关键字参数

    关键字参数的概念仅仅针对函数的调用。这种理念是让调用者通过函数调用中的参数名字来区分参数。这样规范允许参数缺失或者不按顺序

    # 06 - 关键字传参.py
    
    def add(x = 0, y = 0):
        sum = x + y
        return sum
    
    print(add(y = 10, x = 3))		#y是形参,10是实参;x是形参,3是实参
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.3 函数变量作用域

    全局和局部的问题。

    1.3.1 局部变量

    局部变量是在函数内部定义的变量,随着函数的调用而被创建,函数调用完毕之后,即销毁。局部变量在函数调用后,才被创建,其作用范围,仅限于函数内部。

    # 07 - 局部变量.py
    
    def test():
        x = 10
        print(x)
    
    test()
    
    print(x)        # 报错NameError: name 'x' is not defined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.3.2 全局变量

    在函数外部定义的变量。

    # 08 - 全局变量.py
    
    x = 10
    
    def test():
        # x = 3
        print(x)
    
    test()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    函数内部可以直接使用函数外部的变量。

    1.3.3 名字空间

    namespace。

    提到变量作用域,可以想像是否可以“看见”这个标识符。

    任何时候,总有一个到三个活动的作用域,分别为局部、全局和内建。标识符(变量名,函数名)的搜索顺序依次是局部、全局和内建。提到名字空间,可以想像是否有这个标识符。

    1.4 其他特性

    1.4.1 函数属性

    函数属性是就是可以是函数名加上句点add.__ 能够访问到的内容。

    # 09 - 函数的属性.py
    
    def add(x = 0, y = 0):
        "add(x, y) means x + y"
        sum = x + y
        return sum
    
    print(add.__name__)		#输出函数名称
    print(add.__doc__)		#输出双引号中内容,文档的说明
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.4.2 内部函数

    在函数体内创建另外一个函数是完全合法的,这种函数叫做内部/内嵌函数。函数A有内部函数,内部函数只能被函数A调用。

    # 10 - 内部函数.py
    
    def a():
        print(f"This is function {a.__name__}")
    
        def b():
            print(f"This is function {b.__name__}")
    
        b()
    
    a()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.4.3 位置参数

    位置参数,与shell 脚本类似,程序名以及参数都以位置参数的方式传递给Python 程序。

    使用sys 模块的argv 列表接收。

    # 11 - 位置参数.py
    import sys
    
    x = sys.argv[1]
    x = int(x)
    y = sys.argv[2]
    y = int(y)
    
    def add(x = 0, y = 0):
        sum = x + y
        return sum
    
    print(add(x = x, y = y)) #等号左边x和y是形参,右边的是实参
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    随机密码生成

    '''
    密码:
        1. 字符集
        2. 密码位数
    '''
    
    import string           #导入了名为string的模块。
    import random           #导入了名为random的模块。
    
    num = int(input("请输入密码位数"))
    c_set = string.printable.strip()      
    # string.printable:获去键盘所有字符,包括空白字符 
    # strip:去除字符串两端空白字符
    
    def password_generator(x):
    
        password = ""
        for i in range (1,x+1):
            c = random.choice(c_set) #使用random模块中的choice函数随机取c_set中的随机字符
            password += c
        return password
    
    print(password_generator(num))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2. 文件对象

    需求:

    Linux shadow 文件破解,从字典文件中读取密码。
    思路:

    从shadow文件中,提取密码密文。
    从密码密文中,提取盐值。
    从密码字典文件中,读取密码。
    把读取的密码与盐值进行加密运算,得到猜测的密码密文。
    如果猜测的密码密文与shadow文件中的密码密文一致,说明密码猜对了。

    kali中盐值的内容是第一个 符到最后一个 符到最后一个 符到最后一个符的前一个字符

    centos7中盐值的内容是第一个 符到最后一个 符到最后一个 符到最后一个

    加密算法(明文,盐值) = 密文

    import crypt
    
    shadow_line = "root:$y$j9T$uEgezfJhn7Ov5naU8bzZt.$9qIqkWYObaXajS5iLDA43uFhdrtt4ZfbmiZjkZFYve2:18711:0:99999:7:::"
    print(f"[+] The shadow line is: {shadow_line}")
    
    #从shadow文件中提取密码密文
    crypt_text = shadow_line.split(":")[1]	#将源字符串以冒号分割为一个列表,输出下标为1的内容
    print(f"[+] The crypt text is:{crypt_text}")
    
    #从密文中提取盐值
    salt = crypt_text[0:crypt.rindex("$")]	#盐值为crypt_text这个列表第0位开始到从右往左数"$"第一次出现的位置
    print(f"[+] The salt is:{salt}")
    
    #从密码字典文件中提取盐值
    password = "123456"
    
    #把读取的密码与盐值进行加密运算,得到猜测的密码密文
    new_crypt_text = crypt.crypt(password,salt)	#将密码使用盐值进行加密
    
    #如果猜测的密码密文与shadow文件中的密码密文一致,说明密码猜对了
    if new_crypt_text == crypt_text:
    	print(f"[+] PASSWORD FOUND: {password}")
    else:
    	print(f"[+] PASSWORD NOT FOUND!")	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    CRYPT加密后的密文长度是固定的

    文件读写操作:

    • 把大象放冰箱里,拢共分几步?
    • 准备字典文件

    函数速查:

    常用函数功能
    f = open(‘PATH’,“打开方式”)打开指定路径的文件f 是文件对象。
    f.read()从文件对象中读取文件内容
    f.readline()读取一行内容
    f.readlines()返回一个列表,元素是文件的每一行内容
    f.write()向文件中写入内容
    f.writelines()以列表的方式向文件中写入内容。
    f.close()关闭文件
    time.sleep()沉睡响应的秒数

    2.1 文件打开方式

    2.1.1 打开文件

    使用open() 函数打开文件(打开冰箱门)

    基本语法:

    >>> f = open("./pass.dic")
    >>> type(f)
    <class '_io.TextIOWrapper'>	#打开文件的类型
    >>>
    
    • 1
    • 2
    • 3
    • 4

    2.1.2 关闭文件

    >>> f.close()
    >>> f.closed	#文件是否关闭
    True
    >>>
    
    • 1
    • 2
    • 3
    • 4

    2.1.3 文件打开方法

    访问模式操作说明
    r
    read
    以读方式打开默认打开文件的方式 文件不存在则报错。
    w
    write
    以写方式打开文件存在则清空重写,不存在则创建。
    a
    add
    以追加模式打开文件不存在则创建,存在则追加。
    +以读写模式打开如r+,w+,a+。
    b以二进制模式打开如rb,wb,ab。

    常用打开文件的参数:

    r
    w
    a
    rb
    wb
    ab
    >>> f = open("./pass.dic", "r")
    >>> f.closed
    False
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.1.4 建议

    文件读写时,使用文件绝对路径

    2.2 文件读取

    2.2.1 read()

    read() 方法用来读取文件内容。

    >>> f = open("./pass.dic",'r')
    >>> f.read()
    '123456\npassword\nabc123\nqwerty\n1qaz@WSX\na1b2c3\n123.com\ntoor\nzhangsan1990\n88888888'
    >>>
    
    • 1
    • 2
    • 3
    • 4

    read() 方法有点莽,读取文件中所有内容,此方法慎用。

    read() 方法比较适合读取二进制文件,包括exe 程序,图片等文件,不适合读取纯文本文件。

    注意:

    • 危险,可能会造成任意文件读取;
    • 如果读取的文件过大而读取文件时申请的内存过小时可能会使整个程序造成崩溃。

    2.2.2 readline()

    读取打开文件的一行(读取下个行结束符之前的所有字节),包括行结束符,作为字符串返回。它也有一个可选的size 参数,默认为-1,代表读至行结束符,如果提供了该参数,那么在超过size 个字节后会返回不完整的行。

    该函数每执行一次,向下读取一行。最后光标的位置会在末尾

    >>> f = open("./pass.dic",'r')
    >>> f.seek(0,0)	#光标恢复到出初始位置(第一行)
    >>> f.readline()
    '123456\n'
    >>> f.readline()
    'password\n'
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.3 readlines()

    readlines() 方法读取所有(剩余的)行然后把它们作为一个元素是字符串的列表返回。

    >>> f.readlines()
    ['abc123\n', 'qwerty\n', '1qaz@WSX\n', 'a1b2c3\n', '123.com\n', 'toor\n', 'zhangsan1990\n', '88888888']
    >>>
    
    • 1
    • 2
    • 3

    2.2.4 文件迭代

    如果需要逐行处理文件,可以结合for 循环迭代(遍历)文件。迭代文件的方法与处理其他序列类型的数据类似。

    # 12 - 文件迭代.py
    
    f = open(file = "文件绝对路径", mode = "文件打开方式")
    
    for i in f:
        print(i.strip())	#首位去空格
    
    f.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    有可能遇到的问题:

    • 在编程时,f.close() 方法容易忘记写。
    • 在处理文件时,有可能会遇到异常,导致整个脚本强制退出,f.close() 不会被执行到。
    # 13 - 文件迭代的改进方法.py
    
    with open(file = "文件绝对路径", mode = "文件打开方式") as f:
        for line in f:
            print(line.strip())	#去掉首尾空白字符
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解释:

    使用with open()语句可以确保文件在使用完毕后正确关闭,即使遇到异常情况也可以保证文件被关闭。

    执行读取文件的时候如果遇到异常,会导致整个脚本强制退出从而无法关闭文件。采用以上方式这样打开文件,即使遇到异常,也能确保文件被关闭

    2.3 文件输入

    2.3.1 write()

    write() 内建方法功能与read() 和readline() 相反。它把含有文本数据或二进制数据块的字符串写入到文件中去。写入文件时,不会自动添加行结束标志,需要程序员手工输入。

    >>> f = open('sec.dic', 'a')
    >>> f.write('360\n')
    4
    >>> f.close()
    >>> f = open('sec.dic', 'a')
    >>> f.write("eversec\n")
    8
    >>> f.close()
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    说明:

    • 这里回显的4,8是文件中的内容长度

    • 写完文件后要关闭,不然写的内容不会显现

    • \n表示换行

    2.3.2 writelines()

    和readlines() 一样,writelines() 方法是针对列表的操作,它接受一个字符串列表作为参数,将它们写入文件,行结束符并不会被自动加入,所以如果需要的话,必须在调用writelines() 前给每行结尾加上行结束符。

    >>>pass_list = ['venustech\n', 'nsfocus\n', 'topsec\n', 'dbappsecrutiy\n', 'knownsec\n', 'colasoft\n', 'sangfor\n', 'qianxin\n', 'chaitin\n', 'sbr-info\n']
    >>> f = open('sec.dic', 'a')	#将路径下的sec.dic文件以追加的方式打开
    >>> f.writelines(pass_list)
    >>> f.close()
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.4 linux shadow爆破脚本

    import crypt
    from termcolor import cprint
    
    with open(file = "/etc/shadows", mode = "r") as p:
        for line in p:
            shadow_line = line.strip()	#去除shadow行的空白字符
            username = shadow_line.split(":")[0]	#以":"进行分割字符串取下标为0内容赋值给username
    
            #从shadow文件中提取密码密文
            crypt_text = shadow_line.split(":")[1]	#以":"进行分割字符串取下标为1内容赋值给crypt_text(加密的密码字符串)
            if crypt_text[0] == "$":
                print(f"[+] The username is: {username}")	#如果加密后的密码字符串是以"$"开头的输出用户名
                print(f"[+] The crypt text is:{crypt_text}")	#如果加密后的密码字符串是以"$"开头的输出加密后的密码字符串
    
                #从密文中提取盐值
                salt = crypt_text[0:crypt_text.rindex("$")]	#盐值为crypt_text这个列表第0位开始到从右往左数"$"第一次出现的位置
                print(f"[+] The salt is:{salt}")
    
                #从密码字典文件中提取密码
                file_path = "/home/kali/tools/wordlists/top_password.txt"	#将密码字典的路径赋值给file_path
                with open(file = file_path, mode = "r") as f:
                    flag = 0
                    for line in f:
                        password =line.strip()	#将提取出来的密码去掉左右两端空白字符
                        new_crypt_text = crypt.crypt(password,salt)	 #把读取的密码与盐值进行加密运算,得到猜测的密码密文
                        if new_crypt_text == crypt_text:
                            cprint(f"[+] PASSWORD FOUND: {password}",color = "green")	#如果猜测的密码密文与shadow文件中的密码密文一致,说明密码猜对了
                            flag = 1
                            break
                    if flag == 0:
                        print(f"[-] PASSWORD NOT FOUND!!!")	#如果密码字典都遍历完了也没有找到密码输出密码没找到
    
            else:
                continue#如果加密后的密码字符串不是以"$"开头的,那么进入下一层循环
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    Docker 和 Kubernetes:技术相同和不同之处
    【 OpenGauss源码学习 —— 列存储(CU)(二)】
    Pandas之datetime数据基础详解。
    jquery列表顺序倒转排序效果
    【Linux】进程控制 (万字)
    红帽认证 | RHCE考试包括哪些内容?
    考研信息管理系统
    Kotlin中布尔类型、字符类型、字符串类型和数组类型
    Java Socket实现简易多人聊天室传输聊天内容或文件
    Java的ReentrantLock(可重入锁)详解上篇
  • 原文地址:https://blog.csdn.net/weixin_58954236/article/details/133012084