re ak,除了最后的LittleJunk对我来说有点意义,另外两题都很简单,就写一下LittleJunk的wp

LittleJunk

程序加了花指令,没法正常F5

去除花指令后(见常见花指令识别与解决方案

image-20210830223050063

调用loc_404000之前进行了SMC自解密,所以静态分析时loc_404000并不能正常识别为函数

image-20210830223452463

动态调试起来分析,U,C配合把自解密的ByteCode转成汇编,按P

image-20210830223647839

去除花指令后

void __stdcall sub_9F4000(__int64 *key)
{
  int *v1; // [esp+8h] [ebp-48h]
  __int64 v2; // [esp+Ch] [ebp-44h]
  int i; // [esp+14h] [ebp-3Ch]
  int v4; // [esp+18h] [ebp-38h]
  unsigned __int64 v5[6]; // [esp+1Ch] [ebp-34h] BYREF

  sub_9F1A60(); // printf("flag plz:");
  sub_9F1A80(); // 首尾匹配 flag{xxx} 限定长度
  v4 = 0;
  smcDecrypt2();
  v1 = RC4Encrypt(); // RC4加密中括号内的内容 即flag{xxx} -> xxx
  v2 = 0i64;
  for ( i = 0; i < 32; ++i )
  {
    v2 = (v2 << 8) + v1[i];
    if ( !((i + 1) % 8) )
    {
      LODWORD(v5[v4]) = v2;
      HIDWORD(v5[v4++]) = HIDWORD(v2);
      v2 = 0i64;
    }
  } // 转换为QWORD
  sub_9F1660(v5, key); // tea加密
}

这题用的都是魔改过的算法

首先是flag中括号内的32位字符串进行魔改RC4加密,而后将密文转换为QWORD,使用魔改TEA算法加密,与结果比对

// RC4 
int *sub_9F5000()
{
  int v1[600]; // [esp+0h] [ebp-988h]
  int v2; // [esp+960h] [ebp-28h]
  int v3; // [esp+964h] [ebp-24h]
  int i; // [esp+968h] [ebp-20h]
  int k; // [esp+96Ch] [ebp-1Ch]
  int v6; // [esp+970h] [ebp-18h]
  int j; // [esp+974h] [ebp-14h]
  char v8[12]; // [esp+978h] [ebp-10h] BYREF

  for ( i = 0; i < 32; ++i )
    dword_9F8460[i] = byte_9F8430[i]; // byte_9F8430 存放flag{xx} -> xx
  strcpy(v8, "dasctf:3");                       // key
  v6 = 0;
  v3 = 0;
  for ( j = 0; j < 256; ++j )
  {
    v1[j + 300] = j;
    v1[j] = v8[j % 8];
  }
  v6 = 0;
  for ( j = 0; j < 256; ++j )
  {
    v6 = (v1[j] + v1[j + 300] + v6) % 256;
    v2 = v1[j + 300];
    v1[j + 300] = v1[v6 + 300];
    v1[v6 + 300] = v2;
  }
  j = 0;
  v6 = 0;
  v3 = 0;
  for ( k = 0; k < 32; ++k )
  {
    j = (j + 1) % 256;
    v6 = (v1[j + 300] + v6) % 256;
    v1[j + 300] = v1[v6 + 300] & ~v1[j + 300] | v1[j + 300] & ~v1[v6 + 300];
    v1[v6 + 300] = v1[v6 + 300] & ~v1[j + 300] | v1[j + 300] & ~v1[v6 + 300];
    v1[j + 300] = v1[v6 + 300] & ~v1[j + 300] | v1[j + 300] & ~v1[v6 + 300];
    v3 = (v1[v6 + 300] + v1[j + 300]) % 256;
    dword_9F8460[k] ^= v1[k + 300];
  }
  return dword_9F8460;
}

与标准RC4相比,修改点在于dword_9F8460[k] ^= v1[k + 300];,具体而言在于其直接使用sbox顺序进行异或,跳过了v3 = (s[v6] + s[j]) % 256;

image-20210831112954112
// TEA
void __cdecl sub_9F1660(unsigned __int64 *value, __int64 *key)
{
  __int64 v2; // [esp+18h] [ebp-8Ch]
  __int64 v3; // [esp+20h] [ebp-84h]
  __int64 v4; // [esp+2Ch] [ebp-78h]
  __int64 v5; // [esp+34h] [ebp-70h]
  unsigned __int64 i; // [esp+3Ch] [ebp-68h]
  int j; // [esp+44h] [ebp-60h]
  __int64 v8; // [esp+48h] [ebp-5Ch]
  unsigned __int64 v9; // [esp+50h] [ebp-54h]
  unsigned __int64 v10; // [esp+58h] [ebp-4Ch]
  unsigned __int64 v11; // [esp+60h] [ebp-44h]
  unsigned __int64 v12; // [esp+68h] [ebp-3Ch]
  QWORD v13[4]; // [esp+70h] [ebp-34h]
  int v14; // [esp+90h] [ebp-14h]
  int v15; // [esp+94h] [ebp-10h]
  int v16; // [esp+98h] [ebp-Ch]
  int v17; // [esp+9Ch] [ebp-8h]

  v13[0] = 0xE990A522BE80F786ui64;
  v13[1] = 0x8B836286B8A5EB59ui64;
  v13[2] = 0x2FDE61CCEFC70FF8i64;
  v13[3] = 0x56BC19E119C8B07Bi64;
  v14 = 0;
  v15 = 0;
  v16 = 0;
  v17 = 0;
  v11 = *value;
  v9 = value[1];
  v10 = value[2];
  v12 = value[3];
  v8 = 0i64;
  v5 = *key;
  v2 = key[1];
  v4 = key[2];
  v3 = key[3];
  for ( i = 0i64; i < 32; ++i )
  {
    v8 += 0x9E3779B9i64;
    v11 += (v2 + (v9 >> 5)) ^ (v8 + v9) ^ (v5 + 16 * v9);
    v9 += (v3 + (v11 >> 5)) ^ (v8 + v11) ^ (v4 + 16 * v11);
    v10 += (v2 + (v12 >> 5)) ^ (v8 + v12) ^ (v5 + 16 * v12);
    v12 += (v3 + (v10 >> 5)) ^ (v8 + v10) ^ (v4 + 16 * v10);
  }
  *value = v11;
  value[1] = v9;
  value[2] = v10;
  value[3] = v12;
  for ( j = 0; j < 4; ++j )
  {
    if ( value[j] != __PAIR64__(HIDWORD(v13[j]), v13[j]) )
    {
      sub_9F2A80(std::cout, "worry");
      exit(0);
    }
  }
  sub_9F2A80(std::cout, "ttttttttttql!!!");
}

TEA算法在这里魔改了变量的类型。
先写脚本解tea,(tea算法的key通过动态调试dump)

#include<stdio.h>
#include<stdint.h>
#include<windows.h>

void __cdecl decrypt(unsigned __int64 *value, __int64 *a2)
{
    __int64 v2; // [esp+18h] [ebp-8Ch]
    __int64 v3; // [esp+20h] [ebp-84h]
    __int64 v4; // [esp+2Ch] [ebp-78h]
    __int64 v5; // [esp+34h] [ebp-70h]
    unsigned __int64 i; // [esp+3Ch] [ebp-68h]

    __int64 v8; // [esp+48h] [ebp-5Ch]
    unsigned __int64 v9; // [esp+50h] [ebp-54h]
    unsigned __int64 v10; // [esp+58h] [ebp-4Ch]
    unsigned __int64 v11; // [esp+60h] [ebp-44h]
    unsigned __int64 v12; // [esp+68h] [ebp-3Ch]

    v11 = *value;
    v9 = value[1];
    v10 = value[2];
    v12 = value[3];
    v8 = 0x13c6ef3720;
    v5 = *a2;
    v2 = a2[1];
    v4 = a2[2];
    v3 = a2[3];
    for ( i = 0i64; i < 32; ++i )
    {
        v12 -= (v3 + (v10 >> 5)) ^ (v8 + v10) ^ (v4 + 16 * v10);
        v10 -= (v2 + (v12 >> 5)) ^ (v8 + v12) ^ (v5 + 16 * v12);

        v9 -= (v3 + (v11 >> 5)) ^ (v8 + v11) ^ (v4 + 16 * v11);
        v11 -= (v2 + (v9 >> 5)) ^ (v8 + v9) ^ (v5 + 16 * v9);

        v8 -= 0x9E3779B9i64;
    }
    *value = v11;
    value[1] = v9;
    value[2] = v10;
    value[3] = v12;

}

int main(){
    uint64_t v[4] = {
            0xE990A522BE80F786, 0x8B836286B8A5EB59, 0x2FDE61CCEFC70FF8, 0x56BC19E119C8B07B
    };

    int64_t key[4] = {
            0x54466076484C5476, 0x4550504F765F4344, 0x5A796F755F6D6179, 0x5F6E6565645F7468
    };

    decrypt(v, key);
    printf("%016llx %016llx %016llx %016llx", v[0], v[1], v[2], v[3]);
    return 0;
}

解得505a4cc462489e80 03aef16b785c7501 343057844ff5acb8 09616159f51713f3

写个IDAPython带回魔改的RC4函数加密前

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: ida_quick_script.py
@Time: 2021-08-30 16:41
@Desc: It's all about getting better.
"""

addr = 0x009F8430  # patch address

test = "505a4cc462489e80 03aef16b785c7501 343057844ff5acb8 09616159f51713f3"  # patch hex data

ps = [i for i in b''.fromhex(test)]
for i, v in enumerate(ps):
    ida_bytes.patch_byte(addr+i, v)

dump出dword_9F8460

image-20210830224738692

python跑一下

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: dizzy.py
@Time: 2021-08-30 17:03
@Desc: It's all about getting better.
"""
import struct


arr = [0x00000034, 0x00000061, 0x00000035, 0xFFFFFF63, 0x00000030, 0x00000066, 0xFFFFFF31, 0xFFFFFF62, 0x00000035, 0xFFFFFF63, 0xFFFFFF63, 0x00000037, 0x00000066, 0x00000036, 0x00000030, 0x00000065, 0x00000039, 0x00000031, 0x00000038, 0xFFFFFF36, 0x00000031, 0xFFFFFF31, 0xFFFFFF37, 0xFFFFFF32, 0x00000031, 0x00000063, 0x00000038, 0x00000037, 0xFFFFFF62, 0x00000061, 0x00000030, 0xFFFFFF37, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000]
stt = ''
for i in arr:
    stt += chr(i & 0xff)
print(stt)

当然如果你了解魔改点,也可以一把梭解密

#include<stdio.h>
#include<stdint.h>
#include<windows.h>

void __cdecl decrypt(unsigned __int64 *value, __int64 *a2)
{
    __int64 v2; // [esp+18h] [ebp-8Ch]
    __int64 v3; // [esp+20h] [ebp-84h]
    __int64 v4; // [esp+2Ch] [ebp-78h]
    __int64 v5; // [esp+34h] [ebp-70h]
    unsigned __int64 i; // [esp+3Ch] [ebp-68h]

    __int64 v8; // [esp+48h] [ebp-5Ch]
    unsigned __int64 v9; // [esp+50h] [ebp-54h]
    unsigned __int64 v10; // [esp+58h] [ebp-4Ch]
    unsigned __int64 v11; // [esp+60h] [ebp-44h]
    unsigned __int64 v12; // [esp+68h] [ebp-3Ch]

    v11 = *value;
    v9 = value[1];
    v10 = value[2];
    v12 = value[3];
    v8 = 0x13c6ef3720;
    v5 = *a2;
    v2 = a2[1];
    v4 = a2[2];
    v3 = a2[3];
    for ( i = 0i64; i < 32; ++i )
    {
        v12 -= (v3 + (v10 >> 5)) ^ (v8 + v10) ^ (v4 + 16 * v10);
        v10 -= (v2 + (v12 >> 5)) ^ (v8 + v12) ^ (v5 + 16 * v12);

        v9 -= (v3 + (v11 >> 5)) ^ (v8 + v11) ^ (v4 + 16 * v11);
        v11 -= (v2 + (v9 >> 5)) ^ (v8 + v9) ^ (v5 + 16 * v9);

        v8 -= 0x9E3779B9i64;
    }
    *value = v11;
    value[1] = v9;
    value[2] = v10;
    value[3] = v12;

}

void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len) //初始化函数
{
    int i =0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i=0;i<256;i++) {
        s[i] = i;
        k[i] = key[i%Len];
    }
    for (i=0; i<256; i++) {
        j=(j+s[i]+k[i])%256;
        tmp = s[i];
        s[i] = s[j]; //交换s[i]和s[j]
        s[j] = tmp;
    }
}

void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len) //加解密
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for(k=0;k<Len;k++) {
        i=(i+1)%256;
        j=(j+s[i])%256;
        tmp = s[i];
        s[i] = s[j]; //交换s[x]和s[y]
        s[j] = tmp;
        t=(s[i]+s[j])%256;

//        Data[k] ^= s[t];
        Data[k] ^= s[k]; // 魔改点
    }
}

void Qword2BytesLittle(unsigned char* b, uint64_t v) {

    for (int i = 0; i < 8; ++i) {
        b[7 - i] = v & 0xff;
        v >>= 8;
    }

}


int main(){
    uint64_t v[4] = {
            0xE990A522BE80F786, 0x8B836286B8A5EB59, 0x2FDE61CCEFC70FF8, 0x56BC19E119C8B07B
    };

    int64_t key[4] = {
            0x54466076484C5476, 0x4550504F765F4344, 0x5A796F755F6D6179, 0x5F6E6565645F7468
    };

    decrypt(v, key);
    printf("TEADecrypt: %016llx %016llx %016llx %016llx\n", v[0], v[1], v[2], v[3]);

    unsigned char encData[33] = {0};
    for (int i = 0; i < 4; ++i) {
        Qword2BytesLittle(&encData[8*i], v[i]);
    }

    unsigned char sbox[256];
    unsigned char rc4Key[] = "dasctf:3";
    rc4_init(sbox,rc4Key, strlen((char*)rc4Key));
    rc4_crypt(sbox, encData, 32);

    printf("flag{%s}\n", encData);

    return 0;
}

这题考点主要在于对花指令的识别和处理。