-
-
[原创]第八题 惊天阴谋
-
2020-12-6 15:20 7899
-
一.开篇
验证总体分为两部分,后半段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
[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课