安洵杯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}
写完啦,
如有错误,请在下方评论区写下您的想法。感谢!
题目下载