字符编码
字符编码
一、计算机中的数据表示
1.基本数据单位
我们在计算机中看到或听到的所有信息,在计算机内部都是一些二进制数。
bit:位(b),二进制数的1个“0”或1个“1”。
Byte:字节(B),一个8位的二进制数。KB、MB、GB、TB、EB、PB……
数据传输速率:bps,比特/秒。
数据下载速率:Bps,字节/秒。
2.进制转换
二进制:以0b作为前缀,0b1010。
八进制:以0o作为前缀,0o173。
十六进制:以0x作为前缀,0x1f。
用print函数直接输出这些带前缀的数据,都是将它们转换为十进制数。
print(0b101)
print(0o101)
print(0x101)
Python内置函数bin()、oct()、hex()分别用来将十进制数字转换成二进制、八进制、十六进制。
bin(100)
oct(100)
hex(100)
int()函数可将其它进制的数据转换成十进制
语法格式:int('被转换的数据',进制)
被转换的数据必须要以字符串的形式输入。
int('1100',2)
int('17',8)
int('2f',16)
例题:洛谷-进制转换
题目描述:
今天小明学会了进制转换,比如(10101)2 ,那么它的十进制表示的式子就是:1*2^4+0*2^3+1*2^2+0*2^1+1*2^0,那么请你编程实现,将一个M进制的数N转换成十进制表示的式子。
注意:当系数为0时,该单项式要省略
输入输出格式
输入格式:两个数,M和N,中间用空格隔开。
输出格式:共一行,一个十进制表示的式子。
说明
1<M<10,N的位数不超过1000。
m,n=input().split()
res=''
j=len(n)
for i in range(0,j):
if n[i]!='0':
res+=n[i]+'*'+m+'^'+str(j-i-1)+'+'
print(res[0:-1])
字符串的格式化:
1.res+='%s*%s^%d+' %(n[i],m,j-i-1)
2.res+='{0}*{1}^{2}+'.format(n[i],m,j-i-1)
3.res+=f'{n[i]}*{m}^{j-i-1}+' //python3.6引入的特性,f_string方式
注意:如果要算出进制转换的值,使用eval函数,乘方是**。
用列表来完成:
m,n=input().split()
res=[]
j=len(n)
for i in range(0,j):
if n[i]!='0':
res.append(f'{n[i]}*{m}^{j-i-1}')
print('+'.join(res))
二、计算机中的字符编码
字符编码,就是将我们人类所使用的每一个字符都对应到一个唯一的数字上。
ASCII码,美国标准信息交换码。
对英文字母以及一些常用的符号进行编码,一共表示了128个字符。
每个字符在计算机内部都对应了一个8位的二进制数,大小为1个字节。
在Linux中执行man ascii命令可以查看ASCII表帮助信息,根据自己需要输出ascii表。
ASCII表一共只有128个字符,对应的十进制数范围是0~127。
ASCII码实际上只使用了7位二进制数。
ASCII码表中的128个字符分成了两部分:
第一部分,0~32,规定了一些特殊的用途, 当终端设备或者打印机遇上这些字符时, 就要做一些约定的动作,比如空格、换行、响铃等,所以这些字符被称为非打印字符。
第二部分,33~127,可打印字符,也就是可以显示输出的字符,包括了所有的大小写英文字母、数字、标点符号等。
利用ord()函数可以返回某个字符所对应的ASCII码(用十进制表示)。
利用chr()函数可以返回某个十进制数所对应的ASCII码。
文本文件与二进制文件
文本文件中存放的数据在用户读取时可以按照编码类型还原成字符形式。
二进制文件中存放的数据不能还原成字符形式。
图片、视频、音频、可执行文件等都属于是二进制文件。
文本文件也有纯文本文件和非纯文本文件之分。(word文档不是纯文本文件,因为它包含了各种各样的格式信息。)
例如用记事本打开一张图片,图片文件内容不能还原为字符形式。
或者编写一个C程序,编译为可执行文件,再用记事本打开,文件内容同样无法还原为字符形式。
有些信息在二进制文件中仍然是以文本的形式存放的,比如C语言中printf函数中所输出的内容。CTF题型中有隐写的题目,会将文本信息隐藏在二进制文件中。
在linux中,可以使用strings命令查看二进制文件,寻找一些文本信息。
编写一个简单的C程序,文件名为hello.c:
#include <stdio.h>
int main(void){
printf("Hello world\n");
return 0;
}
编译:
gcc hello.c -o hello
使用strings命令就可以显示二进制文件中的文本信息:
strings hello
strings hello | grep Hello
strings hello | grep -i hello (-i选项,在过滤文本时不区分大小写)
在一些题目中,通常可以这样做来寻找flag:
strings 二进制文件 | grep -i key
strings 二进制文件 | grep -i flag
小实验:
在windows中任意截图,文件名为1.jpg,传到linux中。
echo "hello" >> 1.jpg
在linux图形界面下查看图片,图片正常。
strings 1.jpg,可以找到添加到图片中的文本信息。
二进制文件的编辑工具,在windows环境下有ultraedit,010editor等等。
练习:bugkuCTF:linux
练习:隐藏的信息
密文:0126 062 0126 0163 0142 0103 0102 0153 0142 062 065 0154 0111 0121 0157 0113 0111 0105 0132 0163 0131 0127 0143 066 0111 0105 0154 0124 0121 060 0116 067 0124 0152 0102 0146 0115 0107 065 0154 0130 062 0116 0150 0142 0154 071 0172 0144 0104 0102 0167 0130 063 0153 0167 0144 0130 060 0113
(提示:通过ascii码转换得到的是base64的编码)
练习:bugkuCTF:进制转换
练习:蓝盾ascii
密文:d4e8e5a0f1f5e9e3eba0e2f2eff7eea0e6eff8a0eaf5edf0a0eff6e5f2a0f4e8e5a0ece1faf9a0e4efe7a1a0d4e8e5a0e6ece1e7a0e9f3baa0c2c4c3d4c6fbd9b0f5dfe1f2e5dff3ede1f2b7fd
#!/usr/bin/python3
a='d4e8e5a0f1f5e9e3eba0e2f2eff7eea0e6eff8a0eaf5edf0a0eff6e5f2a0f4e8e5a0ece1faf9a0e4efe7a1a0d4e8e5a0e6ece1e7a0e9f3baa0c2c4c3d4c6fbd9b0f5dfe1f2e5dff3ede1f2b7fd
'
for i in range(0,len(a),2):
b=chr(int(a[i:i+2],16)-128)
flag+=b
print(flag)
练习:变异凯撒加密
密文:afZ_r9VYfScOeO_UL^RWUc
格式:flag{}
查阅https://www.qqxiuzi.cn/bianma/kaisamima.php,了解凯撒加密。
解题思路分析:
查阅ascii表,找到密文对应的ascii的值,可以使用命令ascii -d
a 97
f 102
Z 90
_ 95
明文的前四为是flag
f 102
l 108
a 97
g 103
找到规律,a--f,ascii值增加5位,f--l,ascii值增加6位,Z--a,ascii值增加7位,以此类推
#!/usr/bin/python3
a='afZ_r9VYfScOeO_UL^RWUc'
b=5
str1=''
for i in a:
c=chr(ord(i)+b)
b+=1
str1+=c
print(str1)
练习:http://chinalover.sinaapp.com/web2/index.html
三、Unicode和UTF-8编码
1.中文编码
扩展ASCII码:欧洲国家使用,包含ASCII中已有的128个字符,又增加了128个字符,非国际标准。
GB2312编码:用2个字节来表示一个汉字。
(1)对ASCII中原有的字符也按照两个字节进行重新编码,称为全角字符;
(2)原先的ASCII字符称为半角字符。
GBK编码:在GB2312的基础之上新加了20000多个字符(包括繁体字)。
GB18030编码:在GBK的基础之上又新加了很多少数民族的字符。
BIG5码:台湾和香港使用的另外一套汉字编码方案。
要打开一个文本文件,必须知道它的编码方式,如果用错误的编码方式解读,就会出现乱码。
2.Unicode编码
国际标准化组织ISO决定废除所有的地区性编码方案,重新制订一个可以包含世界上所有文化、所有字符的编码方案,这就是Unicode编码,也称为统一码、万国码。从2007年开始,Unicode已逐步取代ASCII成为了通用编码。
Python3默认采用Unicode编码,Python2默认采用ASCII码。
ord()函数在Python3中查看的其实是Unicode编码,只不过对于英文字母和数字,ASCII和Unicode是一致的。对于汉字,显示的就是Unicode编码。
ord('a')
ord('中')
3.Unicode编码的表示方式
Unicode编码在不同的应用场合所采用的表示方式也不一样。
Unicode常见的四种表示方式:
源文本:中国
ord('中') 20013 hex(20013)
ord('国') 22269 hex(22269)
表示方式:
&#x[Hex]; 中国
&#[Dec]; 中国
\U[Hex] \U4e2d\U56fd
\U+[Hex] \U+4e2d\U+56fd
小实验:以下代码保存为html文件
<h1>中国</h1>
<h1>中国</h1>
<h1>\U4e2d\U56fd</h1>
<h1>\U+4e2d\U+56fd</h1>
浏览网页文件,发现前2种表示方式,浏览器可以解析。
练习:bugkuCTF-web-web3
练习:bugkuCTF-杂项-这是一张单纯的图片
3.UTF编码
Unicode只是对所有的字符进行了编码,但没有规定该如何存储和传输这些字符。比如对于Unicode编码的字母a,在计算机中该如何存储?
UTF编码(Unicode Transformation Format),即Unicode的转换格式。
UTF编码有UTF-8、UTF-16、UTF-32。
4.UTF-8编码
UTF-8是一种可变长的编码方式,使用1~6个字节表示一个符号。
英文字母被编码成1个字节,汉字通常是3个字节,生僻字符才会被编码成4~6个字节。
解读 UTF-8 编码:
如果字节的第一位是0,则这个字节单独就是一个字符;
如果字节的第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
UTF-8是Unicode最重要的一种实现方式。
当数据在计算机内存中被处理时,统一使用Unicode编码。当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
a='中'
ord(a)
type(a)
b=a.encode('utf-8')
type(b)
b.decode('utf-8') 或者 b.decode() //默认采用utf-8
四、Base64编码
1.Base64是将二进制数编码成字符。
由二进制转到字符称为编码。
由字符转到二进制称为解码。
(跟之前提到的编码方案相反)
2.Base64编码的作用:
某些系统中只能使用ASCII字符,Base64是将非ASCII字符的数据转换成ASCII字符的一种方法。
在ASCII码中有很多不可见的控制字符,这些控制字符不利于在网上传输,Base64只使用了ASCII码中一部分可见字符。
具体包括:大小写字母各26个、10个数字、加号+、斜杠/,一共64个字符。除了这64个字符之外,在Base64编码中可能还会使用等号=作为后缀,注意的是=在编码末尾作为填充符号。
例如:
将“s 1 3”这三个字符进行编码:
转成ASCII码:115 49 51
转成二进制:01110011 00110001 00110011
以6位二进制数为一组,共分为4组: 011100 110011 000100 110011
转换成十进制:28 51 4 51
转换成Base64编码:c z E z
注意:Base64在编码时有自己专门的码表,而不是使用ASCII码。
3.Base64编码的关键特征:
字符串只可能包含A-Z,a-z,0-9,+,/,=字符。
=只会出现在字符串最后,最多三个,当然也可能没有。
字符个数是4的倍数。
4.Base64命令与工具
linux中的base64命令
编码:
echo -n "hello world" | base64 //-n选项,不带换行符号
结果是aGVsbG8gd29ybGQ=
解码:
echo -n "aGVsbG8gd29ybGQ=" | base64 -d
结果是hello world
也可以把文件进行编码,例如:
base64 1.jpg
python3的base64模块
import base64
编码:
base64.b64encode('hello world'.encode())
或者base64.b64encode(b'hello world') //要求使用bytes数据类型
解码:
base64.b64decode('aGVsbG8gd29ybGQ=')
或者base64.b64decode(b'aGVsbG8gd29ybGQ=')
注意,python3采用UTF-8编码,引入了bytes数据类型。
python3 bytes和str两种类型可以通过函数encode()和decode()相互转换,str通过encode()方法可以转换为bytes。
反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。
练习:i春秋--misc--此去今年
练习:bugkuCTF--杂项--多种方法解决
下载得到KEY.exe
strings KEY.exe
就此了解是图片文件,采用了base64编码,可以使用base64图片在线工具转换,也可以将编码复制到浏览器地址栏,浏览器也可以将编码还原为图片。
或者编写如下html文件
<img src="base64编码放在这里">
通过该题,主要了解二进制文件使用base64编码,图片转换成文本形式进行传输。
练习:bugkuCTF--web--管理员系统
思路:
1.简单查看源码,没有特殊的内容
2.任意填入用户名,密码,注意反馈,IP禁用
3.burpsuite抓包,修改请求头X-FORWARDED-FOR为本地地址127.0.0.1,响应报文发生改变
4.尝试用户名admin,注入漏洞密码' or 1=1 #登录,依然无效
5.再次查看源码,在最后有base64编码,解码得到登录密码
练习:bugkuCTF--加密--告诉你个秘密(ISCCCTF)
脑洞题
636A56355279427363446C4A49454A7154534230526D684356445A31614342354E326C4B4946467A5769426961453067
先通过ascii解码得到cjV5RyBscDlJIEJqTSB0RmhCVDZ1aCB5N2lKIFFzWiBiaE0g(也可以使用后面提到的base16解码)
然后通过base64解码得到r5yG lp9I BjM tFhBT6uh y7iJ QsZ bhM
然后观察键盘,r5yG,这4个键包围的是t
通过这种方式得到tongyuan
提交答案时,试了flag{tongyuan},flag{TONGYUAN}都不正确,最后提交TONGYUAN正确
5.其他base编码
在base编码的家族中,常见的还有以下编码方案。
Base16:由0~9、A~F组成,实际上就是十六进制数。
Base32:由大写字母、234567组成。
Base85:由0-9、A-Z、a-z、以及23个字符组成:!#$%&()*+-;<=>?@^_`{|}~
查阅以下内容:
import base64
dir(base64)
获知在python的Base64模块中,还提供了以上这些base编码与解码的方法。
base64.b16encode(b"hello")
base64.b32encode(b"hello")
base64.b64encode(b"hello")
base64.b85encode(b"hello")
练习:newbugku--你真的了解base的原理吗
https://new.bugku.com/upload/base_python.txt
思路:观察每一次解码后的编码特征
wget https://new.bugku.com/upload/base_python.txt
import base64
with open('base_python.txt','r') as f:
txt=f.read()
txt1=base64.b85decode(txt)
txt2=base64.b64decode(txt1)
txt3=base64.b16decode(txt2)
txt4=base64.b85decode(txt4)
......
在base85--base64--base16三种编码中循环
脚本:
#!/usr/bin/python3
import base64
with open('base_python.txt','r') as f:
txt=f.read()
while True:
try:
txt=base64.b16decode(txt).decode()
except:
try:
txt=base64.b32decode(txt).decode()
except:
try:
txt=base64.b64decode(txt).decode()
except:
try:
txt=base64.b85decode(txt).decode()
except:
break
print(txt)
得到flag{OTRhZTkyOTE0NmJiNGFjNWZhNDMzOTM1ZjkxYzg4Njk==}
echo -n 'OTRhZTkyOTE0NmJiNGFjNWZhNDMzOTM1ZjkxYzg4Njk==' | base64 -d
得到94ae929146bb4ac5fa433935f91c8869base64
提交flag为flag{94ae929146bb4ac5fa433935f91c8869}
注意:base85可以解码base64编码,也可以解码base32编码,不出现异常,但是解码结果错误。
例如:
base64.b32encode(b'hello') //结果是NBSWY3DP
base64.b64decode('NBSWY3DP') //可以解码,不出错,但是解码的结果不正确,得到的是4\x14\x96cp\xcf
base64.b64decode('NBSWY3DP').decode() //将解码结果转换为str类型时,不符合UTF-8编码,也就证明用base64解码不正确
五、URL编码
URL(Uniform Resource Locator,统一资源定位符)是一种可以为互联网中所有资源进行统一定位的机制。
一般格式:“协议名://主机FQDN名(IP地址):端口/路径”。
“协议名”指明了访问网络资源所使用的协议,一般都为HTTP、HTTPS或FTP协议,默认为HTTP协议。
URL中的端口号通常都可以省略。如果使用的是HTTP协议,默认端口为TCP80;如果使用HTTPS协议,默认端口为TCP443,;使用FTP协议,默认端口为TCP21。
在URL中如果指明路径,则是打开一个具体的网页或是某个具体的文件,如果路径省略,则是打开相应网站的首页。
1.URL传参(get方式)
http://主机FQDN名/文件名?参数名1=参数值1&参数名2=参数值2
例如:
192.168.80.100/test.php?user=zhangsan
192.168.80.100/test.php?user=zhangsan&password=123
如果要传输的参数值中包含“=”或“&”这种特殊字符,比如说我的用户名是zhang&san,该怎么办?
2.URL编码
URL编码格式:%十六进制ASCII码
用户名是zhang&san
查看&的ascii码的十六进制,值为26
ascii -x
所以zhang&san的URL编码为zhang%26san
传输标准ASCII字符无需URL编码,传输容易引起歧义的字符时,就要进行URL编码。
如果在URL中对一些ASCII标准字符进行了URL编码,那么浏览器会自动将其解码。
如果要在URL中传送中文,也必须要经过URL编码。
"+"比较特殊,它在URL中会被视为空格,即%20。
练习:bugkuCTF-代码审计--urldecode二次编码绕过
测试urlencode与urldecode函数
php -r "echo urlencode('{flag}');" //结果是%7Bflag%7D
php -r "echo urldecode('%7Bflag%7D');"
练习:bugkuCTF-web-never give up
stripos()函数的作用是查找字符串在另一字符串中第一次出现的位置(不区分大小写)
php -r "echo stripos('linux php','PHP');" //结果是6
解答本题的关键不在于满足PHP代码中设定的条件。注意require引入的文件。
练习:bugkuCTF-加密-一段base64
wget https://ctf.bugku.com/files/39488475fd87c064f9401eec2299c03e/1.txt
import base64
with open("1.txt","r") as f:
txt=f.read()
txt=base64.b64decode(txt).decode()
(综合的编码题,不建议用工具解码,在python中逐步分析)
观察txt的特点,转换为ascii码
八进制--十六进制--unicode
txt=txt.split('\\') //八进制数据
txt.pop(0)
flag=''
for i in txt:
flag+=chr(int(i,8))
txt=flag
txt=txt.split('\\x') //十六进制数据
txt.pop(0)
flag=''
for i in txt:
flag+=chr(int(i,16))
txt=flag
txt=txt.split('\\u') //Unicode编码
txt.pop(0)
flag=''
for i in txt:
flag+=chr(int(i,16))
txt=flag
得到十进制数据(可以直接复制十进制数据继续分析,注意去除多余字符串)
txt=txt.split(',')
flag=''
for i in txt:
flag+=chr(int(i))
txt=flag //unicode编码,可以在浏览器上解析
将unicode编码创建一张html页面,发现浏览器解析得到另外一段unicode编码,再次在浏览器中解析,得到URL编码flag%7Bctf_tfc201717qwe%7D,可以再浏览器地址栏中解析URL编码得到flag{ctf_tfc201717qwe}。
字符编码小测验
1. 图片文件的简单隐写
请查看附件中的1.jpg。
2. 大文件的简单隐写
请查看附件中的文件2。
3. bugkuCTF-web-点击一百万次(题目链接有时会失效,暂时访问不了可以换时间尝试)
(看懂JS代码后,多种方法可以解题,使用curl或python的requests模块或浏览器的开发者工具都可以。)
4. easy
bmN0Znt0aGlzX2lzX2Jhc2U2NF9lbmNvZGV9
5. 解码
请查看附件中的3.txt。
6. Excel表格(二进制文件隐写)
请查看附件中的4.xls。flag就在excel表格里,仔细找找!key格式:CTF{xxx}
7. 转换
666c61677b616537333538376261353662616566357d
(用工具转换最快速,但是建议了解使用python来解答。16进制数转换,除了普通的解法以外,还可以用base64.b16decode()方法来快速解答。但是要注意的是base16编码中16进制的字母是大写的,注意使用字符串的upper()方法转换小写字母为大写)
8. ZERO-ONE
请查看附件中的5.txt。(在python中可以合理使用字符串replace()方法与strip()方法,本题在转码为ascii过程中,会有base64与莫斯密码)
9. 编码转换
253445253641253637253637253444253534253431253738253439253434253642253335253439253434253531253334253439253434253435253737253444253433253431253738253444253434253435253637253446253534253535253637253444253534253431253331253439253434253435253738253445253533253431253335253445253533253431253738253444253434253435253637253445253534253439253637253444253534253435253331253439253434253435253739253444253531253344253344

浙公网安备 33010602011771号