crakeme 用exeinfo打开,发现又upx壳 直接上脱壳 报错upx: D:\google\a\crackme.exe: CantUnpackException: file is possibly modified/hacked/protected; take care! 网上搜一下大概率是数据段被修改了,导致upx不敢脱壳,两种方式解决,手动改回来,或者用改过的upx 我选择手动更省事,exeinfo可以看到一些相关信息, 这里看出再name那一行中upx被改为了pfx,通过010改回来后就可以正常脱壳 脱完后放进ida中就看到flag easyre
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 _main();strcpy (v4, "]P_ISRF^PCY[I_YWERYC" );memset (v5, 0 , sizeof (v5)); v6 = 0 ; v7 = 0 ;puts ("please input your strings:" ); gets(Str); v10 = strlen (Str);while ( Str[i] ) { for ( i = 0 ; i < v10; ++i ) v8[i] = Str[i] ^ 0x11 ; }for ( i = 0 ; i < v10; ++i ) { if ( v8[i] == 'B' || v8[i] == 'X' ) v8[i] = 155 - v8[i]; }for ( i = v10 - 1 ; i >= 0 ; --i ) v8[v10 - i - 1 ] = v8[i]; i = 0 ;if ( v10 > 0 ) { if ( v8[i] == v4[i] ) printf ("yes!!!" ); else printf ("no!!!" ); }return 0 ;
字符串定位后,看到关键代码,代码不难,大致看一下逻辑关系就可以理出来 str->v8->替换一些字符->字符串对称交换(但是似乎代码有问题) 逻辑很简单,代码也很简单
1 2 3 4 5 6 7 8 9 10 11 a = ']P_ISRF^PCY[I_YWERYC' flag = []for i in range (len (a) - 1 , 0 , -1 ): if ord (a[i]) == 155 - ord ('B' ): flag.append('B' ) elif ord (a[i]) == 155 - ord ('X' ): flag.append('X' ) else : flag.append(a[i])for i in range (len (flag)): print (chr (ord (flag[i]) ^ 0x11 ), end='' )
babyre 下载下来后,发现图标是python打包,于是使用pyinstxtractor和uncomple6将其中主要文件反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 p+q=292884018782106151080211087047278002613718113661882871562870811030932129300110050822187903340426820507419488984883216665816506575312384940488196435920320779296487709207011656728480651848786849994095965852212548311864730225380390740637527033103610408592664948012814290769567441038868614508362013860087396409860 (p+1 )*(q+1 )=21292789073160227295768319780997976991300923684414991432030077313041762314144710093780468352616448047534339208324518089727210764843655182515955359309813600286949887218916518346391288151954579692912105787780604137276300957046899460796651855983154616583709095921532639371311099659697834887064510351319531902433355833604752638757132129136704458119767279776712516825379722837005380965686817229771252693736534397063201880826010273930761767650438638395019411119979149337260776965247144705915951674697425506236801595477159432369862377378306461809669885764689526096087635635247658396780671976617716801660025870405374520076160 c=5203005542361323780340103662023144468501161788183930759975924790394097999367062944602228590598053194005601497154183700604614648980958953643596732510635460233363517206803267054976506058495592964781868943617992245808463957957161100800155936109928340808755112091651619258385206684038063600864669934451439637410568700470057362554045334836098013308228518175901113235436257998397401389511926288739759268080251377782356779624616546966237213737535252748926042086203600860251557074440685879354169866206490962331203234019516485700964227924668452181975961352914304357731769081382406940750260817547299552705287482926593175925396 import libnumfrom crypto.Util.number import * flag = 'ISCTF{******************}' flags = flag.encode() e = 65537 p = libnum.generate_prime(1024 ) q = libnum.generate_prime(1024 ) n = p * q m = bytes_to_long(flags) c = pow (m, e, n) output = open ('output.txt' , 'w' ) output.write('p+q =' + str (p + q) + '\n' ) output.write('(p+1)*(q+1)=' + str ((p + 1 ) * (q + 1 )) + '\n' ) output.write('c=' + str (c) + '\n' ) output.close()
得到一下这些代码,这段代码我想挺久想不出来 大概思路是,通过(q+1)(p+1)和q+p得出pq的值也就是n,得出pow(i n+c,1/e)=m,最后算出bytes_to_long(flags) 但是我怎么算都不行,感觉精度都不够,最后由我沈总秒杀,直接出flag easy_z3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 print ("Please input flag:" ) flag = input ()if len (flag)!=42 : print ("Check your length!" ) exit() l=[]for i in range (6 ): s="" for j in flag[i*7 :i*7 +7 ]: s+=hex (ord (j))[2 :] l.append(int (s,16 ))if ( (593 *l[5 ] + 997 *l[0 ] + 811 *l[1 ] + 258 *l[2 ] + 829 *l[3 ] + 532 *l[4 ])== 0x54eb02012bed42c08 and \ (605 *l[4 ] + 686 *l[5 ] + 328 *l[0 ] + 602 *l[1 ] + 695 *l[2 ] + 576 *l[3 ])== 0x4f039a9f601affc3a and \ (373 *l[3 ] + 512 *l[4 ] + 449 *l[5 ] + 756 *l[0 ] + 448 *l[1 ] + 580 *l[2 ])== 0x442b62c4ad653e7d9 and \ (560 *l[2 ] + 635 *l[3 ] + 422 *l[4 ] + 971 *l[5 ] + 855 *l[0 ] + 597 *l[1 ])== 0x588aabb6a4cb26838 and \ (717 *l[1 ] + 507 *l[2 ] + 388 *l[3 ] + 925 *l[4 ] + 324 *l[5 ] + 524 *l[0 ])== 0x48f8e42ac70c9af91 and \ (312 *l[0 ] + 368 *l[1 ] + 884 *l[2 ] + 518 *l[3 ] + 495 *l[4 ] + 414 *l[5 ])== 0x4656c19578a6b1170 ): print ("Good job!" )else : print ("Wrong\nTry again!!!" ) exit()
给出了python文件必然是用z3库来解方程
1 2 3 4 5 6 7 8 9 10 11 12 13 from z3 import * l=[0 ]*6 a,b,c,d,e,f=Ints('a b c d e f' ) z=Solver() z.add((593 *f + 997 *a + 811 *b + 258 *c + 829 *d + 532 *e)== 0x54eb02012bed42c08 ) z.add((605 *e + 686 *f + 328 *a + 602 *b + 695 *c + 576 *d)== 0x4f039a9f601affc3a ) z.add((373 *d + 512 *e + 449 *f + 756 *a + 448 *b + 580 *c)== 0x442b62c4ad653e7d9 ) z.add((560 *c + 635 *d + 422 *e + 971 *f + 855 *a + 597 *b)== 0x588aabb6a4cb26838 ) z.add((717 *b + 507 *c + 388 *d + 925 *e + 324 *f + 524 *a)== 0x48f8e42ac70c9af91 ) z.add((312 *a + 368 *b + 884 *c + 518 *d + 495 *e + 414 *f)== 0x4656c19578a6b1170 )print (z.check())print (z.model())
1 2 3 4 5 6 [d = 32765855640286324 , b = 13615593641303915 , a = 20639221941697358 , f = 26860403902456189 , c = 31015537033047360 , e = 28554726411354222 ]
得到这串答案,这串是由将flag每7个字符分一段的方式分为了6段,每段将他们的ascii的16进制数拼接然后转为10进制得到的结果,然后随便找个16进制转数组,或者自己写也可以,得到flag where 放入32位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 { if ( a1 > 3 ) { if ( a1 != 100 ) { if ( a1 == 404 ) { printf ("Debug Error" ); ExitProcess(0 ); } if ( a1 == 666 ) { printf ("Please try again." ); ExitProcess(0 ); } LABEL_18: printf ("???" ); ExitProcess(0 ); } printf ("Yeah!You are right!\nThe flag is flag{input1+input2}" ); } else { switch ( a1 ) { case 3 : printf ("Now,You need input password." ); break ; case 0 : printf ("Invalid ID" ); ExitProcess(0 ); case 1 : printf ("Length is incorrect!" ); ExitProcess(0 ); case 2 : printf ("Welcome, please enter the correct ID" ); break ; default : goto LABEL_18; } } return printf ("\n" ); }
可以看到已经出现了flag,然后发现进入flag分支的条件是a1=100就可以了,然后回溯到被调用的位置
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 scanf ("%d" , &v16); v15 = v16 / 10000 ; v14 = v16 % 10000 / 100 ; v13 = v16 % 10000 % 100 ; v5 = v14; v3 = sub_401390(7 * v14, 1 ); v4 = sub_401390(11 * (13 * v3 + v13 + 3 ), v5); v17 = ((v4 - v13) / 10 + 11 ) ^ 0x104B4 ; if ( (v16 & 1 ) != 0 || v16 < 233 || v15 > 9999 || v14 > 12 || v13 > 32 || v17 * (v14 + v13 + v15) != 136398636 ) sub_4013C0(0 ); sub_4013C0(3 ); if ( --File._cnt < 0 ) _filbuf(&File); else ++File._ptr; scanf ("%s" , &Str); v18 = strlen (&Str); if ( v18 != 32 ) sub_4013C0(1 ); v19 = &Str; MEMORY[0 ] = v16; NtClose = GetProcAddress(::hModule, "NtClose" ); (NtClose)(v17, v6, sub_401660); if ( sub_4019B0(&Str) == -1 ) sub_4013C0(666 ); else sub_4013C0(100 );
这一段是比较重要的代码,整个代码中输入了一个数字,一个字符串 数字通过判断使得a1=3,而字符串就是上面代码所说的密码, 输入数字的代码好解决,翻译过来就是数字是十万位以上,千百位小于12,个十位小于32,整个数字大于233 然后有个sub_401390函数,返回这个算式~(a2 + ~a1) v3,v4也可以算出来,然后使用列举爆破就行了
1 2 3 4 5 6 7 for i in range (13 ) : for j in range (33 ) : v3 = ~(~(7 * i) + 1 ) v4 = ~(~(11 * (13 * v3 + j + 3 )) + i) v19 = int ((v4 - j) / 10 + 11 ) ^ 0x104B4 print(136398636 /v19)
这样会得到很多值,但是只有一个是被除清了的2046,所以v19==2046,然后我们就可以知道v19==2046时i和j 也就得到了整个数字,20220222,可以运行程序验证 然后继续向下分析,最终要使得传入100,最后刚好就是,不过要先将str进行判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 for ( i = 0 ; i < 32 ; ++i ) *(i + a1) ^= byte_432034[i];for ( j = 0 ; j < 30 ; j += 2 ) { if ( *(j + a1) >= *(j + a1 + 2 ) ) return -1 ; }for ( k = 0 ; k < 32 ; k += 2 ) byte_434D3C[300 * *(k + a1) + *(k + a1 + 1 )] = 1 ;for ( l = 0 ; l < 300 ; ++l ) { for ( m = 0 ; m < 300 ; ++m ) { if ( byte_434D3C[300 * l + m] != byte_41C0A0[300 * l + m] ) return -1 ; } }return 0 ;
这里byte_[432034]和byte_41c0a0是知道了 通过将byte_434d3c赋值,等于byte41c0a0,由于赋值为一,所以重点是索引, byte41c0a0的范围很大,有90000个,所以需要使用idc(ida中的脚本)来输出索引
1 2 3 4 5 6 7 8 auto s=0x41c0a0 ;auto i=0 ; Message("\n" );for (;i<=90000 ;i++){ if (Byte(s+i)==1 ) Message("%d," ,i); }9015 ,12928 ,25805 ,29200 ,36021 ,40589 ,41428 ,46450 ,54099 ,56800 ,60001 ,66213 ,67554 ,70735 ,73866 ,76755 ,90000 ,
得到索引后再来分析程序 第一个for循环是再异或,第二个for循环作为一个限制条件偶数列是递增,第三个for循环才是重点,通过一个表达式来决定byte434d3c的哪个值是1,于是我们可以写出代码
1 2 3 4 5 6 7 8 9 10 11 key = [241 , 239 , 97 , 187 , 201 , 69 , 87 , 67 , 54 , 235 , 195 , 245 , 97 , 31 , 224 , 237 , 95 , 25 , 195 , 131 , 11 , 103 , 91 , 68 , 122 , 157 , 178 , 126 , 245 , 181 , 34 , 101 ] m = [9015 , 12928 , 25805 , 29200 , 36021 , 40589 , 41428 , 46450 , 54099 , 56800 , 60001 , 66213 , 67554 , 70735 , 73866 , 76755 ]for i in range(200 ): for j in range(200 ): for n in range(len(m)): if (i ^ key[0 ]) * 300 + (j ^ key[1 ]) == m[n] : #print(i, j) print(chr(i), chr(j))
这种逻辑可以爆破出来满足条件的字符,虽然每个位置的字符能爆破出来,但是有些位置却没有满足条件(如果每个字符是再32-126范围内)的结果,所以只能放一放了