杭电ctf第二周

ezCPP

本题主要考查tea加密

先拖进exe发现是64字节

拖进ida64分析 ,提取数据先用*改为32位然后shift+e提取

点开sub_140001070观察 发现是一个tea解密

 

根据写出代码得到flag 

注意:解密函数进行了魔改变成了左移5

#include <stdio.h>
#include <stdint.h>
 
//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0, i;     //v0,v1分别为字符串的低字节高字节     
    uint32_t delta=0xdeadbeef;                    
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; 
    for (i=0; i < 32; i++) {               //加密32轮        
        sum += delta;
        v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    }                                             
    v[0]=v0; 
	v[1]=v1;//加密后再重新赋值
}
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0xdeadbeef*32, i;  
    uint32_t delta=0xdeadbeef;                     
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];  
    for (i=0; i<32; i++) {                        //解密时将加密算法的顺序倒过来,还有+=变为-=
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0<<5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1<<5) + k1);//注意这里是左移五
        sum -= delta;
    }                                              
    v[0]=v0; 
	v[1]=v1;//解密后再重新赋值
}
 
int main()
{
 
    unsigned int key[4] = {1234,2341,3412,4123};
    unsigned char data[] =
{
  0x88, 0x04, 0xC6, 0x6A, 0x7F, 0xA7, 0xEC, 0x27,
 0x6E, 0xBF,0xB8, 0xAA, 0x0D, 0x3A, 0xAD, 0xE7, 0x7E,
 0x52, 0xFF, 0x8C,0x8B, 0xEF, 0x11, 0x9C, 0x3D,
 0xC3, 0xEA, 0xFD, 0x23, 0x1F,0x71, 0x4D
};  
for (int i = 0; i < 5; i++)
    decrypt((unsigned int*)data + i * 2, key);
    for (int i = 0; i < 32; i++) 
	{
    printf("%c", data[i]);
    }
    return 0;
}

 hgame{#Cpp_is_0bJ3cT_0r1enTeD?!}

babyandroid

本题主要考查rc4,android逆向,aes加密

aes加密:对称加密算法也就是加密和解密用相同的密钥

Android中的破解大体上可以分为静态分析和动态分析,对于这两种方式又可以细分为Java层(smail和dex)和native层(so)。

发现是apk文件,用jadx打开

 打开之后检索到MainActivity是为了找到flag,然后观察发现check1和check2有两层,check1第一层是简单的java层,check2第二层是native

 点进check1很明显的发现是一个经典rc4加密,那么就需要找到key。

注意:下⽅的key点开是资源ID,并不是真正的数据值。

 真正的key要去资源string目录下找

将数据转换成十六进制(就是hex值)之后放入赛博厨子

Latin1(ISO/IEC 8859-1)是一种字符编码方案,用于表示拉丁字母及其他西欧语言中常用的符号。它是单字节编码,使用一个字节(8位)来表示一个字符,总共可以表示256个不同的字符。

在这out里latin输出一般的字母

在Java中,Latin-1编码通常指的是ISO-8859-1编码,这是一种标准的字符编码方式,与Latin-1基本兼容。在Java中,常见的字符串表示形式是使用Unicode编码,即UTF-16编码。然而,Java中也支持Latin-1编码,可以通过指定编码格式来进行字符编码和解码操作。

 G>IkH<aHu5FE3GSV

解决了Java层接下来就是native层了,先压缩后在解压缩用Bandizip打开找到对应的so文件,然后用ida打开

拖入exe发现是64字节然后用ida64打开

 JNI_OnLoad简介 :当Android的VM(Virtual Machine)执行到C组件(即*so档)里的System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。

打开ida64之后查看导出文件,点击选项卡Exports,会看到一个带有JNI_OnLoad的名字,这是so层的命名规则,双击进入(还有一些函数是无法反编译的) 

进入之后按F5进行反编译然后观察 快捷键Y:修改数据类型

将v4和v7的数据类型改为JNIEnv*会使得代码更好读⼀点

使⽤的是动态注册的⽅式给java层和so层的函数建⽴对应关系

(在使用ida逆向android的so的时候,如果使用的是ida pro7.0的版本并且逆向的so是arm系列的,例如arm64-v8a和armeabi-v7a的由于缺少JNIEnv等结构,反编译后查看函数就变得很不友好,armeabi-v7a的还比较人性化,会自动添加JNIEnv等结构体  
原文链接:https://blog.csdn.net/LoopherBear/article/details/88689354)

v8是存储Java层中的函数名,函数签名以及so层中的函数名的结构体

 

 最后打开sub_18发现是aes加密算法

bool __fastcall sub_B18(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  size_t v7; // w19
  const void *v8; // x0
  __int128 v9; // q0
  char *v10; // x8
  __int64 v11; // x9
  char v12; // w13
  char v13; // w14
  char v14; // w15
  char v15; // w16
  unsigned __int8 v16; // w12
  char v17; // w0
  char v18; // w13
  char v19; // w17
  unsigned __int8 v20; // w14
  char v21; // w0
  unsigned __int8 v22; // w13
  unsigned __int8 v23; // w15
  char v24; // w16
  char v25; // w17
  unsigned __int8 v26; // w13
  unsigned __int8 v27; // w12
  char v28; // w16
  unsigned __int8 v29; // w14
  char v30; // w17
  char v31; // w0
  unsigned __int8 v32; // w15
  unsigned __int8 v33; // w12
  unsigned __int8 v34; // w14
  char v35; // w16
  char v36; // w17
  char v37; // w0
  unsigned __int8 v38; // w13
  char v39; // w16
  unsigned __int8 v40; // w15
  char v41; // w17
  unsigned __int8 v42; // w12
  char v43; // w0
  unsigned __int64 v44; // x0
  unsigned __int64 v45; // x20
  __int64 v46; // x8
  char *v47; // x10
  char *v48; // x9
  __int64 v49; // x8
  char v50; // t1
  int v51; // w21
  __int64 v52; // x8
  unsigned __int64 v53; // x9
  __int64 v54; // x10
  char *v55; // x9
  _BYTE *v56; // x11
  __int64 v57; // x8
  char v58; // t1
  int v59; // w8
  unsigned __int64 v60; // x11
  unsigned __int64 v61; // x10
  int v62; // w12
  int v63; // w13
  unsigned __int64 v65; // x11
  __int128 *v66; // x9
  __int64 *v67; // x10
  __int64 v68; // x11
  __int128 v69; // q0
  __int128 v70; // q1
  __int64 v71; // x11
  __int64 *v72; // x9
  char *v73; // x10
  __int64 v74; // x11
  __int64 v75; // t1
  __int64 *v76; // x10
  __int128 *v77; // x12
  unsigned __int64 v78; // x13
  __int128 v79; // q0
  __int128 v80; // q1
  unsigned __int64 v81; // x14
  char *v82; // x13
  unsigned __int64 v83; // x11
  __int64 *v84; // x14
  __int64 v85; // t1
  __int128 v86; // [xsp+0h] [xbp-100h] BYREF
  __int128 v87[11]; // [xsp+10h] [xbp-F0h] BYREF
  char dest[16]; // [xsp+C8h] [xbp-38h] BYREF
  _BYTE v89[16]; // [xsp+D8h] [xbp-28h] BYREF
  char src[16]; // [xsp+E8h] [xbp-18h] BYREF
  __int64 v91; // [xsp+F8h] [xbp-8h] BYREF

  v91 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v7 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL))(a1, a3);
  v8 = (const void *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL))(a1, a3, 0LL);
  if ( (int)v7 >= 1 )
  {
    memcpy(&v86, v8, v7);
    v9 = v86;
  }
  v10 = (char *)((unsigned __int64)v87 | 15);
  v11 = 1LL;
  v87[0] = v9;
  do
  {
    v12 = byte_703[v11++];
    v13 = byte_3928[(unsigned __int8)*(v10 - 1)];
    v14 = byte_3928[(unsigned __int8)*v10];
    v15 = byte_3928[(unsigned __int8)*(v10 - 3)];
    v16 = v12 ^ byte_3928[(unsigned __int8)*(v10 - 2)] ^ *(v10 - 15);
    v17 = *(v10 - 14);
    v18 = *(v10 - 13);
    v19 = *(v10 - 12);
    v10[1] = v16;
    v20 = v17 ^ v13;
    v21 = *(v10 - 9);
    v22 = v18 ^ v14;
    v23 = v19 ^ v15;
    v24 = *(v10 - 11);
    v25 = *(v10 - 10);
    v10[2] = v20;
    v10[3] = v22;
    v26 = v22 ^ v21;
    v27 = v16 ^ v24;
    v28 = *(v10 - 8);
    v29 = v20 ^ v25;
    v30 = *(v10 - 7);
    v31 = *(v10 - 6);
    v10[4] = v23;
    v10[5] = v27;
    v32 = v23 ^ v28;
    v10[6] = v29;
    v33 = v30 ^ v27;
    v34 = v31 ^ v29;
    v35 = *(v10 - 5);
    v36 = *(v10 - 4);
    v10[7] = v26;
    v37 = *(v10 - 3);
    v10[8] = v32;
    v10[9] = v33;
    v38 = v35 ^ v26;
    v39 = *(v10 - 2);
    v40 = v36 ^ v32;
    v41 = *(v10 - 1);
    v42 = v37 ^ v33;
    v43 = *v10;
    v10[10] = v34;
    v10[11] = v38;
    v10[13] = v42;
    v10[12] = v40;
    v10[14] = v39 ^ v34;
    v10[15] = v41 ^ v38;
    v10[16] = v43 ^ v40;
    v10 += 16;
  }
  while ( v11 != 11 );
  v44 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL))(a1, a4, 0LL);
  if ( (int)v7 > 0 )
  {
    v45 = v44;
    if ( v7 < 8 || (unsigned __int64)src < v44 + v7 && v44 < (unsigned __int64)&src[v7] )
    {
      v46 = 0LL;
LABEL_10:
      v47 = (char *)(v44 + v46);
      v48 = &src[v46];
      v49 = v7 - v46;
      do
      {
        v50 = *v47++;
        --v49;
        *v48++ = v50;
      }
      while ( v49 );
LABEL_12:
      sub_F8C(src, v87);
      memcpy(dest, src, v7);
      v51 = v7 + 16;
      if ( (int)(v7 + 16) <= 17 )
        v52 = 17LL;
      else
        v52 = (unsigned int)v51;
      v53 = v52 - 16;
      if ( (unsigned __int64)(v52 - 16) < 8
        || (unsigned __int64)src < v45 + v52 && v45 + 16 < (unsigned __int64)&v89[v52] )
      {
        v54 = 16LL;
        goto LABEL_19;
      }
      if ( v53 >= 0x20 )
      {
        v65 = v53 & 0xFFFFFFFFFFFFFFE0LL;
        v76 = &v91;
        v77 = (__int128 *)(v45 + 32);
        v78 = v53 & 0xFFFFFFFFFFFFFFE0LL;
        do
        {
          v79 = *(v77 - 1);
          v80 = *v77;
          v77 += 2;
          v78 -= 32LL;
          *((_OWORD *)v76 - 1) = v79;
          *(_OWORD *)v76 = v80;
          v76 += 4;
        }
        while ( v78 );
        if ( v53 == v65 )
          goto LABEL_21;
        if ( (v53 & 0x18) == 0 )
        {
          v54 = v65 | 0x10;
LABEL_19:
          v55 = (char *)(v45 + v54);
          v56 = &v89[v54];
          v57 = v52 - v54;
          do
          {
            v58 = *v55++;
            --v57;
            *v56++ = v58;
          }
          while ( v57 );
          goto LABEL_21;
        }
      }
      else
      {
        v65 = 0LL;
      }
      v81 = v65 + v45;
      v54 = (v53 & 0xFFFFFFFFFFFFFFF8LL) + 16;
      v82 = &src[v65];
      v83 = v65 - (v53 & 0xFFFFFFFFFFFFFFF8LL);
      v84 = (__int64 *)(v81 + 16);
      do
      {
        v85 = *v84++;
        v83 += 8LL;
        *(_QWORD *)v82 = v85;
        v82 += 8;
      }
      while ( v83 );
      if ( v53 != (v53 & 0xFFFFFFFFFFFFFFF8LL) )
        goto LABEL_19;
LABEL_21:
      sub_F8C(src, v87);
      if ( v51 <= 17 )
        v59 = 17;
      else
        v59 = v7 + 16;
      memcpy(v89, src, v59 - 16);
      goto LABEL_26;
    }
    if ( v7 >= 0x20 )
    {
      v46 = v7 & 0xFFFFFFE0;
      v66 = (__int128 *)(v44 + 16);
      v67 = &v91;
      v68 = v46;
      do
      {
        v69 = *(v66 - 1);
        v70 = *v66;
        v66 += 2;
        v68 -= 32LL;
        *((_OWORD *)v67 - 1) = v69;
        *(_OWORD *)v67 = v70;
        v67 += 4;
      }
      while ( v68 );
      if ( v46 == v7 )
        goto LABEL_12;
      if ( (v7 & 0x18) == 0 )
        goto LABEL_10;
    }
    else
    {
      v46 = 0LL;
    }
    v71 = v46;
    v46 = v7 & 0xFFFFFFF8;
    v72 = (__int64 *)(v44 + v71);
    v73 = &src[v71];
    v74 = v71 - v46;
    do
    {
      v75 = *v72++;
      v74 += 8LL;
      *(_QWORD *)v73 = v75;
      v73 += 8;
    }
    while ( v74 );
    if ( v46 == v7 )
      goto LABEL_12;
    goto LABEL_10;
  }
  sub_F8C(src, v87);
  sub_F8C(src, v87);
LABEL_26:
  if ( dest[0] != 100 )
    return 0LL;
  v60 = 0LL;
  do
  {
    v61 = v60;
    if ( v60 == 31 )
      break;
    v62 = (unsigned __int8)dest[v60 + 1];
    v63 = byte_6E3[++v60];
  }
  while ( v62 == v63 );
  return v61 > 0x1E;
}

然后点进byte_703 shift+e提取数据

最后再用赛博厨子进行解密

hgame{df3972d1b09536096cc4dbc5c} 

arithmetic

主要考点:脱壳区段改名修复

看了学长的这篇博客HZNUCTF REVERSE Signin题解——upx壳区段改名修复,动态调试脱壳_upx 特征修复-CSDN博客

现在来分析一下文件数据。用任何一个十六进制编辑器(UltraEdit,还有VS自带的十六进制编辑器等等都可以,但是不如WinHex)打开加壳后的程序。可以看到三个区段名。“UPX0”和“UPX1”是加UPX壳后的两个区段名。其中UPX1区段包含了需要解压的数据块。“.rsrc”是程序资源信息区段名,这个区段含有原资源段的完整头部以及图标、Manifest、版本等未被压缩的资源,当然还有UPX自身需要的导入信息等(如果程序自身不含资源段,加壳后就是“UPX2”)。“UPX0”和“UPX1”可以被随意改成任何字符串,虽然这样改用处不大,但是也能起到伪装的作用。

(上述文字出处手动去upx特征_upx -d-CSDN博客

先用exe打开,是64字节发现有壳且特征码被改过,用010editor打开

发现果然被改过将ari改成UPX (有三处,我最开始只找到两处就没办法脱壳)

 再用exe打开发现这个时候已经可以正常进行脱壳了

把文件放入upx文件夹下面然后在上面输入cmd打开终端输入upx -d+文件名

如下脱壳完成 

完成之后拖入ida64 进行分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  __int64 v4; // rbx
  int v5; // esi
  int v6; // edi
  FILE *i; // rbp
  int v8; // edx
  int v9; // eax
  int v10; // edi
  __int64 v11; // r14
  __int64 v12; // rbp
  __int64 v13; // rsi
  int v14; // eax
  __int64 v15; // rcx
  int v16; // eax

  v3 = time64(0i64);
  srand(v3);
  v4 = 1i64;
  v5 = 1;
  v6 = 1;
  for ( i = fopen("out", "rb"); (unsigned int)sub_140001080(i, "%d") != -1; v5 = v9 )
  {
    v8 = 1;
    if ( v5 != v6 )
      v8 = v6 + 1;//如果v5不等于v6则将v6+1赋值给v8
    v9 = v5 + 1;//将v5的值赋值给v9
    if ( v5 != v6 )
      v9 = v5;//如果v5不等于v6则将v5赋值给v9
    v6 = v8;//将v8又赋值给v6
  }
  v10 = dword_140005084;
  v11 = v5 - 1;
  if ( v11 >= 1 )
  {
    v12 = 1i64;
    v13 = 1000i64;
    do
    {
      v14 = rand() % 2 + 1;//生成一个随机数将结果取余2得到的结果为1
或2
      v15 = v13 + v12;
      dword_1400040B0[v4] = v14;
      if ( v14 == 1 )
      {
        v16 = dword_1400048B0[v15];
      }
      else
      {
        v16 = dword_1400048B4[v15];
        ++v12;
      }
      v10 += v16;
      ++v4;
      v13 += 500i64;
    }
    while ( v4 <= v11 );
  }
  if ( v10 >= 6752833 )//判断是否大于6752833
    sub_140001020("hgame{path_32-bit_md5_lowercase_encrypt}");//flag格式结果要md5
  return 0;
}

分析之后打开附带的out文件观察这个三角形的数据图

根据生成的随机数

1.若生成的随机数为1,则加正下方的数字

2.若生成的随机数为2,则加右下方的数字

2.计算从上到下的数据和,并判断是否大于6752833

网上找了一个代码修改了一下改成了直接输入,因为我的读取文件有点问题 

#include<bits/stdc++.h>
#include<time.h>
#define MAX 6752833
using namespace std;
long a[500][500], f[510][510], last[510][510], lis[510];
int path[510];

int main()
{
    // 设置随机数种子
    srand(time(NULL));
    
    int x = 1, y = 1;
    
    // 输入数据到二维数组a
    // 这里可以根据实际情况修改输入方式
    for (int i = 1; i <= 500; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            cin >> a[i][j];
        }
    }
    
    // 计算最大的路径和
    f[1][1] = a[1][1];
    for (int i = 2; i <= 500; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            // 计算从上一行到达当前位置的两个可能路径的得分
            f[i][j] = f[i - 1][j] + a[i][j];
            last[i][j] = j;
            if (f[i - 1][j - 1] + a[i][j] >= f[i][j])
            {
                f[i][j] = f[i - 1][j - 1] + a[i][j];
                last[i][j] = j - 1;
            }
        }
    }
    
    // 判断是否找到了最大路径和
    for (int i = 1; i <= 500; i++)
    {
        if (f[500][i] == 6752833)
        {
            x = 500, y = i;
            // 回溯路径并记录下每个位置的数字和选择
            while (x > 1)
            {
                lis[x] = a[x][y];
                if (last[x][y] == y - 1)
                {
                    path[x] = 2;
                    y = y - 1;
                }
                else
                {
                    path[x] = 1;
                }
                x--;
            }
        }
    }
    
    // 输出路径的选择
    for (int i = 2; i <= 500; i++)
    {
        printf("%d", path[i]%5);
    }
    
    return 0;
}

得到输出然后用md5加密一下 

 加密之后得到flag

 hgame{934f7f68145038b3b81482b3d9f3a355}

babyre

本题还可以用虚拟机动态调试一下

不知道是什么先拖入exe,是64字节,根据提示信息大致可以查到跟linux系统有关

查了一下gcc

乌班图20.04是一个流行的Linux操作系统。它自带了GCC编译器,无需安装任何附加的软件。GCC是GNU编译器集合的缩写,它是一种程序语言编译器。它支持多种编程语言,例如C,C++,Java,Objective-C等。GCC是Linux系统上标准的编译器,用于开发Linux应用程序。

//

GCC(GNU C Compiler)是编译工具。将 C/C++语言编写的程序 转换成为处理器能够执行的二进制代码的过程即由编译器完成。

GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL 语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。而 GCC 也不再单只是 GNU C 语 言编译器的意思了,而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面,说到 GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。
原文链接:https://blog.csdn.net/qq_48273416/article/details/120662013         

 然后拖入ida64分析,观察一下点击进入main函数

线程相关操作说明(原博客地址:https://www.cnblogs.com/mq0036/p/3710475.html)

  一 pthread_t

  pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:

  typedef unsigned long int pthread_t;

  它是一个线程的标识符

  二 pthread_create

  函数pthread_create用来创建一个线程,它的原型为:

  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,

  void *(*__start_routine) (void *), void *__arg));

  第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL.前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码

原博客:sem_init函数用法_function defenition for sem_init was not found-CSDN博客      sem_init函数是Posix信号量操作中的函数。sem_init() 初始化一个定位在 sem 的匿名信号量。value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。

如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量其结果未定义。

返回值
sem_init() 成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。

用下面一组函数(系统调用)来实现。

观察mian函数,点开这三个线程大致思路相似

将dword410c都改为enc,将dword4244都改为i方便读取 (快捷键N修改名称)

点进40A0发现有四个数据

然后又返回前面进行分析

第一步是传参,点开sub_1708查看分析

 第二步应该是设置密钥

软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据

sigsetjmp函数用于保存程序当前的执行环境,以便稍后使用siglongjmp来恢复。sigsetjmp和siglongjmp是C语言中用于实现非局部跳转的两个函数,它们在<setjmp.h>头文件中定义。这两个函数用于处理信号(Signal),在某些情况下,可以用于实现异常处理和协程。

int sigsetjmp(sigjmp_buf env, int savesigs);
参数:

env:被设置的环境数据对象,siglongjmp会根据该对象跳转。

savesigs: 用来设置env是否需要保存信号相关环境信息。为0时不需要相关信息。

返回值:

如果是由sigsetjmp调用返回,返回值为0。

如果是由siglongjmp调用返回,返回值为siglongjmp的第二个参数。

void siglongjmp(sigjmp_buf env, int val);
                        
原文链接:https://blog.csdn.net/m0_61243666/article/details/131386182

这里应该是对data进行了交叉引用(差不多就是值被替换了)(快捷键x查看被修改的地方),发现data的内容从123456换成了feifei

然后就是与17进行异或,当i=3时,触发除0异常,执行handler函数(出题人的恶趣味:handler函数就是249+1=250)然后退出循环,所以只有前三个被异或了。所以key由feifei转换成wtxfei

#include<stdio.h>
int main()
{
	char key[]="fei";
	for(int i=0;i<4;i++)
	{
		key[i]=key[i]^17;
		printf("%c",key[i]);
	}
	return 0;
} 

结果是:wtx

然后打开sub_1803点击dword4020提取数据(shift+e)

 然后就是一开始说到的那个线程,进行不同操作依次加一

 

 copy的别人的代码(pthread_join()详解及实验-CSDN博客

int pthread_join(pthread_t thread, void **retval);
args:
    pthread_t thread: 被连接线程的线程号
    void **retval : 指向一个指向被连接线程的返回码的指针的指针
return:
    线程连接的状态,0是成功,非0是失败

 

看了题解发现是一个按照每个线程的逆运算进行反向递归

 

 根据数据和线程分析写出代码(最开始不是很能理解for循环,然后分析原来的数据之后再逆运算迭代的过程从而得到flag)

#include <stdio.h>
int main() 
{
    // 密钥数组
    char data[] = {"wtxfei"};
    
    // 加密数据数组
    int enc[33] = {12052, 78, 20467, 109, 13016, 109, 27467, -110, 9807, 91,
	21243,-100, 11121, 20, 10863, -107, 10490, 29, 10633, -101, 10420, 78, 
	 17670,-38, 6011, -4, 16590, 125, 10723, 15, 7953, 255, 250};
	 
    // 逆向解密过程
    for (int i = 28; i >= 0; i -= 4) 
	{
        // 异或
        enc[i + 3] = enc[i + 3] ^ (enc[i + 4] - data[(i + 4) % 6]);
        //enc[i+4] ^= enc[i + 3] - *((char *)&data + (i + 4) % 6);
        // 乘法
        enc[i + 2] = enc[i + 2] / (enc[i + 3] + data[(i + 3) % 6]);
        //enc[i+3] *= enc[i + 2] + *((char *)&data + (i + 3) % 6);
        // 减法
        enc[i + 1] = enc[i + 1] + (enc[i + 2] ^ data[(i + 2) % 6]);
        //enc[i+2] -= *((char *)&data + (i + 2) % 6) ^ enc[i + 2];
        // 加法
        enc[i + 0] = enc[i + 0] - (enc[i + 1] * data[(i + 1) % 6]);
		//enc[i+1] += *((char *)&data + (i + 1) % 6) * enc[i + 1];
    }   //通过迭代的方式从数组的末尾向前处理数据
    
    // 输出解密结果
    for (int i = 0; i < 32; i++)
    {
        printf("%c", enc[i]);
    }

    return 0;
}

hgame{you_are_3o_c1ever2_3Olve!}

  • 34
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值