SMC 什么是SMC SMC (Self Modified Code),也就是自修改代码。它是在执行过程中修改自身指令的代码或数据,来组织别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。
VirtualProtect 函数 通常用于代码自加密的场景。代码自加密是一种保护代码不被轻易逆向分析的技术,通过在程序运行时动态地修改代码的内存保护属性,使得代码在执行时可以被修改和执行,但在不执行时则不能被读取或修改。
1 2 3 4 5 6 BOOL VirtualProtect (    LPVOID lpAddress,              SIZE_T dwSize,                 DWORD  flNewProtect,           PDWORD lpflOldProtect        ) 
它的作用是:修改指定内存区域的访问权限(如改为可读写可执行),并返回之前的保护属性。这是在Windows程序里。
mprotect()函数 是在Linux 程序中来改变虚拟内存区域的属性的。
1 2 3 #include  <sys/mman.h>  int  mprotect (void  *addr, size_t  len, int  prot) ;
参数 
含义 
 
 
addr要修改保护属性的内存区域起始地址(必须是页对齐的) 
 
len要修改的内存长度(会按页大小对齐) 
 
prot新的保护属性(如下表) 
 
这些可以组合使用,例如PROT_READ | PROT_WRITE 表示可读可写。
宏名 
含义 
 
 
PROT_NONE无访问权限 
 
PROT_READ可读取 
 
PROT_WRITE可写入 
 
PROT_EXEC可执行 
 
在程序中如果发现VirtualProtect 函数或mprotect()函数,那么极有可能是SMC。
SMC一般有两种破解方法。
第一种 是找到对代码或数据加密的函数 后通过idapython写解密脚本。
第二种 是动态调试 到SMC解密结束的地方dump出来。
例题 1.NSS-[网鼎杯 2020 青龙组]jocker 使用IDA打开后,我们进行分析,发现整个函数头有positive sp value has been detected, the output may be wrong!
这就说明了存在栈不平衡的问题,导致IDA无法生成代码,我们可以在IDA里打开栈指针Options->General->Stack pointer 来帮助分析。在x64架构里rsp 是栈指针,它总是指向当前栈顶,在x86架构里esp是栈指针。
会发现有VirtualProtect 函数,那么这个大概率就是一个smc了。先进行分析,也就是要输入一个24位的flag,然后通过wrong(flag)进行加密,再通过omg(flag)进行比较,先看看加密函数。
这个加密逻辑很简单,就是分出奇偶分别进行加密。
这个检验逻辑也很简单,我们可以直接从unk_4030C0中读取出来。
需要注意的是它是24个int型的数据,读取之后写出exp,但是是错误的
1 2 3 4 5 6 7 8 9 10 v2=[0x66 , 0x6B , 0x63 , 0x64 , 0x7F , 0x61 , 0x67 , 0x64 , 0x3B , 0x56 , 0x6B , 0x61 , 0x7B , 0x26 , 0x3B , 0x50 , 0x63 , 0x5F , 0x4D , 0x5A , 0x71 , 0x0C , 0x37 , 0x66 ] print (len (v2))flag=''  for  i in  range (len (v2)):    if ((i&1 )!=0 ):         flag+=chr (v2[i]+i)     else :         flag+=chr (v2[i]^i)     print (flag)       
就是说嘛,这个是SMC,不会这么简单的。
1 2 if  ( !VirtualProtect (encrypt, 0xC8u , 4u , &flOldProtect) )    exit (1 ); 
意思是:将 encrypt 函数所在的内存区域设置为 PAGE_READWRITEEXECUTE(值是 4),允许后续对其进行修改。0xC8 是要修改的字节长度,即 200 字节。
1 2 for  ( i = 0 ; i <= 186 ; ++i )    *((_BYTE *)encrypt + i) ^= 0x41 u; 
作用是:运行时对 encrypt 函数做 XOR 解密 ,之前程序通过 XOR 将 encrypt 加密存储,这里要先“解锁”。
1 2 if  ( encrypt (Destination) )    sub_40159A (Destination); 
这才是真正验证flag的代码。
我们可以在if ( encrypt(Destination) )这里下断点,然后动态调试看真正的加密方式。动态调试时,会提示我们输入flag,这里任意输只要长度是24就好了。然后直接查看汇编,找到encrypt函数是public __Z7encryptPc
点进去发现,IDA反汇编错误,下面有很大串的数据都没识别出来,我们就将函数头按U解除定义,然后从函数头一直选到0x00401630这里结束,按C让IDA强制重新解析成代码。
这样就好了,然后再在函数头按P进行重新定义
然后F5就能反编译了。下一个函数 loc_40159A也没有办法反编译,就在函数头0x0040159A处,U解除定义,再P重新定义一下就能反编译了。
这就是真正的加密函数了,分析一下,右边的是将flag[i]和Buffer[i]进行异或得到密文v2[i],我们可以从unk_403040处找到v2密文,也能直接点击Buffer得到这个字符串是'hahahaha_do_you_find_me?',那么我们就能得到前19位的flag,只要将v2[i]和Buffer[i]进行异或就好了。我们知道这个flag一共是24位,还有5位不知道,看左边的加密函数。这个v3密文正好是5位,但是这个函数并没有什么卵用,我们就进行猜测这个是flag与某个值进行异或得到了这个密文。已知flag最后一位一定是右括号”}”,那么我们将这个和密文异或就能得到某个值了。
写出exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 buffer='hahahaha_do_you_find_me?'  flag =''  v2=[0x0E , 0x0D , 0x09 , 0x06 , 0x13  ,0x05  ,0x58  ,0x56  ,0x3E  ,0x06  ,0x0C  ,0x3C  ,0x1F  ,0x57  ,0x14  ,0x6B  ,0x57  ,0x59  ,0x0D  ]  print (len (v2))  for  i in  range (len (v2)):    flag+= chr (v2[i]^ord (buffer[i])) print (flag)v3 ='%tp&:'  a=ord (':' )^ord ('}' ) for  i in  range (len (v3)):    flag+=chr (ord (v3[i])^a) print (flag)
2.[NSSRound#2 Able]findxenny 使用IDA打开程序后,找到main函数,进行分析
这个是将输入的flag放到v12里面,长度要大于等于12,并且将这个分成三份,分别放到v14,v15,v16里面。然后进入到一个函数里面,最后的if条件是进行检测判断,通过这三个函数来对flag的三部分进行检测。那我们要进入到sub_140011514()里面来看看它进行了什么操作,里面有没有关于这三个函数的信息。
发现了VirtualProtect 函数,这就说明了考察的是SMC,上面的数据都是加密后的,下面是解密的内容,我们可以通过动态调试的办法找到解密后的数据,最后的那三个函数就是main函数里进行校验flag的函数。我们在 qword_140029370 , qword_140029378 ,qword_140029380 都下上断点,进行调试。
只要输入大于等于12个字符就好了。
我们单步步入到 return 处,然后点击这三个函数,会发现这里原本没有数据,这时候竟然有数据了
我们点进去后,光标原本所在的位置就是函数头,直接按P进行重新定义,然后F5,就能将这个函数编译出来。	
另外两个函数也是用这个相同的方法进行反编译,就能知道校验的内容。
第二个函数
第三个函数
我们会发现第三个将它反过来就是 _x3nny,与提示输入的xenny有异曲同工之妙,这三个内容应该就是flag的内容,但是第一个和第三个反了,将它们反过来后进行拼接就是flag。这一步主要靠经验和猜测。
最后flag就是NSSCTF{oh_you_found_our_x3nny} 
3.NSS-[HDCTF 2023]enc 放到IDA里面进行分析
这个题是两部分组成,第一部分通过TEA解密要找到密钥,输入密钥正确后,我们才能进入第二部分输入flag以及flag的验证。
发现sub_411302(v6)函数点开后不能正确的反编译。先看看TEA函数吧,sub_411523((int)&v7, (int)v9);输入的key在TEA加密后必须输出v7 == 1627184887  // 0x610B6A77,v8 == 37149676    // 0x0235AC4C 这两个特定数值,这是密文,利用它来解密,会得到两个数据,其中一个是v8=4,那另外一个就是v7=key了。
就是简单的TEA,进行解密
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 #include  <iostream>  #include  <stdint.h>  void  TEA_decrypt (uint32_t * v, uint32_t * k)     uint32_t  v0 = v[0 ], v1 = v[1 ];     uint32_t  delta = 0x61C88647 ;     uint32_t  sum = -delta * 32 ;     for  (int  i = 0 ; i < 32 ; i++) {         v1 -= (k[3 ] + (v0 >> 5 )) ^ (sum + v0) ^ (k[2 ] + 16  * v0);         v0 -= (k[1 ] + (v1 >> 5 )) ^ (sum + v1) ^ (k[0 ] + 16  * v1);         sum += delta;     }     v[0 ] = v0;     v[1 ] = v1; } int  main ()     uint32_t  key[4 ] = {18 , 52 , 86 , 120 };     uint32_t  cipher[2 ] = {1627184887 , 37149676 };     TEA_decrypt (cipher, key);     std::cout << "Decrypted v0 = "  << cipher[0 ] << std::endl;     std::cout << "Decrypted v1 = "  << cipher[1 ] << std::endl;     return  0 ; } 
key等于3,我们在sub_411302(v6)函数处打上断点,进行动态调试。
我们在断点位置按F7进行单步步入看看汇编,发现与静态分析的有所不同,这里有很多数据都没有被正确识别出来
我们直接在函数头按P进行重新定义,让IDA重新识别。
然后按F5进行反编译,得到这个函数的内容了,进行分析。
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 void  __cdecl __noreturn sub_41D000 (char  *Str)   char  v1;    size_t  m;    BOOL v3;    int  k;    int  v5;    int  v6;    int  i;    int  j;    int  v9;    char  v10;    char  v11;    char  v12[540 ];    char  v13[16 ];    int  v14;    char  v15[264 ];    char  v16[40 ];    __CheckForDebuggerJustMyCode(&unk_425036);   v16[0 ] = 15 ;   v16[1 ] = -108 ;   v16[2 ] = -82 ;   v16[3 ] = -14 ;   v16[4 ] = -64 ;   v16[5 ] = 87 ;   v16[6 ] = -62 ;   v16[7 ] = -32 ;   v16[8 ] = -102 ;   v16[9 ] = 69 ;   v16[10 ] = 55 ;   v16[11 ] = 80 ;   v16[12 ] = -11 ;   v16[13 ] = -96 ;   v16[14 ] = 94 ;   v16[15 ] = -53 ;   v16[16 ] = 44 ;   v16[17 ] = 22 ;   v16[18 ] = 40 ;   v16[19 ] = 41 ;   v16[20 ] = -2 ;   v16[21 ] = -1 ;   v16[22 ] = 51 ;   v16[23 ] = 70 ;   v16[24 ] = 14 ;   v16[25 ] = 87 ;   v16[26 ] = -126 ;   v16[27 ] = 34 ;   v16[28 ] = 82 ;   v16[29 ] = 38 ;   v16[30 ] = 43 ;   v16[31 ] = 110 ;   v16[32 ] = -28 ;   v16[33 ] = -126 ;   v16[34 ] = 36 ;   j_memset (v15, 0 , 0x100u );   v14 = j_strlen (Str);   strcpy (v13, "you_are_master" );   v12[531 ] = 0 ;   v5 = 0 ;   for  ( i = 0 ; i < 256 ; ++i )   {     v12[i + 264 ] = i;     v12[i] = v13[i % j_strlen (v13)];   }   for  ( j = 0 ; j < 256 ; ++j )   {     v5 = ((unsigned  __int8)v12[j] + v5 + (unsigned  __int8)v12[j + 264 ]) % 256 ;     v10 = v12[j + 264 ];     v12[j + 264 ] = v12[v5 + 264 ];     v12[v5 + 264 ] = v10;   }   v6 = 0 ;   v9 = 0 ;   for  ( k = 0 ; k < v14; ++k )   {     v9 = (v9 + 1 ) % 256 ;     v6 = (v6 + (unsigned  __int8)v12[v9 + 264 ]) % 256 ;     v11 = v12[v9 + 264 ];     v12[v9 + 264 ] = v12[v6 + 264 ];     v12[v6 + 264 ] = v11;     v15[k] = v12[((unsigned  __int8)v12[v6 + 264 ] + (unsigned  __int8)v12[v9 + 264 ]) % 256  + 264 ] ^ Str[k];   }   v3 = j_strlen (Str) == 35 ;   for  ( m = 0 ; m < j_strlen (v16); ++m )   {     if  ( v16[m] != v15[m] )     {       v3 = 0 ;       break ;     }   }   if  ( v3 )     sub_41114F ("right!!!!" , v1);   else      sub_41114F ("please try agin~" , v1); } 
这是标准的RC4对输入的flag进行加密,把加密后的结果v15与v16进行逐字节比较,如果完全匹配,就打印 right!!!!,那么v16就是密文了,密钥就是v13"you_are_master"。
我们可以使用在线网站进行解密:https://www.toolhelper.cn/SymmetricEncryption/RC4 
也可以使用脚本
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 def  rc4_decrypt (ciphertext, key ):         S = list (range (256 ))     j = 0      for  i in  range (256 ):         j = (j + S[i] + ord (key[i % len (key)])) % 256          S[i], S[j] = S[j], S[i]          i = j = 0      keystream = []     for  _ in  range (len (ciphertext)):         i = (i + 1 ) % 256          j = (j + S[i]) % 256          S[i], S[j] = S[j], S[i]         K = S[(S[i] + S[j]) % 256 ]         keystream.append(K)          return  bytes ([c ^ k for  c, k in  zip (ciphertext, keystream)]) if  __name__ == "__main__" :         v16 = [         15 , 148 , 174 , 242 , 192 , 87 , 194 , 224 , 154 , 69 ,         55 , 80 , 245 , 160 , 94 , 203 , 44 , 22 , 40 , 41 ,         254 , 255 , 51 , 70 , 14 , 87 , 130 , 34 , 82 , 38 ,         43 , 110 , 228 , 130 , 36      ]     key = "you_are_master"      decrypted = rc4_decrypt(v16, key)     print ("Recovered flag:" )     print (decrypted.decode()) 
4.NSS-[羊城杯 2021]BabySmc 这道题对于目前的我来讲还挺吃力的,不仅考了SMC,还考察了对于base64的理解,我当时写的时候就没有看出来base64。参考了一位师傅的博客才明白了,不得不说,师傅写的真好!!! https://blog.csdn.net/xiao__1bai/article/details/120342289 
使用IDA打开,然后分析主函数
v4里面存放的是用户输入的flag,知道lpAddress和 qword_7FF62CC1AD88分别是起始和结束地址。然后看看sub_7FF62CBF1E30()函数。
发现这里有VirtualProtect 函数,修改了起始地址和结束地址的权限,进行了解密,所以这个是数据的解密函数。
我们接着看被IDA错误识别的汇编
发现这里有很多大块的没有被识别出来的数据,不是花指令的话,那就是smc了。看到上面调用了解密函数,我们可以使用动态调试dump出来。在解密函数处打上断点,进行调试分析。提示输入flag时,随便输就好了,这个不影响后续的分析。我们按F8进行单步步过,这时候会出现一个弹窗,意思是是否让IDA自己根据RIP生成代码,这里选no,因为IDA不知道哪里是代码,哪里是数据,如果根据RIP进行分析,就可能会错误的把数据当成代码,从而整个main函数结构就被拆分了。
然后我们看到了这里有更多的大块数据,没有正确识别出来
我们直接从Main函数头开始选中,一直选到第一个retn为止
然后按C,选择Force 进行强制分析,然后按F5就能进行反编译了。
得到的完整的函数如下所示:
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 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 int  __fastcall main (int  argc, const  char  **argv, const  char  **envp)   __int64 v3;    char  v4;    char  v5;    __int64 v6;    __int64 v8;    __int64 v9;    unsigned  __int128 v10;    unsigned  __int64 v11;    unsigned  __int64 v12;    int  v13;    int  v14;    unsigned  __int64 v15;    __int64 v16;    __int64 v17;    int  v18;    int  v19;    int  v20;    int  v21;    __int64 v22;    int  v23;    __int64 v24;    __int64 v25;    int  v26;    __int64 v27;    __int64 v28;    int  v29;    __int64 v30;    __int64 v31;    __int64 v32;    int  v33;    __int64 v34;    __int64 v35;    int  v36;    __int64 v37;    __int64 v38;    __int64 v39;    int  v40;    __int64 v41;    __int64 v42;    int  v43;    __int64 v44;    __int64 v45;    __int64 v46;    int  v47;    __int64 v48;    __int64 v49;    int  v50;    __int64 v51;    __int64 v52;    __int64 v53;    int  v54;    __int64 v55;    __int64 v56;    int  v57;    __int64 v58;    __int64 v59;    __int64 v60;    int  v61;    __int64 v62;    __int64 v63;    int  v64;    __int64 v65;    int  v66;    __int64 v67;    __int64 v68;    __int64 v69;    unsigned  __int64 v70;    __int64 i;    __int64 v72;    int  v73;    int  v74;    int  v75;    __int64 v76;    char  v77;    char  v78;    int  v79;    int  v80;    char  v81;    char  v82;    const  char  *v83;    char  *v84;    bool  v85;    unsigned  __int8 v86;    int  v87;    const  char  *v88;    __int64 v89;    unsigned  __int64 v90;    __int128 v92[4 ];    char  v93[256 ];    __int128 v94[4 ];    __m128i v95;    __int128 v96;    __int128 v97;    __int64 v98;    sub_7FF62CBF1EB0 (0 i64, 0 i64, envp);   v95 = 0 i64;   v96 = 0 i64;   v97 = 0 i64;   sub_7FF62CBF1D40 ("Input Your Flag : " );   sub_7FF62CBF1DC0 ("%46s" , v95. m128i_i8);   lpAddress = &loc_7FF62CBF1085;   qword_7FF62CC1AD88 = (__int64)&loc_7FF62CBF1D00;   sub_7FF62CBF1E30 ();   v3 = 16 i64;   do    {     v92[v3 + 3 ] = 0 i64;     v92[v3 + 2 ] = 0 i64;     v92[v3 + 1 ] = 0 i64;     v92[v3] = 0 i64;     v3 -= 4 i64;   }   while  ( v3 * 16  );   v94[0 ] = xmmword_7FF62CC0E340 ;   v94[1 ] = xmmword_7FF62CC0E350 ;   v94[2 ] = xmmword_7FF62CC0E360 ;   v94[3 ] = xmmword_7FF62CC0E370 ;   v5 = v4 + 0x80 ;   v6 = v5 & 0xF ;   if  ( !_BitScanForward((unsigned  int  *)&v8, (unsigned  int )_mm_movemask_epi8(_mm_cmpeq_epi8((__m128i)0 i64, v95)) >> v6) )     v8 = sub_7FF62CBF2340 (v6, &v95. m128i_i8[v6]);   v9 = v8;   v10 = 0xAAAAAAAAAAAAAAABu i64 * (unsigned  __int128)(unsigned  __int64)v8;   v11 = *((_QWORD *)&v10 + 1 ) >> 1 ;   if  ( *((_QWORD *)&v10 + 1 ) >> 1  )   {     LODWORD (v12) = 0 ;     LODWORD (v10) = 1 ;     v13 = 0 ;     v14 = 0 ;     v15 = *((_QWORD *)&v10 + 1 ) >> 5 ;     if  ( *((_QWORD *)&v10 + 1 ) >> 5  )     {       do        {         v16 = v13;         v17 = v14;         v18 = v95. m128i_i8[v13 + 1 ];         v19 = 16  * (v95. m128i_i8[v13] & 3 );         v20 = v95. m128i_i8[v13 + 2 ];         v93[v14] = *((_BYTE *)v94 + (v95. m128i_i8[v13] >> 2 )) ^ 0xA6 ;         v93[v14 + 1 ] = *((_BYTE *)v94 + ((v18 >> 4 ) | v19)) ^ 0xA3 ;         v93[v14 + 2 ] = *((_BYTE *)v94 + ((v20 >> 6 ) | (4  * (v18 & 0xF )))) ^ 0xA9 ;         v21 = v95. m128i_i8[v13 + 3 ] & 3 ;         v22 = v95. m128i_i8[v13 + 3 ] >> 2 ;         v93[v14 + 3 ] = *((_BYTE *)v94 + (v20 & 0x3F )) ^ 0xAC ;         v23 = v95. m128i_i8[v13 + 4 ];         v93[v14 + 4 ] = *((_BYTE *)v94 + v22) ^ 0xA6 ;         LODWORD (v22) = v23;         v24 = v95. m128i_i8[v13 + 5 ] & 0x3F ;         v25 = (v95. m128i_i8[v13 + 5 ] >> 6 ) | (4  * (v23 & 0xF ));         v93[v14 + 5 ] = *((_BYTE *)v94 + (((int )v22 >> 4 ) | (16  * v21))) ^ 0xA3 ;         LODWORD (v22) = v95. m128i_i8[v13 + 6 ];         v93[v14 + 6 ] = *((_BYTE *)v94 + v25) ^ 0xA9 ;         v93[v14 + 7 ] = *((_BYTE *)v94 + v24) ^ 0xAC ;         LODWORD (v25) = v95. m128i_i8[v13 + 7 ];         v93[v14 + 8 ] = *((_BYTE *)v94 + ((int )v22 >> 2 )) ^ 0xA6 ;         v26 = v25;         v27 = v95. m128i_i8[v13 + 8 ] & 0x3F ;         v28 = (int )((v95. m128i_i8[v13 + 8 ] >> 6 ) | (4  * (v25 & 0xF )));         v93[v14 + 9 ] = *((_BYTE *)v94 + (int )((v26 >> 4 ) | (16  * (v22 & 3 )))) ^ 0xA3 ;         v93[v14 + 10 ] = *((_BYTE *)v94 + v28) ^ 0xA9 ;         v29 = v95. m128i_i8[v13 + 9 ] & 3 ;         v30 = v95. m128i_i8[v13 + 9 ] >> 2 ;         v93[v14 + 11 ] = *((_BYTE *)v94 + v27) ^ 0xAC ;         LODWORD (v28) = v95. m128i_i8[v13 + 10 ];         v93[v14 + 12 ] = *((_BYTE *)v94 + v30) ^ 0xA6 ;         LODWORD (v30) = v28;         v31 = v95. m128i_i8[v13 + 11 ] & 0x3F ;         v32 = (int )((v95. m128i_i8[v13 + 11 ] >> 6 ) | (4  * (v28 & 0xF )));         v93[v14 + 13 ] = *((_BYTE *)v94 + (((int )v30 >> 4 ) | (16  * v29))) ^ 0xA3 ;         LODWORD (v30) = v95. m128i_i8[v13 + 12 ];         v93[v14 + 14 ] = *((_BYTE *)v94 + v32) ^ 0xA9 ;         v93[v14 + 15 ] = *((_BYTE *)v94 + v31) ^ 0xAC ;         LODWORD (v32) = v95. m128i_i8[v13 + 13 ];         v93[v14 + 16 ] = *((_BYTE *)v94 + ((int )v30 >> 2 )) ^ 0xA6 ;         v33 = v32;         v34 = v95. m128i_i8[v13 + 14 ] & 0x3F ;         v35 = (int )((v95. m128i_i8[v13 + 14 ] >> 6 ) | (4  * (v32 & 0xF )));         v93[v14 + 17 ] = *((_BYTE *)v94 + (int )((v33 >> 4 ) | (16  * (v30 & 3 )))) ^ 0xA3 ;         v93[v14 + 18 ] = *((_BYTE *)v94 + v35) ^ 0xA9 ;         v36 = v95. m128i_i8[v13 + 15 ] & 3 ;         v37 = v95. m128i_i8[v13 + 15 ] >> 2 ;         v93[v14 + 19 ] = *((_BYTE *)v94 + v34) ^ 0xAC ;         LODWORD (v35) = *((char  *)&v96 + v13);         v93[v14 + 20 ] = *((_BYTE *)v94 + v37) ^ 0xA6 ;         LODWORD (v37) = v35;         v38 = *((_BYTE *)&v96 + v13 + 1 ) & 0x3F ;         v39 = (int )((*((char  *)&v96 + v13 + 1 ) >> 6 ) | (4  * (v35 & 0xF )));         v93[v14 + 21 ] = *((_BYTE *)v94 + (((int )v37 >> 4 ) | (16  * v36))) ^ 0xA3 ;         LODWORD (v37) = *((char  *)&v96 + v13 + 2 );         v93[v14 + 22 ] = *((_BYTE *)v94 + v39) ^ 0xA9 ;         v93[v14 + 23 ] = *((_BYTE *)v94 + v38) ^ 0xAC ;         LODWORD (v39) = *((char  *)&v96 + v13 + 3 );         v93[v14 + 24 ] = *((_BYTE *)v94 + ((int )v37 >> 2 )) ^ 0xA6 ;         v40 = v39;         v41 = *((_BYTE *)&v96 + v13 + 4 ) & 0x3F ;         v42 = (int )((*((char  *)&v96 + v13 + 4 ) >> 6 ) | (4  * (v39 & 0xF )));         v93[v14 + 25 ] = *((_BYTE *)v94 + (int )((v40 >> 4 ) | (16  * (v37 & 3 )))) ^ 0xA3 ;         v93[v14 + 26 ] = *((_BYTE *)v94 + v42) ^ 0xA9 ;         v43 = *((_BYTE *)&v96 + v13 + 5 ) & 3 ;         v44 = *((char  *)&v96 + v13 + 5 ) >> 2 ;         v93[v14 + 27 ] = *((_BYTE *)v94 + v41) ^ 0xAC ;         LODWORD (v42) = *((char  *)&v96 + v13 + 6 );         v93[v14 + 28 ] = *((_BYTE *)v94 + v44) ^ 0xA6 ;         LODWORD (v44) = v42;         v45 = *((_BYTE *)&v96 + v13 + 7 ) & 0x3F ;         v46 = (int )((*((char  *)&v96 + v13 + 7 ) >> 6 ) | (4  * (v42 & 0xF )));         v93[v14 + 29 ] = *((_BYTE *)v94 + (((int )v44 >> 4 ) | (16  * v43))) ^ 0xA3 ;         LODWORD (v44) = *((char  *)&v96 + v13 + 8 );         v93[v14 + 30 ] = *((_BYTE *)v94 + v46) ^ 0xA9 ;         v93[v14 + 31 ] = *((_BYTE *)v94 + v45) ^ 0xAC ;         LODWORD (v46) = *((char  *)&v96 + v13 + 9 );         v93[v14 + 32 ] = *((_BYTE *)v94 + ((int )v44 >> 2 )) ^ 0xA6 ;         v47 = v46;         v48 = *((_BYTE *)&v96 + v13 + 10 ) & 0x3F ;         v49 = (int )((*((char  *)&v96 + v13 + 10 ) >> 6 ) | (4  * (v46 & 0xF )));         v93[v14 + 33 ] = *((_BYTE *)v94 + (int )((v47 >> 4 ) | (16  * (v44 & 3 )))) ^ 0xA3 ;         v93[v14 + 34 ] = *((_BYTE *)v94 + v49) ^ 0xA9 ;         v50 = *((_BYTE *)&v96 + v13 + 11 ) & 3 ;         v51 = *((char  *)&v96 + v13 + 11 ) >> 2 ;         v93[v14 + 35 ] = *((_BYTE *)v94 + v48) ^ 0xAC ;         LODWORD (v49) = *((char  *)&v96 + v13 + 12 );         v93[v14 + 36 ] = *((_BYTE *)v94 + v51) ^ 0xA6 ;         LODWORD (v51) = v49;         v52 = *((_BYTE *)&v96 + v13 + 13 ) & 0x3F ;         v53 = (int )((*((char  *)&v96 + v13 + 13 ) >> 6 ) | (4  * (v49 & 0xF )));         v93[v14 + 37 ] = *((_BYTE *)v94 + (((int )v51 >> 4 ) | (16  * v50))) ^ 0xA3 ;         LODWORD (v51) = *((char  *)&v96 + v13 + 14 );         v93[v14 + 38 ] = *((_BYTE *)v94 + v53) ^ 0xA9 ;         v93[v14 + 39 ] = *((_BYTE *)v94 + v52) ^ 0xAC ;         LODWORD (v53) = *((char  *)&v96 + v13 + 15 );         v93[v14 + 40 ] = *((_BYTE *)v94 + ((int )v51 >> 2 )) ^ 0xA6 ;         v54 = v53;         v55 = *((_BYTE *)&v97 + v13) & 0x3F ;         v56 = (int )((*((char  *)&v97 + v13) >> 6 ) | (4  * (v53 & 0xF )));         v93[v14 + 41 ] = *((_BYTE *)v94 + (int )((v54 >> 4 ) | (16  * (v51 & 3 )))) ^ 0xA3 ;         v93[v14 + 42 ] = *((_BYTE *)v94 + v56) ^ 0xA9 ;         v57 = *((_BYTE *)&v97 + v13 + 1 ) & 3 ;         v58 = *((char  *)&v97 + v13 + 1 ) >> 2 ;         v93[v14 + 43 ] = *((_BYTE *)v94 + v55) ^ 0xAC ;         LODWORD (v56) = *((char  *)&v97 + v13 + 2 );         v93[v14 + 44 ] = *((_BYTE *)v94 + v58) ^ 0xA6 ;         LODWORD (v58) = v56;         v59 = *((_BYTE *)&v97 + v13 + 3 ) & 0x3F ;         v60 = (int )((*((char  *)&v97 + v13 + 3 ) >> 6 ) | (4  * (v56 & 0xF )));         v93[v14 + 45 ] = *((_BYTE *)v94 + (((int )v58 >> 4 ) | (16  * v57))) ^ 0xA3 ;         LODWORD (v58) = *((char  *)&v97 + v13 + 4 );         v93[v14 + 46 ] = *((_BYTE *)v94 + v60) ^ 0xA9 ;         v93[v14 + 47 ] = *((_BYTE *)v94 + v59) ^ 0xAC ;         LODWORD (v60) = *((char  *)&v97 + v13 + 5 );         v93[v14 + 48 ] = *((_BYTE *)v94 + ((int )v58 >> 2 )) ^ 0xA6 ;         v61 = v60;         v62 = *((_BYTE *)&v97 + v13 + 6 ) & 0x3F ;         v63 = (int )((*((char  *)&v97 + v13 + 6 ) >> 6 ) | (4  * (v60 & 0xF )));         v93[v14 + 49 ] = *((_BYTE *)v94 + (int )((v61 >> 4 ) | (16  * (v58 & 3 )))) ^ 0xA3 ;         v93[v14 + 50 ] = *((_BYTE *)v94 + v63) ^ 0xA9 ;         v64 = *((_BYTE *)&v97 + v13 + 7 ) & 3 ;         v65 = *((char  *)&v97 + v13 + 7 ) >> 2 ;         v93[v14 + 51 ] = *((_BYTE *)v94 + v62) ^ 0xAC ;         LODWORD (v63) = *((char  *)&v97 + v13 + 8 );         v93[v14 + 52 ] = *((_BYTE *)v94 + v65) ^ 0xA6 ;         LODWORD (v62) = *((char  *)&v97 + v13 + 9 );         v93[v14 + 53 ] = *((_BYTE *)v94 + (((int )v63 >> 4 ) | (16  * v64))) ^ 0xA3 ;         v66 = v62;         v12 = (unsigned  int )(v12 + 1 );         v13 += 48 ;         v14 += 64 ;         LOBYTE (v65) = *((_BYTE *)v94 + (v62 & 0x3F ));         LODWORD (v62) = *((char  *)&v97 + v16 + 10 );         v93[v17 + 55 ] = v65 ^ 0xAC ;         LODWORD (v65) = *((char  *)&v97 + v16 + 11 );         v93[v17 + 54 ] = *((_BYTE *)v94 + (int )((v66 >> 6 ) | (4  * (v63 & 0xF )))) ^ 0xA9 ;         v93[v17 + 56 ] = *((_BYTE *)v94 + ((int )v62 >> 2 )) ^ 0xA6 ;         LODWORD (v63) = (int )v65 >> 4 ;         v67 = *((_BYTE *)&v97 + v16 + 12 ) & 0x3F ;         v68 = (int )((*((char  *)&v97 + v16 + 12 ) >> 6 ) | (4  * (v65 & 0xF )));         v93[v17 + 57 ] = *((_BYTE *)v94 + (int )(v63 | (16  * (v62 & 3 )))) ^ 0xA3 ;         LOBYTE (v62) = *((_BYTE *)v94 + v68);         v69 = *((char  *)&v97 + v16 + 13 ) >> 2 ;         LODWORD (v63) = *((_BYTE *)&v97 + v16 + 13 ) & 3 ;         v93[v17 + 58 ] = v62 ^ 0xA9 ;         v93[v17 + 59 ] = *((_BYTE *)v94 + v67) ^ 0xAC ;         LOBYTE (v67) = *((_BYTE *)v94 + v69);         LODWORD (v69) = *((char  *)&v97 + v16 + 14 );         LODWORD (v62) = v69 & 0xF ;         LODWORD (v63) = ((int )v69 >> 4 ) | (16  * v63);         LODWORD (v69) = *((char  *)&v97 + v16 + 15 );         v93[v17 + 60 ] = v67 ^ 0xA6 ;         LOBYTE (v67) = *((_BYTE *)v94 + (int )v63) ^ 0xA3 ;         LOBYTE (v63) = *((_BYTE *)v94 + (((int )v69 >> 6 ) | (4  * (int )v62))) ^ 0xA9 ;         LOBYTE (v62) = *((_BYTE *)v94 + (v69 & 0x3F )) ^ 0xAC ;         v93[v17 + 61 ] = v67;         v93[v17 + 62 ] = v63;         v93[v17 + 63 ] = v62;       }       while  ( v12 < v15 );       LODWORD (v10) = 16  * v12 + 1 ;     }     v70 = (unsigned  int )(v10 - 1 );     for  ( i = 3  * (int )v70; v70 < v11; v93[v72 + 3 ] = v73 )     {       v72 = 4  * (int )v70;       v70 = (unsigned  int )(v70 + 1 );       v73 = v95. m128i_i8[i + 1 ];       v74 = 16  * (v95. m128i_i8[i] & 3 );       v93[v72] = *((_BYTE *)v94 + (v95. m128i_i8[i] >> 2 )) ^ 0xA6 ;       v75 = v95. m128i_i8[i + 2 ];       i += 3 i64;       v93[v72 + 1 ] = *((_BYTE *)v94 + ((v73 >> 4 ) | v74)) ^ 0xA3 ;       LOBYTE (v74) = *((_BYTE *)v94 + ((v75 >> 6 ) | (4  * (v73 & 0xF )))) ^ 0xA9 ;       LOBYTE (v73) = *((_BYTE *)v94 + (v75 & 0x3F )) ^ 0xAC ;       v93[v72 + 2 ] = v74;     }   }   v76 = v9 - 3  * v11;   if  ( v76 == 1  )   {     v77 = v95. m128i_i8[3  * v11];     v93[4  * v11 + 2 ] = 49 ;     v93[4  * v11 + 3 ] = 52 ;     v78 = *((_BYTE *)v94 + (unsigned  __int8)(16  * (v77 & 3 ))) ^ 0xA3 ;     v93[4  * v11] = *((_BYTE *)v94 + (v77 >> 2 )) ^ 0xA6 ;     v93[4  * v11 + 1 ] = v78;     v93[4  * v11 + 4 ] = 0 ;   }   else  if  ( v76 == 2  )   {     v79 = v95. m128i_i8[3  * v11 + 1 ];     v80 = v95. m128i_i8[3  * v11];     v81 = *((_BYTE *)v94 + 4  * (v79 & 0xFu )) ^ 0xA9 ;     v82 = *((_BYTE *)v94 + ((v79 >> 4 ) | (16  * (v80 & 3 )))) ^ 0xA3 ;     v93[4  * v11] = *((_BYTE *)v94 + (v80 >> 2 )) ^ 0xA6 ;     v93[4  * v11 + 1 ] = v82;     v93[4  * v11 + 2 ] = v81;     v93[4  * v11 + 3 ] = 52 ;     v93[4  * v11 + 4 ] = 0 ;   }   else    {     v93[4  * v11] = 0 ;   }   v83 = "H>oQn6aqLr{DH6odhdm0dMe`MBo?lRglHtGPOdobDlknejmGI|ghDb<4" ;   v84 = v93;   while  ( 1  )   {     v85 = (unsigned  __int8)*v84 < (unsigned  int )*v83;     if  ( *v84 != *v83 )       break ;     if  ( !*v84 )       goto  LABEL_21;     v86 = v84[1 ];     v85 = v86 < (unsigned  int )v83[1 ];     if  ( v86 != v83[1 ] )       break ;     v84 += 2 ;     v83 += 2 ;     if  ( !v86 )     { LABEL_21:       v87 = 0 ;       goto  LABEL_23;     }   }   v87 = v85 ? -1  : 1 ; LABEL_23:   v88 = "No.\r\n" ;   if  ( !v87 )     v88 = "Yes.\r\n" ;   sub_7FF62CBF1D40 (v88);   sub_7FF62CBF6B38 ("pause" );   v89 = v98;   v98 = 0 i64;   v90 = (unsigned  __int64)v92 ^ v89;   if  ( v90 == _security_cookie )     return  0 ;   else      return  sub_7FF62CBF1D40 (v90); } 
进行分析代码,这个代码量有点多欸。通过代码的大概形式我们可以知道这个类似base64加密,其中
这些>>2 , >>4 , >>6 , &3 , &0xF , &0x3F , *16就是<<4,(v*(2^n)==v<<n )这看起来就像是base64从3 * 8,到4 * 6的实现。
举个例子
1 2 3 4 5 6 7 原始字节流:      [11111111 ] [10101010 ] [01010101 ] 位数编号:         00000000  00000001  00000010  (共3 字节) 总共24 位,把它们分为4 组,每组6 位:     第1 组: 位0 -5    → (data [0 ] >> 2 )     第2 组: 位6 -11   → ((data [0 ] & 0x3 ) << 4 ) | (data [1 ] >> 4 )     第3 组: 位12 -17  → ((data [1 ] & 0xf ) << 2 ) | (data [2 ] >> 6 )     第4 组: 位18 -23  → (data [2 ] & 0x3f ) 
‘>>2’  # 取出高6位
 
 
但是这里还有不同的就是这里多了异或操作^0xA6 , ^0xA3 , ^0xA9 , ^0xAc,还有这个还是个变表,真正的表是 v94
 v94[0] = xmmword_7FF62CC0E340;  v94[1] = xmmword_7FF62CC0E350;
大致分析后,知道**”H>oQn6aqLr{DH6odhdm0dMe`MBo?lRglHtGPOdobDlknejmGI|ghDb<4”**这就是加密后的结果,我们对其要进行解密,解密脚本参考了师傅写的。哎,看来我得好好看看base64了……
exp 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 res="H>oQn6aqLr{DH6odhdm0dMe`MBo?lRglHtGPOdobDlknejmGI|ghDb<"  key=[0xA6 ,0xA3 ,0xA9 ,0xAC ] flag=[] for  i in  range (len (res)):    flag+=[ord (res[i]) ^ key[i%len (key)]] print (flag)table=[0xE4 , 0xC4 , 0xE7 , 0xC7 , 0xE6 , 0xC6 , 0xE1 ,  0xC1 , 0xE0 , 0xC0 , 0xE3 , 0xC3 , 0xE2 , 0xC2 , 0xED , 0xCD , 0xEC ,0xCC , 0xEF , 0xCF , 0xEE , 0xCE , 0xE9 , 0xC9 , 0xE8 , 0xC8 , 0xEB , 0xCB , 0xEA , 0xCA , 0xF5 , 0xD5 , 0xF4 , 0xD4 , 0xF7 , 0xD7 , 0xF6 , 0xD6 , 0xF1 , 0xD1 , 0xF0 , 0xD0 , 0xF3 , 0xD3 , 0xF2 , 0xD2 , 0xFD , 0xDD , 0xFC , 0xDC , 0xFF , 0xDF , 0x95 , 0x9C , 0x9D , 0x92 , 0x93 , 0x90 , 0x91 , 0x96 , 0x97 , 0x94 , 0x8A , 0x8E ]result=""  for  i in  flag:    result+=('{:0>6}' .format (bin (table.index(i)).replace("0b" ,"" ))) for  i in  range (0 ,len (result),8 ):    print (chr (int (result[i:i+8 ],2 )),end="" ) 
5.NewStar-SMc_math 这个是ELF64位的程序,使用IDA打开,进行分析
我们看到了mprotect()函数 这就很明显是一个SMC了,这个函数是对encrypt进行修改权限,要求输入的flag长度为28,并且这个encrypt就是最后的加密验证函数,我们可以使用ELF文件动调来看,先下断点到if ( (unsigned int)((__int64 (__fastcall *)(char *))encrypt)(s) )这一行,此时是解密完成的状态。这里主要介绍第二种方法,使用解密脚本。我们也可以使用IDAPython 进行解密,知道解密方法是和0x3E进行异或,并且知道了解密的长度,现在要找到起始地址就ok了。
IDAPython脚本如下
1 2 3 4 5 6 7 8 9 start = 0x11E9    length = 0x3D6    for  i in  range (length):    b = ida_bytes.get_byte(start + i)     ida_bytes.patch_byte(start + i, b ^ 0x3E ) print ("Decrypt done." )
在IDA中打开 File->Script command…->将IDA改成python
	
运行后,我们发现这就发生了很大的变化,原来大块的数据都没有了。我们从函数头一直选中到函数尾也就是retn的位置,然后按C强制进行分析。
再在函数头先U解除定义一下,再P重新定义,就能F5进行反编译了
方程式,使用z3求解
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 from  z3 import  *v = [BitVec(f'v{i} ' , 64 ) for  i in  range (7 )] s = Solver() s.add(5  * (v[1 ] + v[0 ]) + 4  * v[2 ] + 6  * v[3 ] + v[4 ] + 9  * v[6 ] + 2  * v[5 ] == 0xD5CC7D4FF ) s.add(4  * v[6 ] + 3  * v[3 ] + 6  * v[2 ] + 10  * v[1 ] + 9  * v[0 ] + 9  * v[5 ] + 3  * v[4 ] == 0x102335844B ) s.add(9  * v[4 ] + 4  * (v[3 ] + v[2 ]) + 5  * v[1 ] + 4  * v[0 ] + 3  * v[6 ] + 10  * v[5 ] == 0xD55AEABB9 ) s.add(9  * v[1 ] + 5  * v[0 ] + 9  * v[6 ] + 2  * (v[2 ] + 2  * v[3 ] + 5  * v[4 ] + v[5 ]) == 0xF89F6B7FA ) s.add(5  * v[4 ] + 9  * v[3 ] + 7  * v[0 ] + 2  * v[1 ] + v[2 ] + 3  * v[6 ] + 9  * v[5 ] == 0xD5230B80B ) s.add(8  * v[6 ] + 6  * v[3 ] + 10  * v[2 ] + 5  * v[1 ] + 6  * v[0 ] + 3  * v[5 ] + 9  * v[4 ] == 0x11E28ED873 ) s.add(v[0 ] + 4  * (v[2 ] + v[1 ] + 2  * v[3 ]) + 9  * v[4 ] + v[5 ] + 3  * v[6 ] == 0xB353C03E1 ) if  s.check() == sat:    model = s.model()     result = [model[v[i]].as_long() for  i in  range (7 )]     print ("解为:" )     for  i, val in  enumerate (result):         print (f"v{i}  = {val}  (0x{val:016X} )" )          flag = b'' .join(val.to_bytes(8 , 'little' ) for  val in  result)          try :         print ("\n拼接后的字符串为:" )         print (flag.decode('ascii' , errors='ignore' ))       except  Exception as  e:         print ("转换为字符串失败:" , e) else :    print ("无解" )