找flag(继找flag(ps:本文章与buuctf和fctf.jxnusec.cn网站逆向找flag同步)之后)(保姆级教程)

上一次我们讲到了错脚本这个环节。那么我们就来搓一个脚本。

上一次的那个花指令是flowerdance(抱歉哈)

我们继续来进行分析:

上一次我们的伪c代码有部分的函数被我还原了出来,但是还有的函数我们不一定能够一眼就看出来是什么函数,可以点击进去看一看。

我们看到qmemcpy函数我们可能会有点陌生,我在这里进行了补充

//

qmemcpy函数的实现方式是将内存复制操作分成了两个部分。第一部分是将内存块中的数据按照8字节(64位)为单位进行复制,这样可以利用现代CPU的SIMD指令集来进行高速复制。第二部分是将剩余的不足8字节的数据按照字节为单位进行复制。

因此,qmemcpy函数的效率比memcpy函数更高,特别是在复制大块内存时,可以显著提高程序的性能。

由于qmemcpy函数是一个内部函数,因此在标准C库中并没有提供该函数的声明。如果需要使用qmemcpy函数,可以在代码中自行声明该函数。//

在这里的意思就是,将后方的字符串复制到前面的地址上,最后为字符串长度。

//

memcmp是一个C标准库函数,用于比较两个内存区域的内容。它的函数原型为int memcmp(const void *str1, const void *str2, size_t n),其中str1和str2是要比较的内存区域的指针,n是要比较的字节数。函数返回值为0表示两个内存区域相等,返回值小于0表示str1小于str2,返回值大于0表示str1大于str2。

下面是一个使用memcmp函数的例子:

#include <stdio.h>
#include <string.h>

int main() {
    int arr1[5] = { 1,2,3,4,5 };
    int arr2[5] = { 1,2,3,4,0x11223305 };
    int ret = memcmp(arr1, arr2, 18);
    printf("%d", ret); // 输出:-1
    return 0;
}

在这个例子中,我们比较了两个整型数组arr1和arr2的前18个字节。由于arr1的前18个字节与arr2的前18个字节不相等,所以memcmp返回值为-1。//

我们把重心放到最最重要的运算上面:

在上一期我提到,异或的运算时可逆的,因此我们可以将其进行逆向运算的操作:

那么,我们开始搓脚本!

经过一番的折腾,我们将大致的框架搭起来了,不过还需要注意的是,运算也是需要逆向运算进行的,这就说明我们的运算需要进行一遍逆向分析才能搞明白。

现在最最关键的部分就是在这一步运算上了。

我们先将其正向的运算分析一遍:

Buf1[27-i]=Buf1[27-i]^(v5[i%8]^Buf1[26-i])

看起来毫无头绪,对吧?没事,在第一次看到的时候,我也慌了。

那么我们就来看一下最终的头绪是啥,首先我们还得复盘一下异或运算的本质。

异或运算在计算机科学中有广泛的应用,例如数据加密、校验和计算、交换两个变量的值等。其中,交换两个变量的值是异或运算的一个常见应用。下面是一个交换两个变量的值的例子:

a = 10
b = 20

a = a ^ b
b = a ^ b
a = a ^ b

print("a =", a) # 输出:20
print("b =", b) # 输出:10

从搜索到的资料我们可以知道,异或运算的字符必须为ASCII码表格中的字符,并且经常运用在解密过程中。因此解密过程中也必须要有异或运算的逆运算——其实也就是它本身。

上一期文章讲到,我们异或运算的规则:

注意,我们这里的运算是需要进行逆向运算,那么我们就需要一层一层的剥壳,才能将谜团解开。

Buf1[27-i]=Buf1[27-i]^(v5[i%8]^Buf1[26-i])

那么我们就要先把v5[i%8]^Buf1[26-i]这一步的运算分析出来;

这个步骤是将Buf1[26-i]的字符和v5[i%8]的字符进行异或运算得到的字符:

因为我们知道异或运算是具有可逆性的,所以我们可以将脚本编辑成这个样子:

经过编译运行的结果并没有我们想象的那么好;

不能说是一团浆糊,只能说是依托答辩。

那么是哪里出现的问题呢?

结果一系列的比较运算,我们可以发现有一下几个地方有很大的问题存在。

结果一系列的更改,我们将脚本进行了

在解决问题的时候,我们要看清楚所需要的是什么,才能将问题解决。

一下是我的脚本代码(可能有一定的错误)

#include <stdio.h>
#include <string.h>

void reverseXorOperation(unsigned int *data, unsigned int *key, int size) {
    for (int i = 0; i < size - 1; ++i) {
        char temp1 = key[i % 8];
        char temp2 = data[26 - i];
        data[27 - i] ^= temp1 ^ temp2;
    }
}

int main() {
    unsigned int key[9];
    unsigned int data[29];

    strcpy((char*)key, "JunkCode");
    memcpy(&data, "FkbXXL", 6);
    data[6] = 12;
    memcpy(&data[7], "^$iP", 4);
    data[11] = 127;
    data[12] = 100;
    data[13] = 33;
    data[14] = 56;
    data[15] = 112;
    data[16] = 82;
    data[17] = 47;
    data[18] = 62;
    data[19] = 29;
    data[20] = 100;
    data[21] = 35;
    data[22] = 125;
    data[23] = 116;
    data[24] = 74;
    data[25] = 96;
    data[26] = 8;
    data[27] = 74;
    
    reverseXorOperation(data, key, sizeof(data)/sizeof(data[0]));

    // 输出逆向操作后的data数组值
    for (int i = 0; i < sizeof(data)/sizeof(data[0]); ++i) {
        printf("%c", (char)data[i]);
    }

    return 0;
}

经过严格的验证,我们发现这里面还是有一点问题(咱们下次再讲)

那么我们来看下一题

EzSMC

打开界面

按F5

跳转到以下界面

本次我们要在ida里面进行代码的调试和运行

注意上方的调试器(将debugger调试到Local windows debugger),设置断点

点击上方的绿色三角形(调试运行按钮)

这里会给你一个弹窗,点击yes

这里需要注意一下,程序调试的时候会弹出来一个小黑框(只要是学过代码调试的同学都很清楚)

再按一次F5

此时要注意到小黑框里面现在是没有任何输出的

但是点击了下一步(按F8之后)

可以看到有输出的结果了(人话:代码跑起来了)

这说明这个程序有基本的功能,只不过ida还没有识别出来

依旧需要辅助这个函数

点击func

进入所在地址的汇编段

在func的左边按住alt+l

然后选中往下拖,在main函数之前停下来

选中内容之后,按U,取消识别

然后再拉到我们的位置

由于位置太多太杂,怎么办呢?

首先,记住我们的首地址

法一:硬着头皮托上去

法二:先选中一部分,然后按G

输入目标地址

z选中之后,按C

弹出的弹窗选择force,回到func函数最开始的位置

按P重新识别

按F5

伪C代码如下

int func()
{
  size_t v0; // rax
  char Str[8]; // [rsp+20h] [rbp-B0h] BYREF
  __int64 v3; // [rsp+28h] [rbp-A8h]
  __int64 v4; // [rsp+30h] [rbp-A0h]
  __int64 v5; // [rsp+38h] [rbp-98h]
  __int64 v6; // [rsp+40h] [rbp-90h]
  int i22; // [rsp+50h] [rbp-80h]
  int i21; // [rsp+54h] [rbp-7Ch]
  int i20; // [rsp+58h] [rbp-78h]
  int i19; // [rsp+5Ch] [rbp-74h]
  int i18; // [rsp+60h] [rbp-70h]
  int i17; // [rsp+64h] [rbp-6Ch]
  int i16; // [rsp+68h] [rbp-68h]
  int i15; // [rsp+6Ch] [rbp-64h]
  int i14; // [rsp+70h] [rbp-60h]
  int i13; // [rsp+74h] [rbp-5Ch]
  int i12; // [rsp+78h] [rbp-58h]
  int i11; // [rsp+7Ch] [rbp-54h]
  int i10; // [rsp+80h] [rbp-50h]
  int i9; // [rsp+84h] [rbp-4Ch]
  int i8; // [rsp+88h] [rbp-48h]
  int i7; // [rsp+8Ch] [rbp-44h]
  int i6; // [rsp+90h] [rbp-40h]
  int i5; // [rsp+94h] [rbp-3Ch]
  int i4; // [rsp+98h] [rbp-38h]
  int i3; // [rsp+9Ch] [rbp-34h]
  int i2; // [rsp+A0h] [rbp-30h]
  int i1; // [rsp+A4h] [rbp-2Ch]
  int nn; // [rsp+A8h] [rbp-28h]
  int mm; // [rsp+ACh] [rbp-24h]
  int kk; // [rsp+B0h] [rbp-20h]
  int jj; // [rsp+B4h] [rbp-1Ch]
  int ii; // [rsp+B8h] [rbp-18h]
  int n; // [rsp+BCh] [rbp-14h]
  int m; // [rsp+C0h] [rbp-10h]
  int k; // [rsp+C4h] [rbp-Ch]
  int j; // [rsp+C8h] [rbp-8h]
  int i; // [rsp+CCh] [rbp-4h]

  *(_QWORD *)Str = 0i64;
  v3 = 0i64;
  v4 = 0i64;
  v5 = 0i64;
  v6 = 0i64;
  puts("Welcome to FCTF!");
  puts("You are very know SMC");
  puts("Let's play a xor game\nTry Get flag and good luck!");
  printf("Plz input your flag:");
  scanf("%39s", Str);
  for ( i = 0; i <= 34; ++i )
    Str[i] ^= 0x31u;
  for ( j = 0; j <= 34; ++j )
    Str[j] ^= 7u;
  for ( k = 0; k <= 34; ++k )
    Str[k] ^= 5u;
  for ( m = 0; m <= 34; ++m )
    Str[m] ^= 0xDu;
  for ( n = 0; n <= 34; ++n )
    Str[n] ^= 0x67u;
  for ( ii = 0; ii <= 34; ++ii )
    Str[ii] ^= 0x48u;
  for ( jj = 0; jj <= 34; ++jj )
    Str[jj] ^= 0x53u;
  for ( kk = 0; kk <= 34; ++kk )
    Str[kk] ^= 0x50u;
  for ( mm = 0; mm <= 34; ++mm )
    Str[mm] ^= 0x35u;
  for ( nn = 0; nn <= 34; ++nn )
    Str[nn] ^= 0x40u;
  for ( i1 = 0; i1 <= 34; ++i1 )
    Str[i1] ^= 0x44u;
  for ( i2 = 0; i2 <= 34; ++i2 )
    Str[i2] ^= 0x2Bu;
  for ( i3 = 0; i3 <= 34; ++i3 )
    Str[i3] ^= 0x2Eu;
  for ( i4 = 0; i4 <= 34; ++i4 )
    Str[i4] ^= 0x57u;
  for ( i5 = 0; i5 <= 34; ++i5 )
    Str[i5] ^= 0x5Fu;
  for ( i6 = 0; i6 <= 34; ++i6 )
    Str[i6] ^= 0x2Cu;
  for ( i7 = 0; i7 <= 34; ++i7 )
    Str[i7] ^= 0x65u;
  for ( i8 = 0; i8 <= 34; ++i8 )
    Str[i8] ^= 0x2Fu;
  for ( i9 = 0; i9 <= 34; ++i9 )
    Str[i9] ^= 1u;
  for ( i10 = 0; i10 <= 34; ++i10 )
    Str[i10] ^= 0xDu;
  for ( i11 = 0; i11 <= 34; ++i11 )
    Str[i11] ^= 0x1Eu;
  for ( i12 = 0; i12 <= 34; ++i12 )
    Str[i12] ^= 0x22u;
  for ( i13 = 0; i13 <= 34; ++i13 )
    Str[i13] ^= 0x18u;
  for ( i14 = 0; i14 <= 34; ++i14 )
    Str[i14] ^= 0x2Du;
  for ( i15 = 0; i15 <= 34; ++i15 )
    Str[i15] ^= 0x5Fu;
  for ( i16 = 0; i16 <= 34; ++i16 )
    Str[i16] ^= 0x5Fu;
  for ( i17 = 0; i17 <= 34; ++i17 )
    Str[i17] ^= 0x15u;
  for ( i18 = 0; i18 <= 34; ++i18 )
    Str[i18] ^= 0x45u;
  for ( i19 = 0; i19 <= 34; ++i19 )
    Str[i19] ^= 0x53u;
  for ( i20 = 0; i20 <= 34; ++i20 )
    Str[i20] ^= 0x5Du;
  for ( i21 = 0; i21 <= 34; ++i21 )
    Str[i21] ^= 0x6Cu;
  for ( i22 = 0; i22 <= 34; ++i22 )
    Str[i22] ^= 0x24u;
  v0 = strlen(Str);
  if ( !memcmp(&data, Str, v0) )
    return printf("Good!");
  else
    return printf("Try again!");
}

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值