蜀道山2024复现笔记

蜀道山2024复现笔记

周六 3月 08 2025
3800 字 · 28 分钟

蜀道山2024复现笔记


Map_maze

PE32文件,ida分析,由题名可知是一道迷宫题

image-20250301173126171

initial函数是地图的初始化,sub_101C40是验证函数

先看验证部分

C
char __cdecl sub_101C40(_DWORD *a1, _DWORD *a2, int a3)
{
  char result; // al
  int i; // [esp+0h] [ebp-8h]

  for ( i = 0; *(_BYTE *)(i + a3); ++i )
  {
    if ( *(_BYTE *)(i + a3) == 'U' && a1[1] && !*(_DWORD *)a1[1] )
    {
      a1 = (_DWORD *)a1[1];//
    }
    else if ( *(_BYTE *)(i + a3) == 'D' && a1[2] && !*(_DWORD *)a1[2] )
    {
      a1 = (_DWORD *)a1[2];//
    }
    else if ( *(_BYTE *)(i + a3) == 'L' && a1[3] && !*(_DWORD *)a1[3] )
    {
      a1 = (_DWORD *)a1[3];//
    }
    else
    {
      if ( *(_BYTE *)(i + a3) != 'R' || !a1[4] || *(_DWORD *)a1[4] )
        return 0;
      a1 = (_DWORD *)a1[4];//
    }
  }
  result = (char)a2;
  if ( a1[2] == a2[2] )
  {
    result = (char)a2;
    if ( a1[1] == a2[1] )
    {
      result = (char)a2;
      if ( a1[3] == a2[3] )
      {
        result = (char)a2;
        if ( a1[4] == a2[4] )
          return 1;
      }
    }
  }
  return result;
}

上下左右移动可以猜出来,但是具体的验证逻辑有点看不懂

继续看initial

C
int __cdecl initial(_DWORD *a1, _DWORD *a2)
{
  _DWORD *v2; // eax
  _DWORD *v3; // eax
  int result; // eax
  _DWORD v5[105]; // [esp+0h] [ebp-424h] BYREF
  _DWORD v6[119]; // [esp+1A4h] [ebp-280h]//事实上,v5和v6都是地图的一部分,查看内存可知它们是连在一起的,105+119+[0]=225也可以验证这一点
  _DWORD *v7; // [esp+380h] [ebp-A4h]
  int i28; // [esp+384h] [ebp-A0h]
  int i27; // [esp+388h] [ebp-9Ch]
  int i26; // [esp+38Ch] [ebp-98h]
  int i25; // [esp+390h] [ebp-94h]
  int i24; // [esp+394h] [ebp-90h]
  int i23; // [esp+398h] [ebp-8Ch]
  int i22; // [esp+39Ch] [ebp-88h]
  int i21; // [esp+3A0h] [ebp-84h]
  int i20; // [esp+3A4h] [ebp-80h]
  int i19; // [esp+3A8h] [ebp-7Ch]
  int i18; // [esp+3ACh] [ebp-78h]
  int i17; // [esp+3B0h] [ebp-74h]
  int i16; // [esp+3B4h] [ebp-70h]
  int i15; // [esp+3B8h] [ebp-6Ch]
  int i14; // [esp+3BCh] [ebp-68h]
  int i13; // [esp+3C0h] [ebp-64h]
  int i12; // [esp+3C4h] [ebp-60h]
  int i11; // [esp+3C8h] [ebp-5Ch]
  int i10; // [esp+3CCh] [ebp-58h]
  int i9; // [esp+3D0h] [ebp-54h]
  int i8; // [esp+3D4h] [ebp-50h]
  int i7; // [esp+3D8h] [ebp-4Ch]
  int i6; // [esp+3DCh] [ebp-48h]
  int i5; // [esp+3E0h] [ebp-44h]
  int i4; // [esp+3E4h] [ebp-40h]
  int i3; // [esp+3E8h] [ebp-3Ch]
  int i2; // [esp+3ECh] [ebp-38h]
  int i1; // [esp+3F0h] [ebp-34h]
  int nn; // [esp+3F4h] [ebp-30h]
  int mm; // [esp+3F8h] [ebp-2Ch]
  int kk; // [esp+3FCh] [ebp-28h]
  int jj; // [esp+400h] [ebp-24h]
  int ii; // [esp+404h] [ebp-20h]
  int n; // [esp+408h] [ebp-1Ch]
  int m; // [esp+40Ch] [ebp-18h]
  int k; // [esp+410h] [ebp-14h]
  int j; // [esp+414h] [ebp-10h]
  int i; // [esp+418h] [ebp-Ch]
  int i30; // [esp+41Ch] [ebp-8h]
  int i29; // [esp+420h] [ebp-4h]

  for ( i = 0; i < 15; ++i )
  {
    for ( j = 0; j < 15; ++j )
      v5[15 * i + j] = sub_101080(0);//初始化为0(墙),15x15的map
  }
  for ( k = 1; k < 15; ++k )
    *(_DWORD *)v5[k] = 1;
  for ( m = 9; m < 15; ++m )
    *(_DWORD *)v5[m + 15] = 1;
  for ( n = 0; n < 2; ++n )
    *(_DWORD *)v5[n + 30] = 1;
  for ( ii = 3; ii < 8; ++ii )
    *(_DWORD *)v5[ii + 30] = 1;
  for ( jj = 9; jj < 15; ++jj )
    *(_DWORD *)v5[jj + 30] = 1;
  for ( kk = 0; kk < 2; ++kk )
    *(_DWORD *)v5[kk + 45] = 1;
  for ( mm = 3; mm < 8; ++mm )
    *(_DWORD *)v5[mm + 45] = 1;
  for ( nn = 12; nn < 15; ++nn )
    *(_DWORD *)v5[nn + 45] = 1;
  for ( i1 = 0; i1 < 2; ++i1 )
    *(_DWORD *)v5[i1 + 60] = 1;
  for ( i2 = 7; i2 < 10; ++i2 )
    *(_DWORD *)v5[i2 + 60] = 0;
  *(_DWORD *)v5[67] = 1;
  for ( i3 = 11; i3 < 15; ++i3 )
    *(_DWORD *)v5[i3 + 60] = 1;
  for ( i4 = 0; i4 < 2; ++i4 )
    *(_DWORD *)v5[i4 + 75] = 1;
  for ( i5 = 3; i5 < 6; ++i5 )
    *(_DWORD *)v5[i5 + 75] = 1;
  for ( i6 = 11; i6 < 15; ++i6 )
    *(_DWORD *)v5[i6 + 75] = 1;
  for ( i7 = 0; i7 < 2; ++i7 )
    *(_DWORD *)v5[i7 + 90] = 1;
  *(_DWORD *)v5[92] = 0;
  for ( i8 = 3; i8 < 6; ++i8 )
    *(_DWORD *)v5[i8 + 90] = 1;
  for ( i9 = 7; i9 < 10; ++i9 )
    *(_DWORD *)v5[i9 + 90] = 1;
  for ( i10 = 11; i10 < 15; ++i10 )
    *(_DWORD *)v5[i10 + 90] = 1;
  *(_DWORD *)v6[0] = 1;
  *(_DWORD *)v6[1] = 0;
  *(_DWORD *)v6[2] = 0;
  *(_DWORD *)v6[3] = 1;
  for ( i11 = 4; i11 < 6; ++i11 )
    *(_DWORD *)v6[i11] = 1;
  for ( i12 = 7; i12 < 10; ++i12 )
    *(_DWORD *)v6[i12] = 1;
  for ( i13 = 11; i13 < 15; ++i13 )
    *(_DWORD *)v6[i13] = 1;
  for ( i14 = 0; i14 < 2; ++i14 )
    *(_DWORD *)v6[i14 + 15] = 1;
  for ( i15 = 7; i15 < 10; ++i15 )
    *(_DWORD *)v6[i15 + 15] = 1;
  for ( i16 = 11; i16 < 15; ++i16 )
    *(_DWORD *)v6[i16 + 15] = 1;
  for ( i17 = 0; i17 < 6; ++i17 )
    *(_DWORD *)v6[i17 + 30] = 1;
  for ( i18 = 7; i18 < 10; ++i18 )
    *(_DWORD *)v6[i18 + 30] = 1;
  for ( i19 = 11; i19 < 15; ++i19 )
    *(_DWORD *)v6[i19 + 30] = 1;
  for ( i20 = 0; i20 < 6; ++i20 )
    *(_DWORD *)v6[i20 + 45] = 1;
  for ( i21 = 11; i21 < 15; ++i21 )
    *(_DWORD *)v6[i21 + 45] = 1;
  for ( i22 = 0; i22 < 9; ++i22 )
    *(_DWORD *)v6[i22 + 60] = 1;
  for ( i23 = 13; i23 < 15; ++i23 )
    *(_DWORD *)v6[i23 + 60] = 1;
  for ( i24 = 0; i24 < 9; ++i24 )
    *(_DWORD *)v6[i24 + 75] = 1;
  *(_DWORD *)v6[84] = 0;
  *(_DWORD *)v6[85] = 1;
  *(_DWORD *)v6[86] = 1;
  *(_DWORD *)v6[87] = 0;
  for ( i25 = 13; i25 < 15; ++i25 )
    *(_DWORD *)v6[i25 + 75] = 1;
  for ( i26 = 0; i26 < 9; ++i26 )
    *(_DWORD *)v6[i26 + 90] = 1;
  *(_DWORD *)v6[99] = 0;
  *(_DWORD *)v6[100] = 1;
  *(_DWORD *)v6[101] = 1;
  *(_DWORD *)v6[102] = 0;
  for ( i27 = 13; i27 < 15; ++i27 )
    *(_DWORD *)v6[i27 + 90] = 1;
  for ( i28 = 0; i28 < 12; ++i28 )
    *(_DWORD *)v6[i28 + 105] = 1;
  for ( i29 = 0; i29 < 15; ++i29 )
  {
    for ( i30 = 0; i30 < 15; ++i30 )
    {
      if ( i29 > 0 )
        *(_DWORD *)(v5[15 * i29 + i30] + 4) = v5[15 * i29 - 15 + i30];
      if ( i29 < 14 )
        *(_DWORD *)(v5[15 * i29 + i30] + 8) = v5[15 * i29 + 15 + i30];
      if ( i30 > 0 )
        *(_DWORD *)(v5[15 * i29 + i30] + 12) = v5[15 * i29 - 1 + i30];
      if ( i30 < 14 )
        *(_DWORD *)(v5[15 * i29 + i30] + 16) = v5[15 * i29 + 1 + i30];
    }
  }//确定上下左右的指针逻辑,这段代码的主要目的是构建一个包含方向信息的15x15网格结构,使得每个格子都知道它在四个基本方向上的邻居是谁。
  v2 = (_DWORD *)v5[0];
  *a1 = *(_DWORD *)v5[0];
  a1[1] = v2[1];
  a1[2] = v2[2];
  a1[3] = v2[3];
  a1[4] = v2[4];
  v3 = v7;
  *a2 = *v7;
  a2[1] = v3[1];
  a2[2] = v3[2];
  a2[3] = v3[3];
  result = v3[4];
  a2[4] = result;
  return result;
}

事实上,这里的v5v6map[]是结构体类型的数据,具体定义如下

C
typedef enum{
    NotWall,
    IsWall
} WallState;
struct Cell{
    WallState isWall;//是否为墙的枚举值
    struct Cell* U;//指向上方的指针
    struct Cell* D;//指向下方的指针
    struct Cell* L;//指向左方的指针
    struct Cell* R;//指向右方的指针
}

因此,对于一个Cell类型的数据a1,从内存上,

C
a1[0]=a1.isWall;
a1[1]=a1.U;
a1[2]=a1.D;
a1[3]=a1.L;
a1[4]=a1.R;

逻辑很清晰了,接下来就是拿到map

PY
'''
011111111111111
000000000111111
110111110111111
110111110000111
110000010001111
110111000001111
110111011101111
100111011101111
110000011101111
111111011101111
111111000001111
111111111000011
111111111011011
111111111011011
111111111111000
'''

地图有多解,flag只有唯一解,多尝试几次,正确答案是

PY
#DRRDDDDDDDRRRRDDRRRDRRRDDDRR

Potato Toolkit

运行程序,发现要输入http指令和参数

image-20250301181457890

ida分析,查找关键字符串Complie Error

定位到关键逻辑代码

image-20250301181604739

往上看,找到两个字符串1wesa234,qwe123998244353,输进去执行发现直接闪退

image-20250301181621038

找到exit(0),下断点动调,这里应该是数据操作部分

image-20250301181915052

重点看加密部分

C
v10 = v20;
do
{
  v12 = QString::length((QString *)v17);//获取字符串长度
  v13 = (QChar *)QString::operator[](v17, v2 % v12);//根据索引v2%length访问v17中的特定字符给v13
  v14 = QChar::unicode(v13);//获取v13(char型)的unicode值
  v15 = QChar::QChar((QChar *)&v16, *(_BYTE *)v10 ^ *(_BYTE *)v14);//对v10指向的数据和v14处的字符的字节表示进行按字节的异或,结果存在v15(QChar对象)
  QString::operator+=(v18, *(unsigned __int16 *)v15);//将新生成的字符存在v18所指向的QString对象中
//更新循环变量
  ++v2;
  v10 = (__int128 *)((char *)v10 + 4);
  --v11;
}
while ( v11 );

逻辑很清晰,就是循环异或,这里已经动调到exit(0)处,直接进v18,注意,这里的v18是一个指针,其指向的内容才是flag

image-20250301182528990

0x8处按d,得到指向的flag

HelloHarmony

算法逆向,使我旋转

将ets目录下的modules.abc文件用jadx反编译,主要是看index文件p001entry.src.main.ets.pages.Index下面

定位到关键函数

image-20250308152445816

检查输入的password是否正确,password应该就是flag,一步步来

先看第一层加密:

它是导入了Eeee模块的encrypt函数,找一找,在Emmm下

image-20250308152900589

类凯撒加密arg1是基础偏移量,大写字母偏移量为arg2,小写字母为arg1+2,其他不变,在验证函数处找到了传给arg1的值5

image-20250308153302106

解密脚本:

C
void inverse_Casser(unsigned char *flag){
	for(int i=0;i<40;i++){
		if(flag[i]>='A'&&flag[i]<='Z'){
			flag[i]=(flag[i]-'A'-5+26)%26+'A';
		}
		else if(flag[i]>='a'&&flag[i]<='z'){
			flag[i]=(flag[i]-'a'-7+26)%26+'a';
		}
		else{
			flag[i]=flag[i];
		}
	}
}

然后看Native

定位到主函数

image-20250308153717347

key'HelloSDS'

image-20250308153750233

先看WTF::WTF函数,传参为key,ken_len,存储数组

image-20250308153921608

image-20250308153925441

两个函数,先看banana

image-20250308153946671

像是在初始化sbox

image-20250308153951599

再看bananana

image-20250308154028169

看起来是使用key进行初始化:

image-20250308154036069

注意:这里都是this+64,推断WTF是一个结构体,前256为存sbox的数据,后32位存key

直接copy下来,这里将sboxkey分开

C
void banana(unsigned char *box){//初始化box
	for (int i = 0; i < 256; ++i )
	{
		*(box + i) = (167 * i + 173) % 256;
	}
}

void bananana(unsigned int *dkey,char *key,int len){//设置key
	for (int i = 0; i < 8; ++i )
	{
		*(dkey + i) = key[(i + 3) % len] | (key[(i + 2) % len] << 8) | (key[(i + 1) % len] << 16) | (key[i % len] << 24);
	}
}

再看WTF::heiheihei主加密函数

image-20250308154306489

8轮加密

image-20250308154314072

分别进去看

blablablabla:用dkey进行加密

image-20250308154351341

bla:用sbox进行加密

image-20250308155256891

blablabla:密文循环换位

image-20250308155358096

exp:

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

void banana(unsigned char *box){//初始化box
	for (int i = 0; i < 256; ++i )
	{
		*(box + i) = (167 * i + 173) % 256;
	}
}

void bananana(unsigned int *dkey,char *key,int len){//设置key
	for (int i = 0; i < 8; ++i )
	{
		*(dkey + i) = key[(i + 3) % len] | (key[(i + 2) % len] << 8) | (key[(i + 1) % len] << 16) | (key[i % len] << 24);
	}
}

void inverse_blablablablabla(unsigned int *this,unsigned char *cipher,int i){
	((unsigned int*)cipher)[0]^=*(this+i);
	((unsigned int*)cipher)[1]^=*(this+i);
}
void inverse_bla(unsigned char *box,unsigned char *cipher){
	for(int i=0;i<40;i++){
		for(int j=0;j<256;j++){
			if(cipher[i]==box[j]){
				cipher[i]=j;
				break;
			}
		}
	}
}
void inverse_blablabla(unsigned char*cipher){
	unsigned char v4;
	v4=cipher[39];
	for(int i=39;i>0;i--){
		cipher[i]=cipher[i-1];
	}
	cipher[0]=v4;

}

void inverse_Casser(unsigned char *flag){
	for(int i=0;i<40;i++){
		if(flag[i]>='A'&&flag[i]<='Z'){
			flag[i]=(flag[i]-'A'-5+26)%26+'A';
		}
		else if(flag[i]>='a'&&flag[i]<='z'){
			flag[i]=(flag[i]-'a'-7+26)%26+'a';
		}
		else{
			flag[i]=flag[i];
		}
	}
}

int main(){
	unsigned char box[256]={};
	unsigned int dkey[8]={};
	char key[]={"HelloSDS"};
	unsigned char ciphertext[] =
	{
		0xF6, 0xB0, 0xA6, 0x36, 0x9A, 0xB3, 0x2B, 0xBF, 0x94, 0x54,
		0x15, 0x97, 0x93, 0x59, 0xBF, 0x50, 0x4D, 0xBF, 0x0A, 0x59,
		0x06, 0xD7, 0x97, 0x50, 0xD6, 0x59, 0x54, 0xD7, 0xCF, 0x06,
		0x5D, 0x20, 0x1D, 0x5A, 0x22, 0xEE, 0x99, 0x1F, 0xE1, 0x18
	};

	banana(box);
	bananana(dkey,key,8);
	for(int i=7;i>=0;i--){
		inverse_blablabla(ciphertext);
		inverse_bla(box,ciphertext);
		inverse_blablablablabla(dkey,ciphertext,i);
	}
	for(int i=0;i<40;i++){
		printf("%c",ciphertext[i]);
	}
	printf("\n");
	inverse_Casser(ciphertext);
	for(int i=0;i<40;i++){
		printf("%c",ciphertext[i]);
	}
	return 0;
}

super panda girl

游戏逆向,使我进一步旋转

PE64文件,将"Super Panda Girl_Data\Managed"下的Assembly-CSharp.dll文件拖到dnspy里分析

定位到显示flag的函数

image-20250306205245876

str源于this.code(this.GetEvenCharacters(text));

先找text,查看ssSSs5s5s5方法

image-20250306205746071

魔改RC4,标准的应该是array2[k]=(bytes[k]^array[b3]),而这里是b3。密钥是”LZSDS”,密文在下面:

image-20250306205636379

再跑一遍就拿到text

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

// 初始化 S 盒
void rc4_init(unsigned char *s, const unsigned char *key, int keylen) {
	int i, j = 0;
	for (i = 0; i < 256; ++i) {
		s[i] = i;
	}
	for (i = 0; i < 256; ++i) {
		j = (j + s[i] + key[i % keylen]) % 256;
		// 交换 s[i] 和 s[j]
		unsigned char tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
	}
}

// 加密/解密数据
void rc4_crypt(unsigned char *s, unsigned char *data, int len) {
	int i = 0, j = 0, t = 0;
	for (int k = 0; k < len; k++) {
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		// 交换 s[i] 和 s[j]
		unsigned char tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
		t = (s[i] + s[j]) % 256;
		data[k] ^= t;
	}
}

int main() {
	// 密钥
	const char *key = "LZSDS";
	int keylen = strlen(key);

	// 状态数组(S盒)
	unsigned char s[256];
	// 明/密文
	unsigned char v4[] = {
    	57, 244, 117, 200, 213, 87, 194, 195,
    	164, 100, 103, 63, 19, 79, 137, 70,
    	201, 24, 163, 129, 237, 210, 5, 19,
    	35, 21
	};
	int len = sizeof(v4) / sizeof(v4[0]);
	// 初始化 S 盒
	rc4_init(s, (const unsigned char *)key, keylen);

	// 加/解密数据
	rc4_crypt(s, v4, len);
	// 输出解密后的数据
	for (int i = 0; i < len; i++) {
		printf("%c", v4[i]);
	}
	printf("\n");

	return 0;
}
//put_this_in_the_true_brand

再看this.GetEvenCharacters方法

image-20250306210158445

取text的偶数位拼接

在看this.code

image-20250306210346128

base64加密

exp:

PY
import base64

# 原始字符串
original_string = "ptti_ntetu_rn"

# 将字符串编码为字节类型
a = original_string.encode('utf-8')

# 使用base64进行编码
c = base64.b64encode(a)

# 打印编码后的字符串,并解码成utf-8字符串形式展示
print(c.decode('utf-8'))
#cHR0aV9udGV0dV9ybg==

Forhu@hu3

很有难度的题,使我究极旋转

首先给了个PE32,ida分析,定位到main,再去掉一些花指令,还原代码如下

image-20250306211523498

image-20250306211020303

猜数游戏:

image-20250306212015236

生成[email protected]

image-20250306212032656

用idapython提取一下:

PYTHON
ea = 0x0041C190
v = [get_wide_byte(i+ea)for i in range(0x2bac0)]
with open('[email protected]', 'wb') as f:
	f.write(bytes(v))

hu@hu2

一张熊猫图片,没什么用

生成exe的函数:

image-20250306212124232

image-20250306212145859

用idapython提取一下:

PY
ea=0x43c818
v = [get_wide_byte(i+ea)for i in range(0x3400)]
with open("secret.exe","wb") as f:
    f.write(bytes(v))

image-20250308141603441

upx壳,还魔改了标志位,010查看

image-20250308141706651

改回来,保存,再看

image-20250308141819440

image-20250308141846107

upx脱壳就行

image-20250308141931406

ida分析,定位到main

魔改RC4加密(符号是对着标准RC4修改后的)

image-20250308142047341

看别人的wp还提到了一句

image-20250308142237107

找一找还真有

image-20250308142612515

image-20250308142710017

密文有了,逻辑清晰,但是没key

image-20250308142154604

现在找key,看提示

image-20250308143053596

意思是要去找未被调用的数据,回到主exe去看

玩猜数游戏时,30次and()的值被存储起来了而且后面都没有调用

image-20250308145458276

而且看ElementSize附近

image-20250308145759983

image-20250308145837028

它后面有一段没被调用过的数据,int型的,再往下看

image-20250308145912887

交叉引用看谁调用了它

image-20250308145943613

手搓脚本跑一下

C
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
unsigned int dword_41C088[42] = {
	0x00000023, 0x00000029, 0x00000024, 0x00000022, 0x0000003E, 0x00000073, 0x00000020, 0x0000007D,
	0x00000023, 0x00000077, 0x00000070, 0x00000077, 0x00000073, 0x00000068, 0x00000026, 0x00000073,
	0x00000077, 0x00000072, 0x00000068, 0x00000071, 0x00000075, 0x00000026, 0x00000077, 0x00000068,
	0x00000027, 0x00000071, 0x00000021, 0x00000072, 0x00000068, 0x00000027, 0x00000077, 0x00000070,
	0x00000073, 0x00000071, 0x00000071, 0x00000027, 0x00000071, 0x00000027, 0x00000077, 0x00000026,
	0x00000024, 0x00000038
};
int main(){

	for(int i=0;i<42;i++){
		printf("%c",(dword_41C088[i]&0xff)^0x45);
	}
	return 0;
}
//flag{6e8f2526-c627-40c2-b4d7-b25644b4b2ca}

flag?假的!把没被调用的数据提取出来,发现数据长度也是30

rand()和该数据进行异或

C
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
unsigned int data[30] = {
	0xFFFFFFED, 0x00000020, 0xFFFFFFA5, 0xFFFFFFFB, 0xFFFFFF8B, 0x00000075, 0xFFFFFFE4, 0x00000009,
	0x0000004E, 0x00000075, 0x00000008, 0x00000036, 0xFFFFFFB9, 0x00000059, 0xFFFFFFEF, 0xFFFFFFD7,
	0x00000048, 0xFFFFFF83, 0xFFFFFFBC, 0x00000009, 0xFFFFFF96, 0x0000004E, 0x00000069, 0x0000001E,
	0x0000004E, 0x00000012, 0x00000006, 0xFFFFFFE0, 0xFFFFFF83, 0xFFFFFF8A
};
int main(){
	char *Destination;
	Destination = (char *)malloc(0x64u);
	strcpy_s(Destination, 0x64u, "LZSDS");
	srand(*(DWORD *)Destination);
	rand();

	for(int i=0;i<30;i++){
		printf("%c",data[i]^=rand());
	}
	return 0;
}
//y0u_k@n3_p&nd*_f1l3_hu33_hu@@

找到了key

回到第二个exe里,搓一个解密脚本,因为之前去花的时候发现jj的值可能会被修改,不妨两个值都试一试(查看内存初值是0,所以jj尝试0和5)

由于不知道时间戳,那么从16.30往下爆

PY
from ctypes import *
# 加载外部DLL
dll = cdll.LoadLibrary("./Dll1.dll")
# 定义调用外部函数的快捷方式
srand = lambda x: dll.sd(x)
rand = lambda: dll.rd()

seed = 1729672200
key = b'y0u_k@n3_p&nd*_f1l3_hu33_hu@@'

def decrypt(N, Seed):
    jj = N
    ii = 0
    srand(Seed)

    # 调用 rand() 256 次
    for _ in range(256):
        rand()

    ans = [
        10, 23, 122, 47, 138, 50, 186, 151, 61, 130, 133, 221, 124, 97, 238, 21,
        201, 191, 209, 52, 49, 36, 171, 58, 64, 90, 99, 171, 238, 55, 135, 92,
        172, 69, 161, 169, 152, 127, 222, 176, 184, 114, 156, 203, 63, 189, 10,
        177, 22, 83, 20, 93
    ]

    num = rand() % 256
    if num == 0:
        return None

    S = [i for i in range(num)]
    i = 0
    j = 0

    for k in range(num):
        j=(key[(i+ii+jj)%len(key)]+j+S[i])%num
        j^=(rand()&0xffffffff)
        j=j%num
        S[i], S[j] = S[j], S[i]
        i=(rand()&0xffffffff)%num

    for k in range(len(ans)):
        jj=(jj+1)%num
        ii=(jj+S[(ii+jj)%num])%num
        tmp=S[(S[ii]+S[(jj+(rand()&0xffffffff)%num)%num])%num]
        ans[k]^=(num+tmp)
        ans[k] &= 0xff
    try:
        flag = bytes(ans)
        return flag
    except Exception as e:
        print(f"Error during decryption: {e}")
        return None

# 主循环尝试不同的种子值直到找到正确的解密结果
while True:
    ans1 = decrypt(N=0, Seed=seed)
    ans2 = decrypt(N=5, Seed=seed)

    if ans1 and b'LZSDS' in ans1:
        flag = ans1.decode(errors='ignore')  # 忽略无法解码的字节
        print(f"ans1 Seed:{seed}--> flag:{flag}")
        break

    if ans2 and b'LZSDS' in ans2:
        flag = ans2.decode(errors='ignore')  # 忽略无法解码的字节
        print(f"Seed:{seed}--> flag:{flag}")
        break

    seed += 1
#Seed:1729672800--> flag:LZSDS{Hu@hu3_i3_7h3_cute6t_g1an7_p@nd@_ln_t6e_w0r1d}

最终的时间戳在16.40.00即1729672800

补:由于用python写的脚本,这里还用到了自己编译的dll,简单整理一下原因和过程:

image-20250308150947212

因此要自己构建dll文件,然后再在python中调用就可以保证随机出来的值一样了

简单说一下方法:VS新建**动态链接库(dll)**项目

image-20250308151235947

新建后在namemain.cpp文件里输入以下代码:

CPP
#include "pch.h"
#include <cstdlib>  // 或者使用 #include <stdlib.h> 在C语言中
#include <ctime>    // 如果你还需要使用 time() 函数来设置种子

extern "C" __declspec(dllexport) void sd(int seed) {
    srand(seed);
}

extern "C" __declspec(dllexport) int rd() {
    return rand();
}

注意

image-20250308151408325

所以得#include "pch.h"

然后生成解决方案就会在项目DebugorRelease目录下生成对应的dll文件了,然后再将文件放到.py文件同目录下就能调用了(注意运行时工作目录也要在该目录下)

还有一道主要是动调,实在看不下去了,以后有时间再复现调调看吧。

总结:

算法逆向偏多,个人过于依赖ai,结构体数据类型分析能力还有待提高,不过也算积累了一些经验了啦。

距离目标还很远很远~


Thanks for reading!

蜀道山2024复现笔记

周六 3月 08 2025
3800 字 · 28 分钟