Shawn's Blog

一个伪程序员的伪技术博客

0X00 介绍

阅读并了解 LVM 需要了解:Linux 基本操作、分区概念、文件系统概念

首先 LVM 的全称是 Logical Volume Manager 逻辑卷管理。传统的方式是将一个磁盘分成类似于 sda1/sda2/sda3 的分区,然后再将这些分区格式化成类似于 ext4/xfs 这种文件系统,最后将文件系统挂载到某个目录上。但是这种方式下对磁盘空间进行重分配是比较麻烦的,将新安装的磁盘融入到现有系统中也是比较费力的,这就是 LVM 需要解决的问题。

总结下来 LVM 拥有这些功能

  1. LVM 可以方便的对现有逻辑卷进行压缩(初次分配多的空间不会浪费,可以压缩出来)
  2. 空闲的空间可以随时重新分配给逻辑卷(传统模式只能将空间分给最后一个分区,或者创建新分区)
  3. 新加入的磁盘也可以为其他逻辑卷扩容(传统模式并不方便为某个现有分区扩容)
  4. 可以将两块磁盘融合创建出一个更大的逻辑卷(两块 1T 磁盘可以创建出 2T 的分区)

特别需要注意的,RAID0 也可以将两个 1T 的磁盘合并为一个 2T 的,并且理论读写速度都会翻倍,但是这和 LVM 完全是两种不同的操作。

0X01 相关概念

开始使用 LVM 前需要先搞清楚它的几个基本概念

  • PV 是 Physical Volume 物理卷 — 从磁盘上分出来的物理分区
  • VG 是 Volume Group 卷组 — 多个 PV 组成的一个 Group
  • LV 是 Logical Volume 逻辑卷 — 从某个 VG 里创建出来的逻辑卷(可以格式化够挂载)
  • PE 是 Physical Extent 物理区域 — 是 PV 中最小的存储单元
  • LE 是 Logical Extent 逻辑区域 — 是 LV 中做小的存储单元

LVM Cropped

只是「简单用用」的话可以不管 PE/LE 这两个概念

阅读全文 »

0X00 前言

设计一个系统,不论是 Web 还是其他的什么形式,通过用户名和密码认证也是一个再正常不过的事情了。但是如何保存密码却是一个值得讨论的问题,相信各位最开始的一个有用户名和密码的程序多半也是用明文存储的密码吧 🤓

这里总结了六种比较常用于密码存储的方式,接下来可以逐一进行简单的分析以帮助我们更好的保护用户的密码

一清二白:明文存储密码,直接存 password

掩耳盗铃:使用 BASE64 之类的编码,存储为 cGFzc3dvcmQ=

盘古之法:使用早已不再安全的 md5 之类的摘要算法,存储为

5f4dcc3b5aa765d61d8327deb882cf99

祖宗之法:使用也已经不再安全的 sha-1 之类的摘要算法,存储为

5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

凑合能用:使用现代的安全的例如 sha-256 之类额算法,存储为

5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

现代手段:采用系统化的多次迭代的加盐哈希方式加密,例如常见的 PBKDF2

有兴趣的话可以把我上面的这几个 md5/sha-1/sha-256 拿来到 CrackStation 尝试解一下,就知道弱密码究竟有多危险了。

不过在开始之前要先进行一些简单的科普,以便于能够比较好的理解后面的东西。

名词解释

拖库:简单来说就是有不怀好意的小老弟获取了你数据库的访问权限,甚至将其 dump 然后下载下来了
撞库:当你有某个库的用户名和密码明文了,其实可以拿着用户名密码去其他系统也尝试登录,因为很多人的用户名和密码在各个系统都是一样的,比如同一个邮箱注册的密码也用的完全相同
碰撞:一般是说 hash 碰撞,在有一对原文和对应的 hash 值后再找到一个 hash 值相同但原文不同的的方法
:加盐是一种在进行散列前向原文指定位置插入内容,将散列前的原文变得很长从而使彩虹表等方式失效的行为

密码破解

常见的几种破解密码的方式有:暴力破解、用字典的暴力破解、彩虹表、社会工程学。

其中暴力破解最为暴力(废话噢),就是把所有可能的密码组合一个个的尝试一直尝试到天荒地老。如果是一个 8 位密码且只由大小写数字构成的话也有 62^8=218340105584896 这么多种组合,就算每秒钟能算出来十万个也需要七十年才能完全遍历一遍;
其次是用字典,字典就是将常用的弱密码、生日这种存起来,暴力破解的时候不再尝试所有组合,而是从字典中逐个取出来尝试,速度较快但是成功率降低了,毕竟只尝试了可能性最大的一小部分密码;
彩虹表也是类似于字典,但是字典每条只是一个密码,彩虹表是密码明文与 hash 之后的 key-value 对,当数据库被拖/泄露了之后,可以尝试拿泄露出来的 hash 值来反查密码原文;
社会工程学听起来好像很野,但是谁还没有过用生日和手机号作为密码解密的尝试呢 🤣

阅读全文 »

0X00 前言

本篇文章是这篇「使用 git stash save 将暂存区命名」的重置版。因为根据 Google 的统计数据我得知某些问题的关键词搜索出来之后我的博客排行会比较靠前,所以把最容易被各位点击到的文章做了个重置计划,改进之前的一些不足,争取能够说的更清楚一些,也能节省各位一点点时间,希望能真正的帮助到从搜索引擎点进来的各位~

0X01 极度精炼的使用说明

可以使用 git stash save "message" 的方式为 stash 起来的变动命名,方便后面再次使用。
git-stash-save

stash 起来过后可以使用 git stash list 来查看已经被 stash 的列表,这里可以看到已经有两条了,值得注意的是 stash 的 id 每次都在更新,最近 stash 的是 0,1 就是上次 stash 的,以此类推git-stash-list

如果需要从新应用某个 stash 的改动,可以使用 git stash apply STASH_ID 的方式,例如使用 git stash apply 1 就可以重新应用 id 为 1 的这个 stash。如果要丢弃掉某个 stash 的话使用 git stash drop STASH_ID 就可以了

0X00 换个方式定义函数

本篇内容不严格区分 function 与 method 🥹

我们都知道在 Python 中如何定义一个函数,只需要 def foo(arg_1, arg_2, *args, **kwargs)
就足够了。知道的稍微多一些呢可能知道「Python 中万物皆对象,所以函数的调用也只是调用了函数对象中的 __call__
方法」,所以我们可以尝试用这种方式调用一个函数

1
2
3
4
5
6
7
def say_hello():
print('hello, world')

say_hello.__call__()

# output
# hello, world

既然可以这样调用了,我们也就可以用类似的方法来定义一个假的
function,可以发现我们自定义了随便一个类,但是只要它实现了 __call__ 方法就可以被当做函数一样调用

1
2
3
4
5
6
7
8
class Foo:
def __call__(self):
print('hello, world')

foo = Foo()foo()

# output
# hello, world

0X01 callable

根据上面的方法可知我们可以用 hasattr(obj, '__call__')
来判断某个对象是不是函数,事实上我也确实在同事的代码里看到过这样用的。其实 Python 内置了一个名为 callable
的函数可以用,不过跟 hasattr(obj, '__call__') 并不完全一致

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
class Foo:
pass


class Bar:
def __call__(self):
pass

func = lambda x : x


print(hasattr(Foo, '__call__'), callable(Foo))
# output: (False, True)

print(hasattr(Foo(), '__call__'), callable(Foo()))
# output: (False, False)

print(hasattr(Bar, '__call__'), callable(Bar))
# output: (True, True)

print(hasattr(Bar(), '__call__'), callable(Bar()))
# output: (True, True)

print(hasattr(func, '__call__'), callable(func))
# output: (True, True)

测试代码的第一行 Foo 类因为没有实现 __call__ 方法所以 hasattr 返回的是
False,而它是一个类,调用它就会实例化一个对象出来,所以它是可调用的,所以 callable 就返回了 True;第二行
Foo 类也没有实现 __call__ 方法所以 hasattr 返回的是 False,而且它又只是个普通对象,不是一个 class
所以导致 callable 也返回了 False;第三行因为 Bar 类实现了 __call__ 方法所以 hasattrcallable
都返回了 True;第四行也同理;第五行本是一个函数,所以也都返回了 True

需要注意的是官方文档提到「callable 返回了 True 的不一定真的能调用成功,但是返回 False
的一定不能成功」,比如你强行给某个类设置了一个 __call__
但是又不是函数,可能就会出现这样的问题。不过你非要这么写的话,小心被同事打死噢 🤔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo:
def __init__(self):
self.__call__ = '???'

foo = Foo()print(hasattr(foo, '__call__'))
print(callable(foo))
foo()

# output
# True
# True
# Traceback (most recent call last):
# File "hello.py", line 8, in <module>
# foo()
# TypeError: 'str' object is not callable

相关的官方文档:https://docs.python.org/3/library/functions.html#callable

0X00 可以被强制转换的自定义类

但凡写过 Python
的人应该都用过int()这个函数了,而且也都知道这个是将其他类型转换成int类型的内置方法,稍微用的多一点的还会知道这个方法如果传入不能被强制转换的数据时会抛出TypeError的异常。那你知道如何让自己定义的类可以被强制转换吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3


class A:
def __int__(self):
return 233


a = A()
print(int(a))

# output
# 233

而且按照官方文档来说的话,如果你的class定义了__int__()方法,则int(your_obj)则会返回__int__()的值,如果定义了__index__()则会返回__index__(),如果定义了__trunc__()也会返回__trunc__()。当然是有优先级的,优先级`int

index > trunc`,可以使用如下代码分别注释这些方法测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3


class A:
def __int__(self):
return 1

def __index__(self):
return 2

def __trunc__(self):
return 3


a = A()
print(int(a))

0X01 int 的第二个参数

那你知道它其实还能接收第二个参数吗?其实 int()
方法可以接受第二个参数的,也就是用于进制转换的参数。换言说就是可以用内置的int()方法将其他进制的字符串数据转换成10进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3


print(int('12345')) # 将字符串格式10进制数字转为整型
print(int('12345', base=10)) # base 默认就是10
print(int('12345', base=8)) # 8进制的12345转成10进制
print(int('FFFFF', base=16)) # 16进制的转成10进制
print(int('0XDEADBEEF', 16)) # 当然可以带对应的0X前缀
print(int('011111', 2)) # 将2进制的数字转成10进制

# output
# 12345
# 12345
# 5349
# 1048575
# 3735928559
# 31

0X02 hex、 bin 等其他转换方法

上面提到了进制转换,这里也就顺便说一下这两个方法好了。其中hex可以将整型数字转成0x开头的16进制字符串,bin可以将整形数字转成0b开头的2进制字符串

1
2
3
4
5
6
7
#!/usr/bin/env python3


print(hex(12345))
print(hex(3735928559))
print(bin(12345))
print(bin(23333))

还有几个之前从来不知道,这次写博客才在官方文档看到的用法,不仅可以控制大小写还能控制是否展示0X这种标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [1]: '%#x' % 255, '%x' % 255, '%X' % 255
Out[1]: ('0xff', 'ff', 'FF')

In [2]: format(255, '#x'), format(255, 'x'), format(255, 'X')
Out[2]: ('0xff', 'ff', 'FF')

In [3]: f'{255:#x}', f'{255:x}', f'{255:X}'
Out[3]: ('0xff', 'ff', 'FF')

In [4]: format(14, '#b'), format(14, 'b')
Out[4]: ('0b1110', '1110')

In [5]: f'{14:#b}', f'{14:b}'
Out[5]: ('0b1110', '1110')

In [6]: '%#o' % 10, '%o' % 10
Out[6]: ('0o12', '12')

In [7]: format(10, '#o'), format(10, 'o')
Out[7]: ('0o12', '12')

In [8]: f'{10:#o}', f'{10:o}'
Out[8]: ('0o12', '12')

官方文档参考:

https://docs.python.org/zh-cn/3/library/functions.html#int

https://docs.python.org/zh-cn/3/library/functions.html#hex

https://docs.python.org/zh-cn/3/library/functions.html#bin

https://docs.python.org/zh-cn/3/library/functions.html#oct