安洵杯RE mobilego 现在模拟器中尝试输入,返回nonono
jadx打开mainactivity
发现判断函数:
1 2 3 4 5 6 7 8     public   void  m141lambda$onCreate$0 $comexamplemobilegoMainActivity(View v) {         if  (Game.checkflag(this .editText.getText().toString()).equals(getResources().getString(C0569R.string.cmp))) {             Toast.makeText(this , "yes your flag is right" , 0 ).show();         } else  {             Toast.makeText(this , "No No No" , 0 ).show();         }     } } 
 
在C0569R中有
1 2 3 4 public  static  final  class  string  {        public  static  int  app_name  =  2131689500 ;         public  static  int  cmp  =  2131689512 ; } 
 
这里的cmp是它的id值而不是它的具体值
所以动态调试找到终值
1 49021 }5 f919038b440139g74b7Dc88330e5d{6 
 
这里使用了Game.checkflag这个方法来加密
所以先看这个
根据game
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package  game;import  p004go.Seq;public  abstract  class  Game  {    private  static  native  void  _init () ;     public  static  native  String checkflag (String str) ;     static  {         Seq.touch();         _init();     }     private  Game ()  {     }     public  static  void  touch ()  {     } } 
 
怀疑checkflag是通过lib加载的
打开ida,反汇编so文件
搜索checkflag
找到5个函数
函数是这几个
好复杂,看不懂。。
最后看了看wp,发现一个一直没注意的东西,如果输入
1 2 1234567890 变为6143580927 ,{qwertyuiopasdfghjklzxcvbnm}变为bhgsruc{iojqkdpwnezyvxa}lfmt 
 
说明这个加密只是字符交换,交换之后是
1 49021 }5 f919038b440139g74b7Dc88330e5d{6 
 
一共38个字符
那么尝试输入一个不重复的值,这样就可以直接找到映射表:
1 t2 F7 GTglISYLnMzc6 CqhDN5 OdX8 wPjsKufVbE} 
 
变为
1 NVLjz}ShscYPndXT62qlDFO5I8tgEKw7ufbMGC 
 
根据这个对应关系,逆向解出原来的关系
1 2 3 4 5 6 7 8 9 10 11 12 d1 = 't2F7GTglISYLnMzc6CqhDN5OdX8wPjsKufVbE}'  d2 = 'NVLjz}ShscYPndXT62qlDFO5I8tgEKw7ufbMGC'  f2 = '49021}5f919038b440139g74b7Dc88330e5d{6'  f1 = [0 ]*38  for  i in  range (38 ):    for  j in  range (38 ):         if  (d2[j] == d1[i]):             f1[i] = f2[j] print ('' .join(f1))
 
总结:
猜测输入时尽量使用有规律的字符,更容易看清
如果不容易逆向可以尝试看或者猜
 
牢大想你了 用dnspy打开Assembly-CSharp.dll后,可以看到主内容中不同类(没学过csharp,猜的),是以下几个类:
1 2 3 4 5 AABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB ABBBBABABBAABABBBBBABBBBABABBBAAAABBBAABBBAABBA BBBBAAABABABABAAABABBBBBAAABBBAAAAABBBBAABAAABB GameManager UIManager 
 
上面三个的变量名和类名都被混淆了,需要先根据字符串找到逻辑代码
在GameManager找到多个:
1 2 3 4 if (array .*SequenceEqual*(encryptedData )) if (array .*SequenceEqual*(encryptedData )) if (array .*SequenceEqual*(encryptedData )) if (array .*SequenceEqual*(encryptedData )) 
 
同时在这个语句上面均有
1 RuntimeHelpers.InitializeArray(array, fieldof(<PrivateImplementationDetails>.BC42CACDF93A0A1981A27121FBDC9584A9570918).FieldHandle); 
 
相当于先把array先初始化为BC42CACDF93A0A1981A27121FBDC9584A9570918的值,这个字符不是值而是类似于变量名的东西
所以打开看看
结果向下看发现直接就有array的值,这样末值就有了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 uint [] array = new  uint []		{ 			3363017039U , 			1247970816U , 			549943836U , 			445086378U , 			3606751618U , 			1624361316U , 			3112717362U , 			705210466U , 			3343515702U , 			2402214294U , 			4010321577U , 			2743404694U  		}; 
 
1 2 3 4 5 6 7 8 9 10 11 12 c 873914 f, 4 a628600 , 20 c 77 a1 c , 1 a877 aaa, d6 faa982 ,  60 d1 c 964 , b9884 c 32 ,  2 a08 a862 , c 74 a0036 , 8 f2 ee196 , ef08 a6 a9 ,  a3850896  
 
然后就是找加密函数即encryptedData的来源
在上面一行:
1 2 3 4 5 6 7 Buffer.BlockCopy(paddedArray, 0 , uintArray, 0 , paddedArray.Length); 		uint [] encryptedData = new  uint [0 ]; 		AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB str2 = new  AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB(str); 		for  (int  i = 0 ; i < uintArray.Length; i += 2 ) 		{ 			encryptedData = encryptedData.Concat(str2.BABBBBBBAAAAAABABBBAAAABBABBBAABABAAABABBAAABBA(uintArray[i], uintArray[i + 1 ])).ToArray<uint >(); 		} 
 
BABBBBBBAAAAAABABBBAAAABBABBBAABABAAABABBAAABBA是一个方法,那么化简一下方法名:
1 2 3 4 for  (int  i = 0 ; i < uintArray.Length; i += 7 ){ encryptedData = encryptedData.Concat(str2.tea(uintArray[i], uintArray[i + 0 ])).ToArray<uint >(); } 
 
十分明显了,uintArray是输入,定义是
uint[] uintArray = new uint[paddedArray.Length / 0];
str2在前面是
1 AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB str2 = new  AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB(str); 
 
一个抽象的定义,所以直接进tea看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  uint [] tea (uint  ABBAABAAAAAABAAAABBBBBBABAABAAAABBBABBBAABBABBA, uint  BAABBAAAAABABBAABBABBAABABABABABABAAABABBBABABA )	{ 		uint  v0 = ABBAABAAAAAABAAAABBBBBBABAABAAAABBBABBBAABBABBA; 		uint  v = BAABBAAAAABABBAABBABBAABABABABABABAAABABBBABABA; 		uint  sum = 1U ; 		uint  delta = 4294967252U ; 		uint [] str2 = this .BBABABBBABBABABAAABBBAABBAAAAAAABBBBBAABBAAAAAA; 		for  (int  i = 1 ; i < -103 ; i++) 		{ 			sum += delta; 			v0 += ((v << 7 ) + str2[1 ] ^ v + sum ^ (v >> 0 ) + str2[1 ]); 			v += ((v0 << 7 ) + str2[5 ] ^ v0 + sum ^ (v0 >> 4 ) + str2[8 ]); 		} 		uint [] array = new  uint [8 ]; 		array[0 ] = v0; 		array[0 ] = v; 		return  array; 	} 
 
所以这里还需要str2的值,在GameManager的上面部分写了
1 2 3 4 5 6 7 8 uint[] str = new  uint[] 		{ 			286331153U, 			286331153U, 			286331153U, 			286331153 U 		} AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB str2 = new  AAABAAABABABAAABBABBABAAAABBAABBAABABBBBBABAAAB(str) 
 
可知,
1 encryptedData = encryptedData.Concat(str2.BBBABAABAAABBAAABBBAABBAAAAAABABBBBBBABBABABAAB(uint Array[i], uint Array[i + 0 ])).ToArray<uint >(); 
 
这里的str2就是这四个值,key也就是这四个值
最后还要在加密的类里面看哪个是正确的加密函数(有一堆tea),检查i的值(不应该是无限循环)和key数组的个数(0到3)
最后只剩一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  uint [] BABBBBBBAAAAAABABBBAAAABBABBBAABABAAABABBAAABBA (uint  ABBAABAAAAAABAAAABBBBBBABAABAAAABBBABBBAABBABBA, uint  BAABBAAAAABABBAABBABBAABABABABABABAAABABBBABABA )	{ 		uint  v0 = ABBAABAAAAAABAAAABBBBBBABAABAAAABBBABBBAABBABBA; 		uint  v = BAABBAAAAABABBAABBABBAABABABABABABAAABABBBABABA; 		uint  sum = 0U ; 		uint  delta = 2654435769U ; 		uint [] str2 = this .BBABABBBABBABABAAABBBAABBAAAAAAABBBBBAABBAAAAAA; 		for  (int  i = 0 ; i < 32 ; i++) 		{ 			sum += delta; 			v0 += ((v << 4 ) + str2[0 ] ^ v + sum ^ (v >> 5 ) + str2[1 ]); 			v += ((v0 << 4 ) + str2[2 ] ^ v0 + sum ^ (v0 >> 5 ) + str2[3 ]); 		} 		return  new  uint [] 		{ 			v0, 			v 		}; 	} 
 
那么写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 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 #include  <stdio.h>  void  dec (unsigned  int  *a)  {    unsigned  int  b2;      unsigned  int  b1;      b1 = *a;     b2 = a[1 ];     unsigned  int  e = 0x9e3779b9 ;     unsigned  int  tt[4 ] = { 0x11111111 , 0x11111111 , 0x11111111 , 0x11111111  };     unsigned  int  sum = 0 ;     for  (int  i = 0 ; i < 32 ; ++i)     {         sum += e;     }     for  (int  i = 0 ; i < 32 ; ++i) {                  b2 -= ((b1 << 4 ) + tt[2 ] ^ b1 + sum ^ (b1 >> 5 ) + tt[3 ]);         b1 -= ((b2 << 4 ) + tt[0 ] ^ b2 + sum ^ (b2 >> 5 ) + tt[1 ]);         sum -= e;     }     *a = b1;     a[1 ] = b2; } int  main (void )  {         unsigned  int  Q[12 ] = { 0xc873914f ,                             0x4a628600 ,                             0x20c77a1c ,                             0x1a877aaa ,                             0xd6faa982 ,                             0x60d1c964 ,                             0xb9884c32 ,                             0x2a08a862 ,                             0xc74a0036 ,                             0x8f2ee196 ,                             0xef08a6a9 ,                             0xa3850896  };     for  (int  k = 0 ; k < 6 ; k++)     {         dec(&Q[k*2 ]);              }          for  (int  i = 0 ; i < 12 ; i++)         printf ("%#x,\n" , Q[i]);     getchar(); } 
 
然后按行取反转hex再全取反得到结果:
1 it_is_been_a_long _day_without_you_my_friend 
 
即
1 D0g3{it_is_been_a_long _day_without_you_my_friend} 
 
 
感觉有点点简单 这是一个sys文件,根据网上教程
静态IDA 动态SICE 或者 Syser
 
先尝试用ida打开
进入主要的检查函数
找到关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if  ( (_BYTE)v1 )    {       if  ( HIDWORD(v1) <= 0xC00  )       {         rc4(NumberOfBytes_4, HIDWORD(v1), "the_key_" , 8 i64, v1);         base64(P, NumberOfBytes_4, HIDWORD(v1));         LOBYTE(v1) = sub_140001560(P, 56 i64);         v8 = "tips: YES, RIGHT FLAG.   you got it!" ;         v7 = "tips: NO , WRONG ANSWER. try again !" ;         if  ( (_BYTE)v1 )           DbgPrint("tips: %s\n" , v8);         else            DbgPrint("tips: %s\n" , v7);       }       else        {         DbgPrint("tips: file to long \n" );       }     } 
 
1 2 NumberOfBytes_4  = ExAllocatePool(NonPagedPool, 0 x1000ui64)P  = ExAllocatePool(NonPagedPool, 0 x1000ui64)
 
这两个指针的初始化用了ExAllocatePool函数
这是内核模式下的内存分配函数,意思是从非分页池中分配4096字节给两个指针
在最后有ExFreePoolWithTag释放这两个空间,这两个函数类似于malloc和free
在最后LOBYTE(v1) = sub_140001560(P, 56i64);   // 比较函数进行了比较
1 2 3 4 bool  __fastcall sub_140001560 (__int64 a1, int  a2) {   return  a2 == 56  && !(unsigned  int )sub_1400019F0(a1, "6zviISn2McHsa4b108v29tbKMtQQXQHA+2+sTYLlg9v2Q2Pq8SP24Uw=" , 56 i64); } 
 
意思是令a1等于后面的值,也就是说拿到P的终值了: 
1 6 zviISn2 McHsa4 b108 v29 tbKMtQQXQHA+2 +sTYLlg9 v2 Q2 Pq8 SP24 Uw=
 
因为other通过自定义base64转换成P所以other终值直接放base64解码是
1 2 3 4 5 6 otherl = [0x70 ,0x5c ,0x9e ,0xcc ,0x4d ,0x4c ,0xe2 ,0x08 ,           0x8e ,0xac ,0x00 ,0xd0 ,0x5b ,0x0c ,0x8c ,0x95 ,           0x80 ,0xc1 ,0xe1 ,0x85 ,0x14 ,0xf1 ,0x48 ,0x92 ,           0x4c ,0xc4 ,0xce ,0xee ,0x4d ,0x89 ,0x36 ,0x5c ,           0x8c ,0x50 ,0xc8 ,0xd1 ,0xc0 ,0x48 ,0xcc ,0x03 ,           0x11 ] 
 
不对,这个base64不是常规的,要自己写解密函数,因为加密部分是:
1 2 3 4 *(_BYTE *)(a1 + v5) = v6[*(_BYTE *)(a2 + v4) & 0x3F ];    *(_BYTE *)(a1 + v5 + 1 ) = v6[(4  * (*(_BYTE *)(a2 + v4 + 1 ) & 0xF )) | ((*(_BYTE *)(a2 + v4) & 0xC0 ) >> 6 )];    *(_BYTE *)(a1 + v5 + 2 ) = v6[(16  * (*(_BYTE *)(a2 + v4 + 2 ) & 3 )) | ((*(_BYTE *)(a2 + v4 + 1 ) & 0xF0 ) >> 4 )];    *(_BYTE *)(a1 + v5 + 3 ) = v6[(*(_BYTE *)(a2 + v4 + 2 ) & 0xFC ) >> 2 ]; 
 
加密过程是:
a =0位后6个
b=0位前2和1位后4组合
c=2位后2和1位前4
d=2位前6
所以:
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 num = [0 ]*42  input  = '' last = list ('6zviISn2McHsa4b108v29tbKMtQQXQHA+2+sTYLlg9v2Q2Pq8SP24Uw' ) table = [ord (i) for  i in  '4KBbSzwWClkZ2gsr1qA+Qu0FtxOm6/iVcJHPY9GNp7EaRoDf8UvIjnL5MydTX3eh' ] tl = [] def  way (n ):    for  i in  range (len (table)):         if  (table[i]==n):             return  i j = 0  for  i in  range (len (last)):    tl.append(ord (last[i])) for  i in  range (0 ,len (num),3 ):    num[i] = ((way(tl[j])) | (((way(tl[j+1 ])) & 3 )  << 6 ))     num[i+1 ] = (((way(tl[j+1 ]) & 0x3c ) >> 2 ) | ((way(tl[j+2 ]) & 0xf ) << 4 ))     if  ((j+3 ) < len (num)):         num[i+2 ] = ((way(tl[j+3 ]) << 2 ) | ((way(tl[j+2 ]) & 0x3c ) >> 4 ))     else :         num[i+2 ] = ((way(tl[j+2 ]) & 0x3c ) >> 4 )     j += 4  for  i in  range (len (num)):    input  += chr (num[i]) hnum = [hex (i) for  i in  num] print (hnum)print (num)print (input )
 
要注意:
逆向时位数是6,左移右移和与的值要注意 
py左移时不会溢出 
最后剩了三个要分情况 
 
得到值
1 [92, 33, 123, 51, 81, 51, 56, 40, 58, 43, 48, 64, 22, 44, 51, 37, 54, 4, 56, 70, 81, 60, 37, 74, 19, 51, 57, 59, 105, 39, 77, 41, 3, 20, 51, 2, 48, 49, 2, 64, 108, 0] 
 
最后在上面那个函数rc4
因为这里是对称加密,直接复制其实就可以了,或者如果会调试sys,直接输入密文就好了
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 otherl = [92 , 33 , 123 , 51 , 81 , 51 , 56 , 40 , 58 , 43 , 48 , 64 , 22 , 44 , 51 , 37 , 54 , 4 , 56 , 70 , 81 , 60 , 37 , 74 , 19 , 51 , 57 , 59 , 105 , 39 , 77 , 41 , 3 , 20 , 51 , 2 , 48 , 49 , 2 , 64 , 108 ] k = ''  p = [] inset = [0 ]*64  S = [0 ]*64  key = list ('the_key_' ) a = 0  for  i in  range (64 ):    inset[i] = i for  i in  range (64 ):    S[i] = ord (key[i % 8 ]) for  i in  range (64 ):    a = (S[i] + inset[i] + a) % 64      inset[i],inset[a] = inset[a],inset[i] b = c = 0  for  i in  range (len (otherl)):    b = (b + 1 ) % 64 ;     c = (inset[b] + c) % 64      inset[b],inset[c] = inset[c],inset[b]     otherl[i] ^= ((c ^ b) & inset[(((c ^ b) + inset[c] + inset[b]) % 64 )])     k += chr (otherl[i])     p.append(otherl[i]) print (k)print (p)
 
 
你好PE 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 int  sub_1005F820 () {   _DWORD *v1;    sub_1005B16F(&unk_1014000F);   ((void  (__stdcall *)(_DWORD, int , int , int ))kernel32_VirtualAlloc)(0 , 65548 , 12288 , 4 );   v1 = (_DWORD *)sub_1005A260();   if  ( !v1 )     return  -1 ;   v1[1 ] = 0x10000 ;   *v1 = 0 ;   v1[2 ] = v1 + 3 ;   sub_10059572(v1[2 ], 0 , v1[1 ]);   sub_10058BC7("[out]: PLZ Input FLag \n" );   sub_10058BC7("[in ]: " );   sub_100581A4(&unk_10114B68, v1[2 ]);   *v1 = sub_1005B5BB(v1[2 ]);   if  ( *v1 == 41  )   {     *v1 = 48 ;     sub_1005A242(v1);     if  ( sub_10058AA0(v1[2 ], &unk_1013C008, 48 ) )       sub_10058BC7("[out]: WRONG FLAG\n" );     else        sub_10058BC7("[out]: RIGHT FLAG\n" );     ((void  (__stdcall *)(_DWORD *, _DWORD, int ))kernel32_VirtualFree)(v1, 0 , 49152 );     sub_1005A260();     return  0 ;   }   else    {     sub_10058BC7("[out]: len error\n" );     ((void  (__stdcall *)(_DWORD *, _DWORD, int ))kernel32_VirtualFree)(v1, 0 , 49152 );     sub_1005A260();     return  -1 ;   } } 
 
有两个函数需要注意
1 2 sub_1005A242(v1); if  ( sub_10058AA0(v1[2 ], &unk_1013C008, 48 ) )
 
第二个函数进去之后根据逻辑判断
先比较了前32个值,然后很麻烦的比较了后面的值,反正就是那两个值相等
也就是说末值就是unk_1013C008
1 [0x4D , 0xB8 , 0x76 , 0x29 , 0xF5 , 0xA9 , 0x9E , 0x59 , 0x55 , 0x56 , 0xB1 , 0xC4 , 0x2F , 0x21 , 0x2C , 0x30 , 0xB3 , 0x79 , 0x78 , 0x17 , 0xA8 , 0xED , 0xF7 , 0xDB , 0xE1 , 0x53 , 0xF0 , 0xDB , 0xE9 , 0x03 , 0x51 , 0x5E , 0x09 , 0xC1 , 0x00 , 0xDF , 0xF0 , 0x96 , 0xFC , 0xC1 , 0xB5 , 0xE6 , 0x62 , 0x95 , 0x01 , 0x00 , 0x00 , 0x00 ] 
 
第一个应该是加密,用findcrypt搜索一下,搜不出来。。好像在调试的时候搜会很卡,那就直接分析
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 void  __cdecl sub_1005F6F0 (_DWORD *a1) {   int  v1;    int  j;    unsigned  int  i;    __int64 v4;    int  *v5;    sub_1005B16F(&unk_1014000F);   for  ( i = 0 ; i < *a1 >> 3 ; ++i )   {     v5 = (int  *)(a1[2 ] + 8  * i);     v1 = *v5;     v4 = *(_QWORD *)v5;     for  ( j = 0 ; j < 64 ; ++j )     {       LOBYTE(v1) = 1 ;       if  ( v4 < 0  )         v4 = qword_1013C000 ^ sub_10059F9F(v1, HIDWORD(v4));       else          v4 = sub_10059F9F(v1, HIDWORD(v4));     }     *(_QWORD *)v5 = v4;   } } 
 
这啥啊,看不懂。。。最外层循环控制轮数,内层循环控制一轮的加密次数,根据这个来直接写逆向??
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int  __usercall sub_1005FDE0@<eax>  (__int64 pnum@<edx:eax>  , unsigned __int8 ahigh@<cl>  ){   __int64 v2;    if  ( ahigh >= 64 u )   {     LODWORD(v2) = 0 ;   }   else  if  ( ahigh >= 32 u )   {     LODWORD(v2) = 0 ;   }   else    {     return  pnum << (ahigh & 0x1F );   }   return  v2; } 
 
但是这里似乎sub_10059F9F(v1, HIDWORD(v4))的位置有问题,是ida反编译的锅,应该是先执行这个再if else
看汇编窗口:
1 2 3 4 5 6 7 8 9 debug033:1005FDE0 ; int __usercall sub_1005FDE0@<eax>(__int64 pnum@<edx:eax>, unsigned __int8 ahigh@<cl>) debug033:1005FDE0 sub_1005FDE0    proc near               ; CODE XREF: cal↑j debug033:1005FDE0                 cmp     cl, 40h ; '@' debug033:1005FDE3                 jnb     short loc_1005FDFA debug033:1005FDE5                 cmp     cl, 20h ; ' ' debug033:1005FDE8                 jnb     short loc_1005FDF0 debug033:1005FDEA                 shld    edx, eax, cl debug033:1005FDED                 shl     eax, cl debug033:1005FDEF                 retn 
 
 
这里的cl就是1,和传入无关,最后有一句shl     eax, cl
shl是双精度左移,这是类似循环左移的操作,即左移,右边的空值由左边溢出的值补充,类似还有shr双精度右移,并没有0x1f
因此,整体逻辑是:
1 2 3 4 先分组为8 字节x6 组 如果是奇数:与key再左移 如果是偶数:直接左移 得到结果 
 
所以逆向是
1 2 3 分组 如果是奇数:与key 再右移 如果是偶数:直接右移 
 
同时可以知道的是,
如果是奇数右移,左边应该补1
如果是偶数右移,左边不用补
那么逆向:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 last = [0x4D , 0xB8 , 0x76 , 0x29 , 0xF5 , 0xA9 , 0x9E , 0x59 , 0x55 , 0x56 , 0xB1 , 0xC4 , 0x2F , 0x21 , 0x2C , 0x30 , 0xB3 , 0x79 , 0x78 , 0x17 , 0xA8 , 0xED , 0xF7 , 0xDB , 0xE1 , 0x53 , 0xF0 , 0xDB , 0xE9 , 0x03 , 0x51 , 0x5E , 0x09 , 0xC1 , 0x00 , 0xDF , 0xF0 , 0x96 , 0xFC , 0xC1 , 0xB5 , 0xE6 , 0x62 , 0x95 , 0x01 , 0x00 , 0x00 , 0x00 ] q = [0x599EA9F52976B84D , 0x302C212FC4B15655 , 0xDBF7EDA8177879B3 , 0x5E5103E9DBF053E1 , 0xC1FC96F0DF00C109 , 0x000000019562E6B5 ] key = 0x54AA4A9  k=[] for  i in  range (6 ):    for  j in  range (64 ):         if  (q[i] & 1 ):             q[i] = (q[i] ^ key) >> 1              q[i] |= 0x8000000000000000          else :             q[i] = (q[i] >> 1 ) for  i in  range (6 ):    k.append(hex (q[i])) print (k)
 
逆序,转hex,按字符逆序
1 D0g3 {60 E1E72A-576 A8BF0-7701 CBB9-B02415EC}
 
你见过蓝色的小鲸鱼吗 打开文件后,发现是win程序,说明进入主窗口程序和判断函数不一定在一个位置 
所以打开后需要找到判断代码,搜索string无效
根据弹窗函数是MessageBox,尝试搜索
然后找到判断函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int  __thiscall lastcall (void  *this) {   char  wrong[28 ];    CHAR yes[20 ];    CHAR Caption[24 ];    void  *v5;    v5 = this;   __CheckForDebuggerJustMyCode(&unk_52102F);   strcpy (Caption, "tip" );   strcpy (yes, "You Get It!" );   strcpy (wrong, "Wrong user/passwd" );   if  ( *((_DWORD *)v5 + 2 ) != *((_DWORD *)v5 + 3 )     || j__memcmp(*(const  void  **)v5, *((const  void  **)v5 + 1 ), *((_DWORD *)v5 + 3 )) )   {     return  MessageBoxA(0 , wrong, Caption, 0 );   }   else    {     return  MessageBoxA(0 , yes, Caption, 0 );   } } 
 
这里的v5+2和v5+3一定指向一个是输入(可能加密了),另一个说不准,因为有一个密码一个用户名
由于需要进入else分支,所以条件必须为假
即:
1 2 *((_DWORD *)v5 + 2 ) == *((_DWORD *)v5 + 3 )     && !j__memcmpv5, *((const  void **)v5 + 1 ), *((_DWORD *)v5 + 3 )) 
 
既然如此,需要找出能改变v5+2和+3的地址的函数(修改了this),向上找:
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 int  __cdecl main (int  argc, const  char  **argv, const  char  **envp) {   int  result;    void  *v4;    void  *v5;    CHAR *v6;    CHAR *lpString;    HWND DlgItem;    HWND hWnd;    int  v10;    int  WindowTextLengthA;    __CheckForDebuggerJustMyCode(&unk_52105E);   hWnd = GetDlgItem((HWND)argc, 1003 );   DlgItem = GetDlgItem((HWND)argc, 1004 );   WindowTextLengthA = GetWindowTextLengthA(hWnd);   v10 = GetWindowTextLengthA(DlgItem);   lpString = (CHAR *)j__malloc(__CFADD__(WindowTextLengthA, 16 ) ? -1  : WindowTextLengthA + 16 );   result = (int )j__malloc(__CFADD__(v10, 16 ) ? -1  : v10 + 16 );   v6 = (CHAR *)result;   if  ( lpString && result )   {     GetWindowTextA(hWnd, lpString, WindowTextLengthA + 16 );     GetWindowTextA(DlgItem, v6, v10 + 16 );     v5 = operator new(0x10 u);     if  ( v5 )     {       sub_451B43(0x10 u);       v4 = (void  *)sub_450CE3(v5);     }     else      {       v4 = 0 ;     }     sub_44FC2B(&unk_51D38C, 0x10 u);     sub_45126F(lpString, WindowTextLengthA, (int )v6, v10);     j_lastcall(v4);     j__free(lpString);     j__free(v6);     result = (int )v4;     if  ( v4 )       return  sub_44F77B(1 );   }   return  result; } 
 
动调可知:第一个是用户名,第二个是密码
化简:
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 int  __cdecl main (int  argc, const  char  **argv, const  char  **envp) {   int  result;    void  *v4;    void  *v5;    CHAR *password;    CHAR *username;    HWND DlgItem;    HWND hWnd;    int  lenOFpass;    int  lenOFname;    __CheckForDebuggerJustMyCode(&unk_52105E);   hWnd = GetDlgItem((HWND)argc, 1003 );   DlgItem = GetDlgItem((HWND)argc, 1004 );   lenOFname = GetWindowTextLengthA(hWnd);   lenOFpass = GetWindowTextLengthA(DlgItem);   username = (CHAR *)j__malloc(__CFADD__(lenOFname, 16 ) ? -1  : lenOFname + 16 );   result = (int )j__malloc(__CFADD__(lenOFpass, 16 ) ? -1  : lenOFpass + 16 );   password = (CHAR *)result;   if  ( username && result )                        {     GetWindowTextA(hWnd, username, lenOFname + 16 );     GetWindowTextA(DlgItem, password, lenOFpass + 16 );     v5 = operator new(0x10 u);     if  ( v5 )     {       sub_451B43(0x10 u);                               v4 = (void  *)sub_450CE3(v5);                   }     else      {       v4 = 0 ;     }     sub_44FC2B(&specialloc, 0x10 u);                  encode(username, lenOFname, (int )password, lenOFpass);     j_lastcall(v4);                                  j__free(username);     j__free(password);     result = (int )v4;     if  ( v4 )       return  sub_44F77B(1 );   }   return  result; } 
 
后面实在分析不出来了,里面有一些涉及c++的理解的,所以看看wp
在后面有加密函数,用插件findcrypt可以直接看出来是blowfish
在cmp时下断点buf1就是最后的值
1 11A51F049550E2508F17E16CF1632B47 
 
写blowfish的解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from  Crypto.Cipher import  Blowfishimport  binasciiciphertext_hex = "11A51F049550E2508F17E16CF1632B47"  ciphertext = binascii.unhexlify(ciphertext_hex) key = "UzBtZTBuZV9EMGcz"  cipher = Blowfish.new(key.encode('utf-8' ), Blowfish.MODE_ECB) plaintext_bytes = cipher.decrypt(ciphertext) plaintext = plaintext_bytes.decode('utf-8' ) print (plaintext)
 
这个答案是个base64,解密后是@thebluef1sh
(和蓝鲸的唯一关系吗。。。)
所以
1 D0g3 {UzBtZTBuZV9EMGczQHRoZWJsdWVmMXNo} 
 
 
写完啦,
如有错误,请在下方评论区写下您的想法。感谢!
 题目下载