0x00题目
根据以下代码解出flag
flag = 'xxxxxxxxxxxxxxxxxx' list = [47, 138, 127, 57, 117, 188, 51, 143, 17, 84, 42, 135, 76, 105, 28, 169, 25] result = '' for i in range(len(list)): key = (list[i]>>4)+((list[i] & 0xf)<<4) result += str(hex(ord(flag[i])^key))[2:].zfill(2) print(result) # result=bcfba4d0038d48bd4b00f82796d393dfec
0x01解题流程
1.分析代码
重在分析for循环里的代码
这段代码
-
主要是将list列表中的每一个元素的前四位和后四位进行交换,即加密过程
key = (list[i]>>4)+((list[i] & 0xf)<<4)
这段代码
-
首先将flag的每个元素先转成ASCII值,
-
然后将结果与key取异或
-
得到的值再转成十六进制字符串(str)
-
并且去掉了十六进制的标识"0x"以及不满两位补0至两位,确保每个字符加密后的结果都是两位的十六进制数。
-
最后将处理后的字符串追加到result中
result += str(hex(ord(flag[i])^key))[2:].zfill(2)
2.思路
根据已知的result以及代码逻辑,写出逆向的脚本,得到flag
2.写脚本
encrypted_result = "bcfba4d0038d48bd4b00f82796d393dfec" list = [47, 138, 127, 57, 117, 188, 51, 143, 17, 84, 42, 135, 76, 105, 28, 169, 25] flag = '' for i in range(0,len(encrypted_result),2): hex_pair = encrypted_result[i:i+2] decrypted_value = int(hex_pair,16) key = (list[i//2] >> 4) + ((list[i//2] & 0xf) << 4) flag += chr(decrypted_value^key) print(flag) #运行结果如下 #NSSCTF{EZEZ_RERE}
0x02知识点
1.位运算及其应用
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0;有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
1. &
清零
如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。
取一个数的指定位
比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。
判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。
2. |
常用来对一个数据的某些位设置为1
比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。
3. ~
使一个数的最低位为零
使a的最低位为0,可以表示为:
a & ~1~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为" ~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
4. ^
翻转指定位
比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。
与0相异或值不变
例如:1010 1110 ^ 0000 0000 = 1010 1110
3)交换两个数
void Swap(int &a, int &b){ if (a != b){ a ^= b; b ^= a; a ^= b; } }
5.>>
操作数每右移一位,相当于该数除以2。
6. <<
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
2.python语法
1.切片
-
用法:
object[start_index : end_index : step] #object[起始位置(包含) : 终止位置(不包含) : 步长]
如果没有缺省的话,表达式应该包含三个参数以及两个冒号,三个参数的意义分别如下:
start_index:切片的起始位置(包括该位置),0表示从第一个开始,1表示从第二个开始,以此类推。-1表示从倒数第一个开始,-2表示从倒数第二个开始,以此类推。缺省时取0或-1(即step为正数取0,负数取-1)
end_index:切片的结束位置(!!!且不包括该位置),0表示第一个为终点,1表示第二个为终点,以此类推。-1表示倒数第一个为终点,-2表示倒数第二个为终点,以此类推。缺省时默认为序列长度(step为正数取正,step负数取负)
step,表示步长。可取正负数,正数表示从左往右,负数表示从右往左。缺省时取1
-
一些操作举例:
连续切片操作
按照顺序进行切片即可
a=[9,2,8,7,4,5,1,5,6,8] a[:8][2:5][::-1] #a[:8]输出为[9,2,8,7,4,5,1,5] #a[:8][2:5]输出为[8,7,4] #a[:8][2:5][-1]输出为[4,7,8]
其他序列的切片
#字符串的切片 str="ABCDEFG" str[1:3] #结果为'BC' #元组的切片 a=('a','b','c','d','e') a[::-1] #结果为('e', 'd', 'c', 'b', 'a')
python常用切片操作
a=[9,2,8,7,4,5,1,5,6,8] #修改单个元素 a[3]='hello china' #序列变为[9, 2, 8, 'hello china', 4, 5, 1, 5, 6, 8] #插入某个元素 a[3:3]='A' #序列变为[9, 2, 8, 'A', 7, 4, 5, 1, 5, 6, 8] #替换一部分元素 a[3:5] = ['hello','world'] #序列变为[9, 2, 8, 'hello', 'world', 5, 1, 5, 6, 8]
-
总结:
start:表示切片的开始索引位置(包括该位置),此参数也可以不指定,会默认为 0,也就是从序列的开头进行切片;
end:表示切片的结束索引位置(不包括该位置),如果不指定,则默认为序列的长度;
step:表示在切片过程中,隔几个存储位置(包含当前位置)取一次元素,也就是说,如果 step 的值大于1则在进行切片去序列元素时,会“跳跃式”的取元素。如果省略设置 step 的值,则最后一个冒号就可以省略。
2.zfill函数
功能
-
为字符串定义长度,如不满足,缺少的部分用0填补
用法
-
用法:
newstr = string.zfill(width)
-
参数:
width
新字符串希望的宽度
zfill的注意事项
-
与字符串的字符无关
-
如果定义长度小等于当前字符串长度,则不发生变化
In [14]: name = 'insane' In [15]: new_name = name.zfill(10) In [16]: print(new_name) #0000insane
2.代码段中的一些知识
result = ''
-
表示初始化一个空字符串
result
result += str(hex(ord(flag[i])^key))[2:].zfill(2)
-
ord()
函数在Python中返回某个字符对应的Unicode编码(整数值)。 -
hex(...)
:将异或操作后的整数值转换成十六进制字符串。 -
str()
函数在Python中用于将非字符串类型的数据转换为字符串类型。 -
[2:]
:切片操作,截取从第三个字符开始的子串。 -
.zfill(2)
:如果经过异或和转换后得到的十六进制数不足两位,那么用0填充左侧,确保结果始终是两位的十六进制数。
decrypted_value = int(hex_pair,16)
-
int(hex_pair,16)
:十六进制转换成十进制