BUUCTF-Re-wp(1-10)
本文最后更新于 17 天前,其中的信息可能已经有所发展或是发生改变。

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循环

执行顺序总结

  1. 初始化 j = 0
  2. j 的值赋给 v8
  3. 调用 j_strlen 函数计算 Str2 的长度并存储在 v2 中。
  4. 检查 v8 > v2 是否成立,若成立则跳出循环;若不成立则继续。
  5. 检查 Str2[j] 是否等于 111,若相等则将其替换为 48。
  6. j 自增 1。
  7. 重复步骤 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的内容

  1. 将循环变量 i 赋值为 0 。
  2. 每次循环时,调用 strlen 函数计算 flag 字符数组的长度(&flag 传递的是数组地址) 。
  3. 检查 i 是否小于等于计算得到的字符串长度,若成立则进入循环体,否则结束循环。
  4. 在循环体中,通过指针运算 *(&flag + i) 访问 flag 数组中索引为 i 的字符,判断该字符的 ASCII 码值是否为 105(字符 'i')或者 114(字符 'r')。若满足条件,则将该字符替换为 ASCII 码值为 49 的字符(字符 '1')。
  5. 循环体执行完毕后,将 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)操作。

具体步骤如下:

  1. 循环从索引 i = 1 开始,到 i < 33 结束。也就是说,循环会对数组 v6 中索引从 1 到 32 的元素进行处理。
  2. 在每次循环中,使用按位异或运算符 ^ 将当前元素 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"

其中 key1key3 可以双击变量查看具体字符串

二、密钥处理

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 架构上,所以一般默认按小端序分析

感谢阅读!如果你觉得这篇文章对你有帮助,欢迎扫码赞赏支持,你的鼓励是我持续创作的动力 ❤️

本文为原创内容,转载请注明出处。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇