西湖论剑2021

感悟: 收获挺大的,同时意识到自己的做题思路和方法还有所欠缺,还是要做好应对体力活的准备。


1、ROR

  __CheckForDebuggerJustMyCode(&unk_406029);
  v6[0] = 128;
  v6[1] = 64;
  v6[2] = 32;
  v6[3] = 16;
  v6[4] = 8;
  v6[5] = 4;
  v6[6] = 2;
  v6[7] = 1;
  memset(input, 0, sizeof(input));
  memset(Buf2, 0, sizeof(Buf2));
  printf("Input:", v4);
  scanf("%40s", (char)input);
  if ( strlen(input) != 40 )
    exit(0);
  for ( i = 0; i < 40; i += 8 )
  {
    for ( j = 0; j < 8; ++j )
    {
      v5 = ((v6[j] & input[i + 3]) << (8 - (3 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 3]) >> ((3 - j) % 8u)) | ((v6[j] & input[i + 2]) << (8 - (2 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 2]) >> ((2 - j) % 8u)) | ((v6[j] & input[i + 1]) << (8 - (1 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 1]) >> ((1 - j) % 8u)) | ((v6[j] & (unsigned __int8)input[i]) << (8 - -j % 8u)) | ((v6[j] & (unsigned int)input[i]) >> (-j % 8u));
      Buf2[j + i] = byte_405000[(unsigned __int8)(((v6[j] & (unsigned __int8)input[i + 7]) << (8 - (7 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 7]) >> ((7 - j) % 8u)) | ((v6[j] & input[i + 6]) << (8 - (6 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 6]) >> ((6 - j) % 8u)) | ((v6[j] & input[i + 5]) << (8 - (5 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 5]) >> ((5 - j) % 8u)) | ((v6[j] & input[i + 4]) << (8 - (4 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 4]) >> ((4 - j) % 8u)) | v5)];
    }
  }
  if ( memcmp(&unk_405100, Buf2, 0x28u) )
  {
    puts("Wrong");
    exit(0);
  }
  puts("Congratulations");
  puts("flag is DASCTF{your input}");
  return 0;
}

主要就是对输入8个一组进行位运算,之后再进行表byte_405000的索引,看到立马想到z3,修改代码z3解密。

from z3 import *
v6=[128,64,32,16,8,4,2,1]
input=[BitVec('s%d'%i,8) for i in range(40)]
Buf2=[0]*40
byte_405000=[0x65, 0x08, 0xF7, 0x12, 0xBC, 0xC3, 0xCF, 0xB8, 0x83, 0x7B, 0x02, 0xD5, 0x34, 0xBD, 0x9F, 0x33, 0x77, 0x76, 0xD4, 0xD7, 0xEB, 0x90, 0x89, 0x5E, 0x54, 0x01, 0x7D, 0xF4, 0x11, 0xFF, 0x99, 0x49, 0xAD, 0x57, 0x46, 0x67, 0x2A, 0x9D, 0x7F, 0xD2, 0xE1, 0x21, 0x8B, 0x1D, 0x5A, 0x91, 0x38, 0x94, 0xF9, 0x0C, 0x00, 0xCA, 0xE8, 0xCB, 0x5F, 0x19, 0xF6, 0xF0, 0x3C, 0xDE, 0xDA, 0xEA, 0x9C, 0x14, 0x75, 0xA4, 0x0D, 0x25, 0x58, 0xFC, 0x44, 0x86, 0x05, 0x6B, 0x43, 0x9A, 0x6D, 0xD1, 0x63, 0x98, 0x68, 0x2D, 0x52, 0x3D, 0xDD, 0x88, 0xD6, 0xD0, 0xA2, 0xED, 0xA5, 0x3B, 0x45, 0x3E, 0xF2, 0x22, 0x06, 0xF3, 0x1A, 0xA8, 0x09, 0xDC, 0x7C, 0x4B, 0x5C, 0x1E, 0xA1, 0xB0, 0x71, 0x04, 0xE2, 0x9B, 0xB7, 0x10, 0x4E, 0x16, 0x23, 0x82, 0x56, 0xD8, 0x61, 0xB4, 0x24, 0x7E, 0x87, 0xF8, 0x0A, 0x13, 0xE3, 0xE4, 0xE6, 0x1C, 0x35, 0x2C, 0xB1, 0xEC, 0x93, 0x66, 0x03, 0xA9, 0x95, 0xBB, 0xD3, 0x51, 0x39, 0xE7, 0xC9, 0xCE, 0x29, 0x72, 0x47, 0x6C, 0x70, 0x15, 0xDF, 0xD9, 0x17, 0x74, 0x3F, 0x62, 0xCD, 0x41, 0x07, 0x73, 0x53, 0x85, 0x31, 0x8A, 0x30, 0xAA, 0xAC, 0x2E, 0xA3, 0x50, 0x7A, 0xB5, 0x8E, 0x69, 0x1F, 0x6A, 0x97, 0x55, 0x3A, 0xB2, 0x59, 0xAB, 0xE0, 0x28, 0xC0, 0xB3, 0xBE, 0xCC, 0xC6, 0x2B, 0x5B, 0x92, 0xEE, 0x60, 0x20, 0x84, 0x4D, 0x0F, 0x26, 0x4A, 0x48, 0x0B, 0x36, 0x80, 0x5D, 0x6F, 0x4C, 0xB9, 0x81, 0x96, 0x32, 0xFD, 0x40, 0x8D, 0x27, 0xC1, 0x78, 0x4F, 0x79, 0xC8, 0x0E, 0x8C, 0xE5, 0x9E, 0xAE, 0xBF, 0xEF, 0x42, 0xC5, 0xAF, 0xA0, 0xC2, 0xFA, 0xC7, 0xB6, 0xDB, 0x18, 0xC4, 0xA6, 0xFE, 0xE9, 0xF5, 0x6E, 0x64, 0x2F, 0xF1, 0x1B, 0xFB, 0xBA, 0xA7, 0x37, 0x8F]
enc=[0x65, 0x55, 0x24, 0x36, 0x9D, 0x71, 0xB8, 0xC8, 0x65, 0xFB, 0x87, 0x7F, 0x9A, 0x9C, 0xB1, 0xDF, 0x65, 0x8F, 0x9D, 0x39, 0x8F, 0x11, 0xF6, 0x8E, 0x65, 0x42, 0xDA, 0xB4, 0x8C, 0x39, 0xFB, 0x99, 0x65, 0x48, 0x6A, 0xCA, 0x63, 0xE7, 0xA4, 0x79]
tmp=[]
for i in range(len(enc)):
    tmp.append(byte_405000.index(enc[i]))
for i in range(0,40,8):
    for j in range(8):
        v5 = ((v6[j] & input[i + 3]) << (8 - (3 - j) % 8)) | ((v6[j] & input[i + 3]) >> ((3 - j) % 8)) | ((v6[j] & input[i + 2]) << (8 - (2 - j) % 8)) | ((v6[j] & input[i + 2]) >> ((2 - j) % 8)) | ((v6[j] & input[i + 1]) << (8 - (1 - j) % 8)) | ((v6[j] & input[i + 1]) >> ((1 - j) % 8)) | ((v6[j] & input[i]) << (8 - -j % 8)) | ((v6[j] & input[i]) >> (-j % 8))
        Buf2[j + i]=((((v6[j] & input[i + 7]) << (8 - (7 - j) % 8)) | ((v6[j] & input[i + 7]) >> ((7 - j) % 8)) | ((v6[j] & input[i + 6]) << (8 - (6 - j) % 8)) | ((v6[j] & input[i + 6]) >> ((6 - j) % 8)) | ((v6[j] & input[i + 5]) << (8 - (5 - j) % 8)) | ((v6[j] & input[i + 5]) >> ((5 - j) % 8)) | ((v6[j] & input[i + 4]) << (8 - (4 - j) % 8)) | ((v6[j] & input[i + 4]) >> ((4 - j) % 8)) | v5))&0xff
s=Solver()
for i in range(40):
    s.add(Buf2[i]==tmp[i])
s.check()
if s.check():
    m=s.model()
    for i in range(40):
        print(chr(m[input[i]].as_long()),end='')

赛后整理的时候copy一遍就过了,比赛的时候出了点问题,卡了段时间。

另一种解法就是了解程序的位运算在干什么,v6数组对应的8位二进制每一位,单拿一小段分析。

i=0
j=0~8
v6[0]=128->0x10000000
((v6[j] & input[i + 3]) << (8 - (3 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 3]) >> ((3 - j) % 8u)) | ((v6[j] & input[i + 2]) << (8 - (2 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 2]) >> ((2 - j) % 8u)) | ((v6[j] & input[i + 1]) << (8 - (1 - j) % 8u)) | ((v6[j] & (unsigned int)input[i + 1]) >> ((1 - j) % 8u)) | ((v6[j] & (unsigned __int8)input[i]) << (8 - -j % 8u)) | ((v6[j] & (unsigned int)input[i]) >> (-j % 8u))
    
/*上为char input[0~3]  分别与上v6[j]取出最高位,因为第二步限定(unsigned __int8)类型,所以只看右移,左移都舍去了
input 0   intput 1  input2  input3
高位  a0000000  b0000000  c0000000  d0000000
input3 右移3  input2 右移2  input 1 右移1 ..
变成 abcd0000
后半段也类似 加上input4~7 假设7为x0000000则 最后变为abcd...x
也就是8个一组 对应位的二进制重新组合按照顺序做到右放0-7的对应位
逆过程,通过enc在table中的索引转换为8个二进制,之后8个一组,重组即可。
*/

exp:

from z3 import *
v6=[128,64,32,16,8,4,2,1]
Buf2=[0]*40
byte_405000=[0x65, 0x08, 0xF7, 0x12, 0xBC, 0xC3, 0xCF, 0xB8, 0x83, 0x7B, 0x02, 0xD5, 0x34, 0xBD, 0x9F, 0x33, 0x77, 0x76, 0xD4, 0xD7, 0xEB, 0x90, 0x89, 0x5E, 0x54, 0x01, 0x7D, 0xF4, 0x11, 0xFF, 0x99, 0x49, 0xAD, 0x57, 0x46, 0x67, 0x2A, 0x9D, 0x7F, 0xD2, 0xE1, 0x21, 0x8B, 0x1D, 0x5A, 0x91, 0x38, 0x94, 0xF9, 0x0C, 0x00, 0xCA, 0xE8, 0xCB, 0x5F, 0x19, 0xF6, 0xF0, 0x3C, 0xDE, 0xDA, 0xEA, 0x9C, 0x14, 0x75, 0xA4, 0x0D, 0x25, 0x58, 0xFC, 0x44, 0x86, 0x05, 0x6B, 0x43, 0x9A, 0x6D, 0xD1, 0x63, 0x98, 0x68, 0x2D, 0x52, 0x3D, 0xDD, 0x88, 0xD6, 0xD0, 0xA2, 0xED, 0xA5, 0x3B, 0x45, 0x3E, 0xF2, 0x22, 0x06, 0xF3, 0x1A, 0xA8, 0x09, 0xDC, 0x7C, 0x4B, 0x5C, 0x1E, 0xA1, 0xB0, 0x71, 0x04, 0xE2, 0x9B, 0xB7, 0x10, 0x4E, 0x16, 0x23, 0x82, 0x56, 0xD8, 0x61, 0xB4, 0x24, 0x7E, 0x87, 0xF8, 0x0A, 0x13, 0xE3, 0xE4, 0xE6, 0x1C, 0x35, 0x2C, 0xB1, 0xEC, 0x93, 0x66, 0x03, 0xA9, 0x95, 0xBB, 0xD3, 0x51, 0x39, 0xE7, 0xC9, 0xCE, 0x29, 0x72, 0x47, 0x6C, 0x70, 0x15, 0xDF, 0xD9, 0x17, 0x74, 0x3F, 0x62, 0xCD, 0x41, 0x07, 0x73, 0x53, 0x85, 0x31, 0x8A, 0x30, 0xAA, 0xAC, 0x2E, 0xA3, 0x50, 0x7A, 0xB5, 0x8E, 0x69, 0x1F, 0x6A, 0x97, 0x55, 0x3A, 0xB2, 0x59, 0xAB, 0xE0, 0x28, 0xC0, 0xB3, 0xBE, 0xCC, 0xC6, 0x2B, 0x5B, 0x92, 0xEE, 0x60, 0x20, 0x84, 0x4D, 0x0F, 0x26, 0x4A, 0x48, 0x0B, 0x36, 0x80, 0x5D, 0x6F, 0x4C, 0xB9, 0x81, 0x96, 0x32, 0xFD, 0x40, 0x8D, 0x27, 0xC1, 0x78, 0x4F, 0x79, 0xC8, 0x0E, 0x8C, 0xE5, 0x9E, 0xAE, 0xBF, 0xEF, 0x42, 0xC5, 0xAF, 0xA0, 0xC2, 0xFA, 0xC7, 0xB6, 0xDB, 0x18, 0xC4, 0xA6, 0xFE, 0xE9, 0xF5, 0x6E, 0x64, 0x2F, 0xF1, 0x1B, 0xFB, 0xBA, 0xA7, 0x37, 0x8F]
enc=[0x65, 0x55, 0x24, 0x36, 0x9D, 0x71, 0xB8, 0xC8, 0x65, 0xFB, 0x87, 0x7F, 0x9A, 0x9C, 0xB1, 0xDF, 0x65, 0x8F, 0x9D, 0x39, 0x8F, 0x11, 0xF6, 0x8E, 0x65, 0x42, 0xDA, 0xB4, 0x8C, 0x39, 0xFB, 0x99, 0x65, 0x48, 0x6A, 0xCA, 0x63, 0xE7, 0xA4, 0x79]
tmp=[]
for i in range(len(enc)):
    tmp.append(bin(byte_405000.index(enc[i]))[2:].zfill(8))
print(tmp)
m=['']*40
for i in range(0,40,8):
    for p in range(8):#控制tmp的索引
        for j in range(8):
            m[i+j]+=tmp[i+p][j]
for i in m:
    print(chr(int(i,2)),end='')
#Q5la5_3KChtem6_HYHk_NlHhNZz73aCZeK05II96

上述两种方法都可以求逆,不过擅长使用z3来解这道题是非常迅速的,减少了代码分析量。

2、TacticalArmed

赛后复现,学到很多东西,基础还是⑧顶,尤其是对执行流程有修改的代码。

win逆向,有Tlscallback函数,开了一个进程,跟进进程处理函数。

.text:004010D6 ;   __try { // __except at loc_4010F1
.text:004010D6                 mov     [ebp+ms_exc.registration.TryLevel], 0
.text:004010DD                 int     2Dh             ; Windows NT - debugging services: eax = type
.text:004010DF                 nop
.text:004010E0                 jmp     short loc_401142
.text:004010E0 ;   } // starts at 4010D6

存在Int 2d反调试,即正常运行会引发异常而检测到调试器则继续执行,所以真正代码逻辑在异常处理中。

分析代码知是对dword_405000这个内存中赋值了4个int,一般是修改key或某个解密需要的参数。

dword_405000 ->7CE45630h  58334908h  66398867h  0C35195B1h
//retn 指令
retn 4 -> pop eip  add esp,4

之后主函数内用到了一个函数指针v21,来表示lpadress的函数。

  lpAdress = malloc(0x10u);
  VirtualProtect(lpAdress, 0x10u, 0x40u, &flOldProtect);
  v21 = (__int64 (__fastcall *)(int, _DWORD))lpAdress;

//函数指针
    int Func(int x);   /*声明一个函数*/
    int (*p) (int);  /*定义一个函数指针*/
    p = Func;          /*将Func函数的首地址赋给指针变量p*/
	p(1) 等价 Func(1)  

整体main函数如下

  lpAdress = malloc(0x10u);
  VirtualProtect(lpAdress, 0x10u, 0x40u, &flOldProtect);
  v21 = (__int64 (__fastcall *)(int, _DWORD))lpAdress;
  Src = (char *)&loc_405010;                    // 指向一个神秘的数串 类似opcode
  Size = 0;
  v18 = 0;
  printf("Input flag here:", v13);
  scanf("%255s", (char)Str);
  v17 = strlen(Str) >> 3;                       // size_t是unsigned int 的一个宏定义 主要用来计数
  a3 = 0;
  v15 = 0;
  a2 = 0;
  v12 = v4;
  HIDWORD(v11) = v3;
  v5 = __readeflags();
  v10 = v5;
  while ( 1 )                                   // v15循环33次 分清
  {
    while ( 1 )                                 
    {
      while ( Size )
      {
        memset(lpAdress, 0, 0x10u);             // 清0
        memcpy(lpAdress, Src, Size);            // 把src处指向的代码 赋值过去
        dispatch((int)lpAdress, a2, a3);
        *((_BYTE *)lpAdress + Size) = 0xC3;     // ret的机器码是0xc3
        __writeeflags(v10);
        v6 = v21(v12, HIDWORD(v11));
        v12 = v7;
        v11 = v6;
        v8 = __readeflags();
        v10 = v8;
        ++a2;
        Src += 16;
        Size = unk_405220[v18++];               // 读取opcode的大小 控制里面的代码执行
      }
      if ( v15 == 33 )
        break;
      ++v15;
      a2 = 0;
      Src = (char *)&loc_405010;
      Size = unk_405220[0];                     // 6
      v18 = 1;
    }
    if ( ++a3 == v17 )                          // v17是输入长度//8  输入8个一组
      break;
    v15 = 0;
    Size = 0;
  }
  if ( memcmp(Str, dword_40532C, 40u) )
  {
    puts("Wrong");
    exit(0);
  }
  puts("Congratulations");
  puts("flag is DASCTF{your input}");
  return 0;

主要步骤是是while(size)里的循环,主要是代码的执行。

*((_BYTE *)lpAdress + Size) = 0xC3;
是对每个语句末写上ret语句,用于返回。
之后通过v21来调用,在调用前还有一个针对opcode的smc

用switch case语句来进行不同变量的分发,比较新颖。

void __cdecl dispatch(int a1, int a2, int a3)
{
  unsigned int v3; // [esp+Ch] [ebp-50h]
  unsigned int v4; // [esp+50h] [ebp-Ch]
  int i; // [esp+58h] [ebp-4h]

  __CheckForDebuggerJustMyCode(&unk_406015);
  for ( i = 0; *(_BYTE *)(i + a1); ++i )        // 一直找opcode为0的地方停
    ;
  v4 = dword_4052A8[a2] % 0x10u;
  v3 = (unsigned int)dword_4052A8[a2] >> 4;     // 16进制数的高位和低位 16进制模式
  switch ( v3 )
  {
    case 1u:
      *(_DWORD *)(i + a1) = 4 * (v4 + 2 * a3) + 0x405648;// input a3是8个一组 依次越过8个字节
      break;
    case 2u:
      *(_DWORD *)(i + a1) = 4 * v4 + 0x405000;  // key
      break;
    case 3u:
      *(_DWORD *)(i + a1) = &unk_405748;        // sum 看汇编得出 
      break;
  }
}

case语句的左侧是一个int的指针,所以右侧是一个地址,可以跳转到0x405648和0x405000,发现一个是输入的首地址,一个这是在Tlscallback中被修改过的int数据组的地址,还有一个&unk_405748,没有其他的交叉引用所以初始默认为0,(全局变量)。

而switch 和 case中用到的索引在dword_4052A8这个数组中,16进制高位为case索引,低位为寻址的下标。

综上,流程就是把src处的代码依次cpy到lpadress中,通过dispatch来修改操作数的值(操作数也是写入机器码中的),之后依次执行。这片代码通过v15控制一共执行33次,同时根据输入进行8个一组分组再循环上述操作。

Src = (char *)&loc_405010; src每次+16执行

mov操作后面都是00,所以需要dispatch来赋值。

8B 0D 00 00 00 00                 mov     ecx, large ds:0
81 E9 D2 96 5A 7E                 sub     ecx, 7E5A96D2h  
89 0D 00 00 00 00                 mov     large ds:0, ecx
8B 15 00 00 00 00                 mov     edx, large ds:0
C1 EA 05                          shr     edx, 5
A1 00 00 00 00                    mov     eax, large ds:0
03 C2                             add     eax, edx
8B 0D 00 00 00 00                 mov     ecx, large ds:0
03 0D 00 00 00 00                 add     ecx, large ds:0
33 C1                             xor     eax, ecx
8B 15 00 00 00 00                 mov     edx, large ds:0
C1 E2 04                          shl     edx, 4
8B 0D 00 00 00 00                 mov     ecx, large ds:0
03 CA                             add     ecx, edx
33 C1                             xor     eax, ecx
8B 15 00 00 00 00                 mov     edx, large ds:0
03 D0                             add     edx, eax
89 15 00 00 00 00                 mov     large ds:0, edx
A1 00 00 00 00                    mov     eax, large ds:0
C1 E8 05                          shr     eax, 5    
8B 0D 00 00 00 00                 mov     ecx, large ds:0  
03 C8                             add     ecx, eax
8B 15 00 00 00 00                 mov     edx, large ds:0
03 15 00 00 00 00                 add     edx, large ds:0   33 CA                             xor     ecx, edx
A1 00 00 00 00                    mov     eax, large ds:0
C1 E0 04                          shl     eax, 4
8B 15 00 00 00 00                 mov     edx, large ds:0
03 D0                             add     edx, eax
33 CA                             xor     ecx, edx
A1 00 00 00 00                    mov     eax, large ds:0
03 C1                             add     eax, ecx
A3 00 00 00 00                    mov     large ds:0, eax

脚本对4025A8数组处理,拿到修改操作数的流程。

for ( i = 0; *(_BYTE *)(i + a1); ++i )// 一直找opcode为0的地方停

3 0 3 1 0 2 0 3 1 0 1 0 2 0 0 1 0 1 1 0 2 0 3 1 0 1 0 2 0 0 1 0 
0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 0 1 0 
//举例
 3 0 -> switch(3) 也就是 把8D 0D 00 00 00 004个改成unk_405748的地址并取内容赋值给ecx
 0 0 无操做
 1 1 是取出input[1] 右边是v4=1   4 * (v4 + 2 * a3) + 0x405648
 2 1 是取出key[1] //大致分析后确定  
 ...依次补全汇编并翻译

如下:

mov ecx,&unk_405748

sum=0;

sub     ecx, 7E5A96D2h
mov &unk_405748,ecx

sum-=0x7E5A96D2

mov  edx,input[1]
shr edx,5

mov eax,key[1]

add eax,edx //2 1  0 0

mov ecx,sum
add ecx,input[1]

xor eax,ecx   //(input[1]+sum)^(input[1]>>5 + key[1])

mov edx, input[1]
shl edx,4
mov ecx,key[0]
add ecx,edx
xor eax,ecx
// (input[1]<<4 + key[0])^(input[1]+sum)^(input[1]>>5 + key[1])

mov edx ,input[0]
add edx,eax

mov edx,input[0] 

//以上实现的就是 v0+=(v1<<4 + k0)^(v1 + sum)(v1 >>5 + k1)
.....

//不过汇编中没有 mov  xxx,0 并且 对 sum 那边地址内容unk_405748 也没有别的交叉引用  也就是每组

分析到一半就能推出是Tea加密,轮数为33 ,default为0x7E5A96D2,既然分析出&unk_405748为sum,并且对他没有别的引用,所以再对密文多次加密时sum是在上一轮基础上使用的。

解密脚本:

#include<iostream>
void tea_decode(unsigned int* s, unsigned int* key,int count);
int main() {
	int a = 0xE98651DC67185A11;
	unsigned int enc[] = { 0x422F1DED,0x1485E472,0x35578D5,0x0BF6B80A2,0x97D77245,0x2DAE75D1,0x665FA963,0x292E6D74,0x9795FCC1,0x0BB5C8E9};
	unsigned int key[4] = { 0x7CE45630,0x58334908,0x66398867,0x0C35195B1 };
	for(int i=0;i<10;i+=2)
	tea_decode(enc+i, key,i/2);
	return 0;
}
void tea_decode(unsigned int* s, unsigned int* key,int count) {
	unsigned int v0 = s[0];
	unsigned int v1 = s[1];
	uint32_t sum = 0;
    unsigned int defalt = 0x7E5A96D2;
	unsigned int k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3];
	for (int i = 0; i < (33*count+33); i++)// sum状态保存到下一组
		sum -= defalt;
	for (int i = 0; i < 33; i++) {  
		v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
		v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
		sum += defalt;
	}
	s[0] = v0;
	s[1] = v1;
	printf("%d,%d,", v0,v1);
}
/*
a=[826566507,843212655,845236089,1097428850,1198086245,1902206776,1132088430,1181900618,1917866824,1261778286]
for i in a:
    print(int.to_bytes(i,4,'little').decode(),end='')
*/

3、虚假的粉丝

dos界面的代码动画,有点震撼,不过题目没设计到什么算法,主要还是读流程。

首先就是找到3个key,查看strings窗口,发现第一字符串很可疑,对其交叉引用发现了一个未被调用的函数,需要重新定义一下。

//#j#M_OEE!jmhih,=555"xtx
int sub_401379()
{
  char Buffer[100]; // [esp+16h] [ebp-92h] BYREF
  char FileName[30]; // [esp+7Ah] [ebp-2Eh] BYREF
  FILE *Stream; // [esp+98h] [ebp-10h]
  int i; // [esp+9Ch] [ebp-Ch]

  for ( i = 0; i <= 23; ++i )
    FileName[i] = aJMOeeJmhih555X[i] ^ 0xC;
  Stream = fopen(FileName, "r");
  fread(Buffer, 0x57u, 1u, Stream);
  return printf("%s\n", Buffer);
}    

拉出去异或一下,是在读P./f/ASCII-faded 1999P.txt这个文件,打开查看,发现key。

K3y1: (hex(ord('A')) + hex(ord('W'))).replace("0x", "")
K3y2: ord('F') + ord('a') + ord('d') + ord('e') + ord('d') + ord('i') + ord('s') + ord('b') + ord('e') + ord('s') + ord('t')
#key3是文件读取的字节大小,根据if判断知是40
if ( Buffer[0] != 'U' || Buffer[39] != 'S' )

之后它通过上述的key计算了一个新的文件,之后从中读取内容。

"UzNDcmU3X0szeSUyMCUzRCUyMEFsNE5fd0FsSzNS"
base64 -> url解码 -> S3Cre7_K3y = Al4N_wAlK3R

之后观察一个用到key的check结果的处理。

 if ( v24 == 1 )
  {
    v25 = 5317;
    Stream = fopen("./f/ASCII-faded 5315.txt", "rb");
    if ( !Stream )
    {
      printf("ERROR!\n");
      return 0;
    }
    fread(v6, 0x4EDEu, 1u, Stream);
    fclose(Stream);
    v22 = 0;
    for ( i = 0; i <= 0x84E; ++i )
    {
      if ( v22 > 10 )
        v22 = 0;
      v6[i] ^= key[v22++];
    }
    Stream = fopen("./f/ASCII-faded 5315.txt", "w");
    fwrite(v6, 0x84Fu, 1u, Stream);
    fclose(Stream);
  }

对5317进行异或之后再写入,再异或一次观察内容,后面就没什么内容了,就是接着奏乐接着舞了。

key='Al4N_wAlK3R'
f=open('ASCII-faded 5315.txt','r+')
s=f.read()
for i in range(len(s)):
    print(chr(ord(s[i])^ord(key[i%len(key)])),end='')

输入key跑起来还是挺不错的,摘掉眼镜.jpg。

A_TrUe_AW_f4ns

不过这题,静态的话就最后一个key有用,还是在文件中,并且U和S已知,直接文件搜索,也能拿到key,然后直接异或就可以了。

for i in range(1,5317):
    f=open('ASCII-faded %s.txt'%str(i).zfill(4),'r+')
    s=f.read()
    if 'U'in s and 'S' in s:
        print(i)
    else:
        f.close()
        continue
"""
4157 ->key的文件
5315
"""

比赛时候挺麻的,比赛快要截止才有点感觉,还有一个硬件描述语言有待复现,计组实验课用的verilog神似,关键是不会啊! 人不行别怪路不平,还得补!。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值