-
-
CrackMe.cycle注册机
-
2022-5-13 12:01 4398
-
文章已于2022-05-12凌晨首发于我的个人博客:CrackMe之cycle注册机
本来这边想同步发出来的,结果发现新用户不能发帖::>_<::
刚刚接触逆向,这是我做的第三个crackme和第二次编写注册机,第一个是crackhead
如果有不足之处,希望各位前辈指出
references:
下断点的方法和crackhead一样,通过搜索所有引入的函数,从中找出可以用来下断点的函数
此处为GetDlgItemTextA函数
我们在该函数出现的所有位置下断点,然后在cycle程序的输入框中随便输点东西,触发断点即可
断点触发之后,我们就不需要这些断点了,在函数调用的下一行下断点即可
下面我们开始捋代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 004010A6 |. 6A 11 PUSH 11 ; / Count = 11 ( 17. ) 004010A8 |. 68 71214000 PUSH cycle. 00402171 ; | Buffer = cycle. 00402171 004010AD |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 ( 1001. ) 004010B2 |. FF75 08 PUSH DWORD PTR SS:[EBP + 8 ] ; |hWnd 004010B5 |. E8 0F020000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 004010BA |. 0BC0 OR EAX,EAX 004010BC |. 74 61 JE SHORT cycle. 0040111F 004010BE |. 6A 11 PUSH 11 ; / Count = 11 ( 17. ) 004010C0 |. 68 60214000 PUSH cycle. 00402160 ; | Buffer = cycle. 00402160 004010C5 |. 68 E8030000 PUSH 3E8 ; |ControlID = 3E8 ( 1000. ) 004010CA |. FF75 08 PUSH DWORD PTR SS:[EBP + 8 ] ; |hWnd 004010CD |. E8 F7010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA 004010D2 |. 0BC0 OR EAX,EAX 004010D4 |. 74 49 JE SHORT cycle. 0040111F |
GetDlgItemTextA
函数被调用了两次,返回值存储在EAX中,为获取到的字符串的长度,第三个参数为传出参数,用于存储获取到的字符串,两次调用中分别传入了指针0x00402171
和00402160
根据调试结果,第一次调用获取到的是Serial,第二次调用获取到的是Name
只要我们输入不为空(即EAX值不为0),那么上面代码中的JE SHORT cycle.0040111F
就不会执行
接着往下看
1 2 3 4 5 6 7 | 004010D6 |. B9 10000000 MOV ECX, 10 004010DB |. 2BC8 SUB ECX,EAX 004010DD |. BE 60214000 MOV ESI,cycle. 00402160 ; ASCII "1231231231231231" 004010E2 |. 8BFE MOV EDI,ESI 004010E4 |. 03F8 ADD EDI,EAX 004010E6 |. FC CLD 004010E7 |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[> |
这段代码对用户名进行了拷贝,CLD复位DDF标志寄存器,因此在执行REP MOVS时ESI和EDI每次拷贝完成后都会增加,由于此处为REP MOVS BYTE
,因此每次拷贝一个字节,ESI和EDI的增加梯度也为1字节
拷贝的次数由ECX-EAX的结果决定,即(16-用户名长度),其实就是将用户名的长度扩展到了16位
1 2 3 4 5 6 7 8 9 10 11 12 13 | 004010E9 |. 33C9 XOR ECX,ECX 004010EB |. BE 71214000 MOV ESI,cycle. 00402171 ; ASCII "1234567890000000" 004010F0 |> 41 / INC ECX 004010F1 |. AC |LODS BYTE PTR DS:[ESI] 004010F2 |. 0AC0 |OR AL,AL 004010F4 |. 74 0A |JE SHORT cycle. 00401100 004010F6 |. 3C 7E | CMP AL, 7E 004010F8 |. 7F 06 |JG SHORT cycle. 00401100 004010FA |. 3C 30 | CMP AL, 30 004010FC |. 72 02 |JB SHORT cycle. 00401100 004010FE |.^EB F0 \JMP SHORT cycle. 004010F0 00401100 |> 83F9 11 CMP ECX, 11 00401103 |. 75 1A JNZ SHORT cycle. 0040111F |
清空ECX,然后将ESI指向Serial
下面开始循环,其中有一条指令大家可能没见过
LODS指令,Load String Operand
比如此处的LODS BYTE PTR DS:[ESI]
,会将ESI所指向的内容加载到EAX寄存器中,至于是加载到EAX、AX、AL由指令控制,此处由于我们是LODS BYTE
,因此内存中的内容会被加载到AL中
因此上面代码中的循环就干了一件事,就是检测Serial中的字符是否位于0x30
和0x7E
之间,也就是ASCII码表中的48~126之间
最后判断了一下字符串长度是否为16,因为ECX会多加一次,也就是说比实际长度多1,根据CMP ECX, 11
,我们可以确定Serial的长度必须为16位,因为如果无法满足这个条件,程序就会直接跳到RET指令,那就没得玩儿了,你可以自己试一下,如果Serial的长度不是16位,当你点击Check按钮的时候,程序是没有任何反应的
接着看
1 2 3 4 5 6 7 8 9 | 00401105 |. E8 E7000000 CALL cycle. 004011F1 0040110A |. B9 01FF0000 MOV ECX, 0FF01 0040110F |. 51 PUSH ECX 00401110 |. E8 7B000000 CALL cycle. 00401190 00401115 |. 83F9 01 CMP ECX, 1 00401118 |. 74 06 JE SHORT cycle. 00401120 0040111A |> E8 47000000 CALL cycle. 00401166 0040111F |> C3 RETN 00401120 |> A1 68214000 MOV EAX,DWORD PTR DS:[ 402168 ] |
我们需要确保JE SHORT cycle.00401120
执行,因为如果在这里没有跳转的话,那么不可避免的就一定会执行RETN,程序结束,又没得玩了
RETN上面那个CALL只是输出一下错误提示而已
因此我们要保证在前两个CALL执行完毕之后,ECX的值为1
这里我贴一下这两个函数的汇编代码:
cycle.004011f1
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 | 004011F1 / $ A1 60214000 MOV EAX,DWORD PTR DS:[ 402160 ] 004011F6 |. 8B1D 64214000 MOV EBX,DWORD PTR DS:[ 402164 ] 004011FC |. 3305 71214000 XOR EAX,DWORD PTR DS:[ 402171 ] 00401202 |. 331D 75214000 XOR EBX,DWORD PTR DS:[ 402175 ] 00401208 |. 25 0F1F3F7F AND EAX, 7F3F1F0F 0040120D |. 81E3 00010307 AND EBX, 7030100 00401213 |. 33C9 XOR ECX,ECX 00401215 |> 8BF0 / MOV ESI,EAX 00401217 |. 8BFB |MOV EDI,EBX 00401219 |. D3E6 |SHL ESI,CL 0040121B |. D3E7 |SHL EDI,CL 0040121D |. 81E6 80808080 |AND ESI, 80808080 00401223 |. 81E7 80808080 |AND EDI, 80808080 00401229 |. 8BD6 |MOV EDX,ESI 0040122B |. C0EE 07 |SHR DH, 7 0040122E |. 66 :C1E2 07 |SHL DX, 7 00401232 |. C1EA 08 |SHR EDX, 8 00401235 |. C0EE 07 |SHR DH, 7 00401238 |. 66 :C1E2 07 |SHL DX, 7 0040123C |. C1EA 08 |SHR EDX, 8 0040123F |. C0EE 07 |SHR DH, 7 00401242 |. 66 :D1EA |SHR DX, 1 00401245 |. 8BF2 |MOV ESI,EDX 00401247 |. 8BD7 |MOV EDX,EDI 00401249 |. C0EE 07 |SHR DH, 7 0040124C |. 66 :C1E2 07 |SHL DX, 7 00401250 |. C1EA 08 |SHR EDX, 8 00401253 |. C0EE 07 |SHR DH, 7 00401256 |. 66 :C1E2 07 |SHL DX, 7 0040125A |. C1EA 08 |SHR EDX, 8 0040125D |. C0EE 07 |SHR DH, 7 00401260 |. 66 :C1EA 05 |SHR DX, 5 00401264 |. 8BFA |MOV EDI,EDX 00401266 |. 33FE |XOR EDI,ESI 00401268 |. 8BD7 |MOV EDX,EDI 0040126A |. 81E2 FF000000 |AND EDX, 0FF 00401270 |. 51 |PUSH ECX 00401271 |. 52 |PUSH EDX 00401272 |. BA 08000000 |MOV EDX, 8 00401277 |. 91 |XCHG EAX,ECX 00401278 |. 83F8 03 | CMP EAX, 3 0040127B |. 7F 0F |JG SHORT cycle. 0040128C 0040127D |. F6E2 |MUL DL 0040127F |. 5A |POP EDX 00401280 |. 83C0 08 |ADD EAX, 8 00401283 |. 91 |XCHG EAX,ECX 00401284 |. D3C0 |ROL EAX,CL 00401286 |. 33C2 |XOR EAX,EDX 00401288 |. D3C8 |ROR EAX,CL 0040128A |. EB 0D |JMP SHORT cycle. 00401299 0040128C |> 83E8 03 |SUB EAX, 3 0040128F |. F6E2 |MUL DL 00401291 |. 5A |POP EDX 00401292 |. 91 |XCHG EAX,ECX 00401293 |. D3C3 |ROL EBX,CL 00401295 |. 33DA |XOR EBX,EDX 00401297 |. D3CB |ROR EBX,CL 00401299 |> 59 |POP ECX 0040129A |. 41 |INC ECX 0040129B |. 83F9 08 | CMP ECX, 8 0040129E |.^ 0F85 71FFFFFF \JNZ cycle. 00401215 004012A4 \. C3 RETN |
cycle.00401190
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 | 00401190 / $ 5F POP EDI 00401191 |. 59 POP ECX 00401192 |. 57 PUSH EDI 00401193 |. 81F9 80000000 CMP ECX, 80 00401199 |. 7E 55 JLE SHORT cycle. 004011F0 0040119B |. 51 PUSH ECX 0040119C |. 8BF1 MOV ESI,ECX 0040119E |. 81E1 FF000000 AND ECX, 0FF 004011A4 |. 8BF8 MOV EDI,EAX 004011A6 |. 83F9 08 CMP ECX, 8 004011A9 |. 7E 05 JLE SHORT cycle. 004011B0 004011AB |. 8BFB MOV EDI,EBX 004011AD |. C1E9 04 SHR ECX, 4 004011B0 |> C1C7 08 / ROL EDI, 8 004011B3 |. D1E9 |SHR ECX, 1 004011B5 |.^ 75 F9 \JNZ SHORT cycle. 004011B0 004011B7 |. C1EE 08 SHR ESI, 8 004011BA |. 23FE AND EDI,ESI 004011BC |. 81E7 FF000000 AND EDI, 0FF 004011C2 |. 59 POP ECX 004011C3 |> BE 80000000 MOV ESI, 80 004011C8 |> 85FE / TEST ESI,EDI 004011CA |. 74 20 |JE SHORT cycle. 004011EC 004011CC |. 33FE |XOR EDI,ESI 004011CE |. 57 |PUSH EDI 004011CF |. 81E1 00FF0000 |AND ECX, 0FF00 004011D5 |. 87CE |XCHG ESI,ECX 004011D7 |. 32E9 |XOR CH,CL 004011D9 |. 33F1 |XOR ESI,ECX 004011DB |. 87F1 |XCHG ECX,ESI 004011DD |. 51 |PUSH ECX 004011DE |. FF05 82214000 |INC DWORD PTR DS:[ 402182 ] 004011E4 |. E8 A7FFFFFF |CALL cycle. 00401190 004011E9 |. 5F |POP EDI 004011EA |.^EB D7 |JMP SHORT cycle. 004011C3 004011EC |> D1EE |SHR ESI, 1 004011EE |.^ 75 D8 \JNZ SHORT cycle. 004011C8 004011F0 \> C3 RETN |
指令都认识,但是这两个函数具体做了什么,我是不知道的,肯定是某种算法,但是变成机器码之后,几乎面目全非,根本无法辨识
我也是盯着这两段代码看了好久,什么思路都没有,最后就干脆用C语言把这个代码逻辑给实现了
在这段代码中,我固定了用户名的前8位以及序列号的前4位,然后爆破序列号的4~7位
根据代码逻辑,EBX的初始化过程如下:
1 | ebx = name[ 4 - 7 ] ^ serial[ 4 - 7 ] & 0x07030100 |
由于很多bit位都在与操作中被丢弃了,所以serial[4-7]
并没有太多可能的值
但是我的这个代码并没有得到合法的Name和Serial,不过我发现了一些规律
在第一个函数cycle.004011f1
运行完毕之后,EAX的值从未变过,EBX的值呈现周期性循环,循环周期为4
经过第二个函数cycle.00401190
的运算之后,ecx的值总是同一个
后来我就突发奇想,如果我们控制EAX的值,使对应掩码0x7F3F1F0F
所有的保留位全部为1,会发生什么?于是得出如下关系式
1 2 | name[ 0 - 3 ] ^ serial[ 0 - 3 ] = 0x7F3F1F0F serial[ 0 - 3 ] = name[ 0 - 3 ] ^ 0x7F3F1F0F |
上面表达式中的0x7F3F1F0F
可以有多个值,只要每个字节的高4bit的值x
分别满足0<= x <1
、0<= x <2
、0<= x <4
、0<= x <8
即可,具体逻辑可以看代码
但是这样会限制serial[3]
的值不能超过0x50
,后来放松了第一个字节的限制,使其满足0<= x <2
,发现这样也能生成有效的序列号,
然后我就随便找了一组,**JE
、UUUJ
结果发现,居然找出了满足ecx=1
的serial[4-7]
虽然我不知道这究竟是为什么,但事实就是只要我们满足上面那个表达式,那么经过最多64次尝试,就能找到一组合法的用户名和序列号
在通过ecx==1
的检测之后,还有一个检测,但是就比较简单了,具体分析请查看下面的注册机源代码
注册机运行效果:
下面是注册机代码,仍然使用codeblocks编译,你可以取消____DEBUG
宏的注释以观察程序运行过程中各变量的变化:
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 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | / / Author: 12138 / / URL: http: / / 144.34 . 164.217 / / https: / / 144.one / / Date: 2022 / 05 / 12 04 : 00 AM / / Desc: CrackMe.cycle serial generator #include <windows.h> #include <stdio.h> #include <string.h> #include <time.h> / / #define ____DEBUG #ifdef ____DEBUG #define FUCK(_format, _var) printf(_format, _var); #define CUM(_str) printf(_str); #else #define FUCK(_format, _var); #define CUM(_str); #endif typedef unsigned int _ui; / / 这个计数器应该是一个全局变量 _ui _TODO_counter = 0xFEDCBA98 ; char generate_random(char lower, char upper) { return (rand() % (upper - lower + 1 )) + lower; } _ui string_to_hex(char * _string, int _begin) { char _24_31[ 3 ] = { 0 }; char _16_23[ 3 ] = { 0 }; char _8_15[ 3 ] = { 0 }; char _0_7[ 3 ] = { 0 }; sprintf(_24_31, "%02X" , _string[_begin]); sprintf(_16_23, "%02X" , _string[_begin + 1 ]); sprintf(_8_15, "%02X" , _string[_begin + 2 ]); sprintf(_0_7, "%02X" , _string[_begin + 3 ]); char hex_string[ 8 + 1 ] = { 0 }; strcpy(hex_string, _0_7); strcat(hex_string, _8_15); strcat(hex_string, _16_23); strcat(hex_string, _24_31); _ui _ret_val = (_ui)strtoul(hex_string, NULL, 16 ); return _ret_val; } int hex_to_string(_ui _hex_value, char * _ret_string) { _ui _s_15 = _hex_value & 0xFF000000 ; _s_15 = _s_15 >> 24 ; _ui _s_14 = _hex_value & 0x00FF0000 ; _s_14 = _s_14 >> 16 ; _ui _s_13 = _hex_value & 0x0000FF00 ; _s_13 = _s_13 >> 8 ; _ui _s_12 = _hex_value & 0x000000FF ; if (_s_12 < 0x30 || _s_12 > 0x7E || _s_13 < 0x30 || _s_13 > 0x7E || _s_14 < 0x30 || _s_14> 0x7E || _s_15 < 0x30 || _s_15 > 0x7E ) { return 1 ; } char hex_string[ 4 + 1 ] = { 0 }; char _tmp_val[ 2 ] = { 0 }; sprintf(_tmp_val, "%c" , _s_12); strcpy(hex_string, _tmp_val); sprintf(_tmp_val, "%c" , _s_13); strcat(hex_string, _tmp_val); sprintf(_tmp_val, "%c" , _s_14); strcat(hex_string, _tmp_val); sprintf(_tmp_val, "%c" , _s_15); strcat(hex_string, _tmp_val); strcpy(_ret_string, hex_string); return 0 ; } _ui _401190(_ui eax, _ui ebx, _ui ecx) { if (ecx > 0x80 ) { _ui _tmp_val = ecx; _ui esi = ecx; ecx = ecx & 0xFF ; _ui edi = eax; if (ecx > 0x08 ) { edi = ebx; ecx = ecx >> 4 ; } while (ecx ! = 0 ) { edi = _rotl(edi, 8 ); ecx = ecx >> 1 ; } esi = esi >> 8 ; edi = edi & esi; edi = edi & 0xFF ; ecx = _tmp_val; esi = 0x80 ; while ( 1 ) { if ((esi & edi) = = 0 ) { esi = esi >> 1 ; / / 只有esi和edi与运算的结果等于 0 且esi右移 1 位等于 0 的时候,这个循环才会被打破 if (esi = = 0 ) break ; else continue ; } edi = edi ^ esi; _tmp_val = edi; ecx = ecx & 0xFF00 ; _ui _exch_val = ecx; ecx = esi; esi = _exch_val; / / 对CH和CL进行异或运算,并将结果反映到ECX上 _ui ch = ecx & 0xFF00 ; ch = ch >> 8 ; _ui cl = ecx & 0xFF ; ch = ch ^ cl; ch = ch << 8 ; ch = ch & 0xFF00 ; ecx = ecx & 0xFFFF00FF ; ecx = ecx | ch; esi = esi ^ ecx; _exch_val = ecx; ecx = esi; esi = _exch_val; _TODO_counter + = 1 ; FUCK( "[*] _TODO counter: 0x%x\n" , _TODO_counter); ecx = _401190(eax, ebx, ecx); edi = _tmp_val; esi = 0x80 ; continue ; } return ecx; } else return ecx; } int register_func(char * extend_user_name, char * serial) { printf( "[*] current Serial: %s\n" , serial); / / 获取用户名的 0 ~ 3 位并转换成整型数 _ui eax = string_to_hex(extend_user_name, 0 ); FUCK( "[*] eax: %x\n" , eax); / / 获取用户名的 4 ~ 7 位并转换成整型数 _ui ebx = string_to_hex(extend_user_name, 4 ); FUCK( "[*] ebx: %x\n" , ebx); / / 获取序列号的 0 ~ 3 位并转换成整型数 _ui _xor_eax = string_to_hex(serial, 0 ); FUCK( "[*] the oprand xor with eax: %x\n" , _xor_eax); / / 获取序列号的 4 ~ 7 位并转换成整型数 _ui _xor_ebx = string_to_hex(serial, 4 ); FUCK( "[*] the oprand xor with ebx: %x\n" , _xor_ebx); eax = eax ^ _xor_eax; ebx = ebx ^ _xor_ebx; eax = eax & 0x7F3F1F0F ; ebx = ebx & 0x07030100 ; printf( "[*] eax is ready for loop: %x\n" , eax); printf( "[*] ebx is ready for loop: %x\n" ,ebx); / / 下面开始循环 int loop_counter = 0 , ecx = 0 ; while (loop_counter < 8 ) { FUCK( "\n_________________OO<--%d-->OO_________________\n" , loop_counter); ecx = loop_counter; _ui esi = eax; _ui edi = ebx; esi = esi << ecx; edi = edi << ecx; / / 这一步其实就是获取到esi和edi每个字节中的最高位 esi = esi & 0x80808080 ; edi = edi & 0x80808080 ; _ui edx = esi; / / 下面这一堆移位操作其实是把每个字节中的最高位移动到了DL的高四位中 _ui _d = edx & 0x80000000 ; _d = _d >> 31 ; _ui _c = edx & 0x00800000 ; _c = _c >> 23 ; _ui _b = edx & 0x00008000 ; _b = _b >> 15 ; _ui _a = edx & 0x00000080 ; _a = _a >> 7 ; char _DL_H4_0_3[ 3 ] = { 0 }; sprintf(_DL_H4_0_3, "%X" , _d * 8 + _c * 4 + _b * 2 + _a); char * hex_string = (char * )malloc( 2 + 1 ); strcpy(hex_string, _DL_H4_0_3); edx = (_ui)strtoul(hex_string, NULL, 16 ); free(hex_string); edx = edx << 4 ; esi = edx; FUCK( "[*] esi: 0x%x\n" , esi); / * 我们分别用ABCD代表一个 32 位数中每个字节的最高位 | - - - - - - - - - - - - - - - - - | | - - - - - - - - DX - - - - - - - | | | | - - - DH - - | | - - DL - - - | D000 0000 C000 0000 B000 0000 A000 0000 DH >> 7 D000 0000 C000 0000 0000 000B A000 0000 DX << 7 D000 0000 C000 0000 BA00 0000 0000 0000 EDX >> 8 0000 0000 D000 0000 C000 0000 BA00 0000 DH >> 7 0000 0000 D000 0000 0000 000C BA00 0000 DX << 7 0000 0000 D000 0000 CBA0 0000 0000 0000 EDX >> 8 0000 0000 0000 0000 D000 0000 CBA0 0000 DH >> 7 0000 0000 0000 0000 0000 000D CBA0 0000 DX >> 1 0000 0000 0000 0000 0000 0000 DCBA 0000 * / edx = edi; / / 下面这一堆移位操作其实是把每个字节中的最高位移动到了DL的低四位中 / * 我们分别用ABCD代表一个 32 位数中每个字节的最高位 | - - - - - - - - - - - - - - - - - | | - - - - - - - - DX - - - - - - - | | | | - - - DH - - | | - - DL - - - | D000 0000 C000 0000 B000 0000 A000 0000 DH >> 7 D000 0000 C000 0000 0000 000B A000 0000 DX << 7 D000 0000 C000 0000 BA00 0000 0000 0000 EDX >> 8 0000 0000 D000 0000 C000 0000 BA00 0000 DH >> 7 0000 0000 D000 0000 0000 000C BA00 0000 DX << 7 0000 0000 D000 0000 CBA0 0000 0000 0000 EDX >> 8 0000 0000 0000 0000 D000 0000 CBA0 0000 DH >> 7 0000 0000 0000 0000 0000 000D CBA0 0000 DX >> 5 0000 0000 0000 0000 0000 0000 0000 DCBA * / _d = edx & 0x80000000 ; _d = _d >> 31 ; _c = edx & 0x00800000 ; _c = _c >> 23 ; _b = edx & 0x00008000 ; _b = _b >> 15 ; _a = edx & 0x00000080 ; _a = _a >> 7 ; char _DL_L4_0_3[ 3 ] = { 0 }; sprintf(_DL_L4_0_3, "%02X" , _d * 8 + _c * 4 + _b * 2 + _a); hex_string = (char * )malloc( 2 + 1 ); strcpy(hex_string, _DL_L4_0_3); edx = (_ui)strtoul(hex_string, NULL, 16 ); free(hex_string); edi = edx; FUCK( "[*] edi: 0x%x\n" , edi); edi = edi ^ esi; edx = edi; edx = edx & 0xFF ; _ui _tmp_val = ecx; ecx = eax; eax = _tmp_val; FUCK( "[*] edx: 0x%x\n" , edx); if (eax > 3 ) { eax = eax - 3 ; eax = eax * 8 ; _tmp_val = eax; eax = ecx; ecx = _tmp_val; ebx = _rotl(ebx, ecx); ebx = ebx ^ edx; ebx = _rotr(ebx, ecx); } else { eax = eax * 8 + 8 ; _tmp_val = eax; eax = ecx; ecx = _tmp_val; eax = _rotl(eax, ecx); eax = eax ^ edx; eax = _rotr(eax, ecx); } FUCK( "[*] eax: 0x%x\n" ,eax); FUCK( "[*] ebx: 0x%x\n" , ebx); loop_counter + = 1 ; } printf( "[*] after loop, eax: 0x%X\n" , eax); printf( "[*] after loop, ebx: 0x%X\n" , ebx); / / 下面是函数cycle. 401190 ecx = 0xFF01 ; / / 这里需要对一个内存( 0x402182 )的值进行一个加法运算 / / 我暂时还不清楚这个内存的值是否固定,因此先留一个TODO,经调试,这里的初始值应该是 98BADCFE / / 转换成整型数就是FEDCBA98 / / 该函数并不是简单地循环,是一个递归函数,因此需要单独定义 ecx = _401190(eax, ebx, ecx); FUCK( "[*] ecx: 0x%X\n" , ecx); return ecx; } int main_call() { _TODO_counter = 0xFEDCBA98 ; char user_name[ 16 + 1 ] = { 0 }; printf( "[*] give me a max-16-len string, 'q' to exit:\n\t" ); scanf( "%16s" , user_name); FUCK( "%s\n" , user_name); if ( 1 = = strlen(user_name) && 'q' = = user_name[ 0 ]) return 'q' ; / / 将用户名扩充至 16 位 int user_name_length = strlen(user_name); printf( "[*] user name length: %d\n" , user_name_length); char * extend_user_name = (char * )malloc( 16 + 1 ); strcpy(extend_user_name, user_name); int i = 0 ; for (; i< 16 - user_name_length; i + + ) { char _tmp_val[ 2 ] = { 0 }; sprintf(_tmp_val, "%c" , user_name[i % user_name_length]); strcat(extend_user_name, _tmp_val); } printf( "[*] user name after extended: %s\n" , extend_user_name); / / 对于掩码 0x7F3F1F0F / / 0x7F 和username[ 3 ]进行异或运算 / / 如果username[ 3 ]的 1 和 3bit 置位,那么serial[ 3 ]的 1 和 3bit 一定会复位 / / 则serial[ 3 ]一定会小于 0x30 ,这样就无法通过字符范围的检测了[ 0x30 , 0x7E ] / / if ( 0x50 < = extend_user_name[ 3 ]) { / / printf( "[-] after xor with mask(0x7F), the value would less than 0x30, \n\tusername[3]'s ascii value should not be greater than 0x50\n" ); / / continue ; / / } / / 首先获取到注册码的前 4 位 / / __n_0_3 ^ __s_0_3 = = 0x7F3F1F0F _ui _n_0_3 = string_to_hex(extend_user_name, 0 ); FUCK( "0x%X\n" , _n_0_3); char _s_0_3[ 4 + 1 ] = { 0 }; _ui _hex_xor_res = 0 ; i = 0 ; int j = 0 ; int k = 0 ; int l = 0 ; for (; i< 8 ; i + + ) { j = 0 ; for (; j< 4 ; j + + ) { k = 0 ; for (; k< 2 ; k + + ) { l = 0 ; for (; l< 2 ; l + + ) { memset(_s_0_3, 0 , sizeof(_s_0_3)); char _tmp_char[ 2 + 1 ] = { 0 }; char hex_string[ 4 + 1 ] = { 0 }; sprintf(_tmp_char, "%02X" , ( 4 * l + 3 ) * 16 + 0x0F ); strcpy(hex_string, _tmp_char); sprintf(_tmp_char, "%02X" , ( 4 * k + 3 ) * 16 + 0x0F ); strcat(hex_string, _tmp_char); sprintf(_tmp_char, "%02X" , ( 2 * j + 1 ) * 16 + 0x0F ); strcat(hex_string, _tmp_char); sprintf(_tmp_char, "%02X" , i * 16 + 0x0F ); strcat(hex_string, _tmp_char); FUCK( "hex_string = %s\n" , hex_string); / / _hex_xor_res = 0x0F0F0F0F ; _hex_xor_res = (_ui)strtoul(hex_string, NULL, 16 ); FUCK( "hex_xor_res = 0x%X\n" , _hex_xor_res); FUCK( "_n_0_3----%x\n" , _n_0_3 ^ _hex_xor_res); if (hex_to_string(_n_0_3 ^ _hex_xor_res, _s_0_3) = = 0 ) { FUCK( "_s_0_3----%s\n" , _s_0_3); goto __GOTO_ONCE; } } } } } __GOTO_ONCE: FUCK( "%s\n" , _s_0_3); / / 为了通过注册码长度检测,这里先给序列号填充为 16 位,其实后八位暂时是用不着的 char * _s_8_15 = "89abcdef" ; / / 由于掩码 0x07030100 最后会和EBX进行与运算 / / 所以最后EBX的四个字节,从高到低依次拥有 3 、 2 、 1 、 0 个有效bit位 / / 也就是说,无论_n_4_7和_s_4_7是什么样的,EBX只有 2 ^( 3 + 2 + 1 + 0 ) = 64 种可能的值 / / 下面这个组合可以覆盖到所有的可能性 / / 这样一来,我们就可以获取到注册码的 4 ~ 7 位了 char * _s_4 = "@ABCDEFG" ; char * _s_5 = "@ABC" ; char * _s_6 = "@A" ; char * _s_7 = "A" ; i = 0 ; for (; i< 8 ; i + + ) { j = 0 ; for (; j< 4 ;j + + ) { k = 0 ; for (; k< 2 ; k + + ) { char * hex_string = (char * )malloc( 16 + 1 ); strcpy(hex_string, _s_0_3); char _s_4_7[ 4 + 1 ] = { 0 }; char _tmp_val[ 2 ] = { 0 }; sprintf(_tmp_val, "%c" , _s_4[i]); strcpy(_s_4_7, _tmp_val); sprintf(_tmp_val, "%c" , _s_5[j]); strcat(_s_4_7, _tmp_val); sprintf(_tmp_val, "%c" , _s_6[k]); strcat(_s_4_7, _tmp_val); sprintf(_tmp_val, "%c" , _s_7[ 0 ]); strcat(_s_4_7, _tmp_val); strcat(hex_string, _s_4_7); strcat(hex_string, _s_8_15); if (register_func(extend_user_name, hex_string) = = 1 ) { free(hex_string); printf( "[+] half success!!! come on!!!\n\n" ); / / 实现剩下的运算 char _24_31[ 3 ] = { 0 }; char _16_23[ 3 ] = { 0 }; char _8_15[ 3 ] = { 0 }; char _0_7[ 3 ] = { 0 }; / / 获取用户名的 8 ~ 11 并转换成整型数 _ui eax = string_to_hex(extend_user_name, 8 ); FUCK( "[*] eax: %x\n" , eax); / / 获取用户名的 12 ~ 15 并转换成整型数 _ui ebx = string_to_hex(extend_user_name, 12 ); FUCK( "[*] ebx: %x\n" , ebx); eax = eax ^ ebx; eax = eax ^ _TODO_counter; eax = eax | 0x40404040 ; eax = eax & 0x77777777 ; while ( 1 ) { char _eax_bin_format[ 32 + 1 ] = { 0 }; itoa(eax, _eax_bin_format, 2 ); char __eax_bin_format[ 32 + 1 ] = { 0 }; sprintf(__eax_bin_format, "%032s" , _eax_bin_format); int index = 0 ; CUM( "[+] this is the binary format of serial_8_11 ^ serial_12_15 result:\n\t" ); for (; index< 32 ; index + + ) { FUCK( "%c" , __eax_bin_format[index]); if ((index + 1 ) % 4 = = 0 && (index + 1 )! = 32 ) { CUM( " " ); } if ((index + 1 ) % 8 = = 0 && (index + 1 )! = 32 ) { CUM( "%% " ); } } CUM( "\n" ); char _s_8_11[ 4 + 1 ] = { 0 }; sprintf(_tmp_val, "%c" , generate_random( 0x30 , 0x7E )); FUCK( "random char %s" , _tmp_val); strcpy(_s_8_11, _tmp_val); sprintf(_tmp_val, "%c" , generate_random( 0x30 , 0x7E )); FUCK( "random char %s" , _tmp_val); strcat(_s_8_11, _tmp_val); sprintf(_tmp_val, "%c" , generate_random( 0x30 , 0x7E )); FUCK( "random char %s" , _tmp_val); strcat(_s_8_11, _tmp_val); sprintf(_tmp_val, "%c" , generate_random( 0x30 , 0x7E )); FUCK( "random char %s" , _tmp_val); strcat(_s_8_11, _tmp_val); printf( "[*] generating random 4-len string: %s\n" , _s_8_11); / / char _DEBUG_s_8_11[ 4 + 1 ] = "}3d7" ; / / strcpy(_s_8_11, _DEBUG_s_8_11); _ui _s_8_11_hex_value = string_to_hex(_s_8_11, 0 ); / / 根据汇编代码 / / eax = eax ^ s_8_11 / / eax = eax ^ s_12_15 / / eax = = 0 必须成立 / / 那么可以推导出 eax ^ s_8_11 = = s_12_15 FUCK( "_s_8_11_hex_value: 0x%X\n" , _s_8_11_hex_value); FUCK( "eax: 0x%X\n" , eax); _ui _s_12_15_hex_value = eax ^ _s_8_11_hex_value; FUCK( "_s_12_15_hex_value: 0x%X\n" , _s_12_15_hex_value); hex_string = (char * )malloc( 4 + 1 ); if ( 1 = = hex_to_string(_s_12_15_hex_value, hex_string)) { free(hex_string); continue ; } char serial_number[ 16 + 1 ] = { 0 }; strcpy(serial_number, _s_0_3); strcat(serial_number, _s_4_7); strcat(serial_number, _s_8_11); strcat(serial_number, hex_string); free(hex_string); printf( "[+] Name:\n\t%s\n" , user_name); printf( "[+] Serial:\n\t%s\n" , serial_number); char c = 0 ; while ((c = getchar()) ! = EOF && c ! = '\n' ); return 0 ; } } free(hex_string); } } } return 0 ; } int main( int argc, char * * argv) { while ( 1 ) if ( 'q' = = main_call()) break ; return 0 ; } |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课