吾爱破解安全大赛cm6分析
程序通过创建新的线程来完成验证工作:
1 2 3 4 5 6 |
if ( (unsigned __int16)a3 == 1001 ) { ThreadId = 0; CreateThread(0, 0, StartAddress, lpParameter, 0, (LPDWORD)&ThreadId); return 0; } |
其中serial是通过变形的base64算法加密的,在base64算法4合3的过程中对每字节分别进行异或:
1 2 3 4 5 |
v8 = (4 * (_BYTE)v + ((BYTE1(v) >> 4) & 3)) ^ 0x4C; v9 = (16 * BYTE1(v) + ((BYTE2(v) >> 2) & 0xF)) ^ 0x43; v17[v18 + 2] = (BYTE3(v) + (BYTE2(v) << 6)) ^ 0x47; v17[v18] = v8; v17[v18 + 1] = v9; |
真正的serial将由4部分组成,竞赛时提供的key 解密后的内容:
1 2 3 4 5 6 |
{ "name": "360", "key1": "--... .---- ----- ..... ----- ....- ..--- .---- .---- ---.. ----- ....- ...-- ", "key2": "492357816", "key3": "11111111111111119999" } |
第一部分name应与username相同。
接下来的3个key分别对应3个验证函数,只有都验证通过才算成功。
Key1是摩斯电码,解密后的值为7105042118043。以下是验证函数:
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 |
char __cdecl sub_404B60(int a1, const char *a2) { int v2; // ecx@3 unsigned int key1_0_len; // esi@5 unsigned int v4; // ebx@6 unsigned int key1_v0; // edi@8 unsigned int key1_v1; // esi@8 unsigned __int64 v7; // rax@12 int v8; // esi@13 char key1_0_10; // [sp+18h] [bp-28h]@8 int v11; // [sp+19h] [bp-27h]@8 int v12; // [sp+1Dh] [bp-23h]@8 char v13; // [sp+21h] [bp-1Fh]@8 char key1_10_?; // [sp+24h] [bp-1Ch]@8 int v15; // [sp+25h] [bp-1Bh]@8 int v16; // [sp+29h] [bp-17h]@8 char v17; // [sp+2Dh] [bp-13h]@8 CHAR v18; // [sp+30h] [bp-10h]@13 int v19; // [sp+31h] [bp-Fh]@13 int v20; // [sp+35h] [bp-Bh]@13 __int16 v21; // [sp+39h] [bp-7h]@13 char v22; // [sp+3Bh] [bp-5h]@13 if ( a2 ) { if ( strlen(a2) ) { v2 = MorseDecode(a2); // 摩斯电码解密 if ( v2 > 0 ) { if ( (unsigned int)(v0 - '5') > 2 || (key1_0_len = v0 - '0', key1_0_len >= v2 - 1)// 第一个数字为key1_0的长度 || (v4 = v2 - key1_0_len - 1, v4 < 5) || v4 > 7 ) { free(0); } else { key1_0_10 = 0; v11 = 0; v12 = 0; v13 = 0; cutstring((int)&key1_0_10, 10, 1, key1_0_len);// 取前key1_0_Len位 key1_10_? = 0; v15 = 0; v16 = 0; v17 = 0; cutstring((int)&key1_10_?, 10, key1_0_len + 1, v4);// 取之后 free(0); key1_v0 = atoi(&key1_0_10); key1_v1 = atoi(&key1_10_?); if ( key1_v0 >= 10000 ) { if ( IsPrime(key1_v0) ) // 判断是否为素数 { if ( key1_v1 >= 10000 ) { if ( IsPrime(key1_v1) ) { v7 = key1_v0 * (unsigned __int64)key1_v1;// 相乘 if ( v7 - 10000000000i64 <= 89999999999i64 ) { v18 = 0; v19 = 0; v20 = 0; v21 = 0; v22 = 0; wsprintfA(&v18, "%I64d", v7);// 相乘后的字符串 v8 = 0; while ( haveChar(&v18, v8 + '0') )// 判断是否包含0-9所有数字 { if ( (unsigned int)++v8 >= 0xA ) return 1; } } } } } } } } } } return 0; } |
可以得出key1必须满足的条件有:
①.第一个数字为key1_0的长度,key1_0>=10000且为素数
②.key1_1>=10000且为素数
③.Key1_0*key1_1在10000000000-89999999999之间,且包含0-9所有数字
写一个脚本就能获取到很多这样的数字:
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 |
import math def isPrime(n): if n <= 1: return False for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: return False return True t="0123456789" def check(x): s=str(x) for a in t: if s.find(a)==-1: return False return True p=[] for a in range(10000,200000): if isPrime(a): p.append(a) print len(p) max=40 i=0 for a in p: for b in p: c=a*b if c>10000000000 and c<89999999999: if check(c): i=i+1 print a,b if i==max: exit(0) |
Key2的验证就有些复杂了,以下是验证部分:
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 |
char __cdecl sub_407DD0(char *username, const char *key2) { unsigned int v2; // esi@3 unsigned int v3; // edi@3 char v4; // dl@4 int v5; // eax@4 unsigned __int64 v6; // kr08_8@10 unsigned int v7; // eax@10 char v8; // bl@11 _BYTE *v9; // esi@11 char result; // al@14 unsigned int v11; // [sp+Ch] [bp-Ch]@3 unsigned int v12; // [sp+10h] [bp-8h]@3 unsigned int v13; // [sp+14h] [bp-4h]@3 if ( key2 && strlen(key2) == 9 ) { v2 = 1; v13 = 0; v3 = 0; v12 = 0; v11 = 0; do { v4 = key2[v3]; v5 = -1; if ( (unsigned __int8)(v4 - '0') > 9u ) { if ( (unsigned __int8)(v4 - 'A') > 5u ) { if ( (unsigned __int8)(v4 - 'a') <= 5u ) v5 = v4 - 'W'; } else { v5 = v4 - '7'; } } else { v5 = v4 - '0'; } v6 = mulx64(v5, __PAIR__(v2, v11)) + __PAIR__(v12, v13); v12 = HIDWORD(v6); v13 = v6; ++v3; v7 = __PAIR__(v2, v11) >> 4; v2 >>= 4; v11 = v7; } while ( v3 < 9 ); v8 = 0; v9 = alloc(0x20118); *v9 = 1; set_vm_handler((int)(v9 + 4)); *v9 = 0; malloc(v9 + 280, 0, 0x20000); malloc(v9 + 112, 0, 168); *((_DWORD *)v9 + 64) = 0; *((_DWORD *)v9 + 65) = 0; *((_DWORD *)v9 + 66) = 1024; *((_DWORD *)v9 + 67) = 0; *((_DWORD *)v9 + 60) = 10; *((_DWORD *)v9 + 61) = 0; set_vm_data(v6, SHIDWORD(v6)); vm_dispatch(v9); if ( *((_DWORD *)v9 + 28) == 1 && !*((_DWORD *)v9 + 29) ) v8 = 1; free0(v9); result = v8; } else { result = 0; } return result; } |
Key2的验证涉及到虚拟机,大致由25个处理程序组成:
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 |
void __thiscall set_vm_handler(int this) { *(_DWORD *)this = nullsub_1; *(_DWORD *)(this + 4) = v_ZeroAddr; *(_DWORD *)(this + 8) = sub_4084C0; *(_DWORD *)(this + 12) = &loc_408540; *(_DWORD *)(this + 16) = &loc_408610; *(_DWORD *)(this + 20) = &loc_408750; *(_DWORD *)(this + 24) = sub_4089D0; *(_DWORD *)(this + 28) = &loc_408B10; *(_DWORD *)(this + 36) = &loc_408C80; *(_DWORD *)(this + 40) = &loc_408DB0; *(_DWORD *)(this + 44) = &loc_408EE0; *(_DWORD *)(this + 56) = &loc_4091A0; *(_DWORD *)(this + 32) = &loc_408890; *(_DWORD *)(this + 48) = &loc_409020; *(_DWORD *)(this + 52) = &loc_4090E0; *(_DWORD *)(this + 60) = &loc_409260; *(_DWORD *)(this + 64) = &v_SetValue; *(_DWORD *)(this + 68) = &loc_409460; *(_DWORD *)(this + 72) = &loc_409500; *(_DWORD *)(this + 76) = v_Call; *(_DWORD *)(this + 80) = &loc_409640; *(_DWORD *)(this + 84) = sub_4096C0; *(_DWORD *)(this + 88) = sub_409740; *(_DWORD *)(this + 92) = sub_4098B0; *(_DWORD *)(this + 96) = sub_409A20; *(_DWORD *)(this + 100) = &loc_409B10; } |
这里我只大致识别了几个,全部分析太耗时间了(实在是扛不住)。另外还有vmdata的结构组成,在外部由64字节组成,大致包含影响的内存位置(可以理解为是vm_reg)、位数、handler序号等,具体的分析还未完成。
这里因为虚拟机执行的步数不是很多,在提供可用key的情况下一共运行0x185次,所以这里就动态调试观察一些数据的变化来大致判断真正的验证过程,大致的执行流程应该为:
①.判断key中是否含有0-9的所有数字
②.将key数字排列成3×3格式,判断横竖斜直线上的数字和是否均为0xF(15)
最后得出的验证过程是判断key的3×3格式是否是一个3阶幻方(横竖斜数字和相等)。
Key3的验证过程涉及到了luajit,这验证函数404D50中首先将luajit脚本的bytecode加载到堆栈,然后调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
bool __cdecl sub_404D50(const char *username, const char *key) { ...//加载luajit code到堆栈 if ( username && strlen(username) && key && strlen(key) ) { v2 = luaL_newstate(); lua_openlibs(v2); luaL_Loadbuffer(v2, (int)&v5, 2410, "nJmqfKMor2g8AYmoZYL6Cmo9hcfkHhmM"); sub_40F480(v2, 0); lua_getglobal(v2, -10002, "nXqKFV8gj6HzUElOp6LPVXxHCXQ0Nkpi0Q0nb1KtJk8cnVGkAY"); lua_pushstring(v2, username); lua_pushstring(v2, key); lua_call(v2, 2, 1, 0); v3 = lua_tonumber(v2, -1) == 1; lua_pop(v2, -2); lua_close(v2); result = v3; } else { result = 0; } return result; } |
这里要了解验证过程需要提取luajit数据进行反编译。以下是反编译后的lua代码:
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 |
FCNbwSwAXZaK6Ar={} function ToBin (var_0_INPUT_VAR_0_,var_0_INPUT_VAR_1_) var_0_2 = {} var_0_3 = 1 --var_0_3 NUMBER-NUMBER var_0_4 = 32 --var_0_4 NUMBER-NUMBER var_0_5 = 1 --var_0_5 NUMBER-NUMBER for var_0_6 = var_0_3,var_0_4,var_0_5 do --location 0005, loop ends at 0019-1 if FCNbwSwAXZaK6Ar[var_0_6] <= var_0_INPUT_VAR_1_ then --jump to 0016 (if previous if statement is false) --0016 JMP-JMP var_0_7 = 1 --var_0_7 NUMBER-NUMBER var_0_2[var_0_6] = var_0_7 var_0_INPUT_VAR_1_ = var_0_INPUT_VAR_1_ - FCNbwSwAXZaK6Ar[var_0_6] --var_0_1 NUMBER-NUMBER else --location 0016--0016 LOCATION-LOCATION_ var_0_7 = 0 --var_0_7 NUMBER-NUMBER var_0_2[var_0_6] = var_0_7 end end --location 0018, loops back to 0006-1 return var_0_2 end function BinToValue (var_1_INPUT_VAR_0_,var_1_INPUT_VAR_1_) var_1_2 = 0 --var_1_2 NUMBER-NUMBER var_1_3 = 1 --var_1_3 NUMBER-NUMBER var_1_4 = 32 --var_1_4 NUMBER-NUMBER var_1_5 = 1 --var_1_5 NUMBER-NUMBER for var_1_6 = var_1_3,var_1_4,var_1_5 do --location 0005, loop ends at 0014-1 if var_1_INPUT_VAR_1_[var_1_6] == 1 then --jump to 0013 (if previous if statement is false) --0013 JMP-JMP var_1_7 = 32 - var_1_6 --var_1_7 NUMBER-NUMBER var_1_8 = 2 --var_1_8 NUMBER-NUMBER var_1_7 = var_1_8 ^ var_1_7 --var_1_7 NUMBER-NUMBER var_1_2 = var_1_2 + var_1_7 --var_1_2 NUMBER-NUMBER end end --location 0013, loops back to 0006-1 return var_1_2 end function BinToXor (var_2_INPUT_VAR_0_,var_2_INPUT_VAR_1_,var_2_INPUT_VAR_2_) var_2_4 = var_2_INPUT_VAR_0_ var_2_5 = var_2_INPUT_VAR_1_ var_2_3 = ToBin(var_2_4, var_2_5) var_2_5 = var_2_INPUT_VAR_0_ var_2_6 = var_2_INPUT_VAR_2_ var_2_4 = ToBin(var_2_5, var_2_6) var_2_5 = {} var_2_6 = 1 --var_2_6 NUMBER-NUMBER var_2_7 = 32 --var_2_7 NUMBER-NUMBER var_2_8 = 1 --var_2_8 NUMBER-NUMBER for var_2_9 = var_2_6,var_2_7,var_2_8 do --location 0013, loop ends at 0024-1 if var_2_3[var_2_9] == var_2_3[var_2_9] then --jump to 0021 (if previous if statement is false) --0021 JMP-JMP var_2_10 = 0 --var_2_10 NUMBER-NUMBER var_2_5[var_2_9] = var_2_10 else --location 0021--0021 LOCATION-LOCATION_ var_2_10 = 1 --var_2_10 NUMBER-NUMBER var_2_5[var_2_9] = var_2_10 end end --location 0023, loops back to 0014-1 var_2_7 = var_2_INPUT_VAR_0_ var_2_8 = var_2_5 return BinToNot(var_2_7, var_2_8) end function BinToNor (var_3_INPUT_VAR_0_,var_3_INPUT_VAR_1_,var_3_INPUT_VAR_2_) var_3_4 = var_3_INPUT_VAR_0_ var_3_5 = var_3_INPUT_VAR_1_ var_3_3 = ToBin(var_3_4, var_3_5) var_3_5 = var_3_INPUT_VAR_0_ var_3_6 = var_3_INPUT_VAR_2_ var_3_4 = ToBin(var_3_5, var_3_6) var_3_5 = {} var_3_6 = 1 --var_3_6 NUMBER-NUMBER var_3_7 = 32 --var_3_7 NUMBER-NUMBER var_3_8 = 1 --var_3_8 NUMBER-NUMBER for var_3_9 = var_3_6,var_3_7,var_3_8 do --location 0013, loop ends at 0026-1 if var_3_3[var_3_9] == 0 and var_3_4[var_3_9] == 0 then --jump to 0023 (if previous if statement is false) --0023 JMP-JMP --jump to 0023 (if previous if statement is false) --0023 JMP-JMP var_3_10 = 1 --var_3_10 NUMBER-NUMBER var_3_5[var_3_9] = var_3_10 else --location 0023--0023 LOCATION-LOCATION_ var_3_10 = 0 --var_3_10 NUMBER-NUMBER var_3_5[var_3_9] = var_3_10 end end --location 0025, loops back to 0014-1 var_3_7 = var_3_INPUT_VAR_0_ var_3_8 = var_3_5 return BinToNot(var_3_7, var_3_8) end function Fun4 (var_4_INPUT_VAR_0_,var_4_INPUT_VAR_1_,var_4_INPUT_VAR_2_) var_4_4 = var_4_INPUT_VAR_0_ var_4_5 = var_4_INPUT_VAR_1_ var_4_3 = ToBin(var_4_4, var_4_5) var_4_5 = var_4_INPUT_VAR_0_ var_4_6 = var_4_INPUT_VAR_2_ var_4_4 = ToBin(var_4_5, var_4_6) var_4_5 = {} var_4_6 = 1 --var_4_6 NUMBER-NUMBER var_4_7 = 32 --var_4_7 NUMBER-NUMBER var_4_8 = 1 --var_4_8 NUMBER-NUMBER for var_4_9 = var_4_6,var_4_7,var_4_8 do --location 0013, loop ends at 0026-1 if var_4_3[var_4_9] ~= 0 and var_4_4[var_4_9] == 0 then --jump to 0020 (if previous if statement is false) --0020 JMP-JMP --location 0023--0023 LOCATION-LOCATION_ var_4_10 = 0 --var_4_10 NUMBER-NUMBER var_4_5[var_4_9] = var_4_10 else --location 0020--0020 LOCATION-LOCATION_ var_4_10 = 1 --var_4_10 NUMBER-NUMBER var_4_5[var_4_9] = var_4_10 end end --location 0025, loops back to 0014-1 var_4_7 = var_4_INPUT_VAR_0_ var_4_8 = var_4_5 return BinToNot(var_4_7, var_4_8) end function Fun5 (var_5_INPUT_VAR_0_,var_5_INPUT_VAR_1_) var_5_3 = var_5_INPUT_VAR_0_ var_5_4 = var_5_INPUT_VAR_1_ var_5_2 = ToBin(var_5_3, var_5_4) var_5_3 = {} var_5_4 = 1 --var_5_4 NUMBER-NUMBER var_5_5 = 32 --var_5_5 NUMBER-NUMBER var_5_6 = 1 --var_5_6 NUMBER-NUMBER for var_5_7 = var_5_4,var_5_5,var_5_6 do --location 0009, loop ends at 0019-1 if var_5_2[var_5_7] == 0 then --jump to 0016 (if previous if statement is false) --0016 JMP-JMP var_5_8 = 0 --var_5_8 NUMBER-NUMBER var_5_3[var_5_7] = var_5_8 else --location 0016--0016 LOCATION-LOCATION_ var_5_8 = 1 --var_5_8 NUMBER-NUMBER var_5_3[var_5_7] = var_5_8 end end --location 0018, loops back to 0010-1 var_5_5 = var_5_INPUT_VAR_0_ var_5_6 = var_5_3 return BinToNot(var_5_5, var_5_6) end function Fun6 (var_6_INPUT_VAR_0_,var_6_INPUT_VAR_1_,var_6_INPUT_VAR_2_) var_6_4 = var_6_INPUT_VAR_0_ var_6_5 = var_6_INPUT_VAR_1_ var_6_3 = ToBin(var_6_4, var_6_5) var_6_5 = var_6_INPUT_VAR_0_ var_6_6 = 0 --var_6_6 NUMBER-NUMBER var_6_4 = ToBin(var_6_5, var_6_6) var_6_5 = 32 --var_6_5 NUMBER-NUMBER if var_6_INPUT_VAR_2_ < var_6_5 then --jump to 0031 (if previous if statement is false) --0031 JMP-JMP var_6_5 = 0 --var_6_5 NUMBER-NUMBER if var_6_5 < var_6_INPUT_VAR_2_ then --jump to 0031 (if previous if statement is false) --0031 JMP-JMP var_6_5 = 1 --var_6_5 NUMBER-NUMBER var_6_6 = var_6_INPUT_VAR_2_ var_6_7 = 1 --var_6_7 NUMBER-NUMBER for var_6_8 = var_6_5,var_6_6,var_6_7 do --location 0018, loop ends at 0030-1 var_6_9 = 31 --var_6_9 NUMBER-NUMBER var_6_10 = 1 --var_6_10 NUMBER-NUMBER var_6_11 = -1 --var_6_11 NUMBER-NUMBER for var_6_12 = var_6_9,var_6_10,var_6_11 do --location 0022, loop ends at 0027-1 var_6_13 = var_6_12 + 1 --var_6_13 NUMBER-NUMBER var_6_3[var_6_13] = var_6_3[var_6_12] end --location 0026, loops back to 0023-1 var_6_9 = 0 --var_6_9 NUMBER-NUMBER var_6_3[1] = var_6_9 end --location 0029, loops back to 0019-1 var_6_4 = var_6_3 end end var_6_6 = var_6_INPUT_VAR_0_ var_6_7 = var_6_4 return BinToNot(var_6_6, var_6_7) end function Fun7 (var_7_INPUT_VAR_0_,var_7_INPUT_VAR_1_,var_7_INPUT_VAR_2_) var_7_4 = var_7_INPUT_VAR_0_ var_7_5 = var_7_INPUT_VAR_1_ var_7_3 = ToBin(var_7_4, var_7_5) var_7_5 = var_7_INPUT_VAR_0_ var_7_6 = 0 --var_7_6 NUMBER-NUMBER var_7_4 = ToBin(var_7_5, var_7_6) var_7_5 = 32 --var_7_5 NUMBER-NUMBER if var_7_INPUT_VAR_2_ < var_7_5 then --jump to 0031 (if previous if statement is false) --0031 JMP-JMP var_7_5 = 0 --var_7_5 NUMBER-NUMBER if var_7_5 < var_7_INPUT_VAR_2_ then --jump to 0031 (if previous if statement is false) --0031 JMP-JMP var_7_5 = 1 --var_7_5 NUMBER-NUMBER var_7_6 = var_7_INPUT_VAR_2_ var_7_7 = 1 --var_7_7 NUMBER-NUMBER for var_7_8 = var_7_5,var_7_6,var_7_7 do --location 0018, loop ends at 0030-1 var_7_9 = 1 --var_7_9 NUMBER-NUMBER var_7_10 = 31 --var_7_10 NUMBER-NUMBER var_7_11 = 1 --var_7_11 NUMBER-NUMBER for var_7_12 = var_7_9,var_7_10,var_7_11 do --location 0022, loop ends at 0027-1 var_7_13 = var_7_12 + 1 --var_7_13 NUMBER-NUMBER var_7_3[var_7_12] = var_7_3[var_7_13] end --location 0026, loops back to 0023-1 var_7_9 = 0 --var_7_9 NUMBER-NUMBER var_7_3[32] = var_7_9 end --location 0029, loops back to 0019-1 var_7_4 = var_7_3 end end var_7_6 = var_7_INPUT_VAR_0_ var_7_7 = var_7_4 return BinToNot(var_7_6, var_7_7) end function Fun8 (var_8_INPUT_VAR_0_,var_8_INPUT_VAR_1_) var_8_3 = MyClass var_8_4 = var_8_INPUT_VAR_0_ var_8_2 = Fun5(var_8_3, var_8_4) var_8_r1 = var_8_2 var_8_3 = MyClass var_8_4 = var_8_INPUT_VAR_1_ var_8_2 = Fun5(var_8_3, var_8_4) var_8_r2 = var_8_2 var_8_3 = MyClass var_8_2 = BinToNor(var_8_3, var_8_r1, var_8_r2) var_8_ret = var_8_2 return var_8_ret end function Fun9 (var_9_INPUT_VAR_0_,var_9_INPUT_VAR_1_) var_9_5 = var_9_INPUT_VAR_0_ var_9_6 = var_9_INPUT_VAR_0_ var_9_4 = Fun8(var_9_5, var_9_6) var_9_6 = var_9_INPUT_VAR_1_ var_9_7 = var_9_INPUT_VAR_1_ var_9_3 = Fun8(var_9_4, Fun8(var_9_6, var_9_7) ) var_9_5 = var_9_INPUT_VAR_0_ var_9_6 = var_9_INPUT_VAR_1_ var_9_2 = Fun8(var_9_3, Fun8(var_9_5, var_9_6) ) var_9_ret = var_9_2 print(var_9_ret) return var_9_ret end function Fun10 (var_10_INPUT_VAR_0_) var_10_1 = 0 --var_10_1 NUMBER-NUMBER var_10_ret = var_10_1 var_10_1 = 1 --var_10_1 NUMBER-NUMBER var_10_3 = var_10_INPUT_VAR_0_ var_10_2 = string.len(var_10_3) var_10_3 = 1 --var_10_3 NUMBER-NUMBER for var_10_4 = var_10_1,var_10_2,var_10_3 do --location 0009, loop ends at 0022-1 var_10_8 = var_10_INPUT_VAR_0_ var_10_9 = var_10_4 var_10_7 = string.byte(var_10_8, var_10_9) var_10_8 = 66 --var_10_8 NUMBER-NUMBER var_10_6 = Fun9(var_10_7, var_10_8) var_10_5 = var_10_ret + var_10_6 --var_10_5 NUMBER-NUMBER var_10_ret = var_10_5 end --location 0021, loops back to 0010-1 return var_10_ret end function Fun11 (var_11_INPUT_VAR_0_) var_11_1 = 0 --var_11_1 NUMBER-NUMBER var_11_ret = var_11_1 var_11_1 = 1 --var_11_1 NUMBER-NUMBER var_11_3 = var_11_INPUT_VAR_0_ var_11_2 = string.len(var_11_3) var_11_3 = 1 --var_11_3 NUMBER-NUMBER for var_11_4 = var_11_1,var_11_2,var_11_3 do --location 0009, loop ends at 0022-1 var_11_8 = var_11_INPUT_VAR_0_ var_11_9 = var_11_4 var_11_7 = string.byte(var_11_8, var_11_9) var_11_8 = 99 --var_11_8 NUMBER-NUMBER var_11_6 = Fun9(var_11_7, var_11_8) var_11_5 = var_11_ret + var_11_6 --var_11_5 NUMBER-NUMBER var_11_ret = var_11_5 end --location 0021, loops back to 0010-1 return var_11_ret end function Verity (var_12_INPUT_VAR_0_,var_12_INPUT_VAR_1_) var_12_3 = var_12_INPUT_VAR_0_ var_12_2 = Fun10(var_12_3) print(var_12_2) print("xxxx") var_12_4 = var_12_INPUT_VAR_1_ var_12_3 = Fun11(var_12_4) print(var_12_3) var_12_2 = var_12_2 + var_12_3 --var_12_2 NUMBER-NUMBER if var_12_2 == 2015 then --jump to 0013 (if previous if statement is false) --0013 JMP-JMP var_12_2 = 1 --var_12_2 NUMBER-NUMBER return var_12_2 else --location 0013--0013 LOCATION-LOCATION_ var_12_2 = 0 --var_12_2 NUMBER-NUMBER return var_12_2 end return end function someFunc13() var_13_0 = {} --to find out the contents of this table look inside the lua file var_13_1 = {} MyClass = var_13_0 var_13_0 = 1 --var_13_0 NUMBER-NUMBER var_13_1 = 32 --var_13_1 NUMBER-NUMBER var_13_2 = 1 --var_13_2 NUMBER-NUMBER for var_13_3 = var_13_0,var_13_1,var_13_2 do --location 0008, loop ends at 0016-1 var_13_5 = 32 - var_13_3 --var_13_5 NUMBER-NUMBER var_13_6 = 2 --var_13_6 NUMBER-NUMBER var_13_5 = var_13_6^var_13_5 --var_13_5 NUMBER-NUMBER FCNbwSwAXZaK6Ar[var_13_3] = var_13_5 end --location 0015, loops back to 0009-1 return end someFunc13() print("Hello") print("result:",Verity("360","11111111111111119999")) |
这里用到一个github上的luajit反编译工具,不过这个工具有很多bug,反编译后需要仔细修复(对某些变量不能识别,以及对ISNEN的错误翻译)。
验证算法中涉及到对每个字符如下的逻辑运算:
nor(nor(nor(a,a),nor(b,b)),nor(a,b)) = xor(a,b)
可得出最终的验证方式是将用户名的每一位异或66和key中每一位异或99相加,如果最后值为2015则验证成功。
最终的KeyGen:
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 |
import base64 table="V3FKpjJyw6XgsZeSQkGl7vofHWMqimurD5A+PIURcxCb1TOzE9d8L4B0Y2ntN/ah" base64Table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def decode(p): s="" for a in p: for i in range(0,len(table)): if a=="=": s+="=" break if a==table[i]: s+=base64Table[i] break ss=base64.b64decode(s) j=0 x=[0x4C,0x43,0x47] out="" for k in range(0,len(ss)): out+=chr(ord(ss[k])^x[j]) j+=1 if(j==3): j=0 return out def encode(s): ss="" j=0 x=[0x4C,0x43,0x47] out="" for k in range(0,len(s)): ss+=chr(ord(s[k])^x[j]) j+=1 if(j==3): j=0 out="" ss=base64.b64encode(ss) for a in ss: for i in range(0,len(base64Table)): if a=="=": out+="=" break if a==base64Table[i]: out+=table[i] break return out def GetKey2(s): namesum=0 for a in s: namesum+=(ord(a)^66) r=2015-namesum b=r/82 c=r%82 ret="1"*b if c!=0: ret+=chr(c^99) return ret name=raw_input("Enter username:") key='{"name": "'+name key+='","key1": "--... .---- ----- ..... ----- ....- ..--- .---- .---- ---.. ----- ....- ...-- ","key2": "492357816","key3": "' key+=GetKey2(name) key+='"}' pwd=encode(key) print "key:" print pwd #print "" #print "key:" #print decode(pwd) |
Reference:
source: