-
-
[原创]KCTF2022春季赛第三题:解题过程
-
2022-5-16 00:19 6915
-
1.初步流程分析
题目流程比较简洁,初步判断该exe定义了一个大的结构体进行操作,简单根据偏移量进行了结构体判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct context{ int iterator1; / / 0 int unknow1; / / 1 unsigned int md5[ 5 ]; / / 2 - 6 int unknow7; / / 7 int unknow8; / / 8 byte keys[ 16 ]; / / 9 - 12 int unknow13; / / 13 int unknow14; / / 14 int unknow15; / / 15 int iterator2; / / 16 int unknow17; / / 17 unsigned int cipher[ 10 ]; / / 18 - 27 int unknow28; / / 28 int unknow29; / / 29 char sn[ 4000 ]; int a3; int a4; }; |
重新对main进行解析,比较友好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | int __cdecl main_0( int argc, const char * * argv, const char * * envp) { void * v3; / / esp int result; / / eax char v7; / / [esp - 10h ] [ebp - 1048h ] context ctx; / / [esp + 0h ] [ebp - 1038h ] BYREF CPPEH_RECORD ms_exc; / / [esp + 1020h ] [ebp - 18h ] v3 = alloca( 4128 ); memset(&ctx, 0xCCu , sizeof(ctx)); ms_exc.registration.ScopeTable = (ms_exc.registration.ScopeTable ^ __security_cookie); memset(ctx.sn, 0 , sizeof(ctx.sn)); ctx.cipher[ 0 ] = 0 ; ctx.cipher[ 1 ] = 0 ; ctx.cipher[ 2 ] = 0 ; ctx.cipher[ 3 ] = 0 ; ctx.cipher[ 4 ] = 0 ; ctx.cipher[ 5 ] = 0 ; ctx.cipher[ 6 ] = 0 ; ctx.cipher[ 7 ] = 0 ; ctx.cipher[ 8 ] = 0 ; ctx.cipher[ 9 ] = 0 ; gets_s(ctx.sn, 4000u ); if ( strlen(ctx.sn) = = 32 ) { for ( ctx.iterator2 = 0 ; ctx.iterator2 < 512 ; + + ctx.iterator2 ) { ctx.unknow15 = 0 ; MEMORY[ 0 ] = ctx.iterator2; ms_exc.registration.TryLevel = 0xFFFFFFFE ; } strcpy(ctx.keys, "Enj0y_1t_4_fuuuN" ); / / 16bytes _DX = 0x5F00 ; * (&ctx.unknow13 + 1 ) = 0 ; HIBYTE(ctx.unknow13) = 0 ; ctx.md5[ 0 ] = 0 ; ctx.md5[ 1 ] = 0 ; ctx.md5[ 2 ] = 0 ; ctx.md5[ 3 ] = 0 ; ctx.md5[ 4 ] = 0 ; for ( ctx.iterator1 = 0 ; ctx.iterator1 < 512 ; + + ctx.iterator1 ) { __asm { insb } ms_exc.registration.TryLevel = - 2 ; _DX = LOWORD(ctx.iterator1) + 1 ; } j_MD5_402DB0(ctx.keys, 0x10u , ctx.md5); j_aes_402070(ctx.md5, 0x10u , ctx.sn, ctx.cipher, 32 ); if ( !memcmp(ctx.cipher, byte_40B200, 0x20u ) ) printf_401037( "OK\n" , v7); else printf_401037( "NO\n" , v7); result = 0 ; } else { printf_401037( "NO\n" , v7); result = 0 ; } return result; } |
存在较多的反调试。
流程较为清晰:
1、keys = MD5(Enj0y_1t_4_fuuuN)
2、AES(keys , 16bytes , plain , cipher , 32bytes) == byte_40B200[0:20]
直接进行解密计算发现是乱码,判断算法进行了改变。
2.算法分析
2.1 MD5分析
直接使用MD5(Enj0y_1t_4_fuuuN)结果。并未具体分析过程,因为是固定值。
2.2 AES分析
整个AES算法结构比较标准,在正常的128bit密钥流程中增加了对16字节输入输出的转置操作。
需要找一份类似的代码实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | int __cdecl aes_402070(void * Src, size_t KeySize, unsigned __int8 input [], unsigned __int8 uccipher[], int iLen) { int j; / / [esp + 4h ] [ebp - 1C8h ] unsigned int i; / / [esp + 8h ] [ebp - 1C4h ] int state[ 6 ]; / / [esp + 10h ] [ebp - 1BCh ] BYREF int key[ 11 ]; / / [esp + 28h ] [ebp - 1A4h ] BYREF unsigned int * iKeyOffset; / / [esp + 54h ] [ebp - 178h ] unsigned __int8 * __attribute__((__org_arrdim( 0 , 0 ))) output; / / [esp + 58h ] [ebp - 174h ] unsigned int KeyExtend[ 90 ]; / / [esp + 60h ] [ebp - 16Ch ] BYREF output = uccipher; iKeyOffset = KeyExtend; key[ 6 ] = 0 ; key[ 7 ] = 0 ; key[ 8 ] = 0 ; key[ 9 ] = 0 ; key[ 0 ] = 0 ; key[ 1 ] = 0 ; key[ 2 ] = 0 ; key[ 3 ] = 0 ; state[ 0 ] = 0 ; state[ 1 ] = 0 ; state[ 2 ] = 0 ; state[ 3 ] = 0 ; if ( !Src || ! input || !uccipher ) return - 1 ; if ( KeySize > 0x10 ) return - 1 ; if ( iLen % 0x10u ) return - 1 ; memcpy(key, Src, KeySize); KeyExtend_4010BE(key, 16 , KeyExtend); for ( i = 0 ; i < iLen; i + = 16 ) { input_transpose_401145(state, input ); AddRoundKey_401186(state, iKeyOffset); for ( j = 1 ; j < 10 ; + + j ) { iKeyOffset + = 4 ; ByteSub_401172(state); ShiftRow_40122B(state); MixColumn_40100F(state); AddRoundKey_401186(state, iKeyOffset); } ByteSub_401172(state); ShiftRow_40122B(state); AddRoundKey_401186(state, iKeyOffset + 4 ); output_transpose_40125D(state, output); output + = 16 ; input + = 16 ; iKeyOffset = KeyExtend; } return 0 ; } |
2.2.1 KeyExtend_4010BE
密钥扩展函数正常。
2.2.2 ByteSub_401172
字节替换函数中,查表方式没变,但是查看交叉引用时,发现sub_402330函数对其进行了修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | int __cdecl SubBytes_401A60( int a1[ 4 ][ 4 ]) { int j; / / [esp + 0h ] [ebp - 8h ] int i; / / [esp + 4h ] [ebp - 4h ] for ( i = 0 ; i < 4 ; + + i ) { for ( j = 0 ; j < 4 ; + + j ) * (&( * a1)[i] + j) = RijnDael_AES_LONG_40B000[ * (&( * a1)[i] + j)]; } return 0 ; } int sub_402330() { RijnDael_AES_LONG_40B000[ 113 ] ^ = RijnDael_AES_LONG_40B000[ 163 ]; RijnDael_AES_LONG_40B000[ 163 ] ^ = RijnDael_AES_LONG_40B000[ 113 ]; RijnDael_AES_LONG_40B000[ 113 ] ^ = RijnDael_AES_LONG_40B000[ 163 ]; return 1 ; } |
原S盒中的:SBox[0x71] = 0xa3、SBox[0xa3]=0x0a,
这里修改为了:SBox[0x71] = 0x0a、SBox[0xa3]=0xa3
因此解密inv_sub_bytes的时候,RBox也应该进行相应的修改:
原本RBox[0xA3] = 0x71、RBox[0x0A] = 0xA3
修改为:RBox[0xA3] = 0xA3、RBox[0x0A] = 0x71
2.2.3 ShiftRow_40122B
反编译时提示401bef无法转换,直接nop掉反调试的指令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | int __cdecl sub_401AE0(byte a1[ 4 ][ 4 ]) { int i; / / [esp + 10h ] [ebp - 38h ] int v3[ 6 ]; / / [esp + 18h ] [ebp - 30h ] CPPEH_RECORD ms_exc; / / [esp + 30h ] [ebp - 18h ] v3[ 4 ] = 0xCCCCCCCC ; v3[ 5 ] = 0xCCCCCCCC ; v3[ 0 ] = 0 ; v3[ 1 ] = 0 ; v3[ 2 ] = 0 ; v3[ 3 ] = 0 ; for ( i = 0 ; i < 4 ; + + i ) { v3[i] = _byteswap_ulong( * &( * a1)[ 4 * i]); v3[i] | = v3[i] >> ( 32 - 8 * i); ms_exc.registration.TryLevel = - 2 ; ( * a1)[ 4 * i] = HIBYTE(v3[i]); ( * a1)[ 4 * i + 1 ] = BYTE2(v3[i]); ( * a1)[ 4 * i + 2 ] = BYTE1(v3[i]); ( * a1)[ 4 * i + 3 ] = v3[i]; } return 0 ; } |
该题目对行变换函数进行了修改。直接分析16字节输入,魔改的变换如下:
0 1 2 3 -> 0 1 2 3
4 5 6 7 -> 7 4 5 6
8 9 10 11 -> 10 11 8 9
12 13 14 15 -> 13 14 15 12
在进行inv_shift_rows直接对如上变换方式进行逆变换即可。
2.2.4 MixColumn_40100F
列混淆函数中,计算时取的是state矩阵的转置,在编写Inv_MixColumn时也在取计算结果时,取state矩阵的转置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | int __cdecl sub_401E50(byte state[ 4 ][ 4 ]) { char v1; / / bl char v2; / / bl char v3; / / bl int j_; / / [esp + Ch] [ebp - 40h ] int i_; / / [esp + 10h ] [ebp - 3Ch ] int j; / / [esp + 14h ] [ebp - 38h ] int i; / / [esp + 18h ] [ebp - 34h ] byte a[ 4 ][ 4 ]; / / [esp + 20h ] [ebp - 2Ch ] BYREF byte state_[ 4 ][ 4 ]; / / [esp + 38h ] [ebp - 14h ] BYREF * &a[ 0 ][ 0 ] = 0x1010302 ; / / 2 3 1 1 * &a[ 1 ][ 0 ] = 0x1030201 ; * &a[ 2 ][ 0 ] = 0x3020101 ; * &a[ 3 ][ 0 ] = 0x2010103 ; for ( i = 0 ; i < 4 ; + + i ) { for ( j = 0 ; j < 4 ; + + j ) state_[i][j] = ( * state)[ 4 * i + j]; } for ( i_ = 0 ; i_ < 4 ; + + i_ ) { for ( j_ = 0 ; j_ < 4 ; + + j_ ) { v1 = XTIME_401118(a[i_][ 0 ], state_[ 0 ][j_]); v2 = XTIME_401118(a[i_][ 1 ], state_[ 1 ][j_]) ^ v1; v3 = XTIME_401118(a[i_][ 2 ], state_[ 2 ][j_]) ^ v2; ( * state)[ 4 * i_ + j_] = XTIME_401118(a[i_][ 3 ], state_[ 3 ][j_]) ^ v3; } } return 0 ; } |
2.2.5 AddRoundKey_401186
加轮密钥中,也对要异或的16字节密钥进行了转置操作ExtendKey[i][j] = u32Extendkey[j] >> (8 * (3 - i))。解密时直接调用该函数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | int __cdecl sub_401970(byte state[ 4 ][ 4 ], unsigned int u32Extendkey[ 4 ]) { int j; / / [esp + 0h ] [ebp - 20h ] int i; / / [esp + 4h ] [ebp - 1Ch ] byte ExtendKey[ 4 ][ 4 ]; / / [esp + Ch] [ebp - 14h ] BYREF int v6; / / [esp + 1Ch ] [ebp - 4h ] * &ExtendKey[ 0 ][ 0 ] = 0xCCCCCCCC ; * &ExtendKey[ 1 ][ 0 ] = 0xCCCCCCCC ; * &ExtendKey[ 2 ][ 0 ] = 0xCCCCCCCC ; * &ExtendKey[ 3 ][ 0 ] = 0xCCCCCCCC ; v6 = 0xCCCCCCCC ; for ( i = 0 ; i < 4 ; + + i ) { for ( j = 0 ; j < 4 ; + + j ) { ExtendKey[i][j] = u32Extendkey[j] >> ( 8 * ( 3 - i)); ( * state)[ 4 * i + j] ^ = ExtendKey[i][j]; } } return 0 ; } |
3. 解密
根据对AES加密函数相应的修改,编写对应的解密函数。直接对byte_40B200开始的32字节进行解密,发现不对,直接获取内存值发现有修改,查看交叉引用,发现sub_4023D0对校验值进行修改。求解得到flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | void aes_decrypt(uint8_t * in , uint8_t * out, uint8_t * w) { uint8_t state[ 4 * Nb]; uint8_t r, i, j; for (i = 0 ; i < 4 ; i + + ) { for (j = 0 ; j < Nb; j + + ) { state[Nb * i + j] = in [j + 4 * i]; / / transposition } } add_round_key(state, w, Nr); for (r = Nr - 1 ; r > = 1 ; r - - ) { inv_shift_rows(state); inv_sub_bytes(state); add_round_key(state, w, r); inv_mix_columns(state); } inv_shift_rows(state); inv_sub_bytes(state); add_round_key(state, w, 0 ); for (i = 0 ; i < 4 ; i + + ) { for (j = 0 ; j < Nb; j + + ) { out[i + 4 * j] = state[Nb * j + i]; / / transposition } } } int sub_4023D0() { byte_40B200[ 16 ] = 0xF4 ; byte_40B200[ 17 ] = 0xF2 ; return 1 ; } |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课