-
-
[推荐]看雪·安恒 2020 KCTF 春季赛 | 第七题设计思路及解析
-
发表于: 2020-5-6 18:25 6323
-
4月28日,金左手战队的 ccfer 率先攻破题目,遥遥领先。
受到激励,其他战队也不甘心落后,紧追猛赶,辣鸡战队、雨落星辰等战队陆续攻破此题。
赛题点评
出题团队简介
本题出题战队 void(0):
团队简介:
设计思路
简介
故事背景简介
春夏秋冬,寒来暑往,日复一日,这人间循环往复空换时光流逝,似乎少了点乐趣。 无所事事便没有好事。 不知从哪儿传出的“巳蛇将出”的谣言,传说巳蛇亦蛇亦弓,入水为蛇,踪迹全无,出水为弓,毙敌百步。竟也有人捕风捉影、信以为真,一传十,十传百,事实变得更加扑朔迷离,人心惶惶。 遂智者言,地势坤、风入松、松如浪,一景一画皆有其意,善用者,良马也。
脱壳
脱壳
VM识别解析
VM识别解析
模拟存储识别
模拟存储识别
1. 代码空间
1. 代码标识 CD 8字节
2. 代码sizeof 8字节
2. 代码大小 8字节
2. 环境寄存器[9]
#define TE 0 //Text End
#define HS 1 //Heap Start
#define HE 2 //Heap End
#define SS 3 //Stack Start
#define TOP 4 //Stack End
//3个指针寄存器
#define IP 5 //指令指针 (指令运行后必然使用 ER[5])
#define SP 6 //栈指针
#define FP 7 //过程调用帧栈指针
...
3. 寄存器[10];
类型 unsigned long long
寄存器[1] - 寄存器[9]
GR[1] - GR[9]
指令解析
指令解析
ps:(有些操作会随着SetProperty属性更改而进行修改)
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 指令操作 [默认编号0]:LEA(取地址) [默认编号1]:SetProperty 寄存器,属性值(立即数)()更改不同属性的值 [默认编号2]:SetProperty 寄存器,属性值(立即数)()更改不同属性的值 [默认编号3]:LOADDI [默认编号4]:LOADQI [默认编号5]:LOADF4I [默认编号6]:LOADF8I [默认编号7]:LOADB [默认编号8]:LOADW [默认编号9]:LOADD [寄存器],[内存] [默认编号10]:LOADQ [默认编号11]:取地址 [默认编号12]:- [默认编号13]:STOREBI [默认编号14]:STOREWI [默认编号15]:STOREDI[寄存器],立即数 [默认编号16]:STOREQI [默认编号17]:STOREF4I [默认编号18]:STOREF8I [默认编号19]:STORERB [内存][内存] SetProperty 属性为准 [默认编号20]:STORERW [默认编号21]:STORERD [默认编号22]:STORERQ [默认编号23]:-待扩展 [默认编号24]:-待扩展 [默认编号25]:MOVB [内存][内存] [默认编号26]:MOVW [默认编号27]:MOVD [默认编号28]:MOVQ [默认编号29]:-待扩展 [默认编号30]:-待扩展 [默认编号31]:MOVRR [寄存器][寄存器] [默认编号32]:MOVRF4 [默认编号33]:MOVRF8 [默认编号34]:PUSHFP [默认编号35]:PUSHB [默认编号36]:PUSHW [默认编号37]:PUSHD [默认编号38]:PUSHQ [默认编号39]:PUSHF4 [默认编号40]:PUSHF8 [默认编号41]:POPFP [默认编号42]:POPB [默认编号43]:POPW [默认编号44]:POPD [默认编号45]:POPQ [默认编号46]:待扩展 [默认编号47]:待扩展 [默认编号48]:INCR [默认编号49]:DECR [默认编号50]:ADDR [默认编号51]:SUBR [默认编号52]:MULR [默认编号53]:DIVR [默认编号54]:-- [默认编号55]:-- [默认编号56]:-- [默认编号57]:-- [默认编号58]:-- [默认编号59]:-- [默认编号60]:-- [默认编号61]:-- [默认编号62]:AND [默认编号63]:OR [默认编号64]:XOR [默认编号65]:NOT [默认编号66]:SHRA [默认编号67]:SHRL [默认编号68]:SHL [默认编号69]:CALL [默认编号70]:RET [默认编号71]:MOVSF [默认编号72]:LDFPOB [默认编号73]:LDFPOW [默认编号74]:LDFPOD [默认编号75]:LDFPOQ [默认编号76]:JMPI [默认编号77]:JNZ [默认编号78]:JNS [默认编号79]:JNL [默认编号80]:JZ [默认编号81]:JS [默认编号82]:JL [默认编号83]:COMP [默认编号84]:COMPI [默认编号85]:OUTO [默认编号86]:OUTD [默认编号87]:OUTH [默认编号88]:OUTC [默认编号89]:-- [默认编号90]:-- [默认编号91]:NOP |
例子strlen:
1 2 3 4 5 6 7 8 9 10 11 12 13 | LEA R1,HELLO MOVRR R3,R1 L1: LOADB R2,R1 JZ END INCR R1 JMPI L1 END: SUBR R1,R3 OUTD R1 HALT ; 字符串 AA AA AA 00 HELLO: DB AA AA AA 00 |
二进制解析CODE:
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 | unsigned AnsiChar data[ 70 ] = { //标识符 0 x 43 , 0 x 44 , 0 x 00 , 0 xCC, 0 xCC, 0 xCC, 0 xCC, 0 xCC, // code 大小 0 x 2 A, 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , //data 数据大小 0 x 01 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , //lea 寄存器[ 1 ],地址 0 x 00 , 0 x 01 , [ 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 ], //MOVRR 寄存器 3 ,寄存器 1 0 x 1 F, 0 x 03 , 0 x 01 , //loadb 寄存器 2 ,寄存器 1 0 x 07 , 0 x 02 , 0 x 01 , //jz 地址 0 x 50 , 0 x 24 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , //INCR 0 x 30 , 0 x 01 , //jmp跳转 0 x 4 C, 0 x 0 D, [ 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 , 0 x 00 ,] //SUBR 寄存器 1 ,寄存器 3 0 x 33 , 0 x 01 , 0 x 03 , //OUTD 0 x 56 , 0 x 01 , //结束符 0 x 5 C, //数据字符串 0 xAA, 0 xAA, 0 xAA, 0 x 00 }; |
(1)非混淆代码会 与外界交互
(2)前面会执行多个pushd 指令 popd指令结束
算法破解
密码算法描述
明文预处理
密钥预处理
加密
其中m为明文的分组组数
R为加密得到的密文数据块
n为输入的明文和密钥个数
offset为加密时生成的随机数
其中n为输入的明文和密钥个数
t为密钥的分组组数
解密
出题
crackme运行过程
解析过程
文章分析的是修复多解的版本。
程序有反调试(检测Context,NtQueryInformationProcess等),有壳,反调试可以用sharpod插件绕过,直接在GetSystemTimeAsFileTime下断运行,回溯到OEP,再手动修IAT,脱壳即可。脱壳以后直接拉IDA分析。
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | int __cdecl main( int argc, const char **argv, const char **envp) { int v3; // eax int v4; // eax int v5; // et2 int v6; // ecx int result; // eax char *v8; // eax int *v9; // esi int v10; // edi unsigned __int8 v11; // cl unsigned __int8 v12; // cl char v13; // bl unsigned __int8 v14; // bl char v15; // dl unsigned __int8 v16; // dl char v17; // dl unsigned __int8 v18; // dl char v19; // dl unsigned __int8 v20; // dl char v21; // dl unsigned __int8 v22; // dl char v23; // dl unsigned __int8 v24; // dl char v25; // dl unsigned __int8 v26; // dl __int128 *v27; // edi __int64 v28; // kr00_8 int v29; // eax int v30; // ebx char *v31; // esi int v32; // eax char *v33; // ecx int v34; // ecx int v35; // eax int i; // eax bool v37; // zf int v38; // edx int v39; // esi int v40; // ecx int v41; // ecx int v42; // [esp-Ch] [ebp-2F84h] int v43; // [esp-8h] [ebp-2F80h] unsigned __int8 v44; // [esp+10h] [ebp-2F68h] int v45; // [esp+10h] [ebp-2F68h] unsigned __int8 v46; // [esp+14h] [ebp-2F64h] unsigned __int8 v47; // [esp+18h] [ebp-2F60h] unsigned __int8 v48; // [esp+1Ch] [ebp-2F5Ch] unsigned __int8 v49; // [esp+20h] [ebp-2F58h] int v50; // [esp+28h] [ebp-2F50h] int v51; // [esp+2Ch] [ebp-2F4Ch] int v52; // [esp+3Ch] [ebp-2F3Ch] char v53[21]; // [esp+44h] [ebp-2F34h] __int64 v54; // [esp+59h] [ebp-2F1Fh] int v55; // [esp+61h] [ebp-2F17h] __int16 v56; // [esp+65h] [ebp-2F13h] char v57; // [esp+67h] [ebp-2F11h] DWORD vmenv[40]; // [esp+68h] [ebp-2F10h] __int128 v59; // [esp+120h] [ebp-2E58h] __int128 v60; // [esp+130h] [ebp-2E48h] __int64 v61; // [esp+140h] [ebp-2E38h] char v62[80]; // [esp+148h] [ebp-2E30h] char v63[1000]; // [esp+198h] [ebp-2DE0h] _OWORD vmcode[671]; // [esp+580h] [ebp-29F8h] v53[4] = 0; v54 = 0i64; *(_OWORD *)&v53[5] = 0i64; v55 = 0; v56 = 0; v57 = 0; memset (v63, 0, sizeof (v63)); printf (aPleaseInputUse); scanf (aS, &v53[4]); printf (aPleaseInputSer); scanf (aS, v63); initusernamekey(&v53[4]); // username生成usernamekey v3 = strlen (v63); v5 = v3 % 24; v4 = v3 / 24; v6 = v4; v51 = v4; if ( v5 ) { printf (aSorryTheSerial); LABEL_3: system (aPause); result = 0; } else { if ( v4 > 0 ) { v8 = &v63[1]; v50 = v6; v9 = serial; do { v10 = 3; do { v11 = *(v8 - 1); if ( v11 <= 0x39u ) v12 = v11 - 48; else v12 = v11 - 55; v13 = *v8; if ( (unsigned __int8 )*v8 <= 0x39u ) v14 = v13 - 48; else v14 = v13 - 55; v15 = v8[1]; if ( (unsigned __int8 )v15 <= 0x39u ) v16 = v15 - 48; else v16 = v15 - 55; v44 = v16; v17 = v8[2]; if ( (unsigned __int8 )v17 <= 0x39u ) v18 = v17 - 48; else v18 = v17 - 55; v46 = v18; v19 = v8[3]; if ( (unsigned __int8 )v19 <= 0x39u ) v20 = v19 - 48; else v20 = v19 - 55; v47 = v20; v21 = v8[4]; if ( (unsigned __int8 )v21 <= 0x39u ) v22 = v21 - 48; else v22 = v21 - 55; v48 = v22; v23 = v8[5]; if ( (unsigned __int8 )v23 <= 0x39u ) v24 = v23 - 48; else v24 = v23 - 55; v49 = v24; v25 = v8[6]; if ( (unsigned __int8 )v25 <= 0x39u ) v26 = v25 - 48; else v26 = v25 - 55; v8 += 8; *v9 = v26 | (16 * (v49 | (16 * (v48 | (16 * (v47 | (16 * (v46 | (16 * (v44 | (16 * (v14 | (16 * v12))))))))))))); ++v9; --v10; } while ( v10 ); --v50; } while ( v50 ); v6 = v51; } // hexstrtodata v27 = &xmmword_45AA50; v52 = v6 - 8; v28 = 4i64 * v6 - 32; v29 = 0; v45 = 0; LABEL_35: v30 = *(_DWORD *)v27; v31 = &aVm[v29]; // 字符串跟username生成的数据拼接到一起,加密 v32 = strlen (&aVm[v29]); v31[v32 + 3] = v30; v33 = &v31[v32]; v33[2] = BYTE1(v30); v33[1] = BYTE2(v30); *v33 = HIBYTE(v30); v33[4] = 0; v34 = 0; v35 = 0; while ( v31[v35] ) { if ( !v31[v35 + 1] ) { ++v34; break ; } if ( !v31[v35 + 2] ) { v34 += 2; break ; } if ( !v31[v35 + 3] ) { v34 += 3; break ; } if ( !v31[v35 + 4] ) { v34 += 4; break ; } v35 += 5; v34 += 5; if ( v35 >= 2000 ) break ; } encrypt(v31, ( int )constkey, v34); initvmcode(vmcode); // 初始化虚拟机代码 vmenv[4] = 0; memset (&vmenv[8], 0, 0x48u); memset (&vmenv[26], 0, 0x50u); v59 = 0i64; v60 = 0i64; v61 = 0i64; memset (v62, 0, sizeof (v62)); initenv(( int )vmenv, vmcode, 10737); // 初始化虚拟机环境 vmenv[28] = ( DWORD )serial; vmenv[29] = ( int )serial >> 31; vmenv[30] = ( DWORD )constkey; vmenv[31] = ( int )constkey >> 31; vmenv[32] = ( DWORD )usernamekey; vmenv[33] = ( int )usernamekey >> 31; vmenv[34] = ( DWORD )text; // 虚拟机输出 vmenv[35] = ( int )text >> 31; vmenv[38] = ( DWORD )flagresult; // 虚拟机输出 vmenv[39] = ( int )flagresult >> 31; vmstart(vmenv); // 执行虚拟机解密 xordecrypt(v42, v43, v52); // text xor flagresult 结果存放到key encrypt(key, ( int )encresult, v28); for ( i = strlen (key) - 1; i > 0; --i ) { v37 = key[i] == ( char )0x80; // 末尾的0x80替换为0 key[i] = 0; if ( v37 ) break ; } v38 = strlen (key); v39 = 0; v40 = 0; if ( v38 > 0 ) // 比较 { while ( key[v40] == truekey[v45 + v40] ) { if ( ++v40 >= v38 ) goto LABEL_55; } v39 = 1; } LABEL_55: v41 = 0; while ( flagresult[v41] == encresult[v41] ) { ++v41; if ( v41 >= 8 ) { if ( v39 == 1 ) break ; if ( vmenv[7] ) j_j_j___free_base(( void *)vmenv[7]); v27 = (__int128 *)(( char *)v27 + 4); v29 = v45 + 256; v45 += 256; if ( ( int )v27 >= 0x45AA5C ) { printf (aCongratulation); goto LABEL_3; } goto LABEL_35; } } printf (aSorryTheSerial); system (aPause); if ( vmenv[7] ) j_j_j___free_base(( void *)vmenv[7]); result = 0; } return result; } |
(一共三轮加密,分别使用"这杀软好多呀,好像是个VM","这机器里文件修改时间分布广","伊娃找到了理想植物"),再加密生成constkey。
抓取"KCTF"相关的usernamekey和constkey,人肉004016EC处的虚拟机即可。
004016EC的解密流程转写成python大致如下:
def testdecrypt(roundnum): constroundkey = constkey[roundnum] for i in range(5): r = (serial[i * 3] + serial[i * 3 + 1] * constroundkey[i] + ((constroundkey[i]**2) * serial[i * 3 + 2])) % 0xFFFFFFFB r ^= usernamekey[i] text.append(r) for i in range(5, 13): r = (serial[i * 3] + serial[i * 3 + 1] * constroundkey[i % 8] + ((constroundkey[i % 8]**2) * serial[i * 3 + 2])) % 0xFFFFFFFB r ^= usernamekey[i] flagresult.append(r)
text和flagresult均为虚拟机输出,可以在后面的流程找到真正的结果。
修改程序流程,在00401705 下断点,输入KCTF和官方提供的真码,停下后,0045B270替换为正确的text("我是个任务管理器","找包含关键词的文件","赶紧带回去给船长"),末尾的一个"00"要改成"80",步过00401712,在0045A230就能看到flagresult了。
结合"KCTF"的usernamekey和constkey,可以列出下面的方程。
1 2 | m = flagresult[i]^text[i]^usernamekey[i] (xi + yi * constkey[r][i% 8 ] + zi * constkey[r][i% 8 ]** 2 ) mod 0 xFFFFFFFB == m |
其中i表示当前位置,r表示轮数,xi=serial[3i],yi=serial[3i+1],zi=serial[3i+2]
(x0 + y0 0x835904E1 + z0 0x835904E1**2) mod 0xFFFFFFFB == 0x1CC4FA98
写代码列出三轮的方程组,python代码如下,测试testdecrypt的代码也包含在里面了。
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 | # KCTF serial = [ 0xe84de727, 0xb4c7223f, 0xc4c8b34f, 0x9d4e4221, 0x225dd4a2, 0x6a95e624, 0xe5eb4526, 0x9de64c0a, 0x9ed50b44, 0xbba723f9, 0x878e4b4d, 0xd8841263, 0x453dc515, 0x7b401d6b, 0x5af4f140, 0x1193faa1, 0x433ada48, 0x145a358a, 0xbce1b843, 0x9c7f5d39, 0x0c31987e, 0x39bb056e, 0x1a21b92b, 0x8de2358e, 0xcec1ff6c, 0x206cf8c1, 0x7c46c891, 0x44ba8da0, 0xec483438, 0x9ffa54b3, 0x2c8d3174, 0xe97ac3c2, 0x024783d0, 0xdfdc2bc9, 0x524b9c81, 0xb40f78f2, 0xe184a49b, 0x2292b4d7, 0x9a58ef0b ] constkey = [[ 0x835904e1, 0xc834944b, 0x027a19e0, 0xfeb308a3, 0x621ff195, 0x4b705c5c, 0x3eb5d9d0, 0x9a5c73f4 ], [ 0xccffa00f, 0x490c46ed, 0xf78be5a1, 0x81a56274, 0x165deb5c, 0xf6f46796, 0x44de5146, 0x00e4984c ], [ 0xdf77dfa8, 0xae0ed8d8, 0x064da354, 0x4c8b95cc, 0xf934ca39, 0xc4e9de04, 0x18ee2793, 0x945ac9c2 ]] usernamekey = [ 0xd9c3e463, 0x11c9af78, 0x6485bf9e, 0xff4bd05d, 0x65769726, 0xf5c38988, 0xbf3a2423, 0x4b718cc0, 0xc70d8f49, 0xdfc73315, 0x74470070, 0x94b89f71, 0x0e60f6b1, 0x2051a122, 0x1f061047, 0x4ced9e38 ] textr = [[3305578235L, 3274609060L, 2824850596L, 382260195L, 2418685203L], [2670343418L, 3143641187L, 3172232216L, 4193180192L, 2540967463L], [3311948515L, 4048345354L, 1943049539L, 810068865L, 2159988552L]] flagresultr = [[198562876L, 2077776234L, 1722709368L, 3592754452L, 271201555L, 1745403541L, 14394681L, 417773631L], [1257800710L, 30183867L, 32037074L, 1277826276L, 730502695L, 1053176226L, 2658935662L, 3990671698L], [2108157719L, 1169449682L, 3145018811L, 2229540901L, 12504904L, 855930893L, 1706174636L, 1402764800L]] def testdecrypt(roundnum): constroundkey = constkey[roundnum] for i in range(5): r = (serial[i * 3] + serial[i * 3 + 1] * constroundkey[i] + ((constroundkey[i]**2) * serial[i * 3 + 2])) % 0xFFFFFFFB r ^= usernamekey[i] text.append(r) for i in range(5, 13): r = (serial[i * 3] + serial[i * 3 + 1] * constroundkey[i % 8] + ((constroundkey[i % 8]**2) * serial[i * 3 + 2])) % 0xFFFFFFFB r ^= usernamekey[i] flagresult.append(r) def testencrypt(roundnum): constroundkey = constkey[roundnum] for i in range(5): r = textr[roundnum][i] ^ usernamekey[i] rc.append(r) for i in range(8): r = flagresultr[roundnum][i] ^ usernamekey[i + 5] rc.append(r) for j in range(3): text = [] flagresult = [] truetext = [] rc = [] testdecrypt(j) for i in range(5): truetext.append(text[i] ^ flagresult[i]) truetextstr = '' for i in truetext: truetextstr += hex(i)[2:-1] # print truetextstr testencrypt(j) for i in range(len(rc)): print ( '(x%d + y%d * 16^^%08X + z%d * 16^^%08X ^2) == 16^^%08X ,' ) % ( i, i, constkey[j][i % 8], i, constkey[j][i % 8], rc[i]) |
x0 == 3897419559 && x1 == 2639151649 && x10 == 747450740 &&
x11 == 3755748297 && x12 == 3783566491 && x2 == 3857401126 &&
x3 == 3148293113 && x4 == 1161676053 && x5 == 294910625 &&
x6 == 3168909379 && x7 == 968557934 && x8 == 3468820332 &&
x9 == 1153076640 && y0 == 3032949311 && y1 == 576574626 &&
y10 == 3917136834 && y11 == 1380686977 && y12 == 580039895 &&
y2 == 2649115658 && y3 == 2274249549 && y4 == 2067799403 &&
y5 == 1127930440 && y6 == 2625592633 && y7 == 438417707 &&
y8 == 544012481 && y9 == 3964154936 && z0 == 3301487439 &&
z1 == 1788208676 && z10 == 38241232 && z11 == 3020912882 &&
z12 == 2589519627 && z2 == 2664762180 && z3 == 3632534115 &&
z4 == 1526001984 && z5 == 341456266 && z6 == 204576894 &&
z7 == 2380412302 && z8 == 2085013649 && z9 == 2683983027
E84DE727B4C7223FC4C8B34F9D4E4221225DD4A26A95E624E5EB45269DE64C0A9ED50B44BBA723F9878E4B4DD8841263453DC5157B401D6B5AF4F1401193FAA1433ADA48145A358ABCE1B8439C7F5D390C31987E39BB056E1A21B92B8DE2358ECEC1FF6C206CF8C17C46C89144BA8DA0EC4834389FFA54B32C8D3174E97AC3C2024783D0DFDC2BC9524B9C81B40F78F2E184A49B2292B4D79A58EF0B