需求:随机密码生成器。
逻辑上讲,函数就是一个功能;代码上讲,多条Python 语句的集合,语句块。
函数是对程序逻辑进行结构化或过程化的一种编程方法,将整块代码巧妙地隔离成易于管理的小块。把重复代码放到函数中而不是进行大量的拷贝,这样既能节省空间,也有助于保持一致性;通常函数都是用于实现某一种功能。
Python 中函数是用==def==语句来创建的,语法如下:
def function_name(args):
"""
定义一个函数
"""
pass # 可以表示任意语句
标题行由def 关键字,函数的名字,以及参数的集合(如果有的话)组成。
def 子句(函数体)的部分包括:
同大多数语言相同,Python 用()
调用函数:
# 01 - 函数的定义和调用.py
def test(): # 定义函数,函数名为test
print("This is function test")
test() # 调用函数
函数调用过程:
# 02 - 函数的调用过程.py
def a():
print("This is function a")
def b():
print("Function b start...")
a()
print("Function b stop!")
b()
先定义,后调用。
>>> 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
>>>
多数情况下,函数并不直接打印数据,而是向调用者返回一个值:
# 03 - 函数的返回值.py
def add():
x = 10
y = 3
sum = x + y
# print(sum)
return sum
print(add() + 7) # 函数自身是没有值(为None)。如果该函数有返回值,那么该函数就有值了。
类似于数学上的代数,用字母表示数。参数分为:
参数类型 | 说明 |
---|---|
形式参数 | 函数定义时,紧跟在函数名后圆括号内的参数被称为形式参数,简称形参。 由于它不是实际存在变量,所以又称虚拟变量。 |
实际参数 | 函数调用时,函数名后面括号中的参数(可以是一个表达式) 称为“实际参数”,简称实参。 |
# 04 - 形参和实参.py
def add(x, y):
sum = x + y
return sum
print(add(10, 3))
注意:实参要与形参一一对应。
默认参数,不向该参数传入值也是允许的
# 05 - 默认参数.py
def add(x = 0, y = 0):
sum = x + y
return sum
print(add())
关键字参数的概念仅仅针对函数的调用。这种理念是让调用者通过函数调用中的参数名字来区分参数。这样规范允许参数缺失或者不按顺序。
# 06 - 关键字传参.py
def add(x = 0, y = 0):
sum = x + y
return sum
print(add(y = 10, x = 3)) # 这里的x和y是形参。
全局和局部的问题。
局部变量是在函数内部定义的变量,随着函数的调用而被创建,函数调用完毕之后,即销毁。局部变量在函数调用后,才被创建,其作用范围,仅限于函数内部。
# 07 - 局部变量.py
def test():
x = 10
print(x)
test()
print(x) #NameError: name 'x' is not defined
在函数外部定义的变量。
# 08 - 全局变量.py
x = 10
def test():
# x = 3
print(x)
test()
函数内部可以直接使用函数外部的变量。
namespace。
提到变量作用域,可以想像是否可以“看见”这个标识符。
任何时候,总有一个到三个活动的作用域,分别为局部、全局和内建。标识符(变量名,函数名)的搜索顺序依次是局部、全局和内建。提到名字空间,可以想像是否有这个标识符。
函数属性是就是可以是函数名加上句点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__) # 输出双引号中的内容 文档的说明
说明:__doc__
属性来获取函数、模块、类或方法的文档字符串(双引号中的内容)。
在函数体内创建另外一个函数是完全合法的,这种函数叫做内部/内嵌函数
# 10 - 内部函数.py
def a():
print(f"This is function {a.__name__}")
def b():
print(f"This is function {b.__name__}")
b()
a()
位置参数,与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))
# 随机密码生成器.py
import string
import random
# 方式二
# import sys
# x = sys.argv[1]
x = input("请输入需要生成的密码位数:")
# strip()去除首位空格
c_set = string.printable.strip()
def password_generator(x):
password = ""
for i in range(1, x + 1):
c = random.choice(c_set)
# print(c)
password += c
return password
print("密码生成成功请妥善保管:" + password_generator(int(x)))
说明:
需求:Linux shadow 文件破解,从字典文件中读取密码。
函数速查:
常用函数 | 功能 |
---|---|
f = open(‘PATH’) | 打开指定路径的文件f 是文件对象。 |
f.read() | 从文件对象中读取文件内容 |
f.readline() | 读取一行内容 |
f.readlines() | 返回一个列表,元素是文件的每一行内容 |
f.write() | 向文件中写入内容 |
f.writelines() | 以列表的方式向文件中写入内容。 |
f.close() | 关闭文件 |
time.sleep() | 沉睡响应的秒数 |
使用open() 函数打开文件
基本语法:
>>> f = open("./pass.dic")
>>> type(f)
<class '_io.TextIOWrapper'>
>>>
>>> f.close()
>>> f.closed
True
>>>
访问模式 | 操作 | 说明 |
---|---|---|
r | 以读方式打开 | 默认打开文件的方式 文件不存在则报错。 |
w | 以写方式打开 | 文件存在则清空,不存在则创建。 |
a | 以追加模式打开 | 不存在则创建。 |
+ | 以读写模式打开 | 如r+,w+,a+。 |
b | 以二进制模式打开 | 如rb,wb,ab。 |
常用打开文件的参数:
r
w
a
rb
wb
ab
>>> f = open("./pass.dic", "r")
>>> f.closed
False
>>>
read() 方法用来读取文件内容。
>>> f = open("./pass.dic",'r')
>>> f.read()
'123456\npassword\nabc123\nqwerty\n1qaz@WSX\na1b2c3\n123.com\ntoor\nzhangsan1990\n88888888'
>>>
read() 方法有点莽,读取文件中所有内容,此方法慎用。
read() 方法比较适合读取二进制文件,包括exe 程序,图片等文件,不适合读取纯文本文件。
读取打开文件的一行(读取下个行结束符之前的所有字节),包括行结束符,作为字符串返回。它也有一个可选的size 参数,默认为-1,代表读至行结束符,如果提供了该参数,那么在超过size 个字节后会返回不完整的行。
该函数每执行一次,向下读取一行。
>>> f = open("./pass.dic",'r')
>>> f.readline()
'123456\n'
>>> f.readline()
'password\n'
>>>
readlines() 方法读取所有(剩余的)行然后把它们作为一个元素是字符串的列表返回。
>>> f.readlines()
['abc123\n', 'qwerty\n', '1qaz@WSX\n', 'a1b2c3\n', '123.com\n', 'toor\n', 'zhangsan1990\n', '88888888']
>>>
如果需要逐行处理文件,可以结合for 循环迭代(遍历)文件。迭代文件的方法与处理其他序列类型的数据类似。
# 12 - 文件迭代.py
f = open(file = "./pass.dic", mode = "r")
for i in f:
print(i.strip())
f.close()
说明:strip()
是字符串对象的一个方法,用于去除字符串两端的空白字符(包括空格、制表符、换行符等)。
有可能遇到的问题:
# 13 - 文件迭代的改进方法.py
with open(file = "./pass.dic", mode = "r") as f:
for line in f:
print(line.strip())
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()
>>>
和readlines() 一样,writelines() 方法是针对列表的操作,它接受一个字符串列表作为参数,将它们写入文件,行结束符并不会被自动加入,所以如果需要的话,必须在调用writelines() 前给每行结尾加上行结束符。
>>> l
['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')
>>> f.writelines(l)
>>> f.close()
>>>
with 语句是用来简化代码的。比如在将打开文件的操作放在with 语句中,代码块结束后,文件将自动关闭。用来简化文件操作的打开和关闭。
>>> with open('foo.py') as f:
shadow文件中的内容
root:$y$j9T$uEgezfJhn7Ov5naU8bzZt.$9qIqkWYObaXajS5iLDA43uFhdrtt4ZfbmiZjkZFYve2:18711:0:99999:7:::
说明:
$y$j9T$uEgezfJhn7Ov5naU8bzZt.$9qIqkWYObaXajS5iLDA43uFhdrtt4ZfbmiZjkZFYve2
是加密后的密码。加密的密码
$y$j9T$uEgezfJhn7Ov5naU8bzZt.$9qIqkWYObaXajS5iLDA43uFhdrtt4ZfbmiZjkZFYve2
说明:
$
符号最后一次出现的位置(随机的),$密码密文。脚本内容如下:
import crypt # 只有linux中才能用crypt
# 读取shadow文件
shadow_line = "root:$y$j9T$uEgezfJhn7Ov5naU8bzZt.$9qIqkWYObaXajS5iLDA43uFhdrtt4ZfbmiZjkZFYve2:18711:0:99999:7:::"
print(f"[+] The passwd line is: {shadow_line}")
# 从shadow文件中,提取密文。
crypt_text = shadow_line.split(":")[1]
print(f"[+] The crypt line is: {crypt_text}")
# 从密码密文中,提取盐值。
salt = crypt_text[0:crypt_text.rindex("$")]
print(f"[+] The salt line is: {salt}")
# 从密码字典文件中,读取密码。
password = "root"
# 把读取的密码与盐值进行加密运算,得到猜测的密码密文。
new_text = crypt.crypt(password,salt)
# 如果猜测的密码密文与shadow文件中的密码密文一致,说明密码猜对了。
if new_text == crypt_text:
print(f"[+] PASSWORD FOUND:{password}")
else:
print("[-] PASSWORD NOT FOUND!")
说明:
crypt_text[0:crypt_text.rindex("$")]
:从字符串中获取由0到crypt_text字符串中的最后一个$符号的的字符子串。
crypt_text.rindex("$")
是一个字符串方法 rindex()
的调用,用于找到字符串 crypt_text
中最后一个出现 $
字符的索引位置。
crypt.crypt(password, salt)
是一个 crypt
模块中的函数调用,用于对密码进行加密和哈希处理。
盐值:在第一个 符号到最后一个 符号到最后一个 符号到最后一个符号之间的所有字符。
解密是通过密码或者密码字典以及盐值来进行解密的。
完善该脚本:
import crypt
from termcolor import colored, cprint
# 读取shadow文件
with open(file="/home/kali/shadowWuHu.txt", mode="r") as f:
for line in f:
# 去除字符串首尾空格
shadow_line = line.strip()
# 从shadow文件中,提取密文。
crypt_text = shadow_line.split(":")[1]
# if crypt_text != "*" and crypt_text != "!" and crypt_text != "!*":
if crypt_text[0] == "$":
# 从密码密文中,提取盐值。
salt = crypt_text[0:crypt_text.rindex("$")]
# 从密码字典文件中,读取密码。
with open(file="/home/kali/tools/wordlists/top_password.txt", mode="r") as password:
for line in password:
# 去除密码字符串首尾空格
password_line = line.strip()
print(f"\r[-] Trying password: {password_line}",end= "")
# 把读取的密码字符串与盐值进行加密运算,得到猜测的密码密文。
new_text = crypt.crypt(password_line,salt)
# 如果猜测的密码密文与shadow文件中的密码密文一致,说明密码猜对了。
if new_text == crypt_text:
print(f"\n[+] PASSWORD FOUND= \033[31m{password_line}\033[0m")
break
else:
continue
说明:end=""
参数用于指定打印结束后不换行。
输出结果如下: