python基础知识笔记

以前写的笔记,参考慕课网上廖雪峰老师的视频和廖雪峰老师的博客,感谢廖雪峰老师!
地址为:
http://www.imooc.com/learn/177
http://www.imooc.com/learn/317
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000

1Python变量和数据类型

1.1 变量

a = ‘ABC’时,Python解释器干了两件事情:

  1. 在内存中创建了一个’ABC’的字符串;
  2. 在内存中创建了一个名为a的变量,并把它指向’ABC’。

1.2字符串

如果字符串本身包含’怎么办?比如我们要表示字符串 I’m OK ,这时,可以用” “括起来表示:

“I’m OK”

类似的,如果字符串包含”,我们就可以用’ ‘括起来表示:

‘Learn “Python” in imooc’

如果字符串既包含’又包含”怎么办?
这个时候,就需要对字符串的某些特殊字符进行“转义”,Python字符串用\进行转义。

如果一个字符串包含很多需要转义的字符,对每一个字符都进行转义会很麻烦。为了避免这种情况,我们可以在字符串前面加个前缀 r ,表示这是一个 raw 字符串,里面的字符就不需要转义了。例如:

r’(~~)/ (~~)/‘

如果要表示多行字符串,可以用’’’…’’’表示:

‘’’Line 1
Line 2
Line 3’’’

还可以在多行字符串前面添加 r ,把这个多行字符串也变成一个raw字符串:

r’’’Python is created by “Guido”.
It is free and easy to learn.
Let’s start learn Python in imooc!’’’

1.3 Unicode编码

Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。
Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u’…’表示,比如:

print u’中文’
中文

注意: 不加 u ,中文就不能正常显示。

如果中文字符串在Python环境下遇到 UnicodeDecodeError,这是因为.py文件保存的格式有问题。可以在第一行添加注释

# -- coding: utf-8 --

目的是告诉Python解释器,用UTF-8编码读取源代码。

1.4 布尔类型

Python把0、空字符串’’和None看成 False,其他数值和非空字符串都看成 True

2 List 和 Tuple类型

2.1 List

list是一种有序的集合,可以随时添加和删除其中的元素。
由于Python是动态语言,所以list中包含的元素并不要求都必须是同一种数据类型,我们完全可以在list中包含各种数据:

>>> L = [‘Michael’, 100, True]

通过索引来获取list中的指定元素。
需要特别注意的是,索引从 0 开始,也就是说,第一个元素的索引是0,第二个元素的索引是1,以此类推。

print L[0], L[1]

倒序访问:-1表示最后一个元素,-2表示倒数第二个元素。。。

添加元素:
appent(元素) 在列表末尾添加
insert(索引号,元素) 在指定位置添加

删除元素:
pop() 删除list中最后一个元素
pop(索引号) 删除list中指定位置的元素

对list中的某一个索引赋值,就可以直接用新的元素替换掉原来的元素,list包含的元素个数保持不变。

L = [‘Adam’, ‘Lisa’, ‘Bart’]
L[0] = ‘Bart’
L[2] = ‘Adam’
print L

[‘Bart’, ‘Lisa’, ‘Adam’]

2.2 tuple 元组

另一种有序列表,但是一旦创建完毕,就不能修改。
创建tuple用小括号( )

t = (‘Adam’, ‘Lisa’, ‘Bart’)

获取 tuple 元素的方式和 list 是一模一样的,我们可以正常使用 t[0],t[-1]等索引方式访问元素,但是不能赋值成别的元素。

包含 0 个元素的 tuple,也就是空tuple,直接用 ()表示:

>>> t = ()
>>> print t
()

创建包含1个元素的 tuple 呢?来试试:

>>> t = (1)
>>> print t
1

好像哪里不对!t 不是 tuple ,而是整数1。为什么呢?
因为()既可以表示tuple,又可以作为括号表示运算时的优先级,结果 (1) 被Python解释器计算出结果 1,导致我们得到的不是tuple,而是整数 1。
正是因为用()定义单元素的tuple有歧义,所以 Python 规定,单元素 tuple 要多加一个逗号“,”,这样就避免了歧义。

>>> t = (1,)
>>> print t
(1,)

tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
理解了“指向不变”后,要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。
Alt text

当我们把list的元素’A’和’B’修改为’X’和’Y’后,tuple变为:
Alt text

3 条件判断和循环

3.1 if条件判断

输入用户年龄,根据年龄打印不同的内容,在Python程序中,可以用if语句实现:

1
2
3
4
5
age = 20
if age >= 18:
print 'your age is', age
print 'adult'
print 'END'

python缩进:Python的习惯写法:4个空格,不要使用Tab,更不要混合Tab和空格,否则很容易造成因为缩进引起的语法错误。

if 语句后接表达式,然后用:表示代码块开始。

避免嵌套结构的 if … else …,我们可以用 if … 多个elif … else … 的结构,一次写完所有的规则:

1
2
3
4
5
6
7
8
if age >= 18:
print 'adult'
elif age >= 6:
print 'teenager'
elif age >= 3:
print 'kid'
else:
print 'baby'

3.2 for循环

Python的 for 循环就可以依次把list或tuple的每个元素迭代出来:

1
2
3
L = ['Adam', 'Lisa', 'Bart']
for name in L:
print name

求平均值:

1
2
3
4
5
L = [75, 92, 59, 68]
sum = 0.0
for i in L:
sum = sum + i
print sum / 4

3.3 while循环

计算100以内的所有奇数和:

1
2
3
4
5
6
sum = 0
x = 1
while x < 100:
sum = sum + x;
x = x + 2
print sum

break退出循环。
计算 1 + 2 + 4 + 8 + 16 + … 的前20项的和:

1
2
3
4
5
6
7
8
9
10
sum = 0
x = 1
n = 1
while True:
sum = sum + x
x = x * 2
n = n + 1
if n > 20:
break
print sum

continue继续循环
对已有的计算 0 - 100 的while循环进行改造,通过增加 continue 语句,使得只计算奇数的和:

1
2
3
4
5
6
7
8
9
10
sum = 0
x = 0
while True:
x = x + 1
if x > 100:
break
if x % 2 == 0:
continue
sum = sum + x
print sum

4 dict字典

4.1 dict定义

键值对 key : value。

d = {
‘Adam’: 95,
‘Lisa’: 85,
‘Bart’: 59
}

python 中用花括号{}表示。dict是集合,用len()函数可以计算任意集合的大小。

4.2dict查找

使用 d[key] 的形式来查找对应的 value。
通过 key 访问 dict 的value,只要 key 存在,dict就返回对应的value。如果key不存在,会直接报错:KeyError。
要避免 KeyError 发生,有两个办法:

  • (1)是先判断一下 key 是否存在,用 in 操作符:

    1
    2
    if 'Paul' in d:
    print d['Paul']
  • (2)是使用dict本身提供的一个 get 方法,在Key不存在的时候,返回None:

    1
    print d.get('Paul')

4.3 dict的特点

dict的特点:

  • (1) 查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度随着元素增加而逐渐下降。dict的缺点是占用内存大,还会浪费很多内容,list正好相反,占用内存小,但是查找速度慢。
    由于dict是按 key 查找,所以,在一个dict中,key不能重复。
  • (2) 存储的key-value序对是没有顺序的!
1
2
3
4
5
6
7
8
9
10
11
d = {
'Adam': 95,
'Lisa': 85,
'Bart': 59
}
print 'Adam',": ", d['Adam']
print 'Lisa',": ", d['Lisa']
print 'Bart',": ", d['Bart']

for name in d:
print name, ": ", d.get(name)

如上for循环语句:
打印的顺序不一定是我们创建时的顺序,而且,不同的机器打印的顺序都可能不同,这说明dict内部是无序的,不能用dict存储有序的集合。

  • (3) 作为key的元素必须不可变。Python的基本类型如字符串、整数、浮点数都是不可变的,都可以作为 key。但是list是可变的,就不能作为 key。不可变这个限制仅作用于key,value是否可变无所谓。

4.4 更新dict

往dict中添加新的 key-value:

d[key] = value

如果key已经存在,则赋值会用新的value替换掉原来的value。

1
2
3
4
5
6
7
8
9
10
d = {
95: 'Adam',
85: 'Lisa',
59: 'Bart'
}

d[72] = 'Paul'

for name in d:
print name, ": ", d.get(name)

4.5遍历dict

for循环实现:

1
2
for key in dict:
do something

5 set集合

5.1 set的定义与创建

set的元素没有重复,而且是无序的。
创建 set 的方式是调用 set() 并传入一个 list,list的元素将作为set的元素:

s = set([‘A’, ‘B’, ‘C’])

5.2访问set

由于set存储的是无序集合,所以我们没法通过索引来访问。
访问 set中的某个元素实际上就是判断一个元素是否在set中,用in操作符判断。

5.3set的特点

  • set的内部结构和dict很像,唯一区别是不存储value,因此,判断一个元素是否在set中速度很快。
  • set存储的元素和dict的key类似,必须是不变对象,因此,任何可变对象是不能放入set中的。
  • set存储的元素是没有顺序的。

set的这些特点,可以应用在哪些地方呢?
星期一到星期日可以用字符串’MON’, ‘TUE’, … ‘SUN’表示。
假设我们让用户输入星期一至星期日的某天,如何判断用户的输入是否是一个有效的星期呢?
可以用 if 语句判断,但这样做非常繁琐:

1
2
3
4
5
x = '???' # 用户输入的字符串
if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':
print 'input error'
else:
print 'input ok'

注意:if 语句中的…表示没有列出的其它星期名称,测试时,请输入完整。

如果事先创建好一个set,包含’MON’ ~ ‘SUN’:

weekdays = set([‘MON’, ‘TUE’, ‘WED’, ‘THU’, ‘FRI’, ‘SAT’, ‘SUN’])

再判断输入是否有效,只需要判断该字符串是否在set中:

1
2
3
4
5
x = '???' # 用户输入的字符串
if x in weekdays:
print 'input ok'
else:
print 'input error'

这样一来,代码就简单多了。
白名单,黑名单过滤。

5.4遍历set

for循环实现:

1
2
3
s = set(['Adam', 'Lisa', 'Bart'])
for name in s:
print name

1
2
3
s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])
for x in s:
print x[0], ': ', x[1]

5.5 更新set

添加元素add(),删除元素remove()。
如果删除的元素不存在set中,remove()会报错:KeyError。remove()前要判断。

针对下面的set,给定一个list,对list中的每一个元素,如果在set中,就将其删除,如果不在set中,就添加进去。

1
2
3
4
5
6
7
8
s = set(['Adam', 'Lisa', 'Paul'])
L = ['Adam', 'Lisa', 'Bart', 'Paul']
for key in L:
if key in s:
s.remove(key)
else:
s.add(key)
print s

6 函数

要调用一个函数,需要知道函数的名称和参数,调用函数的时候,如果传入的参数数量不对,会报TypeError的错误,并且给出错误信息。如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError的错误,并且给出错误信息。

以在交互式命令行通过 help(abs) 查看abs函数的帮助信息。
int()函数可以把其他数据类型转换为整数。
str()函数把其他类型转换成 str。

sum()函数接受一个list作为参数,并返回list所有元素之和。请计算 11 + 22 + 33 + … + 100100。

1
2
3
4
5
6
7
L = []
n = 1
while n<=100:
x = n * n
L.append(x)
n = n + 1
print sum(L)

##6.1 函数编写

在Python中,定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用 return 语句返回。

1
2
3
4
5
def my_abs(x):
if x >= 0:
return x
else:
return -x

函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
如果没有return语句,函数执行完毕后也会返回结果,只是结果为 None。

请定义一个 square_of_sum 函数,它接受一个list,返回list中每个元素平方的和:

1
2
3
4
5
6
7
8
def square_of_sum(L):
sum = 0
for value in L:
sum = sum + value * value
return sum

print square_of_sum([1, 2, 3, 4, 5])
print square_of_sum([-5, 0, 5, 15, 25])

6.2函数返回多值

一元二次方程的定义是:ax² + bx + c = 0
请编写一个函数,返回一元二次方程的两个解。

1
2
3
4
5
6
7
8
9
10
11
import math

def quadratic_equation(a, b, c):
delta = math.sqrt(b * b - 4.0 * a * c)
return (-b + delta) / (2.0 * a), (-b -delta) / (2.0 * a)

x1, x2 = quadratic_equation(2, 3, 0)
print x1, x2

print quadratic_equation(2, 3, 0)
print quadratic_equation(1, -6, 5)

其实,Python函数返回的仍然是单一值,为tuple。
在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple。

6.3 递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
注意递归出口。
举个例子,我们来计算阶乘 n! = 1 2 3 n,用函数 fact(n)表示,可以看出:

fact(n) = n! = 1 2 3 (n-1) n = (n-1)! n = fact(n-1) * n

所以,fact(n)可以表示为 n * fact(n-1),只有n=1时需要特殊处理。
于是,fact(n)用递归的方式写出来就是:

1
2
3
4
def fact(n):
if n==1:
return 1
return n * fact(n - 1)

上面就是一个递归函数。可以试试:

>>> fact(1)
1
>>> fact(5)
120
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

如果我们计算fact(5),可以根据函数定义看到计算过程如下:

===> fact(5)
===> 5 fact(4)
===> 5
(4 fact(3))
===> 5
(4 (3 fact(2)))
===> 5 (4 (3 (2 fact(1))))
===> 5 (4 (3 (2 1)))
===> 5 (4 (3 2))
===> 5
(4 6)
===> 5
24
===> 120

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

汉诺塔问题:

1
2
3
4
5
6
7
8
9
def move(n, a, b, c):
if n==1:
print a, '-->', c
return
move(n-1, a, c, b)
print a, '-->', c
move(n-1, b, a, c)

move(4, 'A', 'B', 'C')

6.4 定义默认参数

定义一个计算 x 的N次方的函数:

1
2
3
4
5
6
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

假设计算平方的次数最多,我们就可以把 n 的默认值设定为 2:

1
2
3
4
5
6
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

这样一来,计算平方就不需要传入两个参数了:

>>> power(5)
25

由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面。

6.5 定义可变参数

如果想让一个函数能接受任意个参数,我们就可以定义一个可变参数:

1
2
def fn(*args):
print args

可变参数的名字前面有个 * 号,我们可以传入0个、1个或多个参数给可变参数。
原理:Python解释器会把传入的一组参数组装成一个tuple传递给可变参数,因此,在函数内部,直接把变量 args 看成一个 tuple 就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def average(*args):
sum = 0.0
ave = 0.0
n = 0.0
for t in args:
n = n + 1
sum = sum + t
if n != 0:
ave = sum / n
return ave

print average()
print average(1, 2)
print average(1, 2, 2, 3, 4)

7切片

7.1对list和tuple进行切片

用操作符冒号:

L[起始索引 : 结束索引]

切片操作还可以指定第三个参数:

>>> L[::2]

第三个参数表示每N个取一个,上面的 L[::2] 会每两个元素取出一个来,也就是隔一个取一个。

range()函数可以创建一个数列:

>>> range(1, 101)
[1, 2, 3, …, 100]

请利用切片,取出:

  1. 前10个数;
  2. 3的倍数;
  3. 不大于50的5的倍数。
1
2
3
4
5
L = range(1, 101)

print L[0:10]
print L[2::3]
print L[4:50:5]

倒序切片:

1
2
3
L = range(1, 101)
print L[-10:]
print L[-46::5]

7.2 对字符串切片

Python没有针对字符串的截取函数,只需要切片一个操作就可以完成。

字符串有个方法 upper() 可以把字符变成大写字母:

>>> ‘abc’.upper()
‘ABC’

但它会把所有字母都变成大写。请设计一个函数,它接受一个字符串,然后返回一个仅首字母变成大写的字符串。

1
2
3
4
5
6
7
8
def firstCharUpper(s):
first = s[0].upper()
t = s[1:]
return first + t

print firstCharUpper('hello')
print firstCharUpper('sunday')
print firstCharUpper('september')

8迭代

8.1什么是迭代

在Python中,如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们成为迭代(Iteration)。

Python 的 for循环不仅可以用在list或tuple上,还可以作用在其他任何可迭代对象上。
因此,迭代操作就是对于一个集合,无论该集合是有序还是无序,我们用 for 循环总是可以依次取出集合的每一个元素。
注意: 集合是指包含一组元素的数据结构,我们已经介绍的包括:

  1. 有序集合:list,tuple,str和unicode;
  2. 无序集合:set
  3. 无序集合并且具有 key-value 对:dict

请用for循环迭代数列 1-100 并打印出7的倍数。

1
2
3
for i in range(100):
if i % 7 == 0 :
print i

8.2索引迭代

Python中,迭代永远是取出元素本身,而非元素的索引。
对于有序集合,元素确实是有索引的。有的时候,我们确实想在 for 循环中拿到索引,怎么办?
方法是使用 enumerate() 函数:

1
2
3
L = ['Adam', 'Lisa', 'Bart', 'Paul']
for index, name in enumerate(L):
print index, '-', name

结果是:

0 - Adam
1 - Lisa
2 - Bart
3 - Paul

使用 enumerate() 函数,我们可以在for循环中同时绑定索引index和元素name。但是,这不是 enumerate() 的特殊语法。实际上,enumerate() 函数把:
[‘Adam’, ‘Lisa’, ‘Bart’, ‘Paul’]
变成了类似:
[(0, ‘Adam’), (1, ‘Lisa’), (2, ‘Bart’), (3, ‘Paul’)]
因此,迭代的每一个元素实际上是一个tuple

zip()函数可以把两个 list 变成一个 list:

>>> zip([10, 20, 30], [‘A’, ‘B’, ‘C’])
[(10, ‘A’), (20, ‘B’), (30, ‘C’)]

在迭代 [‘Adam’, ‘Lisa’, ‘Bart’, ‘Paul’] 时,如果我们想打印出名次 - 名字(名次从1开始),请考虑如何在迭代中打印出来。
提示:考虑使用zip()函数和range()函数

1
2
3
L = ['Adam', 'Lisa', 'Bart', 'Paul']
for index, name in zip(range(1, len(L) + 1), L):
print index, '-', name

8.3迭代dict的value

dict 对象有一个 values() 方法,这个方法把dict转换成一个包含所有value的list,这样,我们迭代的就是 dict的每一个 value:

d = { ‘Adam’: 95, ‘Lisa’: 85, ‘Bart’: 59 }
print d.values()
# [85, 95, 59]
for v in d.values():
print v
# 85
# 95
# 59

dict除了values()方法外,还有一个 itervalues() 方法,用 itervalues() 方法替代 values() 方法,迭代效果完全一样。

那这两个方法有何不同之处呢?

  1. values() 方法实际上把一个 dict 转换成了包含 value 的list。
  2. 但是 itervalues() 方法不会转换,它会在迭代过程中依次从 dict 中取出 value,所以 itervalues() 方法比 values() 方法节省了生成 list 所需的内存。
  3. 打印 itervalues() 发现它返回一个 对象,这说明在Python中,for 循环可作用的迭代对象远不止 list,tuple,str,unicode,dict等,任何可迭代对象都可以作用于for循环,而内部如何迭代我们通常并不用关心。

如果一个对象说自己可迭代,那我们就直接用 for 循环去迭代它,可见,迭代是一种抽象的数据操作,它不对迭代对象内部的数据有任何要求。

给定一个dict:
d = { ‘Adam’: 95, ‘Lisa’: 85, ‘Bart’: 59, ‘Paul’: 74 }
请计算所有同学的平均分。

1
2
3
4
5
6
7
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59, 'Paul': 74 }

sum = 0.0
for value in d.values():
print value
sum = sum + value
print sum / len(d)

8.4迭代dict的key和value

首先,我们看看 dict 对象的 items() 方法返回的值:

>>> d = { ‘Adam’: 95, ‘Lisa’: 85, ‘Bart’: 59 }
>>> print d.items()
[(‘Lisa’, 85), (‘Adam’, 95), (‘Bart’, 59)]

可以看到,items() 方法把dict对象转换成了包含tuple的list,我们对这个list进行迭代,可以同时获得key和value:

1
2
for key, value in d.items():
print key, ':', value

9列表生成式

9.1 生成列表

列表生成式则可以用一行语句代替循环生成list:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

这种写法就是Python特有的列表生成式。

请利用列表生成式生成列表 [1x2, 3x4, 5x6, 7x8, …, 99x100]
提示:range(1, 100, 2) 可以生成list [1, 3, 5, 7, 9,…]

1
print [x * (x+1) for x in range(1, 100,2)]

9.2复杂表达式

假设有如下的dict:

d = { ‘Adam’: 95, ‘Lisa’: 85, ‘Bart’: 59 }
完全可以通过一个复杂的列表生成式把它变成一个

HTML 表格:
Alt text

注:字符串可以通过 % 进行格式化,用指定的参数替代 %s。字符串的join()方法可以把一个 list 拼接成一个字符串。

在生成的表格中,对于没有及格的同学,请把分数标记为红色。

1
2
3
4
5
6
7
8
9
10
11
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
def generate_tr(name, score):
if score < 60:
return '<tr><td >%s</td><td style="color:red">%s</td></tr>' % (name, score)
return '<tr><td>%s</td><td>%s</td></tr>' % (name, score)

tds = [generate_tr(name, score) for name, score in d.iteritems()]
print '<table border="1">'
print '<tr><th>Name</th><th>Score</th><tr>'
print '\n'.join(tds)
print '</table>'

9.3条件过滤

列表生成式的 for 循环后面还可以加上 if 判断。例如:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

如果我们只想要偶数的平方,不改动 range()的情况下,可以加上 if 来筛选:

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

请编写一个函数,它接受一个 list,然后把list中的所有字符串变成大写后返回,非字符串元素将被忽略。
提示:

  1. isinstance(x, str) 可以判断变量 x 是否是字符串;
  2. 字符串的 upper() 方法可以返回大写的字母。
1
2
3
4
def toUppers(L):
return [x.upper() for x in L if isinstance(x, str)]

print toUppers(['Hello', 'world', 101])

9.4多层表达式

for循环可以嵌套,因此,在列表生成式中,也可以用多层 for 循环来生成列表。
对于字符串 ‘ABC’ 和 ‘123’,可以使用两层循环,生成全排列:

>>> [m + n for m in ‘ABC’ for n in ‘123’]
[‘A1’, ‘A2’, ‘A3’, ‘B1’, ‘B2’, ‘B3’, ‘C1’, ‘C2’, ‘C3’]

利用 3 层for循环的列表生成式,找出对称的 3 位数。例如,121 就是对称数,因为从右到左倒过来还是 121。

1
print [100*x + 10*y + z for x in range(1, 10) for y in range(10) for z in range(10) if x==z]

10函数式编程

10.1函数式编程简介

函数式编程时一种抽象计算的编程模式。
Alt text

函数式编程的特点:

  • 把计算视为函数而非指令
  • 纯函数式编程:不需要变量,没有副作用,测试简单
  • 支持高阶函数,代码简洁

Python支持的函数式编程特点:

  • 不是纯函数式编程:允许有变量
  • 支持高阶函数:函数也可以作为变量传入
  • 支持闭包:有了闭包就能返回函数
  • 有限度的支持匿名函数

10.2高阶函数

变量可以指向函数:
Alt text

函数名其实就是指向函数的变量:
Alt text
可以将abs指向len

高阶函数:能接收函数做参数的函数。
三段论:
Alt text

1
2
3
4
5
6
import math

def add(x, y, f):
return f(x) + f(y)

print add(25, 9, math.sqrt)

10.3 map函数

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。

注意:map()函数不改变原有的 list,而是返回一个新的 list。

假设用户输入的英文名字不规范,没有按照首字母大写,后续字母小写的规则,请利用map()函数,把一个list(包含若干不规范的英文名字)变成一个包含规范英文名字的list:
输入:[‘adam’, ‘LISA’, ‘barT’]
输出:[‘Adam’, ‘Lisa’, ‘Bart’]

1
2
3
4
def format_name(s):
return s[0].upper() + s[1:].lower()

print map(format_name, ['adam', 'LISA', 'barT'])

10.4reduce()数

reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。

例如,编写一个f函数,接收x和y,返回x和y的和:

1
2
def f(x, y):
return x + y

调用 reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:

先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。

上述计算实际上是对 list 的所有元素求和。虽然Python内置了求和函数sum(),但是,利用reduce()求和也很简单。
reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为100,计算:

reduce(f, [1, 3, 5, 7, 9], 100)
结果将变为125,因为第一轮计算是:
计算初始值和第一个元素:f(100, 1),结果为101。

Python内置了求和函数sum(),但没有求积的函数,请利用recude()来求积:
输入:[2, 4, 5, 7, 12]
输出:245712的结果

1
2
3
4
def prod(x, y):
return x * y

print reduce(prod, [2, 4, 5, 7, 12])

10.5filter()函数

filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
例如,要从一个list [1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数,首先,要编写一个判断奇数的函数:

1
2
3
def is_odd(x):
return x % 2 == 1
filter(is_odd, [1, 4, 6, 7, 9, 12, 17])

结果:[1, 7, 9, 17]
利用filter(),可以完成很多有用的功能,例如,删除 None 或者空字符串:
def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, [‘test’, None, ‘’, ‘str’, ‘ ‘, ‘END’])
结果:[‘test’, ‘str’, ‘END’]
注意: s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符。
当rm为空时,默认删除空白符(包括’\n’, ‘\r’, ‘\t’, ‘ ‘)

请利用filter()过滤出1~100中平方根是整数的数,即结果应该是:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

1
2
3
4
5
6
7
import math

def is_sqr(x):
return math.sqrt(x) % 1 == 0
# return math.sqrt(x) * math.sqrt(x) == x

print filter(is_sqr, range(1, 100))

1
2
3
4
5
6
7
import math

def is_sqr(x):
t = int(math.sqrt(x))
return t * t == x

print filter(is_sqr, range(1, 100))

10.6自定义排序函数

Python内置的 sorted()函数可对list进行排序:

>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

但 sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

因此,如果我们要实现倒序排序,只需要编写一个reversed_cmp函数:

1
2
3
4
5
6
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0

这样,调用 sorted() 并传入 reversed_cmp 就可以实现倒序排序:

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]

sorted()也可以对字符串进行排序,字符串默认按照ASCII大小来比较。
对字符串排序时,有时候忽略大小写排序更符合习惯。请利用sorted()高阶函数,实现忽略大小写排序的算法。
输入:[‘bob’, ‘about’, ‘Zoo’, ‘Credit’]
输出:[‘about’, ‘bob’, ‘Credit’, ‘Zoo’]

1
2
3
4
5
6
7
8
9
10
def cmp_ignore_case(s1, s2):
temp1 = s1.lower()
temp2 = s2.lower()
if temp1 < temp2:
return -1
if temp2 > temp1:
return 1
return 0

print sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)

10.7返回函数

定义一个函数 f(),我们让它返回一个函数 g,可以这样写:

1
2
3
4
5
6
7
def f():
print 'call f()...'
# 定义函数g:
def g():
print 'call g()...'
# 返回函数g:
return g

仔细观察上面的函数定义,我们在函数 f 内部又定义了一个函数 g。由于函数 g 也是一个对象,函数名 g 就是指向函数 g 的变量,所以,最外层函数 f 可以返回变量 g,也就是函数 g 本身。
调用函数 f,我们会得到 f 返回的一个函数:

>>> x = f() # 调用f()
call f()…
>>> x # 变量x是f()返回的函数:


>>> x() # x指向函数,因此可以调用
call g()… # 调用x()就是执行g()函数定义的代码

请注意区分返回函数和返回值:

1
2
3
4
def myabs():
return abs # 返回函数
def myabs2(x):
return abs(x) # 返回函数调用的结果,返回值是一个数值

返回函数可以把一些计算延迟执行。
例如,如果定义一个普通的求和函数:

1
2
def calc_sum(lst):
return sum(lst)

调用calc_sum()函数时,将立刻计算并得到结果:

>>> calc_sum([1, 2, 3, 4])
10

但是,如果返回一个函数,就可以“延迟计算”:

1
2
3
4
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum

调用calc_sum()并没有计算出结果,而是返回函数:

>>> f = calc_sum([1, 2, 3, 4])
>>> f

对返回的函数进行调用时,才计算出结果:

>>> f()
10

由于可以返回函数,我们在后续代码里就可以决定到底要不要调用该函数。

请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

1
2
3
4
5
6
7
8
9
10
def calc_prod(lst):
def product():
pro = 1
for x in lst:
pro = pro * x
return pro
return product

f = calc_prod([1, 2, 3, 4])
print f()

1
2
3
4
5
6
7
8
def calc_prod(lst):
def lazy_prod():
def f(x, y):
return x * y
return reduce(f, lst, 1)
return lazy_prod
f = calc_prod([1, 2, 3, 4])
print f()

10.8闭包

在函数内部定义的函数无法被外部访问:

1
2
3
4
5
6
def g():
print 'g()...'

def f():
print 'f()...'
return g

将g的定义移入函数f内部,防止其他代码调用g:

1
2
3
4
5
def f():
print 'f()...'
def g():
print 'g()...'
return g

但是,考察上一小节定义的 calc_sum 函数:

1
2
3
4
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum

注意:发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 lst。

像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)

闭包的特点
返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:
希望一次返回3个函数,分别计算1x1,2x2,3x3:

1
2
3
4
5
6
7
8
9
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs

f1, f2, f3 = count()

可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9(请自己动手验证)。
原因就是当count()函数返回了3个函数时,这3个函数所引用的变量 i 的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算 i*i,当 f1 被调用时:

>>> f1()
9 # 因为f1现在才计算i*i,但现在i的值已经变为3

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
返回闭包不能引用循环变量。

考察下面的函数 f:

1
2
3
4
def f(j):
def g():
return j*j
return g

它可以正确地返回一个闭包g,g所引用的变量j不是循环变量,因此将正常执行。

正确做法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
def count():
fs = []
for i in range(1, 4):
def f(j):
def g():
return j * j
return g
r = f(i)
fs.append(r)
return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

10.9匿名函数

高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算 f(x)=x2 时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

通过对比可以看出,匿名函数 lambda x: x * x 实际上就是:

1
2
def f(x):
return x * x

关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。返回函数的时候,也可以返回匿名函数。

1
2
3
4
5
6
7
def is_not_empty(s):
return s and len(s.strip()) > 0

print filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])

#lamba匿名函数改写
print filter(lambda s: s and len(s.strip()) > 0, ['test', None, '', 'str', ' ', 'END'])

杂七杂八工具收集

收集有用的工具

不定时更新

1 图片类

在线图标制作工具 https://freeiconmaker.com/

阿里在线矢量图标生成库 http://iconfont.cn/

2 编程类

Eclipse主题 http://eclipsecolorthemes.org/

嵌入式Linux网站wiki http://elinux.org/Main_Page

在线画流程图工具 https://www.processon.com/

3 Google相关

直接下载Google Play上的应用 http://apkleecher.com/http://apps.evozi.com/apk-downloader/

4 阅读类

豆瓣读书量统计 http://www.yuedudna.com/ 

kindle的RSS推送网站  http://kindle4rss.com/

git使用小记

1配置github

参考github官方文档https://help.github.com/articles/set-up-git/ 配置了HTTPS和SSH连接。

2 在github中创建版本库

略。可参见github官方文档https://help.github.com/articles/create-a-repo

3 初始化本地版本库

进入项目根目录,输入:

git init

4 将项目文件添加到版本库中

还是在项目根目录中输入:

git add .

5 添加项目改动说明

还是在项目根目录中输入:

git commit -m “第一次提交,创建项目。”

6 将本地仓库与github项目仓库xxx相关联

在本地项目仓库的根目录中,输入:

git remote add origin git@github.com:yourname/xxx.git

7 将本地库的内容同步到github上

git push -u origin master

注意:由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

如果发生了这样的错误: 无法推送一些引用到 'git@github.com:yourname/xxx.git' 提示:更新被拒绝,因为远程版本库包含您本地尚不存在的提交。这通常是因为另外 提示:一个版本库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更 提示:(如 'git pull ...')。

则使用强行更新 +master:

git push -u origin +master

8 将本地库与github项目仓库相关联的另外一种办法

在本地仓库的根目录中,输入:

git clone https://github.com/yourname/xxx.git

进入xxx目录中,将所有文件复制到上一层目录中:

cd xxx

cp -r * ../

回到本地仓库根目录,删除xxx目录的所有文件

rm -rf xxx/

这样也能将本地仓库和远程仓库相关联,接下来可以添加文件到本地仓库,再提交内容到github。

8 git的回滚操作

8.1 先使用git log查看日志,找到想要回滚的版本

在项目目录中,输入

git log

结果如下图所示: enter image description here

8.2 再使用git reset回滚到指定版本

git reset –hard 4bb7bbc07f4b3792b48a6001bdfcc2b694cd3c81

即可回滚成功。

2014年读书统计

从12年开始在豆瓣读书上记录自己读过的书,这几天看到有人把读书情况统计出来了,创意跟支付宝十年消费记录总结很像,但是找了豆瓣读书里面没有发现有这样的功能,用google发现了阅读DNA这个网站http://www.yuedudna.com/,它利用豆瓣的开放API,可以统计账户的读书数量。 大憨2014年共读过40本书。其中五星好书18本, 12月最勤快读了7本, 平均 9.10天读一本书。不统计不知道自己比2013年少读了一半数量的书,各种审计统计还是蛮重要的,哈哈哈。

2014年各月读书数量 夏天的时候读书最少,难道是因为天气太热了?可能是因为找实习,找工作分了心。11月份买了个kindle,的确对阅读量的提高有很大的帮助。 2013年各月读书数量 13年读书1月份、3月份读书多那是因为把以前自己读过的书,能想起来的也给记录了。

12-14年对比

2014年读的书: 2014年读的书

读研以后,各种书读的比以前多了,读书到一定量后,知道书中哪些东西需要看慢一点,哪些东西可以看快点,感觉自己读书的速度也变快了。

2015年的目标,根据自己的工作和兴趣爱好,每周最少读一本书,总共读60本以上。

App二次打包

Android APP二次打包

1 使用Apktool逆向应用

apktool d xxx.apk xxx-re

2 修改逆向出来的smali文件

3 使用apktool重新打包

进入xxx-re目录中:

apktool b

进入dist目录,修改重新打包的apk的名字xxx1.apk.

4 生成签名证书

keytool -genkey -v -keystore nstrt -alias nstrt -keyalg RSA -keysize 2048 -validity 365

5 使用第4步生成的证书对apk进行签名

jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore nstrt xxx1.apk nstrt

6对签名后的App进行优化

zipalign -v 4 xxx1.apk xxx_align.apk

7 使用测试用的签名证书

java -jar signapk.jar testkey.x509.pem testkey.pk8 xxx_align.apk xxx_new.apk

8第5步和第7步的区别

如果apk中申请的有系统权限,则需要执行第7步的操作,否则,只要第5步的即可。

Kali更新Metaploit失败解决办法

Kali的Metasploit两个多星期没有用,没有更新,再去更新使用,发生了如下的错误:

无法下载 http://http.kali.org/kali/pool/main/m/metasploit-framework/metasploit-framework_4.10.0-2014101501-1kali0_amd64.deb 404 Not Found 无法下载 http://http.kali.org/kali/pool/non-free/m/metasploit/metasploit_4.10.0-2014101501-1kali0_amd64.deb 404 Not Found E: 有几个软件包无法下载,您可以运行 apt-get update 或者加上 –fix-missing 的选项再试试?

原来用的是官方源,换了阿里云的kali源,成功解决办法。 阿里云Kali源为:

deb http://mirrors.aliyun.com/kali kali main non-free contrib deb-src http://mirrors.aliyun.com/kali kali main non-free contrib deb http://mirrors.aliyun.com/kali-security kali/updates main contrib non-free

修改软件更新源配置文件:

leafpad /etc/apt/sources.list

将阿里云的Kali源粘贴进去,同时将官方源用#号注释掉。 再:

apt-get update apt-get upgrade

升级Kali

Android取证基础知识

1 常用命令

df -h 查看挂载的设备 dmesg 用来显示开机信息,kernel会将开机信息存储在ring buffer中。您若是开机时来不及查看信息,可利用dmesg来查看。开机信息亦保存在/var/log目录中,名称为dmesg的文件里。 开机时候的硬件检测信息在这里面存着。

2 数据恢复工具sleuthkit

先了解了,以后再深入学习使用。

sudo apt-get install sluthkit sudo apt-get install autospy

查看某个分区命令:

sudo fsstat /dev/sdc sudo mmls /dev sdb

3 Android数据存储

持久数据存储的五种存储方式: - 共享优先 - 内部存储 - 外部存储 - SQLite - 网络

Android应用一般把数据存储在两个地方:内部存储和外部存储。应用在安装的时候会在内部存储的

3.1 共享优先

Shared Preference是一种简单的、轻量级的名称/值对(VNP)机制,用于保存原始应用程序数据,最常见的是用户的应用程序首选项。存储格式为XML,存储的类型有: - boolean - folat 单精度32位IEEE 754浮点数 - int - long - strings 通常是utf-8编码

比如美团的客户端: Alt text

其中loginStroe.xml内容如下:

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
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<int name="bindedBankCard" value="0" />
<int name="hasPaymentPassword" value="0" />
<string name="saveAmount">0.0</string>
<string name="avatarurl"></string>
<long name="id" value="32793294" />
<string name="username">大憨846</string>
<string name="token">×××××××××××××××××××</string>
<string name="value">0.0</string>
<int name="growthlevel" value="2" />
<int name="hasPassword" value="1" />
<int name="saveTimes" value="0" />
<int name="pointvalue" value="164" />
<int name="growthvalue" value="1167" />
<string name="SID">××××××××××××××××</string>
<null name="al" />
<int name="isAppUser" value="1" />
<string name="oauth_login_result">{&quot;a&quot;:&quot;tencent&quot;,&quot;b&quot;:&quot;E765B31B8DB83B6309D41A472ED9093D&quot;,&quot;d&quot;:&quot;&quot;,&quot;c&quot;:1401330213950}</string>
<string name="email"></string>
<int name="avatartype" value="0" />
<int name="reallevel" value="2" />
<int name="loginTimes" value="19" />
<int name="bindedMobile" value="1" />
<string name="mobile">135****×××××××</string>
</map>

3.2 内部存储

在内部存储器上的/data/data目录下建立一个以应用程序包名为名字的子目录存储应用的数据。 最常见的应用程序数据标准目录为: 表1 /data/data/<软件包名称>子目录

目录 作用
shared_prefs 以XML格式存储的共享优先目录
lib 自定义的库文件
files 开发者存储的文件
cache 应用程序缓存文件
databases SQLite数据库和日志文件

3.3 外部存储

3.4 SQLite

Android应用的SQLite文件通常存储在内部存储器的/data/data/<应用程序包名>/databases目录下,属于应用私有的,只能被创建他们的应用程序访问。但是,对于在其他地方创建的数据库没有设置任何限制。 Alt text

如何将其拷贝出来? 这主要是因为权限问题,adb shell中使用命令su root转为root权限,然后修改文件夹和文件的权限为777

adb shell su root chmod 777 /data/data/com.sankuai.meituan/databases chmod 777 /data/data/com.sankuai.meituan/databases/meituan.db

再在Ubutud的另外一个终端中输入:

adb pull /data/data/com.sankuai.meituan/databases/meituan.db

将meituan.db拷贝到了Ubuntu终端中的当前目录

拷贝出来后,可以使用一下sqlite3命令打开

sqlite3 dbname.db

然后可以用SQL语句查看数据库

Alt text

或者使用图形界面的SQLite管理工具,在Ubuntu下,大憨推荐使用SQLiteMan。

4 内核、系统和应用日志

4.1 Linux内核日志

使用命令dmesg查看。

4.2 Logcat

Logcat 显示系统更新和应用程序调试信息 查看无线电日志

adb shell logcat -b radio

这些日志有如下信息: - 事件时间(以UNIX时间格式表达) - 蜂窝调制解调器进行通信使所使用的AT命令 - 接收者、时间以及编码的短信消息 - 终端设备的蜂窝IP地址、网络以及地点信息 - 无线通信服务提供商信息

tips Unix 时间转标准时间:

date -d @1407199900

Alt text

查看事件日志

adb shell logcat -b event

4.3 dumpsys

dumpsys提供关于服务、存储及系统其他方面很细节的信息。

dumpsys [Option] [package_name | pid] Option: meminfo 显示内存信息 cpuinfo 显示CPU信息 account 显示accounts信息,感觉这个最有用了 activity 显示所有的activities的信息 window 显示键盘,窗口和它们的关系 wifi 显示wifi信息 location 显示位置信息,有基站和GPS的 更多信息: 1 SurfaceFlinger 2 accessibility 3 account 4 activity 5 alarm 6 appwidget 7 audio 8 backup 9 battery 10 batteryinfo 11 bluetooth 12 bluetooth_a2dp 13 clipboard 14 connectivity 15 content 16 cpuinfo 17 device_policy 18 devicestoragemonitor 19 diskstats 20 dropbox 21 entropy 22 ethernet 23 hardware 24 input_method 25 iphonesubinfo 26 isms 27 keybar 28 location 29 media.audio_flinger 30 media.audio_policy 31 media.camera 32 media.player 33 meminfo 34 mount 35 netstat 36 network_management 37 notification 38 package 39 permission 40 phone 41 power 42 search 43 sensorservice 44 simphonebook 45 statusbar 46 telephony.registry 47 throttle 48 uimode 49 usagestats 50 vibrator 51 wallpaper 52 wifi 53 window

4.4 dumpstate

显示了很多调试信息和系统信息。 dumpstate信息:






























































































































































































































































































































































分段区域

文件或者命令

备注

Strack traces

没有


Device info

没有


System

没有


Memory info

/proc/meminfo


Cpu info

top -n 1 -d 1 -m 30 -t


Procrank



Virtual memory stats

/proc/vmstat


Vmalloc info

/proc/vmallocinfo


Slab info

/proc/slabinfo


Zone info

/proc/zoneinfo


System log

logcat -v time -d :v


Envent log

logcat -b envents -v time -d :v


Radio log

logcat -b radio -v time -d *:v


Network interfaces

netcfg


Network routes

/proc/net/route


Arp cache

/proc/net/arp


Dump Wi-Fi firmware log

su root dhutil -i eth0 upload/data/local/tmp/ wlan_crash.dump

命令未成功

System propertiess

没有


Kernel log

dmesg


Kernel wakelocks

/proc/wakelocks


Kernel cpufreq

/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state


Vold dump

vdc dump


Secure containers

vdc asec list


Processes

ps -p


Processes and threads

ps -t -p -p


Librank

(librank)


Binder failed transaction log

/proc/binder/failed_transcation_log

华为M1未发现

Binder transaciton log

/proc/binder/transcation_log

华为M1未发现

Binder transcations

/proc/binder/transcations

华为M1未发现

Binder stats

/proc/binder/stats

华为M1未发现

Binder process stat

sh -c cat/proc/binder/proc/ -p

华为M1未发现

File System and free space

df


Package settings

/data/system/packages.xml


Package uid errors

/data/system/uiderrors.txt


Last kmsg

/proc/last_kmsg


Last radio log

parse_radio_log/proc/last_radio_log

华为M1未发现

Last panic console

/data/dontpanic/apanic_console


Last panic threads

/data/dontpanic/apanic_threads


Blocked process wait

没有


channels



Backlights

没有


Dumpsys

dumpsys

4.5 bugreporter

将logcat、dumpsys和dumpstate输出结合在一起,提交调试报告。 可将结果重定向到一个文件中,以便分析。

5 RAM进程内存转储

在POSIX兼容的平台上,SIGUSR1和SIGUSR2是发送给一个进程的信号,它表示了用户定义的情况。它们的符号常量在头文件signal.h中定义。在不同的平台上,信号的编号可能发生变化。

Android将应用程序所使用的内存转储成为一个文件的机制:向该应用程序发送一个特别的信号(SIGUSR1),信号编号为1294。 具体操作步骤: S1 改变转储结果所在目录的访问权限:

adb shell su root chmod 777 /data/misc

S2 在获取应用程序的进程号,比如美团客户端的:

adb shell ps | grep meituan

Alt text

其PID为14606

S3 使用kill命令向进程发送信号:

kill -10 14606

可是没有成功,后来查询stack overflow才知道,在新Android的新平台上已经舍弃了这种方法。 可以使用MAT。

使用drozer对Android应用进行安全评估

使用drozer对Android应用进行安全评估的测试例子

看着drozer的用户说明文档,试了几个Android App,测试发现这几个App都基本没什么问题,只好又用drozer提供的sieve来进行练习了。进行安装评估的步骤一般也就是下面的标题步骤了。

1 在Android设备上安装使用sieve

sieve是一个密码管理器App,用来展示Android应用的一些共同缺陷,可以用来练习使用drozer。 https://www.mwrinfosecurity.com/system/assets/380/original/sieve.apk下载sieve.apk。 打开模拟器,安装sieve: adb intsall sieve apk 然后设置sieve,第一次使用需要设置打开sieve软件的密码和PIN码。 给sieve增加内容,添加一些要管理的帐号密码信息 Alt text

Alt text

在seting里面还有其他的功能:

Alt text

2 获取App Package信息

drozer每个模块的作用: Alt text

获取App包信息的模块是\app.package.* **: Alt text

2.1获取获取Android设备上的所有的安装的App的包名

命令是:

run app.package.info -a com.mwr.example.sieve

run app.package.list

这条命令会把所有的App都列出来,如果想具体查找某个App可加上-f [App关键字]的参数,如查找sieve在Android设备中的包名:

run app.package.list -f sieve

Alt text

注意:在输入命令时可以使用Tab键自动补齐 需要记住com.mwr.example.sieve的包名,以后的命令要针对这个操作

2.2获取sieve的一些基本信息

命令是:

run app.package.info -a com.mwr.example.sieve

Alt text

可以看到Sieve的版本信息,数据存储的目录,用户ID,组ID,是否有共享库,还有权限信息等。

2.3Itentify the Attack Surface(确定攻击面?)

这个测试教程主要关注的是Android 固有的IPC通信机制的脆弱性,这些特点导致了App泄漏敏感信息给同一台设备上的其它App。 查找可以进行Attack Surface的组件的命令:

run app.package.attacksurface com.mwr.example.sieve

Alt text

结果显示了潜在可以利用的组件个数: “exported”表示组件可以被其他App使用。 services is debuggable表示我们可以用adb绑定一个调试器到进程。

2.4进一步获取Attack Surface的信息

如进一步获取ativity组建的attack surface信息的命令是:

run app.activity.info -a com.mwr.example.sieve

Alt text

其中上图的MainLoginActivity是程序启动时主界面,必须是exported,其他两个activity是理论上说是不能exported的。

2.5启动Activities

上图的PWList和FileSelectActivity是exported并且不需要任何权限,我们可以用drozer启动他们,比如感觉PWList这个含金量应该大一点,所以就启动它了,命令是:

run app.activity.start –component com.mwr.example.sieve com.mwr.example.sieve.PWList

启动后的效果:

Alt text

app.activity.start的使用方法:

help app.activity.start usage: run app.activity.start [-h] [–action ACTION] [–category CATEGORY [CATEGORY …]] [–component PACKAGE COMPONENT] [–data-uri DATA_URI] [–extra TYPE KEY VALUE] [–flags FLAGS [FLAGS …]] [–mimetype MIMETYPE]

Starts an Activity using the formulated intent.

2.6从Content Provider中获取信息

接上2.3节,进一步获取content provider的attact surface的信息的命令是:

run app.provider.info -a com.mwr.example.sieve

Alt text 从上图可以看到2.3节中两个exported的content provider的具体信息,包括名字,权限,访问路径等。

2.6.1查找可以访问content provider的URI(数据泄漏)

从上节图中我们猜测DBContentProvider会有某种格式的数据库,但是我们不知道其中的数据是如何组织的。Content URI必须是 “content:///” 的形式,因此我们可以构造部分的content URIs来访问DBcontent Provider。 上图存在一个需要READ_KEYS和WRITE_KEYS权限才能读和写的“/Keys”的路径。

drozer的scanner模块提供了一些方法去猜测可能存在的content URIs:

run scanner.provider.finduris -a com.mwr.example.sieve

Alt text 上图中检测出了可以访问content的URI,接下来我们可以用drozer的其他模块和URI从content中获取,甚至更改信息。 如:

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ –vertical

Alt text 如上图我们获取了用户名,邮箱帐号,和Base64编码的密码字符串。

2.6.2进行SQL注入

Android操作系统建议使用SQLite数据库存储用户数据。SQLite数据库使用SQL语句,所以可以进行SQL注入。 使用projection参数和seleciton参数可以传递一些简单的SQL注入语句到Content provider。如:

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ –projection “‘“

Alt text

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ –selection “‘“

Alt text

上面两条命令执行后Android设备返回了非常详细的错误信息。

使用Sql注入列出数据库中的所有数据表:

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ –projection “* FROM SQLITE_MASTER WHERE type=’table’;–”

Alt text

使用SQL注入列出数据表的内容

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ –projection “* FROM Key;–”

Alt text

2.6.3从File System-backed Content Providers获取信息

File System-backed Content Provider提供了访问底层文件系统的方法,Android沙盒会阻止App共享文件允许,而File System-backed Content Provider允许App共享文件。 对于sieve来说,我们可以推测出的FileBackupProvider就是一个file system-backed content provider。 我们可以使用drozer的app.provider.read模块查看某个文件

run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/etc/hosts

Alt text

可以使用app.provider.download下载文件

run app.provider.download content://com.mwr.example.sieve.FileBackupProvider/data/data/com.mwr.example.sieve/databases/database.db /home/user/database.db

2.6.4检查Content Provider的脆弱性

检查是否有SQL注入:

run scanner.provider.injection -a com.mwr.example.sieve

Alt text

检查是否存在遍历文件的漏洞

run scanner.provider.traversal -a com.mwr.example.sieve

Alt text

总结体会:我觉得在刚开始获取了软件包的基本信息后,就先用模块scanner里面的工具扫一扫,找到一些漏洞或者利用点后再进行下一步。

2.7和Services交互

获取是exported状态的services的命令:

run app.service.info -a com.mwr.example.sieve

Alt text

关于Services的模块: Alt text

如向某个服务发送信息:

run app.service.send com.mwr.example.sieve com.mwr.example.sieve.CryptoService –msg 1 5 3

返回的结果: Alt text 没搞明白为什么返回这个,段数还不够。

2.8 其他常用模块

  • shell.start 在Android设备上开启一个交互式Linux Shell
  • tools.file.upload / tools.file.download
  • tools.setup.busybox / tools.setup.minimalsu 安装busybox或者minimalsu到Android设备上