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!}