首页
论坛
课程
招聘
[原创]第八题 惊天阴谋
2020-12-6 15:20 2850

[原创]第八题 惊天阴谋

2020-12-6 15:20
2850

一.开篇

    验证总体分为两部分,后半段ccfer很快就完成了,"注意是很快啊,啪的一下"就完成了,前部分ccfer老大第二天没空,留给我解决了

二.正文

     将程序拖入IDA,发现很多花指令,然后打开程序输入提供的username和serial,发现程序退出了,都还没调试就退出,看来是检测到了一些它不高兴的进程。

首先把花指令去掉吧,看它究竟做着什么神秘的事情。


junk=[
'66 B8 EB 05 31 C0 74 FA ??',
'33 C0 74 01 ??',
'EB FF C0 48',
'74 03 75 01 ??',
'E8 01 00 00 00 ?? 80 04 24 06 C3',
'55 8B EC 6A FF 6A 00 6A 00 64 A1 00 00 00 00 50 64 89 25 00 00 00 00 83 EC 44 53 56 57 58 58 58 83 C4 44 58 64 A3 00 00 00 00 58 58 58 58 8B E8',
'81 FC 00 10 00 00 7C 05 E9 06 00 00 00 81 C4 04 01 00 00 40 48',
'E8 03 00 00 00 F8 73 07 50 8B 44 24 04 FF D0 8B 44 24 04 83 C4 0C',
#'E8 09 00 00 00 61 64 76 61 70 69 33 32 00 5B E8 3A FF FF FF' 404024
#'E8 09 00 00 00 61 64 76 61 70 69 33 32 00 5B'
]

经过大致的整理,基本就是上面的。

然后就是去反调试

addr=[0x402FF2,0x4032C3,0x4032D0,0x4032DD,0x4032EA,0x403B4F,0x404382,0x4048FA,0x404382,0x4048FA,0x40427C,0x404289,0x404296,0x4042A3,0x40524E,0x40384B]

 基本就是上面的,还有一处在调试器里面patch一下就可以了。

.text:00404022 E8 09 00 00 00                          call    loc_404030
.text:00404027 61                                      popa
.text:00404028                                         db      64h
.text:00404028 64 76 61                                jbe     short loc_40408C
.text:0040402B 70 69                                   jo      short near ptr loc_404094+2
.text:0040402D 33 32                                   xor     esi, [edx]
.text:0040402D                         sub_404010      endp ; sp-analysis failed
.text:0040402D
.text:0040402D                         ; ---------------------------------------------------------------------------
.text:0040402F 00                                      db 0
.text:00404030                         ; ---------------------------------------------------------------------------
.text:00404030
.text:00404030                         loc_404030:                             ; CODE XREF: sub_404010+12↑p
.text:00404030 5B                                      pop     ebx
.text:00404031 E8 3A FF FF FF                          call    sub_403F70

需要注意的是这个地方,不能简单的patch掉

.text:00404022 E8 09 00 00 00                          call    sub_404030
.text:00404022                         sub_404010      endp ; sp-analysis failed
.text:00404022
.text:00404022                         ; ---------------------------------------------------------------------------
.text:00404027 61 64 76 61 70 69 33 32+aAdvapi32_0     db 'advapi32',0
.text:00404030
.text:00404030                         ; =============== S U B R O U T I N E =======================================
.text:00404030
.text:00404030
.text:00404030                         sub_404030      proc near               ; CODE XREF: sub_404010+12↑p
.text:00404030 5B                                      pop     ebx
.text:00404031 E8 3A FF FF FF                          call    sub_403F70

 其中夹杂着后面需要调用的advapi32,应该是作者手工改的,我们手工改一改就行了

1)前半段算法分析

    整体的检验函数大致如下

bool __fastcall sub_405000(const char *username, char *serial)
{
  int i; // ecx
  char v5; // al
  char *j; // edi
  int k; // esi
  int l; // ecx
  int v9; // ecx
  int i_1; // esi
  int v11; // eax
  char flag1; // [esp+12h] [ebp-2FEh]
  char flag2; // [esp+13h] [ebp-2FDh]
  int v15; // [esp+14h] [ebp-2FCh]
  unsigned int v16; // [esp+18h] [ebp-2F8h]
  int v17; // [esp+1Ch] [ebp-2F4h]
  int v18; // [esp+20h] [ebp-2F0h]
  int v19; // [esp+24h] [ebp-2ECh]
  int v20; // [esp+28h] [ebp-2E8h]
  int v21; // [esp+2Ch] [ebp-2E4h]
  LARGE_INTEGER v22; // [esp+70h] [ebp-2A0h]
  LARGE_INTEGER PerformanceCount; // [esp+78h] [ebp-298h]
  int key[8]; // [esp+80h] [ebp-290h]
  int Dst[8]; // [esp+A0h] [ebp-270h]
  int Block[8]; // [esp+C0h] [ebp-250h]
  char result[16]; // [esp+E0h] [ebp-230h]
  char v28[16]; // [esp+F0h] [ebp-220h]
  char Tail[192]; // [esp+100h] [ebp-210h]
  char ArgList[332]; // [esp+1C0h] [ebp-150h]

  if ( strlen(serial) != 448 || !strlen(username) || strlen(username) >= 0xFF )
    return 0;
  for ( i = 0; i < 448; ++i )
  {
    v5 = serial[i];
    if ( (v5 < 48 || v5 > 57) && (v5 < 97 || v5 > 102) )
      return 0;
  }
  v16 = 0;
  v17 = 0;
  v18 = 1732584193;
  v19 = -271733879;
  v20 = -1732584194;
  v21 = 271733878;
  encrypt1((char *)username, &v16, strlen(username));
  encrypt2(&v16, (int)Dst);
  v16 = 0;
  v17 = 0;
  v18 = 1732584193;
  v19 = -271733879;
  v20 = -1732584194;
  v21 = 271733878;
  encrypt1((char *)Dst, &v16, 8u);
  encrypt2(&v16, (int)result);
  v16 = 0;
  v17 = 0;
  v18 = 1732584193;
  v19 = -271733879;
  v20 = -1732584194;
  v21 = 271733878;
  encrypt1((char *)&Dst[2], &v16, 8u);
  encrypt2(&v16, (int)v28);
  for ( j = ArgList; *serial; LOBYTE(j) = (_BYTE)j + 1 )
  {
    sub_405BD0(serial, "%2x", (char)j);
    serial += 2;
  }
  memmove(key, ArgList, 32u);
  memmove(Tail, &ArgList[32], 192u);
  for ( k = 0; k < 8; ++k )
    Block[k] = T_000_KeyExpand_c1(key[k]);
  memmove(key, Block, 0x20u);                   // Head_32[k * 4] = c1(*(_DWORD *)&Head_32[k * 4]);
  Block[4] = 0x215245;                          // init Block
  *(_OWORD *)Block = xmmword_5AE21C;
  windows_api_encrypt_c2((int)key, (int)Block, strlen((const char *)Block));// Head Change
  if ( (_BYTE)MessageBoxW == 0xCC )
  {
    _loaddll(0);
    goto LABEL_31;
  }
  memmove(Dst, key, 0x20u);
  for ( l = 0; l < 16; ++l )
  {
    if ( *((_BYTE *)Dst + 2 * l) + 0x7F != *((_BYTE *)Dst + 2 * l + 1) )// pan duan 1
      return 0;
    *((_BYTE *)Block + l) = *((_BYTE *)Dst + 2 * l);
  }
  key[0] = 0x59206F57;
  key[1] = 0x59676E6F;
  key[2] = 0x206E6175;
  key[3] = 0x75486958;
  key[4] = 0x4B206E61;
  key[5] = 0x75586E61;
  key[6] = 0x754C206E;
  key[7] = 0x6E61546E;
  init_table();
  Dst[0] = 4;
  Dst[1] = 8;
  Dst[2] = 14;
  Dst[3] = 16;
  v15 = 0;
  i_1 = 0;
  v11 = ModAES_c3(Dst, Block, v9, (int)key, (int)&v15) - (_DWORD)result;
  while ( result[i_1] == result[i_1 + v11] )
  {
    if ( ++i_1 >= 16 )
    {
      flag1 = 1;
      goto LABEL_23;
    }
  }
  flag1 = 0;
LABEL_23:
  if ( !sub_4041A0((int)Tail, key) )
    return 0;
  sub_404860((char *)key, (int)Block);
  QueryPerformanceCounter(&PerformanceCount);
  flag2 = sub_404D90((int)Block, (int)v28);
  QueryPerformanceCounter(&v22);
  if ( (signed int)(v22.LowPart - PerformanceCount.LowPart) > 100 )
  {
LABEL_31:
    _loaddll(0);
    JUMPOUT(0x4054A3);
  }
  return flag1 && flag2;
}

前半段算法大致是这样的 首先经过函数sub_402EE0转个十几万次的圈 -> 然后windows api加密一次 -> 再用改了的AES加密一手

下面说下怎么分析加密流程的

简单看了下402EE0,大致是这样的

unsigned int __fastcall T_000_KeyExpand_c1(int a1)
{
  unsigned int v1; // ecx
  int v2; // eax
  unsigned __int8 v3; // dl
  unsigned int v5[16]; // [esp+0h] [ebp-35Ch]
  unsigned int v6; // [esp+40h] [ebp-31Ch]
  unsigned int v7; // [esp+44h] [ebp-318h]
  int v8[7]; // [esp+48h] [ebp-314h]
  unsigned int v9; // [esp+64h] [ebp-2F8h]
  int v10[5]; // [esp+68h] [ebp-2F4h]
  char v11; // [esp+7Fh] [ebp-2DDh]
  unsigned int i; // [esp+80h] [ebp-2DCh]
  char v13; // [esp+85h] [ebp-2D7h]
  unsigned __int8 r; // [esp+86h] [ebp-2D6h]
  unsigned __int8 l; // [esp+87h] [ebp-2D5h]

  v10[1] = 0x45670123;
  v8[6] = 0x4567;
  v10[2] = 0xCDEF89AB;
  v8[5] = 0x89AB;
  v8[4] = 0xCDEF;
  v8[3] = 0xF1E;
  v8[2] = 0x2D3C;
  v8[1] = 0x4B5A;
  *(_OWORD *)&v5[12] = 0i64;
  v10[3] = 0x2D3C0F1E;
  v10[4] = 0x69784B5A;
  v8[0] = 0x6978;
  i = 0;
  *(_OWORD *)v5 = 0i64;
  v5[15] = a1;
  *(_OWORD *)&v5[4] = 0i64;
  *(_OWORD *)&v5[8] = 0i64;
  do
  {
    v7 = *(_DWORD *)&BOX[i];
    v1 = v7 >> 16;
    LOBYTE(v1) = ((v7 >> 7) & 1) + 2 * (BYTE2(v7) & 1);
    v9 = v1;
    v10[0] = (unsigned __int8)((BYTE1(v7) & 1) + 2 * (((v7 >> 0x13) & 1) + 2 * ((v7 >> 0x1B) & 1)));
    v2 = (unsigned __int8)((BYTE1(v7) & 1) + 2 * (((v7 >> 0x13) & 1) + 2 * ((v7 >> 0x1B) & 1)));
    LOBYTE(v1) = *((_BYTE *)&v10[1] + 2 * v2 + 1);
    v10[0] = (int)&v10[1] + 2 * v2;
    r = BOX[i + 4] ^ v1;
    l = r >> 4;
    r &= 0xFu;
    LOBYTE(v2) = *((_BYTE *)&v10[1] + 2 * v2);
    v6 = i + (unsigned __int8)v9;
    LOWORD(v1) = (unsigned __int8)BOX[i + 4];
    v3 = BOX[v6 + 5];
    v11 = v3 ^ v2;
    v13 = BOX[v6 + 6];
    v9 = (unsigned __int16)(v3 + ((_WORD)v1 << 8));
    HIWORD(v2) = HIWORD(v8[6]);
    LOWORD(v10[1]) = v8[6];
    LOWORD(v2) = v8[5];
    v8[6] = v2;
    HIWORD(v10[1]) = v8[5];
    LOWORD(v2) = v8[4];
    v8[5] = v2;
    LOWORD(v10[2]) = v8[4];
    LOWORD(v2) = v8[3];
    v8[4] = v2;
    HIWORD(v10[2]) = v8[3];
    LOWORD(v2) = v8[2];
    v8[3] = v2;
    LOWORD(v10[3]) = v8[2];
    LOWORD(v2) = v8[1];
    v8[2] = v2;
    HIWORD(v10[3]) = v8[1];
    LOWORD(v2) = v8[0];
    v8[1] = v2;
    LOWORD(v10[4]) = v8[0];
    i = 7;
    do
      --i;
    while ( i );
    v8[0] = (unsigned __int16)v9;
    HIWORD(v10[4]) = v9;
    i = v6 + 7;
    if ( v11 & 0x40 )
    {
      v5[l] &= v7;
    }
    else if ( v11 & 0x20 )
    {
      v5[l] >>= v13;
    }
    else if ( v11 & 0x10 )
    {
      v5[l] <<= v13;
    }
    else if ( v11 & 8 )
    {
      v5[l] ^= v5[r];
    }
    else if ( v11 & 4 )
    {
      v5[l] = v5[r];
    }
    else if ( v11 & 2 )
    {
      v5[l] += v5[r];
    }
  }
  while ( i < 0x111F71 );
  return v5[14];
}

计算的操作的都是加减法,异或之类的,应该可以解决,但是转圈圈的次数太多比较麻烦,后面再来分析

接着就来到windows api的地方

int __stdcall windows_api_encrypt_c2(int a1, int a2, int a3)
{
  int (__stdcall *v3)(_DWORD); // eax
  void (__stdcall *v4)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // eax
  int v6; // [esp+C0h] [ebp-40h]
  int key; // [esp+C8h] [ebp-38h]
  int v8; // [esp+CCh] [ebp-34h]
  int v9; // [esp+D0h] [ebp-30h]
  int (__stdcall *v10)(int, _DWORD, _DWORD, _DWORD, int, int *, int); // [esp+D8h] [ebp-28h]
  void (__stdcall *v11)(int, int, int, _DWORD, int *); // [esp+DCh] [ebp-24h]
  void (__stdcall *v12)(int, int, int, _DWORD); // [esp+E0h] [ebp-20h]
  void (__stdcall *v13)(int, int, _DWORD, _DWORD, int *); // [esp+E4h] [ebp-1Ch]
  void (__stdcall *v14)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // [esp+E8h] [ebp-18h]
  int (__stdcall *v15)(_DWORD); // [esp+ECh] [ebp-14h]
  void (__stdcall *v16)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // [esp+F8h] [ebp-8h]
  int (__stdcall *v17)(_DWORD); // [esp+FCh] [ebp-4h]

  v3 = (int (__stdcall *)(_DWORD))sub_403F70();
  v17 = v3;
  sub_403FB0((int)v3, -334606706);
  v15 = v3;
  v4 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))v3("advapi32");
  v16 = v4;
  sub_403FB0((int)v4, 1136823258);
  v14 = v4;
  sub_403FB0((int)v4, 1090887984);
  v13 = (void (__stdcall *)(int, int, _DWORD, _DWORD, int *))v4;
  sub_403FB0((int)v4, -1038997975);
  v12 = (void (__stdcall *)(int, int, int, _DWORD))v4;
  sub_403FB0((int)v4, -1251137718);
  v11 = (void (__stdcall *)(int, int, int, _DWORD, int *))v4;
  sub_403FB0((int)v4, -651942520);
  v10 = (int (__stdcall *)(int, _DWORD, _DWORD, _DWORD, int, int *, int))v4;
  v9 = 0;
  v4(&v9, 0, 0, 24, 0xF0000000);
  v8 = 0;
  v13(v9, 0x800C, 0, 0, &v8);                   // CryptCreateHash
  v12(v8, a2, a3, 0);                           // CryptHashData("1_L0V3_BXS_F0REVER!")
  key = 0;
  v11(v9, 0x6610, v8, 0, &key);
  v6 = 32;
  return v10(key, 0, 0, 0, a1, &v6, 32);
}

照着它的参数调用,然后去问一问度娘,就可以基本实现了,实现代码如下

// windows api aes.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

#include < stdio.h >
#include < windows.h >
#include < wincrypt.h >
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH  0x00800000
void HandleError(char* s);

//--------------------------------------------------------------------
//  These additional #define statements are required.
#define ENCRYPT_ALGORITHM  0x6610
#define ENCRYPT_BLOCK_SIZE 8 

//   Declare the function EncryptFile. The function definition
//   follows main.



//--------------------------------------------------------------------
//   Begin main.

static BOOL Encrypt(
	PBYTE InBytes,
	DWORD dwCount,//strlen(InBytes)
	PBYTE Out,
	PCHAR szPassword)
	//--------------------------------------------------------------------
	//   Parameters passed are:
	//     szSource, the name of the input, a plaintext file.
	//     szDestination, the name of the output, an encrypted file to be 
	//         created.
	//     szPassword, the password.
{
	//--------------------------------------------------------------------
	//   Declare and initialize local variables.

	FILE* hSource;
	FILE* hDestination;

	HCRYPTPROV hCryptProv;
	HCRYPTKEY hKey;
	HCRYPTHASH hHash;

	//PBYTE pbBuffer;
	DWORD dwBlockLen;
	DWORD dwBufferLen;

	//--------------------------------------------------------------------


	//以下获得一个CSP句柄

	if(CryptAcquireContext(
		&hCryptProv,
		NULL,
		NULL,
		PROV_RSA_AES,
		CRYPT_VERIFYCONTEXT))//创建密钥容器
	{
		//创建密钥容器成功,并得到CSP句柄
		printf("A new key container has been created.\n");
	} else{
		HandleError((char*)"Could not create a new key container.\n");
	}



	//--------------------------------------------------------------------
	// 创建一个会话密钥(session key)
	// 会话密钥也叫对称密钥,用于对称加密算法。
	// (注: 一个Session是指从调用函数CryptAcquireContext到调用函数
	//   CryptReleaseContext 期间的阶段。会话密钥只能存在于一个会话过程)

	//--------------------------------------------------------------------
	// Create a hash object. 
	int a = CALG_SHA_256;
	if(CryptCreateHash(
		hCryptProv,
		0x800C,
		0,
		0,
		&hHash)){
		printf("A hash object has been created. \n");
	} else{
		HandleError((char*)"Error during CryptCreateHash!\n");
	}

	//--------------------------------------------------------------------
	// 用输入的密码产生一个散列
	if(CryptHashData(
		hHash,
		(BYTE*)szPassword,
		strlen(szPassword),
		0)){
		printf("The password has been added to the hash. \n");
	} else{
		HandleError((char*)"Error during CryptHashData. \n");
	}
	/*
	DWORD dwSize;
	DWORD dwLen = sizeof(dwSize);
	CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&dwSize), &dwLen, 0);
	BYTE* pHash = new BYTE[dwSize];
	dwLen = dwSize;
	CryptGetHashParam(hHash, HP_HASHVAL, pHash, &dwLen, 0);
	*/

	//--------------------------------------------------------------------
	// 通过散列生成会话密钥
	if(CryptDeriveKey(
		hCryptProv,
		0x6610,
		hHash,
		0,
		&hKey)){
		printf("An encryption key is derived from the password hash. \n");
	} else{
		HandleError((char*)"Error during CryptDeriveKey!\n");
	}
	//--------------------------------------------------------------------
	// Destroy the hash object. 

	CryptDestroyHash(hHash);
	hHash = NULL;




	//--------------------------------------------------------------------
	// 加密数据
	if(!CryptEncrypt(
		hKey,			//密钥
		0,				//如果数据同时进行散列和加密,这里传入一个
		//散列对象
		0,	//如果是最后一个被加密的块,输入TRUE.如果不是输.
		//入FALSE这里通过判断是否到文件尾来决定是否为
		//最后一块。
		0,				//保留
		InBytes,		//输入被加密数据,输出加密后的数据
		&dwCount,		//输入被加密数据实际长度,输出加密后数据长度
		32
	))	//pbBuffer的大小。
	{
		HandleError((char*)"Error during CryptEncrypt. \n");
	}

	//--------------------------------------------------------------------
	// Write data to the destination file. 

	memcpy(Out,InBytes,32);

	//--------------------------------------------------------------------
	// Destroy session key. 

	if(hKey)
		CryptDestroyKey(hKey);

	//--------------------------------------------------------------------
	// Destroy hash object. 

	if(hHash)
		CryptDestroyHash(hHash);

	//--------------------------------------------------------------------
	// Release provider handle. 

	if(hCryptProv)
		CryptReleaseContext(hCryptProv, 0);
	return(TRUE);
} // End of Encryptfile


static BOOL Decrypt(
	PBYTE InBytes,
	DWORD dwCount,//strlen(InBytes)
	PBYTE Out,
	PCHAR szPassword)
	//--------------------------------------------------------------------
	//   Parameters passed are:
	//     szSource, the name of the input, a plaintext file.
	//     szDestination, the name of the output, an encrypted file to be 
	//         created.
	//     szPassword, the password.
{
	//--------------------------------------------------------------------
	//   Declare and initialize local variables.

	FILE* hSource;
	FILE* hDestination;

	HCRYPTPROV hCryptProv;
	HCRYPTKEY hKey;
	HCRYPTHASH hHash;

	//PBYTE pbBuffer;
	DWORD dwBlockLen;
	DWORD dwBufferLen;

	//--------------------------------------------------------------------


	//以下获得一个CSP句柄

	if(CryptAcquireContext(
		&hCryptProv,
		NULL,
		NULL,
		PROV_RSA_AES,
		CRYPT_VERIFYCONTEXT))//创建密钥容器
	{
		//创建密钥容器成功,并得到CSP句柄
		printf("A new key container has been created.\n");
	} else{
		HandleError((char*)"Could not create a new key container.\n");
	}



	//--------------------------------------------------------------------
	// 创建一个会话密钥(session key)
	// 会话密钥也叫对称密钥,用于对称加密算法。
	// (注: 一个Session是指从调用函数CryptAcquireContext到调用函数
	//   CryptReleaseContext 期间的阶段。会话密钥只能存在于一个会话过程)

	//--------------------------------------------------------------------
	// Create a hash object. 
	int a = CALG_SHA_256;
	if(CryptCreateHash(
		hCryptProv,
		CALG_SHA_256,
		0,
		0,
		&hHash)){
		printf("A hash object has been created. \n");
	} else{
		HandleError((char*)"Error during CryptCreateHash!\n");
	}

	//--------------------------------------------------------------------
	// 用输入的密码产生一个散列
	if(CryptHashData(
		hHash,
		(BYTE*)szPassword,
		strlen(szPassword),
		0)){
		printf("The password has been added to the hash. \n");
	} else{
		HandleError((char*)"Error during CryptHashData. \n");
	}
	/*
	DWORD dwSize;
	DWORD dwLen = sizeof(dwSize);
	CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&dwSize), &dwLen, 0);
	BYTE* pHash = new BYTE[dwSize];
	dwLen = dwSize;
	CryptGetHashParam(hHash, HP_HASHVAL, pHash, &dwLen, 0);
	*/

	//--------------------------------------------------------------------
	// 通过散列生成会话密钥
	if(CryptDeriveKey(
		hCryptProv,
		0x6610,
		hHash,
		0,
		&hKey)){
		printf("An encryption key is derived from the password hash. \n");
	} else{
		HandleError((char*)"Error during CryptDeriveKey!\n");
	}
	//--------------------------------------------------------------------
	// Destroy the hash object. 

	CryptDestroyHash(hHash);
	hHash = NULL;




	//--------------------------------------------------------------------
	// 加密数据
	if(!CryptDecrypt(
		hKey,			//密钥
		0,				//如果数据同时进行散列和加密,这里传入一个
		//散列对象
		0,	//如果是最后一个被加密的块,输入TRUE.如果不是输.
		//入FALSE这里通过判断是否到文件尾来决定是否为
		//最后一块。
		0,				//保留
		InBytes,		//输入被加密数据,输出加密后的数据
		&dwCount		//输入被加密数据实际长度,输出加密后数据长度W
	))	//pbBuffer的大小。
	{
		HandleError((char*)"Error during CryptEncrypt. \n");
	}

	//--------------------------------------------------------------------
	// Write data to the destination file. 


	memcpy(Out,InBytes,32);
	//--------------------------------------------------------------------
	// Destroy session key. 

	if(hKey)
		CryptDestroyKey(hKey);

	//--------------------------------------------------------------------
	// Destroy hash object. 

	if(hHash)
		CryptDestroyHash(hHash);

	//--------------------------------------------------------------------
	// Release provider handle. 

	if(hCryptProv)
		CryptReleaseContext(hCryptProv, 0);
	return(TRUE);
} // End of Encryptfile


void main(void){
	CHAR szPassword[100] = "1_L0V3_BXS_F0REVER!";
	int way[1];
	/*
		CHAR InBytes[32] = {
		0xD4, 0xC1, 0x95, 0xEE, 0x99, 0x43, 0x3A, 0x33, 0xDF, 0x55, 0xA4, 0xE2, 0xF1, 0xFC, 0xD6, 0xA3,
		0x31, 0xCD, 0x01, 0xAB, 0x9F, 0x50, 0xD4, 0x2E, 0x2D, 0x94, 0xB7, 0x52, 0x11, 0x18, 0xE2, 0x6E
	};
	*/

	//CHAR InBytes[32]={0x05,0xB0,0x59,0xB9,0x20,0x67,0xCD,0xD4,0x8A,0x33,0xA5,0xA6,0x86,0xAB,0x61,0x52,0x67,0x08,0xC2,0x0C,0x54,0x0F,0x1A,0x99,0x32,0xBB,0xE0,0x90,0xF9,0x05,0x92,0x17};
	CHAR InBytes[32]={31, 158, 147, 18, 197, 68, 51, 178, 39, 166, 249, 120, 42, 169, 215, 86, 145, 16, 152, 23, 175, 46, 46, 173, 245, 116, 151, 22, 202, 73, 74, 201};
	CHAR out[32] = {0};
	way[0] = 2;
	if(way[0] == 1){
		if(Encrypt((PBYTE)InBytes, 32, (PBYTE)out, (PCHAR)szPassword)){
			printf("over\n");
		} else{
			HandleError((char*)"Error encrypting data!");
		}
	} else{
		if(Decrypt((PBYTE)InBytes, 32, (PBYTE)out, (PCHAR)szPassword)){
			printf("over\n");
		} else{
			HandleError((char*)"Error decrypting data!");
		}
	}


} // End of main


//--------------------------------------------------------------------
//   Code for the function EncryptFile called by main.



//--------------------------------------------------------------------
//  This example uses the function HandleError, a simple error
//  handling function, to print an error message to the standard error 
//  (stderr) file and exit the program. 
//  For most applications, replace this function with one 
//  that does more extensive error reporting.

void HandleError(char* s){
	fprintf(stderr, "An error occurred in running the program. \n");
	fprintf(stderr, "%s\n", s);
	fprintf(stderr, "Error number %x.\n", GetLastError());
	fprintf(stderr, "Program terminating. \n");
	exit(1);
} // End of HandleError




// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

继续往下面看

这处应该就是倒推,能将16个扩展32字节的地方

继续向下推

来到4033B0,进去看到这个地方

感觉像是密钥扩展

为了验证一下,上网随便搜了个代码把里面得SBOX改了下

最后确实发现是个改了SBOX的AES,因此只需求出逆盒就行了

实现算法如下

/*

This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.

The implementation is verified against the test vectors in:
  National Institute of Standards and Technology Special Publication 800-38A 2001 ED

ECB-AES128
----------

  plain-text:
    6bc1bee22e409f96e93d7e117393172a
    ae2d8a571e03ac9c9eb76fac45af8e51
    30c81c46a35ce411e5fbc1191a0a52ef
    f69f2445df4f9b17ad2b417be66c3710

  key:
    2b7e151628aed2a6abf7158809cf4f3c

  resulting cipher
    3ad77bb40d7a3660a89ecaf32466ef97
    f5d3d58503b9699de785895a96fdbaaf
    43b1cd7f598ece23881b00e3ed030688
    7b0c785e27e8ad3f8223207104725dd4


NOTE:   String length must be evenly divisible by 16byte (str_len % 16 == 0)
        You should pad the end of the string with zeros if this is not the case.
        For AES192/256 the key size is proportionally larger.

*/


/*****************************************************************************/
/* Includes:                                                                 */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include <stdint.h>
#include <stdio.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.

// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif

#ifndef ECB
#define ECB 1
#endif

#ifndef CTR
#define CTR 1
#endif


//#define AES128 1
//#define AES192 1
#define AES256 1

#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only

#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16   // Key length in bytes
#define AES_keyExpSize 176
#endif

struct AES_ctx{
    uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
    uint8_t Iv[AES_BLOCKLEN];
#endif
};

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif

#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes; 
// you need only AES_init_ctx as IV is not used in ECB 
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);

#endif // #if defined(ECB) && (ECB == !)


#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
//        no IV should ever be reused with the same key 
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);

#endif // #if defined(CBC) && (CBC == 1)


#if defined(CTR) && (CTR == 1)

// Same function for encrypting as for decrypting. 
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
//        no IV should ever be reused with the same key 
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);

#endif // #if defined(CTR) && (CTR == 1)

/*****************************************************************************/
/* Defines:                                                                  */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4

#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4        // The number of 32 bit words in a key.
#define Nr 10       // The number of rounds in AES Cipher.
#endif

// jcallan@github points out that declaring Multiply as a function 
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif




/*****************************************************************************/
/* Private variables:                                                        */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];



// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM - 
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
    //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
    0x63, 0x7C, 0x7E, 0x8A, 0x7F, 0x27, 0x97, 0x73, 0xFF, 0x8F,
    0xD3, 0x36, 0x8B, 0x91, 0x6B, 0xA0, 0x2D, 0xDD, 0x87, 0xC1,
    0x3B, 0xB2, 0x5B, 0x2E, 0x17, 0x55, 0x1A, 0xDB, 0x67, 0x50,
    0x10, 0xE5, 0xD6, 0x02, 0xAE, 0x30, 0x83, 0xD7, 0x32, 0x8D,
    0x4F, 0x16, 0x19, 0x71, 0xED, 0xF4, 0x57, 0xEA, 0x59, 0x06,
    0x78, 0x09, 0x4D, 0xE1, 0x3F, 0xD4, 0xF3, 0x58, 0x68, 0x93,
    0x48, 0x25, 0x20, 0x2C, 0x2B, 0x45, 0x41, 0xD8, 0x85, 0x5E,
    0xCA, 0xBD, 0x13, 0x49, 0xAB, 0x69, 0xCB, 0x33, 0x86, 0x1C,
    0x75, 0x08, 0xD9, 0xBF, 0xCC, 0xBA, 0x6A, 0x4A, 0x24, 0xF1,
    0xA8, 0x77, 0x79, 0x40, 0x35, 0xE2, 0xEC, 0x96, 0xD1, 0x5F,
    0xEE, 0xAD, 0xC4, 0x54, 0x74, 0xC6, 0xB0, 0x3D, 0xDF, 0xA7,
    0x2A, 0xF0, 0xB9, 0x07, 0x6C, 0x21, 0xE6, 0xA2, 0x1B, 0xF2,
    0x64, 0xF6, 0xD2, 0x53, 0xC2, 0x92, 0x56, 0x5C, 0x47, 0x89,
    0x70, 0x4C, 0xE0, 0x84, 0xBE, 0x2F, 0x82, 0x15, 0xFD, 0xEF,
    0xB7, 0x8C, 0x0C, 0x43, 0xC9, 0x9F, 0xE4, 0xA3, 0x95, 0x5D,
    0x66, 0xCE, 0x37, 0x0F, 0x4B, 0x05, 0x03, 0x1E, 0xDC, 0xC0,
    0xFA, 0x28, 0x44, 0xCF, 0x3E, 0x88, 0x0D, 0xFE, 0x26, 0x6D,
    0x1D, 0x80, 0xE7, 0x8E, 0x65, 0xC5, 0x52, 0x12, 0xB8, 0xC3,
    0x14, 0x0A, 0xFB, 0x3C, 0x6E, 0x46, 0x60, 0x00, 0xDA, 0xB5,
    0x31, 0xD0, 0xA4, 0x5A, 0x0B, 0x9D, 0x3A, 0xF5, 0x7D, 0xB4,
    0xA5, 0x29, 0x04, 0xEB, 0x22, 0x81, 0xF8, 0x94, 0x7A, 0xAA,
    0x23, 0xBC, 0x18, 0xB6, 0xDE, 0xAC, 0xAF, 0x9E, 0x01, 0x99,
    0xC7, 0x9A, 0x38, 0x1F, 0x9C, 0xE3, 0x51, 0x7B, 0x76, 0x62,
    0x42, 0x61, 0xA1, 0xB1, 0x11, 0x0E, 0xCD, 0x6F, 0x39, 0xE8,
    0x72, 0xF7, 0xA9, 0xA6, 0xBB, 0x34, 0xE9, 0x4E, 0xB3, 0x98,
    0x9B, 0x90, 0xF9, 0xD5, 0xFC, 0xC8};

static const uint8_t rsbox[256] = {
    187, 218, 33, 156, 202, 155, 49, 113, 81, 51, 181, 194, 142, 166, 235, 153, 30, 234, 177, 72, 180, 137, 41, 24, 212, 42, 26, 118, 79, 170, 157, 223, 62, 115, 204, 210, 88, 61, 168, 5, 161, 201, 110, 64, 63, 16, 23, 135, 35, 190, 38, 77, 245, 94, 11, 152, 222, 238, 196, 20, 183, 107, 164, 54, 93, 66, 230, 143, 162, 65, 185, 128, 60, 73, 87, 154, 131, 52, 247, 40, 29, 226, 176, 123, 103, 25, 126, 46, 57, 48, 193, 22, 127, 149, 69, 99, 186, 231, 229, 0, 120, 174, 150, 28, 58, 75, 86, 14, 114, 169, 184, 237, 130, 43, 240, 7, 104, 80, 228, 91, 50, 92, 208, 227, 1, 198, 2, 4, 171, 205, 136, 36, 133, 68, 78, 18, 165, 129, 3, 12, 141, 39, 173, 9, 251, 13, 125, 59, 207, 148, 97, 6, 249, 219, 221, 250, 224, 195, 217, 145, 15, 232, 117, 147, 192, 200, 243, 109, 90, 242, 209, 74, 215, 101, 34, 216, 106, 233, 21, 248, 199, 189, 213, 140, 178, 112, 85, 244, 211, 71, 134, 83, 159, 19, 124, 179, 102, 175, 105, 220, 255, 144, 70, 76, 84, 236, 151, 163, 191, 98, 122, 10, 55, 253, 32, 37, 67, 82, 188, 27, 158, 17, 214, 108, 132, 53, 95, 225, 146, 31, 116, 172, 239, 246, 47, 203, 96, 44, 100, 139, 111, 89, 119, 56, 45, 197, 121, 241, 206, 252, 160, 182, 254, 138, 167, 8};

// The round constant word array, Rcon[i], contains the values given by 
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
    0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};

/*
 * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
 * that you can remove most of the elements in the Rcon array, because they are unused.
 *
 * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
 *
 * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
 *  up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
 */


 /*****************************************************************************/
 /* Private functions:                                                        */
 /*****************************************************************************/
 /*
 static uint8_t getSBoxValue(uint8_t num)
 {
   return sbox[num];
 }
 */
#define getSBoxValue(num) (sbox[(num)])
 /*
 static uint8_t getSBoxInvert(uint8_t num)
 {
   return rsbox[num];
 }
 */
#define getSBoxInvert(num) (rsbox[(num)])

 // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key){
    unsigned i, j, k;
    uint8_t tempa[4]; // Used for the column/row operations

    // The first round key is the key itself.
    for(i = 0; i < Nk; ++i){
        RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
        RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
        RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
        RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
    }

    // All other round keys are found from the previous round keys.
    for(i = Nk; i < Nb * (Nr + 1); ++i){
        {
            k = (i - 1) * 4;
            tempa[0] = RoundKey[k + 0];
            tempa[1] = RoundKey[k + 1];
            tempa[2] = RoundKey[k + 2];
            tempa[3] = RoundKey[k + 3];

        }

        if(i % Nk == 0){
            // This function shifts the 4 bytes in a word to the left once.
            // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]

            // Function RotWord()
            {
                const uint8_t u8tmp = tempa[0];
                tempa[0] = tempa[1];
                tempa[1] = tempa[2];
                tempa[2] = tempa[3];
                tempa[3] = u8tmp;
            }

            // SubWord() is a function that takes a four-byte input word and 
            // applies the S-box to each of the four bytes to produce an output word.

            // Function Subword()
            {
                tempa[0] = getSBoxValue(tempa[0]);
                tempa[1] = getSBoxValue(tempa[1]);
                tempa[2] = getSBoxValue(tempa[2]);
                tempa[3] = getSBoxValue(tempa[3]);
            }

            tempa[0] = tempa[0] ^ Rcon[i / Nk];
        }
#if defined(AES256) && (AES256 == 1)
        if(i % Nk == 4){
            // Function Subword()
            {
                tempa[0] = getSBoxValue(tempa[0]);
                tempa[1] = getSBoxValue(tempa[1]);
                tempa[2] = getSBoxValue(tempa[2]);
                tempa[3] = getSBoxValue(tempa[3]);
            }
        }
#endif
        j = i * 4; k = (i - Nk) * 4;
        RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
        RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
        RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
        RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
    }
}

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key){
    KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv){
    KeyExpansion(ctx->RoundKey, key);
    memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv){
    memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
#endif

// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey){
    uint8_t i, j;
    for(i = 0; i < 4; ++i){
        for(j = 0; j < 4; ++j){
            (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
        }
    }
}

// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state){
    uint8_t i, j;
    for(i = 0; i < 4; ++i){
        for(j = 0; j < 4; ++j){
            (*state)[j][i] = getSBoxValue((*state)[j][i]);
        }
    }
}

// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state){
    uint8_t temp;

    // Rotate first row 1 columns to left  
    temp = (*state)[0][1];
    (*state)[0][1] = (*state)[1][1];
    (*state)[1][1] = (*state)[2][1];
    (*state)[2][1] = (*state)[3][1];
    (*state)[3][1] = temp;

    // Rotate second row 2 columns to left  
    temp = (*state)[0][2];
    (*state)[0][2] = (*state)[2][2];
    (*state)[2][2] = temp;

    temp = (*state)[1][2];
    (*state)[1][2] = (*state)[3][2];
    (*state)[3][2] = temp;

    // Rotate third row 3 columns to left
    temp = (*state)[0][3];
    (*state)[0][3] = (*state)[3][3];
    (*state)[3][3] = (*state)[2][3];
    (*state)[2][3] = (*state)[1][3];
    (*state)[1][3] = temp;
}

static uint8_t xtime(uint8_t x){
    return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}

// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state){
    uint8_t i;
    uint8_t Tmp, Tm, t;
    for(i = 0; i < 4; ++i){
        t = (*state)[i][0];
        Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
        Tm = (*state)[i][0] ^ (*state)[i][1]; Tm = xtime(Tm);  (*state)[i][0] ^= Tm ^ Tmp;
        Tm = (*state)[i][1] ^ (*state)[i][2]; Tm = xtime(Tm);  (*state)[i][1] ^= Tm ^ Tmp;
        Tm = (*state)[i][2] ^ (*state)[i][3]; Tm = xtime(Tm);  (*state)[i][2] ^= Tm ^ Tmp;
        Tm = (*state)[i][3] ^ t;              Tm = xtime(Tm);  (*state)[i][3] ^= Tm ^ Tmp;
    }
}

// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
//       The compiler seems to be able to vectorize the operation better this way.
//       See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y){
    return (((y & 1) * x) ^
        ((y >> 1 & 1) * xtime(x)) ^
        ((y >> 2 & 1) * xtime(xtime(x))) ^
        ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
        ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y)                                \
      (  ((y & 1) * x) ^                              \
      ((y>>1 & 1) * xtime(x)) ^                       \
      ((y>>2 & 1) * xtime(xtime(x))) ^                \
      ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^         \
      ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))   \

#endif

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state){
    int i;
    uint8_t a, b, c, d;
    for(i = 0; i < 4; ++i){
        a = (*state)[i][0];
        b = (*state)[i][1];
        c = (*state)[i][2];
        d = (*state)[i][3];

        (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
        (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
        (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
        (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
    }
}


// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state){
    uint8_t i, j;
    for(i = 0; i < 4; ++i){
        for(j = 0; j < 4; ++j){
            (*state)[j][i] = getSBoxInvert((*state)[j][i]);
        }
    }
}

static void InvShiftRows(state_t* state){
    uint8_t temp;

    // Rotate first row 1 columns to right  
    temp = (*state)[3][1];
    (*state)[3][1] = (*state)[2][1];
    (*state)[2][1] = (*state)[1][1];
    (*state)[1][1] = (*state)[0][1];
    (*state)[0][1] = temp;

    // Rotate second row 2 columns to right 
    temp = (*state)[0][2];
    (*state)[0][2] = (*state)[2][2];
    (*state)[2][2] = temp;

    temp = (*state)[1][2];
    (*state)[1][2] = (*state)[3][2];
    (*state)[3][2] = temp;

    // Rotate third row 3 columns to right
    temp = (*state)[0][3];
    (*state)[0][3] = (*state)[1][3];
    (*state)[1][3] = (*state)[2][3];
    (*state)[2][3] = (*state)[3][3];
    (*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey){
    uint8_t round = 0;

    // Add the First round key to the state before starting the rounds.
    AddRoundKey(0, state, RoundKey);

    // There will be Nr rounds.
    // The first Nr-1 rounds are identical.
    // These Nr rounds are executed in the loop below.
    // Last one without MixColumns()
    for(round = 1; ; ++round){
        SubBytes(state);
        ShiftRows(state);
        if(round == Nr){
            break;
        }
        MixColumns(state);
        AddRoundKey(round, state, RoundKey);
    }
    // Add round key to last round
    AddRoundKey(Nr, state, RoundKey);
}

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey){
    uint8_t round = 0;

    // Add the First round key to the state before starting the rounds.
    AddRoundKey(Nr, state, RoundKey);

    // There will be Nr rounds.
    // The first Nr-1 rounds are identical.
    // These Nr rounds are executed in the loop below.
    // Last one without InvMixColumn()
    for(round = (Nr - 1); ; --round){
        InvShiftRows(state);
        InvSubBytes(state);
        AddRoundKey(round, state, RoundKey);
        if(round == 0){
            break;
        }
        InvMixColumns(state);
    }

}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

/*****************************************************************************/
/* Public functions:                                                         */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)


void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf){
    // The next function call encrypts the PlainText with the Key using AES algorithm.
    Cipher((state_t*)buf, ctx->RoundKey);
}

void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf){
    // The next function call decrypts the PlainText with the Key using AES algorithm.
    InvCipher((state_t*)buf, ctx->RoundKey);
}


#endif // #if defined(ECB) && (ECB == 1)





#if defined(CBC) && (CBC == 1)


static void XorWithIv(uint8_t* buf, const uint8_t* Iv){
    uint8_t i;
    for(i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
    {
        buf[i] ^= Iv[i];
    }
}

void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length){
    uintptr_t i;
    uint8_t* Iv = ctx->Iv;
    for(i = 0; i < length; i += AES_BLOCKLEN){
        XorWithIv(buf, Iv);
        Cipher((state_t*)buf, ctx->RoundKey);
        Iv = buf;
        buf += AES_BLOCKLEN;
    }
    /* store Iv in ctx for next call */
    memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}

void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length){
    uintptr_t i;
    uint8_t storeNextIv[AES_BLOCKLEN];
    for(i = 0; i < length; i += AES_BLOCKLEN){
        memcpy(storeNextIv, buf, AES_BLOCKLEN);
        InvCipher((state_t*)buf, ctx->RoundKey);
        XorWithIv(buf, ctx->Iv);
        memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
        buf += AES_BLOCKLEN;
    }

}

#endif // #if defined(CBC) && (CBC == 1)



#if defined(CTR) && (CTR == 1)

/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length){
    uint8_t buffer[AES_BLOCKLEN];

    unsigned i;
    int bi;
    for(i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi){
        if(bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
        {

            memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
            Cipher((state_t*)buffer, ctx->RoundKey);

            /* Increment Iv and handle overflow */
            for(bi = (AES_BLOCKLEN - 1); bi >= 0; --bi){
                /* inc will overflow */
                if(ctx->Iv[bi] == 255){
                    ctx->Iv[bi] = 0;
                    continue;
                }
                ctx->Iv[bi] += 1;
                break;
            }
            bi = 0;
        }

        buf[i] = (buf[i] ^ buffer[bi]);
    }
}

#endif // #if defined(CTR) && (CTR == 1)

int main(){

#if defined(AES256)
        uint8_t key[] = {0x57, 0x6F, 0x20, 0x59, 0x6F, 0x6E, 0x67, 0x59, 0x75, 0x61,
            0x6E, 0x20, 0x58, 0x69, 0x48, 0x75, 0x61, 0x6E, 0x20, 0x4B,
            0x61, 0x6E, 0x58, 0x75, 0x6E, 0x20, 0x4C, 0x75, 0x6E, 0x54,
            0x61, 0x6E};
        uint8_t out[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8};
#elif defined(AES192)
        uint8_t key[] = {0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
            0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b};
        uint8_t out[] = {0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc};
#elif defined(AES128)
        uint8_t key[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
        uint8_t out[] = {0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97};
#endif

        uint8_t in[] = {0x51, 0x0B, 0x3D, 0x82, 0xCB, 0x55, 0x77, 0xB5, 0xDC, 0x1A, 0x90, 0x82, 0x33, 0xC1, 0x2A, 0x36};
        struct AES_ctx ctx;

        /*
         AES_init_ctx(&ctx, key);
        AES_ECB_encrypt(&ctx, in);       
        */


        printf("ECB encrypt: ");

        AES_init_ctx(&ctx, key);
        AES_ECB_decrypt(&ctx, in);

        if(0 == memcmp((char*)out, (char*)in, 16)){
            printf("SUCCESS!\n");
            return(0);
        } else{
            printf("FAILURE!\n");
            return(1);
        }

}

至此前半部分基本完了

我们只需0x51, 0x0B, 0x3D, 0x82, 0xCB, 0x55, 0x77, 0xB5, 0xDC, 0x1A, 0x90, 0x82, 0x33, 0xC1, 0x2A, 0x36

倒退回去就行了

现在还有一个难点是第一部分的算法解还没有解决

根据输入与输出的关系,可以将其整理成这种形式

// CrackBoring.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

unsigned char g_op[130054] = {0};
unsigned char g_inv_op[130054] = {0};

unsigned int g_v7[130054] = {0};
unsigned int g_inv_v7[130054] = {0};

unsigned char g_v13[130054] = {0};
unsigned char g_inv_v13[130054] = {0};

unsigned char g_l[130054] = {0};
unsigned char g_inv_l[130054] = {0};

unsigned char g_r[130054] = {0};
unsigned char g_inv_r[130054] = {0};
unsigned int T(int a1){
    unsigned int v5[16];
    unsigned int v7;
    unsigned char v11;
    unsigned char v13;
    unsigned __int8 r; // [esp+86h] [ebp-2D6h]
    unsigned __int8 l; // [esp+87h] [ebp-2D5h]
    v5[15] = a1;
    for(int i = 0; i < 130054; i++){
        v11 = g_op[i];
        v7 = g_v7[i];
        v13 = g_v13[i];
        l = g_l[i];
        r = g_r[i];
        if(v11 & 0x40){
            v5[l] &= v7;
        } else if(v11 & 0x20){
            v5[l] >>= v13;
        } else if(v11 & 0x10){
            v5[l] <<= v13;
        } else if(v11 & 8){
            v5[l] ^= v5[r];
        } else if(v11 & 4){
            v5[l] = v5[r];
        } else if(v11 & 2){
            v5[l] += v5[r];
        }
    }
    return v5[14];
}



void readFile(const char* fn, void* buf){
    FILE* f = fopen(fn, "rb");
    fseek(f, 0, SEEK_END);
    unsigned int size = ftell(f);
    fseek(f, 0, SEEK_SET);
    fread(buf, size, 1, f);
    fclose(f);
}
void init(){
    readFile("C:/Users/wei/source/repos/Boring/Debug/op.bin", g_op);
    readFile("C:/Users/wei/source/repos/Boring/Debug/v7.bin", g_v7);
    readFile("C:/Users/wei/source/repos/Boring/Debug/v13.bin", g_v13);
    readFile("C:/Users/wei/source/repos/Boring/Debug/l.bin", g_l);
    readFile("C:/Users/wei/source/repos/Boring/Debug/r.bin", g_r);

}
int main(){
    init();
    int a = T(0xD55D8CD1);
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

现在就是求逆了,看它的运算关系,用二进制符号执行应该可以解,但是还缺个半盲道人64 核的服务器,跑了大概半天它也不理我,还是把它弄成python的形式

用z3解吧

import struct
from z3 import *
def u32(a1):
    return struct.unpack('<I',a1)[0]
def p32(a1):
    return struct.pack('<I',a1)
def readFileChar(fn,buf):
    f = open(fn, "rb");
    buffer=f.read()
    for i in range(len(buffer)):
        buf[i]=ord(buffer[i])
def ToInt(buffer,buf):
    for i in range(len(buffer)/4):
        buf[i]=u32(buffer[4*i:4*(i+1)])
def readFileInt(fn,buf):
    f = open(fn, "rb");
    buffer=f.read()
    for i in range(len(buffer)/4):
        buf[i]=u32(buffer[4*i:4*(i+1)])
def T(a1):
    v5=[0 for i in range(16)]
    v5[15] = a1
    for i in range(130054):
        v11 = g_op[i]
        v7 = g_v7[i]
        v13 = g_v13[i]
        l = g_l[i]
        r = g_r[i]
        if v11 & 0x40:
            v5[l]=v5[l] & v7
        elif v11 & 0x20:
            if str(type(v5[l]))=="<type 'long'>":
                v5[l]=v5[l] >> v13 #v13
            else:
                #print type(v5[l])
                v5[l]=LShR(v5[l], v13) #v13
        elif v11 & 0x10:
            v5[l]= (v5[l] << v13) & 0xFFFFFFFF
        elif (v11 & 8):
            v5[l] ^= v5[r]
        elif(v11 & 4):
            v5[l] = v5[r]
        elif(v11 & 2):
            v5[l] = (v5[l]+v5[r]) & 0xFFFFFFFF
        else:
            print "ERROR"
    return v5[14]
    
def Solve(expr,x):
    
    s=Solver()
    s.add(expr==x)
    if s.check()==sat:
        m=s.model()
        return int(str(m[X]))
g_op=[0 for i in range(130054)]
g_v7=[0 for i in range(130054)]
g_v13=[0 for i in range(130054)]
g_l=[0 for i in range(130054)]
g_r=[0 for i in range(130054)]
RS='05B059B92067CDD48A33A5A686AB61526708C20C540F1A9932BBE090F9059217'.decode('hex')
R=[0 for i in range(len(RS)/4)]
ToInt(RS,R)
print map(hex,R)
readFileChar("C:/Users/wei/source/repos/Boring/Debug/op.bin", g_op)
readFileInt("C:/Users/wei/source/repos/Boring/Debug/v7.bin", g_v7)
readFileChar("C:/Users/wei/source/repos/Boring/Debug/v13.bin", g_v13)
readFileChar("C:/Users/wei/source/repos/Boring/Debug/l.bin", g_l)
readFileChar("C:/Users/wei/source/repos/Boring/Debug/r.bin", g_r)
reulst=[0 for i in range(len(RS)/4)]
X=BitVec('X',32)
expr=T(X)

for i in range(8):
    reulst[i]=Solve(expr,R[i])
print map(hex,reulst)

#t=[0x2ee7bd83, 0x3cce0628, 0x244e428f, 0x63a65925, 0x37bb14, 0x9ba6628dL, 0xf961afd, 0xc802418eL]

flag=''
for i in reulst:
    flag+=p32(i)
print flag.encode('hex')

需要注意右移得用LShR

先将表达式关系建立,最后依次改变约束条件,会大大调高速度

到此前半部分就完成了

2)后半段算法分析

    这部分来源于ccfer老大

为了便于分析流程,首先去除花指令,手动在ollydbg里填充nop即可完成
因为有一组公开序列号,可以正常分支一路跑到底的
整体验证分成两块
username取md5,拆分成两半,再分别取md5
sn分成长度0x20和0xC0的两部分,分别和上面两部分md5做验证
第一部分算法复杂,难度大,留给大帅锅


第二部分就下面3个函数:
.text:004053D5                 lea     edx, [esp+310h+var_290]
.text:004053DC                 lea     ecx, [esp+310h+var_210]
.text:004053E3                 call    sub_4041A0
.text:004053E8                 test    al, al
.text:004053EA                 jz      loc_405478
.text:004053F0                 lea     edx, [esp+310h+Mem]
.text:004053F7                 lea     ecx, [esp+310h+var_290]
.text:004053FE                 call    sub_404860
.text:00405403                 nop
.text:00405404                 nop
.text:00405405                 nop
.text:00405406                 nop
.text:00405407                 nop
.text:00405408                 nop
.text:00405409                 nop
.text:0040540A                 nop
.text:0040540B                 nop
.text:0040540C                 nop
.text:0040540D                 nop
.text:0040540E                 nop
.text:0040540F                 nop
.text:00405410                 lea     edx, [esp+310h+var_220]
.text:00405417                 lea     ecx, [esp+310h+Mem]
.text:0040541E                 call    loc_404D90
.text:00405423                 mov     [esp+310h+var_2FD], al
3个call倒着顺序解决即可

最后一个loc_404D90里面算法部分:
.text:00404F1B                 mov     ebx, [ebp-0ECh]
.text:00404F21                 lea     esi, [ebp-90h]
.text:00404F27                 mov     eax, [ebp-0E4h]
.text:00404F2D                 add     esi, ebx
.text:00404F2F                 movzx   ecx, byte ptr [ebp+ebx-0C0h]
.text:00404F37                 mov     dl, [eax+esi]
.text:00404F3A                 and     ecx, 7
.text:00404F3D                 xor     dl, [ebp+ebx-0E0h]
.text:00404F44                 mov     al, [esi]
.text:00404F46                 ror     dl, cl
.text:00404F48                 mov     ecx, [ebp-0E8h]
.text:00404F4E                 sub     al, [ecx+esi]
.text:00404F51                 add     dl, al
.text:00404F53                 mov     [ebp+ebx-14h], dl
.text:00404F57                 inc     ebx
.text:00404F58                 mov     [ebp-0ECh], ebx
.text:00404F5E                 cmp     ebx, 10h
.text:00404F61                 jl      short loc_404EF0
逆一下,算法代码如下:
BYTE tr1[16] = {0x1A,0x1C,0x11,0x18,0x08,0x0C,0x19,0x01,0x20,0x0B,0x07,0x10,0x17,0x02,0x1D,0x15};
BYTE tx1[16] = {0x15,0x17,0x16,0x14,0x13,0x0C,0x04,0x12,0x03,0x06,0x10,0x0E,0x0A,0x18,0x1C,0x0F};
BYTE ta1[16] = {0x03,0xEE,0xEC,0x11,0x14,0x0E,0x05,0x0C,0x03,0xED,0xF7,0x05,0x00,0xF6,0x00,0xE7};
BYTE td1[16] = {0x15,0x09,0x0C,0x1F,0x1C,0x13,0x16,0x19,0x1D,0x03,0x10,0x0F,0x01,0x02,0x1E,0x06};

BYTE rotb(BYTE x, BYTE n)
{
	BYTE r = 0;

	r = (x >> n) | (x << (8-n));

	return r;
}

void decode3(BYTE *h,BYTE *b)
{
	int i;

	for (i=0;i<16;i++)
	{
		b[i] = rotb(td1[i] + h[i] - ta1[i], 8 - (tr1[i] & 7)) ^ tx1[i];
		printf("%02X", b[i]);
	}
}

得到结果:097581FB3B34C617480C41646BB2FD63


然后看sub_404860实际就是个高斯消元法解方程组,逆过程就简单了,只要抓取出系数矩阵,把上面的解代入方程得到常数项即可,代码如下:
DWORD aa[16*16] = 
{
0x00005251,0x0000309D,0x0000C48A,0x0000296D,0x000035EC,0x00006CD1,0x0000CFC4,0x0000D8F7,0x000059C6,0x0000E486,0x0000252D,0x0000D60A,0x000058AA,0x0000D411,0x000000E9,0x00000001,
0x0000827A,0x00007CFB,0x0000F715,0x00004758,0x000087D2,0x000031DB,0x00000601,0x00003E0E,0x000074F6,0x0000B17F,0x0000B3D2,0x0000141D,0x000072C6,0x00004840,0x00000088,0x00000001,
0x0000D54F,0x00000A98,0x000089FA,0x00008EB5,0x0000DA74,0x0000A0B6,0x0000DFF5,0x00008B1B,0x00006F9D,0x00005721,0x00003532,0x0000B010,0x0000319C,0x00008B89,0x000000BD,0x00000001,
0x00008453,0x0000FEA0,0x00005EDC,0x00001035,0x0000E2A4,0x0000EA0B,0x00007400,0x0000105E,0x000003FF,0x0000BFB3,0x0000B1A2,0x00008120,0x000027B3,0x00004410,0x00000084,0x00000001,
0x000040A0,0x00003480,0x0000E77A,0x000083B2,0x00002647,0x0000FC8C,0x0000D397,0x00004416,0x0000E452,0x00000315,0x0000857F,0x0000D7B7,0x000026D0,0x00006049,0x0000009D,0x00000001,
0x00000BB6,0x0000BD3B,0x000098AC,0x0000ECA4,0x00006897,0x0000C0B7,0x00008E7C,0x0000E251,0x0000D144,0x00005E18,0x00007163,0x00008378,0x000048DF,0x00002710,0x00000064,0x00000001,
0x00002B3D,0x000098C6,0x0000BF25,0x0000B39B,0x0000C5CD,0x0000DD5D,0x000084D2,0x0000612A,0x0000F304,0x000073FB,0x00002903,0x0000A5CD,0x00001703,0x00009610,0x000000C4,0x00000001,
0x00000FE6,0x00000016,0x0000E550,0x00008054,0x00008C37,0x000091CE,0x00003548,0x0000532C,0x000054B7,0x00002B48,0x0000BAB9,0x00004351,0x0000C749,0x000085B1,0x000000B9,0x00000001,
0x000035D8,0x00001A51,0x00007890,0x00004EA7,0x0000A563,0x00001CFA,0x0000D707,0x000049C8,0x0000BD6D,0x0000F7AC,0x00003D0E,0x000085C7,0x00002B90,0x00004A64,0x0000008A,0x00000001,
0x00003746,0x0000D8AB,0x0000E174,0x0000F510,0x00007D6E,0x000019E4,0x0000A395,0x00005713,0x0000469D,0x0000520D,0x00004B32,0x000047B6,0x00003C2F,0x0000C084,0x000000DE,0x00000001,
0x0000E7D7,0x0000EE42,0x0000411E,0x0000E3E3,0x0000C0C8,0x0000A128,0x000034E7,0x0000368A,0x0000BC04,0x00006850,0x00003FA1,0x0000DB5A,0x00002483,0x00001FA4,0x0000005A,0x00000001,
0x00001081,0x0000AC39,0x0000E7F6,0x00003F06,0x00000837,0x0000B643,0x0000202B,0x0000DC74,0x0000EAF8,0x0000C019,0x0000671D,0x000093C7,0x0000BF3C,0x000027D9,0x00000065,0x00000001,
0x0000B577,0x0000EAEA,0x0000160B,0x000056DC,0x00001DA5,0x00009526,0x0000D6A2,0x0000FCFB,0x00008550,0x0000BBD4,0x0000D931,0x00006172,0x00003F12,0x000033A9,0x00000073,0x00000001,
0x00007604,0x000003DD,0x0000E054,0x0000BB59,0x0000AB90,0x00000ACB,0x00008CA9,0x000074AD,0x00003BA8,0x0000FEB4,0x00005DBD,0x00000A74,0x00008EE4,0x0000CCD9,0x000000E5,0x00000001,
0x0000871A,0x00005B51,0x00008AAA,0x000041F1,0x0000D849,0x00008844,0x000033A4,0x00005DF9,0x0000466D,0x00001B6C,0x0000EB18,0x00000FBF,0x0000C9B0,0x00006541,0x000000A1,0x00000001,
0x00002838,0x00000AF4,0x000066DD,0x00004832,0x00000361,0x0000B08E,0x0000D9DC,0x0000247F,0x0000F2C3,0x00005972,0x0000BBFB,0x00002EBA,0x0000F2DE,0x000024C1,0x00000061,0x00000001,
};

void decode2(BYTE *b)
{
	int i,j;
	DWORD x[16];

	for (i=0;i<16;i++)
	{
		x[i] = 0;
		for (j=0;j<16;j++)
		{
			x[i] += b[j] * aa[i*16+j];
			x[i] %= 0xFF8F;
		}
	}

	for (i=0;i<16;i++)
	{
		printf("%02X%02X", x[i]&0xFF, x[i]>>8);
	}
}

得到结果:401A35247EFDE0B2278B646A53D85133E0D389840DB065A58236DC8A32210E65


最后来解决sub_4041A0
这是个啥算法我也不知道,我只是发现一些关键点,输入3个字节一组,得到4个中间值都满足等于8或-8即可,主要看下面这里:
.text:00404600                 mov     eax, [ebp+var_2E8]
.text:00404606                 add     edx, 4
.text:00404609                 mov     ebx, [ebp+var_2F0]
.text:0040460F                 inc     eax
.text:00404610                 mov     [ebp+var_2E8], eax
.text:00404616                 mov     [ebp+var_2E0], edx
.text:0040461C                 cmp     edx, 20h
.text:0040461F                 jl      loc_404540
.text:00404625                 mov     eax, ebx
.text:00404627                 cmp     eax, 0FFFFFFF8h
.text:0040462A                 jz      short loc_404635
.text:0040462C                 cmp     eax, 8
.text:0040462F                 jnz     loc_4047DB
.text:00404635
.text:00404635 loc_404635:                             ; CODE XREF: sub_4041A0+48A↑j
.text:00404635                 mov     eax, [ebp+var_300]
.text:0040463B                 cmp     eax, 0FFFFFFF8h
.text:0040463E                 jz      short loc_404649
.text:00404640                 cmp     eax, 8
.text:00404643                 jnz     loc_4047DB
.text:00404649
.text:00404649 loc_404649:                             ; CODE XREF: sub_4041A0+49E↑j
.text:00404649                 mov     eax, [ebp+var_304]
.text:0040464F                 cmp     eax, 0FFFFFFF8h
.text:00404652                 jz      short loc_40465D
.text:00404654                 cmp     eax, 8
.text:00404657                 jnz     loc_4047DB
.text:0040465D
.text:0040465D loc_40465D:                             ; CODE XREF: sub_4041A0+4B2↑j
.text:0040465D                 cmp     ecx, 0FFFFFFF8h
.text:00404660                 jz      short loc_40466B
.text:00404662                 cmp     ecx, 8
.text:00404665                 jnz     loc_4047DB
.text:0040466B
.text:0040466B loc_40466B:                             ; CODE XREF: sub_4041A0+4C0↑j
.text:0040466B                 nop
.text:0040466C                 nop
.text:0040466D                 nop
.text:0040466E                 nop
.text:0040466F                 nop
.text:00404670                 mov     ecx, [ebp+var_2F8]
.text:00404676                 mov     ebx, 1
.text:0040467B                 shl     ebx, cl
然后每次得到4个bit,循环64次后得到完整32个字节

所以我在0040466B这里hook一下加个全局计数器,每次加1,等函数返回后我就知道3个字节一组的输入,我成功进行到哪一组了
可以3个字节字节的往下穷举,直到全部成功,代码如下:
BYTE xx[3*64];
BYTE zz[32] = {0x40,0x1A,0x35,0x24,0x7E,0xFD,0xE0,0xB2,0x27,0x8B,0x64,0x6A,0x53,0xD8,0x51,0x33,0xE0,0xD3,0x89,0x84,0x0D,0xB0,0x65,0xA5,0x82,0x36,0xDC,0x8A,0x32,0x21,0x0E,0x65};
DWORD cntx;

DWORD calc()
{
	DWORD rv = 0;

	__asm
	{
		lea ecx,xx
		lea edx,yy
		mov eax,0x4041A0
		call eax
		mov rv,eax
	}

	return rv;
}

void saveinc()
{
	cntx++;
}

void __declspec(naked) hook_x1()
{
	_asm
	{
		pushad
		call saveinc
		popad
		ret
	}
}

void hook08()
{
	DWORD dwTmp;
	DWORD prot;

	dwTmp = 0x40466B;
	VirtualProtect((LPVOID)dwTmp,5,PAGE_EXECUTE_READWRITE,&prot);
	*(BYTE *)dwTmp = 0xE8;
	*(DWORD *)(dwTmp+1) = (DWORD)hook_x1 - dwTmp - 5;
	VirtualProtect((LPVOID)dwTmp,5,prot,&prot);
}

void LoopX()
{
	DWORD i,j;
	DWORD rv;
	
	hook08();

	for (i=0;i<64;i++)
	{
		for (j=0;j<0x1000000;j++)
		{
			cntx = 0;
			memset(yy,0,32);
			*(DWORD *)&xx[i*3] = j;
			rv = calc();
			if (cntx == (i+1))
			{
				if (((yy[i/8 + 0x00] & (1 << (7 - (i % 8)))) == (zz[i/8 + 0x00] & (1 << (7 - (i % 8)))))
					&& ((yy[i/8 + 0x08] & (1 << (7 - (i % 8)))) == (zz[i/8 + 0x08] & (1 << (7 - (i % 8)))))
					&& ((yy[i/8 + 0x10] & (1 << (7 - (i % 8)))) == (zz[i/8 + 0x10] & (1 << (7 - (i % 8)))))
					&& ((yy[i/8 + 0x18] & (1 << (7 - (i % 8)))) == (zz[i/8 + 0x18] & (1 << (7 - (i % 8))))) )
				{
					break;
				}
			}
		}
	}
}

穷举完成后得到:
900800804800800900c12400c12400820800020000820800800900d12c00800000004000020804800000000800800900900800020000020804000004900800010412c12400804800900800820800020804c12400020000804800020000c12400c12400020804000004010412804800804800010412800900412c00020804004000412c00020804800804c12400000004800804412c00804800820800800000900800800000800900804800800000000800020804c12400900800020804000100
就是sn的后半部分

3.结果

将前后拼接在一起,应该是多解得一组吧

83bde72e2806ce3c8f424e242559a66314bb37008d62a69bfd1a960f8e4102c8900800804800800900c12400c12400820800020000820800800900d12c00800000004000020804800000000800800900900800020000020804000004900800010412c12400804800900800820800020804c12400020000804800020000c12400c12400020804000004010412804800804800010412800900412c00020804004000412c00020804800804c12400000004800804412c00804800820800800000900800800000800900804800800000000800020804c12400900800020804000100



看雪学院推出的专业资质证书《看雪安卓应用安全能力认证 v1.0》(中级和高级)!

最后于 2020-12-6 15:27 被大帅锅编辑 ,原因:
上传的附件:
收藏
点赞4
打赏
分享
最新回复 (1)
雪    币: 28693
活跃值: 活跃值 (105333)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 活跃值 2020-12-7 15:08
2
0
支持!
游客
登录 | 注册 方可回帖
返回