Re刷题记录(一)

新年快乐

image-20230713130721672

64位,有壳,先脱壳

在工具下打开cmd

image-20230713130858435

image-20230713130809617

Xor

tips:shift+e,可以直接得到数组:

image-20230713133819487

helloword

.apk文件,用IDA打开的时候选择apk打开,shift+F12查看字符串,搜索flag{直接找到flag

另外,.apk文件涉及到JBE工具的安装

SimpleRev

unsigned __int64 Decry()
{
  v11 = __readfsqword(0x28u);
  *(_QWORD *)src = 0x534C43444ELL;
  v7 = 0LL;
  v8 = 0;
  v9[0] = 0x776F646168LL;
  v9[1] = 0LL;
  v10 = 0;
  text = (char *)join(key3, v9);//text = key3 + v9
  //key3 = "kills", v9 = "hadow"=>text = "killshadow"
  strcpy(key, key1);			//key = key1 = "ADSFK"
  strcat(key, src);				//key = "ADSFKNDCLS"
  v2 = 0;
  v3 = 0;
  getchar();
  v5 = strlen(key);				//v5 = 10
  for ( i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
      key[i] = key[v3 % v5] + 32;#大写转小写
    ++v3;
  }
  //key = "adsfkndcls"
  printf("Please input your flag:");
  while ( 1 )
  {
    v1 = getchar();
    if ( v1 == 10 )					//换行
      break;
    if ( v1 == 32 )					//空格
    {
      ++v2;
    }
    else
    {
      if ( v1 <= 96 || v1 > 122 )	//不为小写字母
      {
        if ( v1 > 64 && v1 <= 90 )	//为大写字母
        {
          str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
          
          ++v3;
        }
      }
      else							//如果为小写字母
      {
        str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
        ++v3;
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  if ( !strcmp(text, str2) )
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return __readfsqword(0x28u) ^ v11;
}
key = "adsfkndcls"
text = "killshadow"
flag = ""

for i in range(len(text)):
    for j in range(10):
        a = ord(text[i]) - 97 + 26*j - 97 + ord(key[i%10]) + 39
        if (a >= 65 and a<=90) :
            flag += chr(a)

print(flag)

[GXYCTF2019]luck_guy

主要分析代码


v6 = __readfsqword(0x28u);
v0 = time(0LL);//获取时间
srand(v0);//以时间为种子
for ( i = 0; i <= 4; ++i )
{
switch ( rand() % 200 )//200以内随机数
{
case 1:
puts("OK, it's flag:");
memset(&s, 0, 0x28uLL);
strcat((char *)&s, f1);
strcat((char *)&s, &f2);
printf("%s", (const char *)&s);
break;
case 2:
printf("Solar not like you");
break;
case 3:
printf("Solar want a girlfriend");
break;
case 4:
s = 0x7F666F6067756369LL;
v5 = 0;
strcat(&f2, (const char *)&s);
break;
case 5:
for ( j = 0; j <= 7; ++j )
{
if ( j % 2 == 1 )
*(&f2 + j) -= 2;
else
--*(&f2 + j);
}
break;
default:
puts("emmm,you can't find flag 23333");
break;
}

f1、f2为flag,f1有了:GXY{do_not_,f2初始为空,需要根据case 4和case 5求。

猜测是按4 5 1的顺序执行,得到f2。

s = 0x7F666F6067756369LL

按小端存储的思想,f2 =[0x69,0x63,0x75,0x67,0x60,0x6f,0x66,0x7f]

for i in range(0,8):
    if i%2==1:
        f2[i] -= 2
        print(chr(f2[i]),end="")
    else:
        f2[i]-=1
        print(chr(f2[i]), end="")

得到后半部分:hate_me}

小端存储是一种计算机存储数据的方式,其中数据的最低有效位(即最右侧的位)存储在内存单元的最低地址,而数据的最高有效位(即最左侧的位)存储在内存单元的最高地址。这与大端存储相反,大端存储是将数据的最高有效位存储在最低地址,而最低有效位存储在最高地址。小端存储在x86架构的计算机上被广泛使用。

简单注册器

.apk文件,用JEB打开,找到主函数,进行解析,即反汇编,分析主要代码:

if(flag == 1) {
    char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
    x[2] = (char)(x[2] + x[3] - 50);
    x[4] = (char)(x[2] + x[5] - 0x30);
    x[30] = (char)(x[0x1F] + x[9] - 0x30);
    x[14] = (char)(x[27] + x[28] - 97);
    int i;
    for(i = 0; i < 16; ++i) {
        char a = x[0x1F - i];
        x[0x1F - i] = x[i];
        x[i] = a;
    }

    textview.setText("flag{" + String.valueOf(x) + "}");
    return;
}

按着顺序操作就行

x = list("dd2940c04462b4dd7c450528835cca15")


x[2] = chr(ord(x[2]) + ord(x[3]) - 50)
x[4] = chr(ord(x[2]) + ord(x[5]) - 0x30)
x[30] = chr(ord(x[0x1F]) + ord(x[9]) - 0x30)
x[14] = chr(ord(x[27]) + ord(x[28]) - 97)

for i in range(16):
    a = x[0x1F - i]
    x[0x1F - i] = x[i]
    x[i] = a

flag = "".join(x)
print(flag)

注:在 Python 中,字符串是不可变的对象,一旦创建了一个字符串,就不能直接修改它的值。当尝试修改字符串中的某个字符时,会引发 TypeError 异常。

字符串是不可变的序列对象。这意味着你可以像访问列表或元组中的元素一样访问字符串中的字符,但是你不能修改字符串中的任何字符。

故转为列表后再修改。

[GWCTF 2019]pyre

.pyc,python反编译(在线工具)得到:

print ('Welcome to Re World!')
print ('Your input1 is your flag~')
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num
 
for i in range(l - 1):
    code[i] = code[i] ^ code[i + 1]
 
print (code)
code =['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']

直接解:

code =['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']

for i in range(len(code)-1,0,-1):
    code[i-1] = chr(ord(code[i-1]) ^ ord(code[i]))

for i in range(len(code)):
    print(chr((ord(code[i]) - i % 128 - 128) % 128),end="")

[ACTF新生赛2020]easyre

先脱壳,32位

主要函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
  _DWORD v5[3]; // [esp+1Eh] [ebp-22h]
  _BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
  int v7; // [esp+2Fh] [ebp-11h]
  int v8; // [esp+33h] [ebp-Dh]
  int v9; // [esp+37h] [ebp-9h]
  char v10; // [esp+3Bh] [ebp-5h]
  int i; // [esp+3Ch] [ebp-4h]

  __main();
  qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
  printf("Please input:");
  scanf("%s", v6);
  if ( v6[0] != 65 || v6[1] != 67 || v6[2] != 84 || v6[3] != 70 || v6[4] != 123 || v10 != 125 )
    return 0;
  v5[0] = v7;
  v5[1] = v8;
  v5[2] = v9;
  for ( i = 0; i <= 11; ++i )
  {
    if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
      return 0;
  }
  printf("You are correct!");
  return 0;
}

看了wp才知道少了好一部分,总感觉我的IDA有问题

下面是正确的:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+12h] [ebp-2Eh]
  char v5; // [esp+13h] [ebp-2Dh]
  char v6; // [esp+14h] [ebp-2Ch]
  char v7; // [esp+15h] [ebp-2Bh]
  char v8; // [esp+16h] [ebp-2Ah]
  char v9; // [esp+17h] [ebp-29h]
  char v10; // [esp+18h] [ebp-28h]
  char v11; // [esp+19h] [ebp-27h]
  char v12; // [esp+1Ah] [ebp-26h]
  char v13; // [esp+1Bh] [ebp-25h]
  char v14; // [esp+1Ch] [ebp-24h]
  char v15; // [esp+1Dh] [ebp-23h]
  int v16; // [esp+1Eh] [ebp-22h]
  int v17; // [esp+22h] [ebp-1Eh]
  int v18; // [esp+26h] [ebp-1Ah]
  __int16 v19; // [esp+2Ah] [ebp-16h]
  char v20; // [esp+2Ch] [ebp-14h]
  char v21; // [esp+2Dh] [ebp-13h]
  char v22; // [esp+2Eh] [ebp-12h]
  int v23; // [esp+2Fh] [ebp-11h]
  int v24; // [esp+33h] [ebp-Dh]
  int v25; // [esp+37h] [ebp-9h]
  char v26; // [esp+3Bh] [ebp-5h]
  int i; // [esp+3Ch] [ebp-4h]

  __main();
  v4 = 42;
  v5 = 70;
  v6 = 39;
  v7 = 34;
  v8 = 78;
  v9 = 44;
  v10 = 34;
  v11 = 40;
  v12 = 73;
  v13 = 63;
  v14 = 43;
  v15 = 64;
  printf("Please input:");
  scanf("%s", &v19);
  if ( (_BYTE)v19 != 65 || HIBYTE(v19) != 67 || v20 != 84 || v21 != 70 || v22 != 123 || v26 != 125 )
    return 0;
  v16 = v23;
  v17 = v24;
  v18 = v25;
  for ( i = 0; i <= 11; ++i )
  {
    if ( *(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1] )
      return 0;
  }
  printf("You are correct!");
  return 0;
}
  if ( (_BYTE)v19 != 65 || HIBYTE(v19) != 67 || v20 != 84 || v21 != 70 || v22 != 123 || v26 != 125 )

这部分刚好是flag的格式

for ( i = 0; i <= 11; ++i )
  {
    if ( *(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1] )
      return 0;
  }

关键在这。

我们知道v4,就是上面给的数,求v16,flag里面的东西。

需要注意的地方:

*(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1]

“&v4 + i”这里相当于”v4[i]”。这句的含义是在_data_start__里找v16里的字符,然后-1与v4比较是否相等。

然后查看data_start,我一开始是shift+F12,但会漏掉东西,直接点击_data_start__查看:

image-20230715234321093

从上面的7Eh算起,直到往下第三行的’!’:

key = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(&%$# !"'

#注意:27h表示单引号,用\进行转义,即'\''

开始敲:

key = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"'
t = [42,70,39,34,78,44,34,40,73,63,43,64]

for i in t:
    print(chr(key.find(chr(i))+1),end="")
#U9X_1S_W6@T?

find函数:

s = 'hello world'
pos = s.find('world')
print(pos)  
#返回字符串第一次出现的下标,可指定查找下标的范围。
#输出6

[ACTF新生赛2020]rome

strcpy(v12, "Qsw3sj_lz4_Ujw@l");//这是密文
。。。
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
    if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )//A-Z
        *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;//-51
    if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )//a-z
        *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;//-79
    ++*(_DWORD *)&v12[17];
}
//对明文,是大小写字母,就移位
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
    result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
    if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
        return result;
    ++*(_DWORD *)&v12[17];
}
return printf("You are correct!");

直接爆破:

t = "Qsw3sj_lz4_Ujw@l"
flag = ""

a = 0
for i in range(16):
    for j in range(128):
        if j>=65 and j<=90:
            a = (j - 51) % 26 + 65
        elif  j>=97 and j<=122:
            a = (j - 79) % 26 + 97
        else:
            a = j
        if chr(a) == t[i]:
            flag += chr(j)

print(flag)

[GUET-CTF2019]re

找main:

__int64 __fastcall sub_400E28(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
  int v6; // edx
  int v7; // ecx
  int v8; // r8d
  int v9; // r9d
  __int64 result; // rax
  __int64 v11; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v12; // [rsp+28h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  sub_40F950((unsigned int)"input your flag:", a2, a3, a4, a5, a6, 0LL, 0LL, 0LL, 0LL);
  sub_40FA80((unsigned int)"%s", (unsigned int)&v11, v6, v7, v8, v9, v11);
  if ( (unsigned int)sub_4009AE(&v11) )
    sub_410350("Correct!");
  else
    sub_410350("Wrong!");
  result = 0LL;
  if ( __readfsqword(0x28u) != v12 )
    sub_443550();
  return result;
}

看到Correct,进入sub_4009AE:

_BOOL8 __fastcall sub_4009AE(char *a1)
{
  if ( 1629056 * *a1 != 166163712 )
    return 0LL;
  if ( 6771600 * a1[1] != 731332800 )
    return 0LL;
  if ( 3682944 * a1[2] != 357245568 )
    return 0LL;
  if ( 10431000 * a1[3] != 1074393000 )
    return 0LL;
  if ( 3977328 * a1[4] != 489211344 )
    return 0LL;
  if ( 5138336 * a1[5] != 518971936 )
    return 0LL;
  if ( 7532250 * a1[7] != 406741500 )
    return 0LL;
  if ( 5551632 * a1[8] != 294236496 )
    return 0LL;
  if ( 3409728 * a1[9] != 177305856 )
    return 0LL;
  if ( 13013670 * a1[10] != 650683500 )
    return 0LL;
  if ( 6088797 * a1[11] != 298351053 )
    return 0LL;
  if ( 7884663 * a1[12] != 386348487 )
    return 0LL;
  if ( 8944053 * a1[13] != 438258597 )
    return 0LL;
  if ( 5198490 * a1[14] != 249527520 )
    return 0LL;
  if ( 4544518 * a1[15] != 445362764 )
    return 0LL;
  if ( 3645600 * a1[17] != 174988800 )
    return 0LL;
  if ( 10115280 * a1[16] != 981182160 )
    return 0LL;
  if ( 9667504 * a1[18] != 493042704 )
    return 0LL;
  if ( 5364450 * a1[19] != 257493600 )
    return 0LL;
  if ( 13464540 * a1[20] != 767478780 )
    return 0LL;
  if ( 5488432 * a1[21] != 312840624 )
    return 0LL;
  if ( 14479500 * a1[22] != 1404511500 )
    return 0LL;
  if ( 6451830 * a1[23] != 316139670 )
    return 0LL;
  if ( 6252576 * a1[24] != 619005024 )
    return 0LL;
  if ( 7763364 * a1[25] != 372641472 )
    return 0LL;
  if ( 7327320 * a1[26] != 373693320 )
    return 0LL;
  if ( 8741520 * a1[27] != 498266640 )
    return 0LL;
  if ( 8871876 * a1[28] != 452465676 )
    return 0LL;
  if ( 4086720 * a1[29] != 208422720 )
    return 0LL;
  if ( 9374400 * a1[30] == 515592000 )
    return 5759124 * a1[31] == 719890500;
  return 0LL;
}

新知,直接除或用z3解,相关操作待学习:

from z3 import *

s = Solver()
a1 = [0]*32
for i in range(32):
    a1[i] = Int('a1['+str(i)+']')

s.add( 1629056 * a1[0] == 166163712 )
。。。
s.check()
m = s.model()

# 按照下标的顺序打印模型
for i in range(32):
    if i == 6:
        print('1',end="")#题目问题,少个1
    else:
        print(chr(m[a1[i]].as_long()),end="")

[HDCTF2019]Maze

花指令:目的是干扰ida和od等软件对程序的静态分析。使这些软件无法正常反汇编出原始代码。

找到有实意的字符串后跳转不了,点main函数到这个页面:

image-20230718162633289

call near ptr 0EC85D78Bh调用了一个不是地址的地址,推断出这段代码添加了花指令,IDA分析失败了

先将jnz指令nop掉(右键选nop)。按其它人的wp操作确没有得到想要的结果,搞不懂,然后参考了一个没有nop的wp:

先将loc_40102E转数据,d:

image-20230719114130443

再选中转换的部分转代码,按c

image-20230719114216041

然后下面的代码就变正常了,接着回到上面的main部分,选择按p,IDA就会把这段汇编代码分析成函数:

image-20230719114335263

最后在左栏function选main进行反汇编即可得到伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ecx
  int v5; // [esp-4h] [ebp-28h]
  int i; // [esp+10h] [ebp-14h]
  char v7[16]; // [esp+14h] [ebp-10h] BYREF

  sub_401140(aGoThroughTheMa);
  v5 = scanf("%14s", v7);
  if ( (v3 ^ v5) == v3 )
    JUMPOUT(0x40102E);
  for ( i = 0; i <= 13; ++i )
  {
    switch ( v7[i] )
    {
      case 'a':
        --*(_DWORD *)asc_408078;
        break;
      case 'd':
        ++*(_DWORD *)asc_408078;
        break;
      case 's':
        --dword_40807C;
        break;
      case 'w':
        ++dword_40807C;
        break;
      default:
        continue;
    }
  }
  if ( *(_DWORD *)asc_408078 == 5 && dword_40807C == -4 )
  {
    sub_401140(aCongratulation);
    sub_401140(aHereIsTheFlagF);
  }
  else
  {
    sub_401140(aTryAgain);
  }
  return 0;
}

查看string,可以看到:

*******+********* ******    ****   ******* **F******    **************

再结合maze单词,猜测是走迷宫类型。其长度为70,分成7*10的迷宫:

"*******+**" 
"******* **" 
"****    **" 
"**   *****" 
"** **F****" 
"**    ****" 
"**********"

+号为起点,F为终点。

分别查看asc_408078和dword_40807C的初始值,为7和0,for循环后,分别变成5和-4,所以正确的路线应该是:ssaaasaassdddw。即为flag。

main无法反汇编

image-20230719115348002

按d,找到401095处:

image-20230719120316309

双击sub_401020,再反汇编进入函数:

image-20230719120357020

此时本该返回main再反汇编就能查看伪码,但我还是不行,不理解。

然后直接看汇编写吧,好在汇编勉强能理解。

[FlareOn4]IgniteMe

关键:

int sub_401050()
{
  int v1; // [esp+0h] [ebp-Ch]
  int i; // [esp+4h] [ebp-8h]
  unsigned int j; // [esp+4h] [ebp-8h]
  char v4; // [esp+Bh] [ebp-1h]

  v1 = sub_401020((int)byte_403078);
  v4 = sub_401000();
  for ( i = v1 - 1; i >= 0; --i )
  {
    byte_403180[i] = v4 ^ byte_403078[i];
    v4 = byte_403078[i];
  }
  for ( j = 0; j < 0x27; ++j )
  {
    if ( byte_403180[j] != (unsigned __int8)byte_403000[j] )
      return 0;
  }
  return 1;
}

byte_403000是知道的,关键在初次异或的v4值不知道,进入sub_401000():

__int16 sub_401000()
{
  return (unsigned __int16)__ROL4__(-2147024896, 4) >> 1;
}

返回的固定值,可以直接求:

bin(-2,147,024,896)
=1000 0000 0000 0111 0000 0000 0000 0000 
逻辑左移4位,高位的1进入低位
=0000 0000 0111 0000 0000 0000 0000 1000
取低16位后再右移1位
=0000 0000 0000 0100 = 4

查看这个负数的二进制时,用代码会有误差,不准确,可以用计算器

也可以动调求出结果,但不会:

image-20230724150221619

v4处设下断点

image-20230724150311735

选择Remote Windows debugger,然后start process,手动执行:

image-20230724150932802

image-20230724150753498

可以看到0019FF5C处的值变为4

然后敲代码计算flag:

t = [   13,  38,  73,  69,  42,  23, 120,  68,  43, 108,
   93,  94,  69,  18,  47,  23,  43,  68, 111, 110,
   86,   9,  95,  69,  71, 115,  38,  10,  13,  19,
   23,  72,  66,   1,  64,  77,  12,   2, 105]

flag = [4,]
for i in range(len(t)-1,-1,-1):#注意是倒着来的
   flag.append(t[i]^flag[len(t)-1-i])#每计算处一个flag[i],当作新的v4参与异或
for i in flag[len(flag):0:-1]:
   print(chr(i),end="")

[GWCTF 2019]xxor

记下几个点:

{
  int i; // [rsp+8h] [rbp-68h]
  int j; // [rsp+Ch] [rbp-64h]
  __int64 v6[6]; // [rsp+10h] [rbp-60h] BYREF
  __int64 v7[6]; // [rsp+40h] [rbp-30h] BYREF

  v7[5] = __readfsqword(0x28u);
  puts("Let us play a game?");
  puts("you have six chances to input");
  puts("Come on!");
  memset(v6, 0, 40);
  for ( i = 0; i <= 5; ++i )
  {
    printf("%s", "input: ");
    __isoc99_scanf("%d", (char *)v6 + 4 * i);
  }
  memset(v7, 0, 40);
  for ( j = 0; j <= 2; ++j )
  {
    dword_601078 = v6[j];
    dword_60107C = HIDWORD(v6[j]);
    sub_400686((unsigned int *)&dword_601078, byte_601060);
    LODWORD(v7[j]) = dword_601078;
    HIDWORD(v7[j]) = dword_60107C;
  }
  if ( (unsigned int)sub_400770(v7) != 1 )
  {
    puts("NO NO NO~ ");
    exit(0);
  }
  puts("Congratulation!\n");
  puts("You seccess half\n");
  puts("Do not forget to change input to hex and combine~\n");
  puts("ByeBye");
  return 0LL;
}

输入了6个数,j只到3,再进入下面的sub_400686函数,猜测经过HIDWORD后,每次传参传连续的两个数。查阅了资料:

HIWORD是High Word的缩写,作用是取得某个4字节变量(即32位的值)在内存中处于高位的两个字节,即一个word长的数据
LOWORD是Low Word的缩写,作用是取得某个4字节变量(即32位的值)在内存中处于低位的两个字节,即一个word长的数据

了解了这两个函数便很好理解了,传入的dword_601078代表4个字节,即两个数。

另一个参数byte_601060,点进去:

image-20230724192142794

sub_400686:

__int64 __fastcall sub_400686(unsigned int *a1, _DWORD *a2)
{
  __int64 result; // rax
  unsigned int v3; // [rsp+1Ch] [rbp-24h]
  unsigned int v4; // [rsp+20h] [rbp-20h]
  int v5; // [rsp+24h] [rbp-1Ch]
  unsigned int i; // [rsp+28h] [rbp-18h]

  v3 = *a1;
  v4 = a1[1];
  v5 = 0;
  for ( i = 0; i <= 0x3F; ++i )
  {
    v5 += 1166789954;
    v3 += (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
    v4 += (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
  }
  *a1 = v3;
  result = v4;
  a1[1] = v4;
  return result;
}

经过这个函数后sub_400770(v7) == 1,进入sub_400770:

__int64 __fastcall sub_400770(_DWORD *a1)
{
  if ( a1[2] - a1[3] == 2225223423LL
    && a1[3] + a1[4] == 4201428739LL
    && a1[2] - a1[4] == 1121399208LL
    && *a1 == -548868226
    && a1[5] == -2064448480
    && a1[1] == 550153460 )
  {
    puts("good!");
    return 1LL;
  }
  else
  {
    puts("Wrong!");
    return 0LL;
  }
}

不够熟悉,没想到要用z3解,且解的时候注意把负数转为16进制,不然结果会有问题,且建议用计算器转:

from z3 import *
a = IntVector('a',6)
solver = Solver()
solver.add(a[2] - a[3] == 2225223423)
solver.add(a[3] + a[4] == 4201428739)
solver.add(a[2] - a[4] == 1121399208)
solver.add(a[0] == 0xDF48EF7E)
solver.add(a[5] == 0x84F30420)
solver.add(a[1] == 550153460)
flag = []
if solver.check() == sat:
    m = solver.model()
    for i in a:
        flag.append(m[i])
    print(flag)
f = ''
#[3746099070, 550153460, 3774025685, 1548802262, 2652626477, 2230518816]

接着逆推就好:

a2 = [3746099070, 550153460, 3774025685, 1548802262, 2652626477, 2230518816]
t = [2,2,3,4]

for i in range(0,5,2):
    v3 = a2[i]
    v4 = a2[i+1]
    v5 = 1166789954*0x40
    for j in range(0x40):
        v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + t[2]) ^ ((v3 >> 9) + t[3]) ^ 0x10
        v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + t[0]) ^ ((v4 >> 9) + t[1]) ^ 0x20
        v5 -= 1166789954
    a2[i] = v3
    a2[i+1] = v4
print(a2)

搞不懂为什么用python得不到正确结果,用c:

#include <iostream>
using namespace std;

int main()
{
    //__int64 
    int a[6] = { 3746099070, 550153460, 3774025685, 1548802262, 2652626477, 2230518816 };
    unsigned int a2[4] = { 2,2,3,4 };
    unsigned int v3, v4;
    //"unsigned int" 是一种数据类型,它表示一个无符号整数。这意味着它只能表示非负整数,而不能表示负数。
    int v5;
    for (int j = 0; j <= 4; j += 2) {
        v3 = a[j];
        v4 = a[j + 1];
        v5 = 1166789954*0x40;
        for (int i = 0; i <= 0x3F; ++i) {
            v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
            v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
            v5 -= 1166789954;
        }
        a[j] = v3;
        a[j + 1] = v4;
    }
    for (int i = 0; i < 6; ++i) 
        cout << a[i] << " ";
    return 0;
}
#a = [6712417, 6781810, 6643561, 7561063, 7497057, 7610749]

python里对每个值转字节串即可,也可以在最后的输出循环直接打印:

    //小端排序
    for (int i = 0; i < 6; ++i) {
        cout << *((char*)&a[i] + 2) << *((char*)&a[i] + 1) <<  * ((char*)&a[i]);
    }

但c已经生疏了,不太会。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1666739907@qq.com
github