逆向(异或)

在学习逆向前我们需要掌握一些汇编的基础知识的
同时我们得知道可执行文件的原理
计算机生成可执行文件,我们大致可以简单的这么理解
1.asm源程序文件
2.asm源程序生成obj也就是目标文件
3.由目标文件链接生成可执行文件,Windows的可执行文件通常是EXE,Linux通常是ELF
而在实际的环境中,计算会选择丢弃部分不要重要,或者自己优化一些信息。
逆向就是需要对这些信息进行还原,同时可以进行破译,也就是游戏开外挂,软件正版却有盗版的存在

test1(静态分析)

首先用查壳工具先看看
在这里插入图片描述
64位,ELF文件
查壳的目的:
1.看加了什么保护没
2.看看是多少位的程序,因为IDA有32和64的版本,必须与之分别对应
逆向基本步骤是使用IDA,对源程序进行反汇编,分析其中的代码

IDA

IDA(Interactive Disassembler)是一款强大的逆向工程工具,常用于分析和反汇编计算机程序。它支持各种平台和体系结构,并提供了丰富的功能和工具,可用于深入研究二进制代码的功能、结构和漏洞。
以下是IDA的一些主要特点和功能:
1.反汇编和分析:IDA能够将二进制文件转换为可读的汇编代码,使用户可以更好地理解程序的内部结构和执行逻辑。它提供了丰富的反汇编视图和分析工具,以帮助用户识别代码块、函数、指令和数据。
2.交互式界面:IDA提供了直观且易于使用的图形用户界面,支持用户与反汇编结果进行交互。用户可以浏览代码、跳转到函数或指令,查看和编辑数据以及设置断点等。
3.反编译器:IDA还提供了反编译功能,可以将汇编代码转换为高级语言表示,如C语言。这可以帮助用户更好地理解程序的功能和逻辑,尤其是对于无法获得源代码的情况。
4.插件和脚本支持:IDA支持各种插件和脚本,用户可以根据需要扩展和定制工具。这使得用户可以编写自己的脚本来自动化任务、添加新的功能或与其他工具集成。
总体而言,IDA是一款功能强大且灵活的逆向工程工具。它被广泛用于恶意软件分析、漏洞研究、软件安全评估和逆向工程等领域,并受到安全专家、软件工程师和研究人员的青睐。
同时IDA可以按F5,生成C/C++代码
以Test1为例子
打开IDA左边这一栏代表的是所用到的函数,当然我们知道C语言是从主函数开始的,所以一般我们先会去看看main函数的样子,当函数少的时候,我们可以直接看到,但是当函数多的时候我们就看不到了,此时我们用Ctr+f的方式可以在左边的篮筐进行搜索
在这里插入图片描述由于这题比较简单,突破口就是main函数
这里需要我们有C语言的基础,仔细看好
每一个变量,每一个函数的逻辑关系
当然由于这道题说了考察异或那我们重点观察异或!!(这里其实已经在汇编、离散数学中学过)

什么是异或

异或(XOR)是一种逻辑运算符,常用于计算机科学和电子工程中。异或操作符通常用符号 “^” 表示。
异或操作的定义如下:
当两个操作数的位值不同时,异或结果为1。
当两个操作数的位值相同时,异或结果为0。
例如,对于两个二进制数1和0进行异或操作:
1 XOR 0 = 1
异或操作还具有以下一些重要性质:
1.结合律:(A XOR B) XOR C = A XOR (B XOR C)
2.交换律:A XOR B = B XOR A
3.自反性:A XOR A = 0
4.零元素:A XOR 0 = A
异或操作在计算机科学中广泛应用,包括数据加密、校验和计算、错误检测和纠错编码等领域。在编程中,异或操作也常用于快速交换变量值、判断奇偶性和重复元素的消除等。

为什么要异或?这道题的思路

根据上面我们现在知道,把密码重复异或两次就会出现成本身,那么这道题的思路:
1,找到匹配的数据
2.将匹配的数据进行二次异或
3.最后得到原来的密码,也就是我们所需要的信息

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rbx
  char s[8]; // [rsp+0h] [rbp-40h] BYREF
  __int64 v6; // [rsp+8h] [rbp-38h]
  __int64 v7; // [rsp+10h] [rbp-30h]
  __int64 v8; // [rsp+18h] [rbp-28h]
  char v9; // [rsp+20h] [rbp-20h]
  int i; // [rsp+2Ch] [rbp-14h]

  *(_QWORD *)s = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0;
  puts("Welcome to RE world,Can you solve the problem?");
  printf("Now you should input your flag and i'll tell you if it is right:");
  __isoc99_scanf("%s", s);
  for ( i = 0; ; ++i )
  {
    v3 = i;
    if ( v3 >= strlen(s) )
      break;
    s[i] ^= i;   **重点!!!
  }
  if ( (unsigned int)compare(s) )
    puts("Well done! You find the secret!");**根据这里的提示我们看到如果(unsigned int)compare(s)
    成立==1那就说梦成功了,那这里我们在IDA里面点进这个函数看看
  else
    puts("The flag is wrong! Maybe something run before main");
  return 0;

在这里插入图片描述

__int64 __fastcall compare(const char *a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  if ( strlen(a1) != 32 )
  {
    puts("The length of flag is Wrong!!");
    exit(0);
  }
  for ( i = 0; i <= 31; ++i )
  {
    if ( final[i] != a1[i] )**这里我们看到我们输入的字符串被传输进入了a1[]里面,然后与fianl[]里面的数做了
    一个对比,那其实就是final[]就是加密后的密码,点击进入查看数据
      return 0LL;
  }
  return 1LL;
}

在这里插入图片描述

.data是什么?

学逆向我们还需要懂得一些文件结构的基础,这里我之前有讲过可以去看看下面这篇我写的博客
https://blog.csdn.net/m0_72827793/article/details/130231662
里面讲了PE文件结构,当然这道题是ELF文件结构,但他们都是类似的,都在COFF的基础上

至于db是汇编语言的知识

可以在我博客上的汇编框里面找找

回到刚才

这些数据就是我们需要破译的密码
这里我们需要提取出来选中这些数据
shift+e
在这里插入图片描述
我们选择十六进制转换
在这里插入图片描述
我们把数据提取出来(0X)表示十六进制
0x66, 0x6D, 0x63, 0x64, 0x7F, 0x56, 0x69, 0x6A, 0x6D, 0x7D,
0x62, 0x62, 0x62, 0x6A, 0x51, 0x7D, 0x65, 0x7F, 0x4D, 0x71,
0x71, 0x73, 0x79, 0x65, 0x7D, 0x46, 0x77, 0x7A, 0x75, 0x73,
0x21, 0x62

解密

这里我们可以C语言,也可以用python等一些语言写一个破译的脚本
这里是我写的
在这里插入图片描述
运行即可
在这里插入图片描述

Test2(动态)

学了上面这些,我们再加深一下理解,同时做一个提升
看一下同类型的test2这道题
同样的方式先找到main函数
在这里插入图片描述解析这段

 if ( Size != 38 )**这里判断了输入的数必须是38goto LABEL_22;
  v5 = 0i64;
  v3 = &Buf1;
  do
  {
    v6 = &Buf1**给数赋值
    v7 = &Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v6 = (void **)Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v7 = (void **)Buf1;
    *((_BYTE *)v7 + v5) ^= *((_BYTE *)v6 + v5 + 1);**给数组str[i]^=str[i+1],下面的依次类推分析
    v8 = &Buf1;
    v9 = &Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v8 = (void **)Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v9 = (void **)Buf1;
    *((_BYTE *)v9 + v5 + 1) ^= *((_BYTE *)v8 + v5 + 2);
    v10 = &Buf1;
    v11 = &Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v10 = (void **)Buf1;
    if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
      v11 = (void **)Buf1;
    *((_BYTE *)v11 + v5 + 2) ^= *((_BYTE *)v10 + v5 + 3);
    ++v5;
    v4 = Size;
  }
  while ( v5 < Size - 3 );

跟上一题的思路类似,那这里我们要怎么去寻找到那个匹配的数呢?看到后面这一段代码

 v12 = &Buf2;**先看明白下面 
 Size != qword_7FF7B8B96C40 || (v13 = memcmp(v3, v12, Size), v14 = "Right!", v13)
 这个判断,再看这个!
 **此时我们我们便可以知道Buf2就是判断的值
  if ( (unsigned __int64)qword_7FF7B8B96C48 >= 0x10 )
    v12 = (void **)Buf2;
  if ( (unsigned __int64)qword_7FF7B8B96C68 >= 0x10 )
    v3 = (void **)Buf1;
  if ( Size != qword_7FF7B8B96C40 || (v13 = memcmp(v3, v12, Size), v14 = "Right!", v13) )
LABEL_22:
    v14 = "Wrong!";
  sub_7FF7B8B62410(v3, v14, v4);
  return 0;

memcmp函数

在这里插入图片描述

Buf2问题

可以当我们点开buf2发现,这里面啥也没有
在这里插入图片描述**所以我们需要动态调试!!**数据节(data section)是程序的一部分,它存储了静态和全局变量的初始化值。在程序执行过程中,这些变量的值可能会被修改或者被其他操作所影响。因此,通过动态调试,在程序运行时可以实时查看这些变量的当前值,而不仅仅是它们的初始化值。
由于动态调试可以在程序运行时观察和修改内存中的数据,因此可能需要使用动态调试来确认 Buf2 的具体值,以便确定判断条件的结果
其实IDA通常不用于动态调试,IDA一般当作地图来使用,我们通常会选用OD或者X64dgb进行动态调试
这里我们还是讲解一些IDA动态调试
设立断点
在这里插入图片描述在这里插入图片描述选择start process
或者直接按F9即可
输入38个字符

在这里插入图片描述
在看到IDA
在这里插入图片描述
F7步入,F8不过
不过这里当我们执行过
v12 = &Buf2;这条语句时,我们会看到这个
在这里插入图片描述
点击小坐标我们进去看看
在这里插入图片描述
会出现这样的画面,同时我们再点击进入下面箭头所指的部分

在这里插入图片描述
跳转到这样的画面
在这里插入图片描述
如果你不清楚,数据是哪里开始的你可以回到刚才上一页的地方
看到这样的画面
在这里插入图片描述
至于结束会出现在这里,也就是0的前面
在这里插入图片描述
我们再次提取数据,用上一道题讲过的方法
0x0A, 0x0B, 0x7D, 0x2F, 0x7F, 0x67, 0x65, 0x30, 0x63, 0x60,
0x37, 0x3F, 0x3C, 0x3F, 0x33, 0x3A, 0x3C, 0x3B, 0x35, 0x3C,
0x3E, 0x6C, 0x64, 0x31, 0x64, 0x6C, 0x3B, 0x68, 0x61, 0x62,
0x65, 0x36, 0x33, 0x60, 0x62, 0x36, 0x1C, 0x7D,

最后进行解密

这里给出了两个语言写的代码,自行研究一下

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int i=0;
	int str[38]={ 0x0A, 0x0B, 0x7D, 0x2F, 0x7F, 0x67, 0x65, 0x30, 0x63, 0x60, 
   0x37, 0x3F, 0x3C, 0x3F, 0x33, 0x3A, 0x3C, 0x3B, 0x35, 0x3C, 
   0x3E, 0x6C, 0x64, 0x31, 0x64, 0x6C, 0x3B, 0x68, 0x61, 0x62, 
   0x65, 0x36, 0x33, 0x60, 0x62, 0x36, 0x1C, 0x7D};
   int str1[38]={0};
   int str2[38]={0};
   for(i=0;i<38;i++)
   {
   	str1[i]=str[37-i];
   }
    for(i=3;i<38;i++)
    {
    	str1[i - 2] ^= str1[i - 3];
        str1[i - 1] ^= str1[i - 2];
        str1[i] ^= str1[i - 1];
	}
	for(i=0;i<38;i++)
	{
		str2[i]=str1[37-i];
		printf("%c",str2[i]);
	}
	
   return 0;
 } 

Python注意缩进

 k= [
    0x0A, 0x0B, 0x7D, 0x2F, 0x7F, 0x67, 0x65, 0x30, 0x63, 0x60,
    0x37, 0x3F, 0x3C, 0x3F, 0x33, 0x3A, 0x3C, 0x3B, 0x35, 0x3C,
    0x3E, 0x6C, 0x64, 0x31, 0x64, 0x6C, 0x3B, 0x68, 0x61, 0x62,
    0x65, 0x36, 0x33, 0x60, 0x62, 0x36, 0x1C, 0x7D
   ]
s = k[::-1]
for i in range(3, len(s)):
    s[i - 2] ^= s[i - 3]
    s[i - 1] ^= s[i - 2]
    s[i] ^= s[i - 1]

s=s[::-1]
for i in range(len(s)):
    print(chr(s[i]), end='')

结果
在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Back~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值