PS:以下题目均在BUUCTF
1、easyre(简单使用IDA)
题目

做法
先自行下载Exeinfo PE这个软件
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

64位
把文件进IDA(64位)
找到main,双击
按F5反编译成c语言

flag就这样水灵灵地出现了,去提交就OK了
2、reverse1(字符串定位 + 字符替换 o→0)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

64位,无壳
扔进IDA(64位),找main函数
但是我们并没有发现main函数,那我们先按Shift+F12打开string窗口,一键找出所有的字符串,去寻找它的后门函数

我们从上往下看一遍,发现有些字符串带有flag,因为我们最后交的东西也是flag,所以我们重点关注一下
这里有三个带有flag的:
第2行的wrong flag
第4行的this is the right flag
第7行的input the flag
翻译一下就显而易见了
我们双击this is the right flag

然后直接按Ctrl+x

确定

然后按F5反编译成c语言

进入到主函数(一般是main,一般我们直接找到main,然后双击,再F5反汇编成c语言就可以了),分析代码
因为逆向需要猜像sub_1400111D1这种的实际表达意义是啥,因此我们从我们点进来的this is the right flag这行代码开始往上看(当然,也可以直接从最上面分析到最下面)
sub_1400111D1("this is the right flag!\n");
直观地可以看出sub_1400111D1表示的是print(经验之谈,一般这种有“”且是绿色的都是程序打印给你的东西)
if ( !strncmp(&Str1, Str2, v3) )
比较两个字符串的前 v3 个字符是否相同。如果相同,条件表达式的结果为真,程序会执行 if 语句块内的代码
到这里,我们知道了它的成立条件,我们直接去找str1和str2
往上继续看
sub_14001128F("%20s", &Str1);
我们不难猜出sub_14001128F表示的是类似于 scanf 的函数,表示用户的输入会存放到&str1
因此,我们的输入值要等于str2,就可以得到flag
继续往上看,找到有str2或者跟它有关联的东西
for ( j = 0; ; ++j )
{
v8 = j;
v2 = j_strlen(Str2);
if ( v8 > v2 )
break;
if ( Str2[j] == 111 )
Str2[j] = 48;
}
我们注意到这里有关于str2的东西,这是一个for循环
执行顺序总结
- 初始化
j = 0。 - 把
j的值赋给v8。 - 调用
j_strlen函数计算Str2的长度并存储在v2中。 - 检查
v8 > v2是否成立,若成立则跳出循环;若不成立则继续。 - 检查
Str2[j]是否等于 111,若相等则将其替换为 48。 j自增 1。- 重复步骤 2 – 6,直至满足循环终止条件。
了解大概后,补充一个小知识点——我们可以对111,48这些数字单击后按r将数字转换为字符,eg.
if ( Str2[j] == 'o' )
Str2[j] = '0';
分析完这段代码,再往上也没有关于str2的信息了,我们尝试双击一下str2看看会不会有啥惊喜

诶,我们看到一个{}括起来的字符串,有点像我们要提交的flag格式,然后想到上面要把o换成0
我们把它复制一下,把o换成0,然后再最前面加上flag
回到题目提交看看

Yes,成功解出!给自己的进步点个小赞吧!
3、reverse2(字符串定位 + 字符替换 i 或 r →1)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

64位,无壳
扔进IDA(64位),找到main,F5反编译

老规矩,从得出flag那里开始往上看
if ( !strcmp(&flag, &s2) )
result = puts("this is the right flag!");
如果&flag里的内容等于&s2里的内容,我们的flag就是对的
继续往上
printf("input the flag:", argv);
__isoc99_scanf("%20s", &s2);
不难发现,s2里的内容是用户输入的内容
for ( i = 0; i <= strlen(&flag); ++i )
{
if ( *(&flag + i) == 105 || *(&flag + i) == 114 )
*(&flag + i) = 49;
}
一个for循环,里面有关于&flag的内容
- 将循环变量
i赋值为 0 。 - 每次循环时,调用
strlen函数计算flag字符数组的长度(&flag传递的是数组地址) 。 - 检查
i是否小于等于计算得到的字符串长度,若成立则进入循环体,否则结束循环。 - 在循环体中,通过指针运算
*(&flag + i)访问flag数组中索引为i的字符,判断该字符的 ASCII 码值是否为 105(字符'i')或者 114(字符'r')。若满足条件,则将该字符替换为 ASCII 码值为 49 的字符(字符'1')。 - 循环体执行完毕后,将
i的值自增 1,回到步骤 3 继续判断循环条件,重复上述过程直至循环结束。
看到数字,我们先按r把它变成字符
if ( *(&flag + i) == 'i' || *(&flag + i) == 'r' )
*(&flag + i) = '1';
要把i/r转换成1
然后我们双击flag进去看看有没有东西

有个疑似flag格式的字符串
复制下来,补全格式,然后把i/r转换成1提交看看

成功解出
4、内涵的软件(简单使用IDA+小经验)
题目

做法
下载文件,拖进Exeinfo PE进行分析

32位,无壳
扔进IDA(32位),找到main,F5反编译

一眼看到第9行{}括起来的内容,有点像flag的格式,复制下来,括号前加flag,返回题目试试

哦吼,成功了
5、新年快乐(upx脱壳 + 字符串验证)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

32位,有upx壳,需脱壳(自行下载upx脱壳工具)
返回桌面搜索cmd,以管理员身份运行命令提示符

cd命令切换到你下载的upx脱壳工具(总的,不是某一个文件)存放地址
然后输入代码
upx -d 要脱壳的文件地址

脱壳成功
再把文件拖入Exeinfo PE

已经没有壳了
这时再扔进IDA(32位),找到main,F5反编译(注:这里有俩main,对比一下就很清楚地知道该分析哪个)


那么好,现在我们开始从关键点从上往下进行分析
if ( !strncmp((const char *)&v5, &v4, strlen(&v4)) )
result = puts("this is true flag!");
如果v5 的前 strlen(&v4) 个字符和 v4 相同,就会输出 "this is true flag!"
然后再往上看
scanf("%s", &v5);
v5是用户的输入内容储存地址
因此,我们需要找到v4的内容是什么
strcpy(&v4, "HappyNewYear!");
v5 = 0;
memset(&v6, 0, 0x1Eu);
继续往上看,关键内容就这些了
复制HappyNewYear!到v4…
诶,v4这不找到了嘛
结合上面的分析,我们把v4的内容打包一下就是这题的flag啦

6、xor(前后字节异或还原)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析
(注:当我们进行附件解压时会出现MACOX这一个文件夹,这个是属于mac端解压下来的垃圾文件,不必理会,你拖进Exeinfo PE也不会有什么信息给你)

64位,无壳
扔进IDA(64位),找到main,F5反编译

没有什么像flag这样的直接关键词,我们直接从上往下分析一下代码
上面都是一些无关痛痒的东西,简单会看懂就行,我们看到这里
for ( i = 1; i < 33; ++i )
v6[i] ^= v6[i - 1];
代码功能:
这段代码是一个 for 循环,其功能是对数组 v6 进行按位异或(XOR)操作。
具体步骤如下:
- 循环从索引
i = 1开始,到i < 33结束。也就是说,循环会对数组v6中索引从 1 到 32 的元素进行处理。 - 在每次循环中,使用按位异或运算符
^将当前元素v6[i]与前一个元素v6[i - 1]进行异或操作,然后将结果重新赋值给v6[i]。
然后继续往下
if ( !strncmp(v6, global, 0x21uLL) )
printf("Success", v3);
strncmp(v6, global, 0x21uLL) 会比较 v6 和 global 指向内容的前 0x21 字节
0x21 = 十六进制数,等于十进制 33
u = unsigned,无符号
LL = long long,长长整型
所以:
0x21uLL
意思就是:
无符号 long long 类型的 0x21
结合前面的循环可知,我们输入的内容会先存放到 v6 中,然后程序对 v6 做前后字节异或处理
处理完成后,再将 v6 与 global 指向的密文数据进行比较
printf("Success", v3); 里面的 v3 只是 printf 多传了一个参数,因为 "Success" 里没有 %s、%d 这种格式化占位符,所以这个 v3 实际上没什么作用
下面导出数据看不懂的看下面的补充
我们双击global进去

再双击红框圈起的地方

按Shift+E导出数据

根据global的值,再结合上面的for循环,我们就可以编写一个异或脚本,反向推出这道题的flag
(输入一个值进行异或操作后,再对异或结果使用相同的密钥进行一次异或操作,得到的是最初输入的值,也就是我们原本输入的还没有进行过异或操作的值)
exp(一般我们写脚本都是用Python写的,可以自行下载PyCharm等进行编写)
list1 = [0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11,
0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,
0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F,
0x47, 0x32, 0x4F, 0x00]
flag = chr(list1[0])
#结果为f 虽然本题没有处理第一个数据,但它也是flag的一部分,保留
#计算第一个开始就是v6[1] = v6[1] ^ v6[0];
#左边被修改的是`v6[1]`,`v6[0]` 只是拿来参与异或,没有被改
#也就是没被处理,因此我们直接保留`v6[0]`即可,不用把它再次异或
#chr的作用是是根据 ASCII / Unicode 编码表,把数字变成对应的字符
#我们的flag通常不是数字,而是字符串形式,这样做的目的是构建出一个字符串形式的 flag,下同
#为什么不用str()?
#因为比如str(102),结果是:102,只是把数字 `102` 原样变成文本 `"102"`
#因此,str()达不到我们要的效果,因此我们只能使用chr()
#一句话记:
#chr():编号查字符str():数字原样变文字
# 使用 for 循环从第二个元素开始进行异或操作
for i in range(1, len(list1)):
flag += chr(list1[i] ^ list1[i - 1])
#从输入的第二个数据开始,将其与前一位异或
print(flag)

运行,得出结果,去掉最后的0就是我们这道题的答案啦
补充
为什么要点到特定位置才能导出数据?


第一张图不是密文数据本体,而是一个指针
第一张图里这一行是:
_global dq offset aFKWOXZUPFVMDGH
它的意思不是:
_global 里面存着 flag 密文
而是:
_global 里面存着一个地址这个地址指向 aFKWOXZUPFVMDGH
就像这样:
char *global = data;
char data[] = {0x66, 0x0A, 0x6B, 0x0C, ...};
对应到 IDA 就是:
_global dq offset data
data db 66h, 0Ah, 6Bh, 0Ch, ...
所以:
_global 是指针
aFKWOXZUPFVMDGH 才是真正的数据
第一张图如果你导出,导出来的是“地址”,不是密文。
比如第一张图可能导出的是这种:
6E 0F 00 00 01 00 00 00
这代表一个地址:0x100000F6E。
但我们脚本要的是密文字节,比如:
66 0A 6B 0C 77 26 ...
这些在第二张图里。
你看第一张图关键词:
dq offset aFKWOXZUPFVMDGH
只要看到:
offset
就说明它是“指向某个地方”。
所以你还要继续双击:
aFKWOXZUPFVMDGH
跳到它真正存数据的地方。
第二张图里是:
aFKWOXZUPFVMDGH db 'f', 0Ah
db 'k', 0Ch
db 'w', 26h
...
这里关键词是:
db
db 就是 define byte,意思是这里真的一字节一字节存着数据。
所以第二张图才能导出我们要的密文数组
7、reverse3(Base64 变形 + 索引偏移还原)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

32位,无壳
扔进IDA(32位),找到main,F5反编译

只是因为在人群中多看了你一眼——第31行的right flag,关键词找到,我们就从这里开始向上分析
if ( !strncmp(Dest, Str2, v2) )
sub_41132F("rigth flag!\n");
如果Dest和Str2的前v2个字符相同,系统会打印一个right flag给我们,sub_41132F不难猜出是print
我们点进Dest和Str2看看有啥东西(Dest没啥东西,Str2点进去如下,得到Str2的值)

结合上面分析,得出Dest的值(即Str2的值)
往上继续分析
for ( j = 0; j < v8; ++j )
Dest[j] += j;
一个for循环,Dest[0] = Dest[0]+0,Dest[1] = Dest[1]+1 ……这般规律为结果生成下去,直到j=v8(该循环对Dest做了变化)
再往上就没啥了
但是有一个点不清楚它的作用

点进sub_4110BE看看

继续点进去

遍历一下
注: 看到第 16 行的 *3 和第 19 行的 *4 时,可以联想到 Base64 编码的分组特点:
Base64 会将原始数据按 3 个字节一组进行处理,因为 3 个字节一共是 24 bit,随后再拆成 4 组 6 bit,每组 6 bit 对应 Base64 字符表中的一个字符,因此编码后通常表现为 3 字节变 4 字符
不过单独看到 *3 和 *4 还不能直接确定是 Base64,还需要继续往下看代码逻辑
后面程序中出现了 if 判断、while 循环和 for 循环,用来处理分组、剩余字节以及编码结果
同时结合代码中的字符表、索引变化和最终输出形式
可以基本判断这里使用的是 Base64 相关编码逻辑
exp
import base64
Des = "e3nifIH9b_C@n@dH"
flag = ""
for i in range(len(Des)):
flag += chr(ord(Des[i]) - i)
#Des[i]:取出字符串 Des 中索引为 i 的字符
#ord(Des[i]):使用 ord() 函数获取()内字符的 ASCII 码值
#ord(Des[i]) - i:将该 ASCII 码值减去其索引位置 i
#chr(...) 再将还原后的 ASCII 值转换回字符
print(base64.b64decode(flag)) #进行 Base64 解码并输出结果
得出结果,’ ‘里的内容换成flag{}格式提交即可


补充
原本逻辑确实可以理解成:
Dest[0] = Dest[0] + 0;
Dest[1] = Dest[1] + 1;
Dest[2] = Dest[2] + 2;
...
但关键是:字符在程序底层本来就是用 ASCII 数值存的。
比如字符:
a
它的 ASCII 值是:
97
如果程序做:
Dest[1] = Dest[1] + 1;
假设 Dest[1] 是 'a',那么实际底层做的是:
97 + 1 = 98
而 ASCII 值 98 对应的字符是:
b
所以:
'a' + 1 = 'b'
不是字符串拼接,而是字符 ASCII 值加 1。
Python 里不能直接这样写:
Des[i] - i
因为 Des[i] 是一个字符,比如:
'e'
字符不能直接减数字,所以要先用:
ord(Des[i])
把字符转成 ASCII 数值
8、helloword(简单使用ApkIDE)
题目

做法
下载,不要解压,直接拖入Exeinfo PE进行分析

文件后缀是apk,判断为安卓逆向题
拖进ApkIDE

先找主函数main函数,这题的flag直接出来了
(搜索内容不要习惯性空格之类,这样会找不出来)


9、不一样的flag(迷宫路径搜索)
题目

做法
下载压缩包,解压,把解压后的文件拖进Exeinfo PE进行分析

32位,无壳
扔进IDA(32位),找到main,F5反编译

没啥关键词,Shift+F12也找不到什么有用的点
从上往下分析吧
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
选1234都有对应的上下左右选择,是迷宫题吗
scanf("%d", &v5);
if ( v5 == 2 )
{
++*(_DWORD *)&v3[25];
}
else if ( v5 > 2 )
{
if ( v5 == 3 )
{
--v4;
}
else
{
if ( v5 != 4 )
LABEL_13:
exit(1);
++v4;
}
}
else
{
if ( v5 != 1 )
goto LABEL_13;
--*(_DWORD *)&v3[25];
}
把我们输入的值放进v5,根据我们输入的数字不同,v3或v4会有不同变化,亦或是异常退出
if ( *(_DWORD *)&v3[4 * i + 25] >= 5u )
exit(1);
}
if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 49 )
exit(1);
if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 35 )
{
puts("\nok, the order you enter is the flag!");
我们输入的值让v3或v4变化,满足不同公式,系统会异常退出亦或是返回’好的,你输入的顺序就是标志‘
至此,就再无别的信息了
我们尝试双击运行解压后的文件

分别输入1234进行测试

尝试多次后,发现规律——每次弹出来的四个选项中,只有一个是正确答案,且这个正确答案不是固定的,要自己不断慢慢试出来
结合IDA的代码分析,我们要去算那个公式的话,有很多种不同组合方式,而且更繁杂
我们选择直接打开文件一个个测,测出一个正确的就记下来,直到返回\nok, the order you enter is the flag!
最后成果——222441144222,包上flag{}去提交吧!

10、SimpleRev(小端序字符串还原 + 算法逆向)
题目

做法
下载文件,拖进Exeinfo PE

64位,无壳
扔进IDA(64位),找到main,F5反编译

有数字,我们按r转换为字符看看

若v4=d/D,我们就可以进入Decry函数
若v4=q/Q,我们就退出
输入除去这几个的其他字母,结果均是break
putchar函数呢,则是将用户输入的字符回显到屏幕,没啥作用
我们点进去Decry函数看看

找到真面目了
if ( !strcmp(text, str2) )
puts("Congratulation!\n");
看到这里的congratulation,结合上面的Please input your flag,我们就知道该从这里往上分析了
congratulation的条件是:让text等于str2
然后我们自上而下进行分析(以下只节选重要部分进行分析)
一、变量初始化
程序一开始有:
*(_QWORD *)src = 0x534C43444ELL;
v9[0] = 0x776F646168LL;
这里需要注意小端序存储。
0x534C43444E 按字节看是:
53 4C 43 44 4E
对应字符是:
S L C D N
但在内存中小端序会反过来存:
4E 44 43 4C 53
所以实际字符串是:
NDCLS
同理:
v9[0] = 0x776F646168LL;
实际字符串为:
hadow
具体要啥时候用到大小端序判断?
之前很多题里的数据是直接以字符串或字节数组形式出现的,比如:
db 'f', 0Ah, 'k', 0Ch
或者:
char s[] = "flag{xxx}";
这种情况下,IDA 已经把内存里的字节顺序直接展示出来了,你按看到的顺序读就行,不用再小端序反转
这里用到小端序,是因为这一题的字符串不是直接写成字符串,而是通过整数赋值塞进内存里的
整数本身:一个数值概念,比如 0x12345678
字节:真实存放/传输的数据,比如 78 56 34 12
内存:存放字节的地方
大小端主要发生在“整数”和“字节”互相转换的时候。 原始数据、payload、程序变量最终都是字节,都存放在内存里; 一些转换的命令比如pwn中经常用到的p32/p64/u32/u64 只是帮我们按正确的大小端顺序进行转换。
二、密钥与目标字符串生成
text = (char *)join(key3, v9);
strcpy(key, key1);
strcat(key, src);
可以理解为:
text = key3 + "hadow"
key = key1 + "NDCLS"
其中 key1 和 key3 可以双击变量查看具体字符串


二、密钥处理
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}
v5 是 key 的长度 v3 % v5 用来取 key 中的字符,前面有说v3=0
-
条件判断:
key[v3 % v5] > 64 && key[v3 % v5] <= 90
检查字符是否为大写字母(ASCII 范围 65-90) -
转换操作:
key[i] = key[v3 % v5] + 32
将大写字母转换为小写(ASCII 中,小写字母比大写字母大 32)
补充: 在 C 语言中,字符本质上是以ASCII 码(或其他字符编码)的形式存储的。当你对字符进行数学运算时,C 语言会自动将字符转换为对应的整数值(即 ASCII 码值)
char c = 'A';
int num = c + 32; // 'A'的ASCII码(65) + 32 = 97
printf("%c\n", num); // 输出: 'a' (ASCII码97对应的字符)
python的话则要自己先ord()将字符转换为ASCII码,然后完成减法后,再使用 chr() 将结果转换回字符
三、用户输入处理与加密过程
printf("Please input your flag:");
while (1) {
v1 = getchar();
if (v1 == 10) // ASCII 码,换行符
break;
if (v1 == 32) // ASCII 码,空格
{
++v2;
} //换行符用于结束输入,空格不参与加密,只进行特殊处理
// 加密逻辑
if ( v1 <= 96 || v1 > 122 ) //非小写字母ascll码范畴
{
if ( v1 > 64 && v1 <= 90 ) //如果是大写字母
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
//任意整数 % n 的结果范围是:0 ~ n-1
//最后+97是把ascll码范围回到小写字母的ascll码范围(即(0~25)+97对应)
++v3;
}
}
else //小写字母
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97; //同上处理方式
++v3;
}
if ( !(v3 % v5) ) //如果v3是v5的整数倍(即处理完一轮密钥)
//本来 % 是取余运算,算出结果是余数,这里前面加了!即为没有余数,则只能是整数
putchar(32); //输出一个空格
++v2;
while (1) 是什么意思?
while (1)
{
...
}
while 是循环语句
括号里的条件只要为真,就会一直循环,这里是1,所以会一直循环,除非有break/return等跳出函数
getchar() 的作用是:
从标准输入中读取一个字符。
也就是你在键盘上输入的内容,它一次只读取一个字符。
比如你输入:
ABC
然后按回车。
程序读取过程大概是:
第一次 getchar() 读取 'A'
第二次 getchar() 读取 'B'
第三次 getchar() 读取 'C'
第四次 getchar() 读取 '\n'
一般来说,用户输入完 flag 后才会按下回车。回车会被 getchar() 读取为换行符 ‘\n’,其 ASCII 值为 10。因此当 v1 == 10 时,程序通过 break 跳出 while 循环,表示输入结束。这个 break 只是用于结束输入读取,不会参与前面字符的加密逻辑。
四、验证与结果
if (!strcmp(text, str2))
puts("Congratulation!\n");
else
puts("Try again!\n");
- 将用户输入加密后的结果(
str2)与目标字符串(text)比较。 - 若匹配,输出
"Congratulation!",否则输出"Try again!"。
五、解密方法
要逆向推导出原始 flag,需根据加密公式编写解密函数:
key = "adsfkndcls"
text = "killshadow"
v3 = 0
for i in range(10): #10代表我们要解密的字符数
for j in range(128): #标准 ASCII 一共有 128 个字符,0-127
# 跳过非字母字符
if j < ord('A') or (j > ord('Z') and j < ord('a')) or j > ord('z'):
continue
# 这里的ord是让字符变成ascll码,这里说的是范围规定在英文大小写字母范围内
# 核心解密公式
if (j - 39 - ord(key[v3 % 10]) + 97) % 26 + 97 == ord(text[i]):
print(chr(j), end='')
v3 += 1
break
得出flag,返回题目提交

补充
1、大小端序
大小端序描述的是:多字节数据在内存中的存放顺序。
先记住两个方向
1. 数值写法:左边高字节,右边低字节
比如:
0x123456
拆成字节:
12 34 56
其中:
12 是高字节
56 是低字节
也就是:
数值写法: 12 34 56
高字节 低字节
2. 内存地址:一般从低地址往高地址看
假设从 0x1000 开始存:
地址: 0x1000 0x1001 0x1002
低地址 高地址
也就是说:
左边是低地址,右边是高地址
大端序
大端序规则:
高字节放低地址
低字节放高地址
所以 0x123456 在大端序内存里是:
地址: 0x1000 0x1001 0x1002
低地址 高地址
内容: 12 34 56
高字节 低字节
所以大端序从低地址到高地址看:
12 34 56
和数值写法:
0x123456
顺序一样。
小端序
小端序规则:
低字节放低地址
高字节放高地址
所以 0x123456 在小端序内存里是:
地址: 0x1000 0x1001 0x1002
低地址 高地址
内容: 56 34 12
低字节 高字节
所以小端序从低地址到高地址看:
56 34 12
和数值写法:
0x123456
顺序相反。
最核心总结
数值写法:左边高字节,右边低字节
内存地址:左边低地址,右边高地址
大小端序就是在问:
高字节和低字节,分别放到哪个地址里?
大端序:
高字节放低地址,低字节放高地址。
小端序:
低字节放低地址,高字节放高地址。
2、如何判断当前程序是大端还是小端
1. 使用工具查看
可以使用 Detect It Easy、010 Editor、IDA 等工具查看程序信息。
一些工具会显示字节序:
LE:Little Endian,小端序
BE:Big Endian,大端序
2. 根据架构判断
常见情况下:
x86 / x64:通常是小端序
Windows / Linux 普通 PC 程序:通常是小端序
ARM:常见为小端序,但部分场景也支持大端
网络协议:通常使用大端序,也称网络字节序
CTF 里常见的 ELF、PE 程序大多数运行在 x86 / x64 架构上,所以一般默认按小端序分析



