本文最后更新于:2022-02-16 中午
前言
今年来做一下吾爱论坛的红包题目,之前就知道有这个活动,但是从来没参与过,今年参与一下。
第一题送分题就不说了,因为也不会安卓和web,所以第四题第五题都没做,只做了二三题,两道windows的题目。
好久没写过东西了,感觉写的时候,突然有点不熟悉了,思路啥的也表达不明白了…
Windows初级题
这个题是一个明码比较,比较简单。
上来输入之后,先是对输入的长度进行了限制,必须是23位的pasawd。
sub_402B70
是比较函数,失败的话,JNZ
跳向失败的地方。
比较时看到比较的passwd是2022HappyNewYear52PoJie
,这就是flag了。
Windows中级题
打开程序,看到要输入一个UID
,还有一个KEY
,那么应该是通过UID
计算出来KEY
查壳,看到是UPX的壳
脱壳,过程就不再赘述了,我直接手动脱了。
首先对UID进行了一个限制,输入的UID是不能大于2000000
的。
然后是三个对于输入的UID
进行处理的函数
sub_401100
主要就是返回UID % 25
sub_401080
定义了一个数组,返回table[UID % 0xC]
sub_401110
函数还挺复杂的,我刚开始的也没有仔细的分析这个函数,其实不分析这个也是可以算出来的KEY
的,先不说这个函数,最后再说它。
继续往下跟,可以看到有一个跳转会跳向失败,那么前面的函数就是KEY
的生成加比较了。
进入sub_401520
,前面这些就是会生成一个字符串
生成的字符串flag{Happy_New_Year_52Pojie_2022}
生成这个字符串之后,就进入了计算和比较的地方。
首先会对输入的KEY
逐位判断,当前这一位是否是字母,如果这个字符的ASCII码
小于A
,或大于z
,或大于Z
小于a
,那么它就不是一个字母,然后就不会对其进行处理。
如果是字母的话,分为大写字母和小写字母的情况,不过算法都是一样的,区别是大写字母的计算参与计算的是大写字母A
,小写则是a
。
这里用小写字母来说明,其中V10
就是sub_401110
的结果,V11
是sub_401100
的结果。这两个值,在计算的时候,可以通过动调直接看结果,当然V11
本来计算就很简单,不需要看也可以,主要是V10
也就是sub_401110
函数中的返回值,这个稍微复杂一些,如果不写通用注册机,可以输入一个UID,然后动调查看结果,直接进行计算。所以我刚才说可以暂时不用分析这个函数,不过我们最后再说这个函数。
| 00401400 0FBE07 movsx eax,byte ptr ds:[edi] 00401403 2BC5 sub eax,ebp 00401405 BF 1A000000 mov edi,0x1A 0040140A 83E8 61 sub eax,0x61 0040140D 0FAFC3 imul eax,ebx 00401410 99 cdq 00401411 F7FF idiv edi 00401413 80C2 61 add dl,0x61 00401416 8811 mov byte ptr ds:[ecx],dl
|
如果(flag[i] - V11 - 0x61) * V10) < 0
,那么还需要再给上一步计算后的结果加上0x1A。
| 0040143F 0FBE09 movsx ecx,byte ptr ds:[ecx] 00401442 2BCD sub ecx,ebp 00401444 83E9 61 sub ecx,0x61 00401447 0FAFCB imul ecx,ebx 0040144A 85C9 test ecx,ecx 0040144C 7D 76 jge short 3.004014C4
|
| 00401488 8B5424 1C mov edx,dword ptr ss:[esp+0x1C] 0040148C 8A0F mov cl,byte ptr ds:[edi] 0040148E 80C1 1A add cl,0x1A 00401491 8D0432 lea eax,dword ptr ds:[edx+esi] 00401494 8808 mov byte ptr ds:[eax],cl 00401496 EB 28 jmp short 3.004014C0
|
最后就是和flag{Happy_New_Year_52Pojie_2022}
进行比较。
现在就可以开始尝试算出我们的KEY
了,根据上面分析的算法,我们将V11
定义为parm1
,V10
定义为parm2
,首先拿我自己的UID
来做示例,我的UID
计算出来的parm1
是12,parm2
是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 30 31 32 33 34 35 36 37
| void CalcKey(int parm1, int parm2) { char key[34] = { 0 }; char* flag = "flag{Happy_New_Year_52Pojie_2022}"; for (int i = 0; i < strlen(flag); i++) { if (flag[i] < 'A' || flag[i]>'z' || (flag[i] > 'Z' && flag[i] < 'a')) { key[i] = flag[i]; continue; } for (int j = 'A'; j <= 'z'; j++) { int tmp = 0;
if (j <= 'Z' && j>= 'A') { tmp = ((j - parm1 - 0x41) * parm2) % 0x1A + 0x41; if (((j - parm1 - 0x41) * parm2) < 0) { tmp += 0x1A; } } if (j <= 'z' && j>='a') { tmp = ((j - parm1 - 0x61) * parm2) % 0x1A + 0x61; if (((j - parm1 - 0x61) * parm2) < 0) { tmp += 0x1A; } }
if (tmp == flag[i]) { key[i] = j; break; } } } printf("your key is :\n%s\n", key); }
|
可以求出当前的flag
现在来说一下关于parm2
的计算,也就是sub_401110
函数。
对于这块呢,我的理解就是一个数组的计算,定义一个大的数组,然后通过计算给它赋值,结束循环后,通过循环次数在里面找特定的一个值。这块主要就是通过分析,重现一个它的操作,直接看代码吧。
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
| 00401110 81EC 80000000 sub esp,0x80 00401116 53 push ebx 00401117 55 push ebp 00401118 56 push esi 00401119 8BB424 90000000 mov esi,dword ptr ss:[esp+0x90] ; kernel32.77E2ED6C 00401120 57 push edi 00401121 B9 1F000000 mov ecx,0x1F 00401126 33C0 xor eax,eax 00401128 8D7C24 14 lea edi,dword ptr ss:[esp+0x14] 0040112C F3:AB rep stos dword ptr es:[edi] 0040112E B8 01000000 mov eax,0x1 00401133 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14] 00401137 33ED xor ebp,ebp 00401139 894424 10 mov dword ptr ss:[esp+0x10],eax 0040113D 894424 1C mov dword ptr ss:[esp+0x1C],eax 00401141 BF 1A000000 mov edi,0x1A 00401146 83E9 10 sub ecx,0x10 00401149 8BC7 mov eax,edi 0040114B 83C1 10 add ecx,0x10 0040114E 99 cdq 0040114F F7FE idiv esi 00401151 8BC7 mov eax,edi 00401153 8BDA mov ebx,edx 00401155 99 cdq 00401156 F7FE idiv esi 00401158 8B11 mov edx,dword ptr ds:[ecx] 0040115A 45 inc ebp 0040115B 8951 0C mov dword ptr ds:[ecx+0xC],edx 0040115E 8B51 08 mov edx,dword ptr ds:[ecx+0x8] 00401161 8951 14 mov dword ptr ds:[ecx+0x14],edx 00401164 8B11 mov edx,dword ptr ds:[ecx] 00401166 8B79 FC mov edi,dword ptr ds:[ecx-0x4] 00401169 0FAFD0 imul edx,eax 0040116C 2BFA sub edi,edx 0040116E 8979 10 mov dword ptr ds:[ecx+0x10],edi 00401171 8B51 08 mov edx,dword ptr ds:[ecx+0x8] 00401174 0FAFD0 imul edx,eax 00401177 8B41 04 mov eax,dword ptr ds:[ecx+0x4] 0040117A 8BFE mov edi,esi 0040117C 2BC2 sub eax,edx 0040117E 8BF3 mov esi,ebx 00401180 85DB test ebx,ebx 00401182 8941 18 mov dword ptr ds:[ecx+0x18],eax 00401185 ^ 75 C2 jnz short 3.00401149 00401187 C1E5 04 shl ebp,0x4 0040118A 5F pop edi ; 3.00402319 0040118B 5E pop esi ; 3.00402319 0040118C 8B4C2C 04 mov ecx,dword ptr ss:[esp+ebp+0x4] 00401190 8D442C 04 lea eax,dword ptr ss:[esp+ebp+0x4] 00401194 5D pop ebp ; 3.00402319 00401195 5B pop ebx ; 3.00402319 00401196 85C9 test ecx,ecx 00401198 7D 05 jge short 3.0040119F 0040119A 83C1 1A add ecx,0x1A 0040119D 8908 mov dword ptr ds:[eax],ecx 0040119F 8B00 mov eax,dword ptr ds:[eax] 004011A1 81C4 80000000 add esp,0x80 004011A7 C3 retn
|
我这里定义了一个32的数组,在程序中是一个31的数组(0x7c/4=31)。所以我在每一个运算的时候,都加了1,在原来的401166
处的ecx-4
就是我的table[0]。然后加i是每次循环都要加4,对应40114B
处的add ecx,0x10
。count
就是ebp
的值,然后最后在取数的时候这样table[(count / 4) + 4 - 2]
,是因为在40118C
处的 mov ecx,dword ptr ss:[esp+ebp+0x4]
,这里的基地址esp
实际上要比程序中定义的数组开头地址要小12,比如最开始循环时ecx
值是80,这里的esp
值就是68,然后刚才也说了,我定义的数组是32的,所以我这里减去2就可以了。
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
| int CalcParm2(int parm2) { int table[32] = { 0 }; table[0] = 1; table[3] = 1; int x, y, z; x = 0x1A; z = parm2; int count = 0, i = 0; do { count++; y = x % z; int tmp = x / z; x = z; z = y; table[i + 4] = table[i + 1]; table[i + 6] = table[i + 3]; table[i + 5] = table[i] - table[i + 1] * tmp; table[i + 7] = table[i + 2] - table[i + 3] * tmp; i += 4; } while (y); count = count << 4; int ret = table[(count / 4) + 4 - 2]; if (ret < 0) { ret += 0x1A; } return ret; }
|
主函数
| int main() { int parm1, parm2; int uid; int table[] = { 1,3,5,7,9,11,15,17,19,21,23,25 }; printf("please input your uid:"); scanf("%d", &uid); parm1 = uid % 25; parm2 = CalcParm2(table[uid % 0xC]); CalcKey(parm1, parm2); return 0; }
|
完整的注册机就写完了,这里算一下UID
为123456的KEY
。