b1ackie 发表于 2022-2-16 10:49

【2022春节】windows初级、中级WP

本帖最后由 b1ackie 于 2022-2-16 11:56 编辑

## 前言

今年来做一下吾爱论坛的红包题目,之前就知道有这个活动,但是从来没参与过,今年参与一下。

第一题送分题就不说了,因为也不会安卓和web,所以第四题第五题都没做,只做了二三题,两道windows的题目。

好久没写过东西了,感觉写的时候,突然有点不熟悉了,思路啥的也表达不明白了...

刚才看了下十一七大佬的帖子,才知道原来这些是什么算法,自己做的时候都不知道仿射密码啥的,就直接看,还是太菜了,要学习的东西还是很多。

关于最后的注册机,主要就是 sub_401110 这个函数这块,我写的有点复杂,我也不知道具体是什么算法之类的,我就是直接按照分析的结果对照这进行写了,不看我写的那个介绍的话,可能不好看明白,有疑问的话,欢迎留言,我们交流,如果有任何问题的话,也欢迎大神指出。
## Windows初级题

这个题是一个明码比较,比较简单。

上来输入之后,先是对输入的长度进行了限制,必须是23位的pasawd。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/1.png)


`sub_402B70`是比较函数,失败的话,`JNZ`跳向失败的地方。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/2.png)

比较时看到比较的passwd是`2022HappyNewYear52PoJie`,这就是flag了。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/3.png)

## Windows中级题

打开程序,看到要输入一个`UID`,还有一个`KEY`,那么应该是通过`UID`计算出来`KEY`

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/6.png)

查壳,看到是UPX的壳

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/4.png)

脱壳,过程就不再赘述了,我直接手动脱了。

首先对UID进行了一个限制,输入的UID是不能大于`2000000`的。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/5.png)

然后是三个对于输入的`UID`进行处理的函数

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/7.png)

`sub_401100`主要就是`返回UID % 25`

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/8.png)

`sub_401080`定义了一个数组,返回`table`

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/9.png)

`sub_401110`函数还挺复杂的,我刚开始的也没有仔细的分析这个函数,其实不分析这个也是可以算出来的`KEY`的,先不说这个函数,最后再说它。

继续往下跟,可以看到有一个跳转会跳向失败,那么前面的函数就是`KEY`的生成加比较了。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/10.png)

进入`sub_401520`,前面这些就是会生成一个字符串

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/11.png)

生成的字符串`flag{Happy_New_Year_52Pojie_2022}`

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/12.png)

生成这个字符串之后,就进入了计算和比较的地方。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/13.png)

首先会对输入的`KEY`逐位判断,当前这一位是否是字母,如果这个字符的`ASCII码`小于`A`,或大于`z`,或大于`Z`小于`a`,那么它就不是一个字母,然后就不会对其进行处理。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/14.png)

如果是字母的话,分为大写字母和小写字母的情况,不过算法都是一样的,区别是大写字母的计算参与计算的是大写字母`A`,小写则是`a`。

这里用小写字母来说明,其中`V10`就是`sub_401110`的结果,`V11`是`sub_401100`的结果。这两个值,在计算的时候,可以通过动调直接看结果,当然`V11`本来计算就很简单,不需要看也可以,主要是`V10`也就是`sub_401110`函数中的返回值,这个稍微复杂一些,如果不写通用注册机,可以输入一个UID,然后动调查看结果,直接进行计算。所以我刚才说可以暂时不用分析这个函数,不过我们最后再说这个函数。

```
00401400    0FBE07          movsx eax,byte ptr ds:   
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:,dl
```

`(flag - V11 - 0x61) * V10)% 0x1A + 0x61`

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/15.png)

如果`(flag - V11 - 0x61) * V10) < 0`,那么还需要再给上一步计算后的结果加上0x1A。

```
0040143F    0FBE09          movsx ecx,byte ptr ds:
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:
0040148C    8A0F            mov cl,byte ptr ds:
0040148E    80C1 1A         add cl,0x1A
00401491    8D0432          lea eax,dword ptr ds:
00401494    8808            mov byte ptr ds:,cl
00401496    EB 28         jmp short 3.004014C0
```

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/16.png)

最后就是和`flag{Happy_New_Year_52Pojie_2022}`进行比较。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/17.png)

现在就可以开始尝试算出我们的`KEY`了,根据上面分析的算法,我们将`V11`定义为`parm1`,`V10`定义为`parm2`,首先拿我自己的`UID`来做示例,我的`UID`计算出来的`parm1`是12,`parm2`是25,可以直接代入进行计算。

```
void CalcKey(int parm1, int parm2) {
      char key = { 0 };
      char* flag = "flag{Happy_New_Year_52Pojie_2022}";
      
      for (int i = 0; i < strlen(flag); i++) {
                //除字母以外的字符不进行处理
                if (flag < 'A' || flag>'z' || (flag > 'Z' && flag < 'a')) {
                        key = flag;
                        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) {
                              key = j;
                              break;
                        }
                }
      }
      printf("your key is :\n%s\n", key);
}
```

可以求出当前的flag

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/18.png)

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/20.png)

现在来说一下关于`parm2`的计算,也就是`sub_401110`函数。

对于这块呢,我的理解就是一个数组的计算,定义一个大的数组,然后通过计算给它赋值,结束循环后,通过循环次数在里面找特定的一个值。这块主要就是通过分析,重现一个它的操作,直接看代码吧。

```
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:          ; 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:
0040112C    F3:AB         rep stos dword ptr es:
0040112E    B8 01000000   mov eax,0x1
00401133    8D4C24 14       lea ecx,dword ptr ss:
00401137    33ED            xor ebp,ebp
00401139    894424 10       mov dword ptr ss:,eax
0040113D    894424 1C       mov dword ptr ss:,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:
0040115A    45            inc ebp
0040115B    8951 0C         mov dword ptr ds:,edx
0040115E    8B51 08         mov edx,dword ptr ds:
00401161    8951 14         mov dword ptr ds:,edx
00401164    8B11            mov edx,dword ptr ds:
00401166    8B79 FC         mov edi,dword ptr ds:
00401169    0FAFD0          imul edx,eax
0040116C    2BFA            sub edi,edx
0040116E    8979 10         mov dword ptr ds:,edi
00401171    8B51 08         mov edx,dword ptr ds:
00401174    0FAFD0          imul edx,eax
00401177    8B41 04         mov eax,dword ptr ds:
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:,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:
00401190    8D442C 04       lea eax,dword ptr ss:
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:,ecx
0040119F    8B00            mov eax,dword ptr ds:
004011A1    81C4 80000000   add esp,0x80
004011A7    C3            retn
```

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/19.png)

我这里定义了一个32的数组,在程序中是一个31的数组(0x7c/4=31)。所以我在每一个运算的时候,都加了1,在原来的`401166`处的`ecx-4`就是我的table。然后加i是每次循环都要加4,对应`40114B`处的`add ecx,0x10`。`count`就是`ebp`的值,然后最后在取数的时候这样`table[(count / 4) + 4 - 2]`,是因为在`40118C `处的` mov ecx,dword ptr ss:`,这里的基地址`esp`实际上要比程序中定义的数组开头地址要小12,比如最开始循环时`ecx`值是80,这里的`esp`值就是68,然后刚才也说了,我定义的数组是32的,所以我这里减去2就可以了。

```c
int CalcParm2(int parm2) {
      int table = { 0 };
      table = 1;
      table = 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 = table;
                table = table;
                table = table - table * tmp;
                table = table - table * 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;                //sub_401100的结果
      parm2 = CalcParm2(table);      //sub_401080与sub_401110的结果
      CalcKey(parm1, parm2);
      return 0;
}
```

完整的注册机就写完了,这里算一下`UID`为123456的`KEY`。

![](https://b1ackie.cn/2022/02/11/%E5%90%BE%E7%88%B12022%E6%98%A5%E8%8A%82%E7%BA%A2%E5%8C%85%E9%A2%98%E7%9B%AE/21.png)

Guardian09 发表于 2022-2-16 11:07

感谢分享

字符数字 发表于 2022-2-16 11:19

谢谢分享,学习了{:301_1003:}

cococat 发表于 2022-2-16 12:05

不明觉厉,不断学习!

csp3949160 发表于 2022-2-16 14:31

想学习,但没看懂{:1_924:}

唯一笑解千愁 发表于 2022-2-16 15:57

没看懂...............

cuiwangliu 发表于 2022-2-16 17:28

感谢分享

Meiosis 发表于 2022-2-16 19:33

学习一个。

weizhuqiang 发表于 2022-2-17 06:53

感谢分享

ycuny 发表于 2022-2-17 08:23

想学习,但是完全看不懂{:1_896:}
页: [1] 2
查看完整版本: 【2022春节】windows初级、中级WP