kiseyzed 发表于 2019-7-20 20:49

一道CTF入门题目

本帖最后由 kiseyzed 于 2019-7-20 21:07 编辑

0x0.写在前面
萌新,以前没有接触过CTF,今天同学给我分享了个入门的题目,几经波折还是做出来了,具体就只涉及到代码分析和简单的算法编写。在这里把经验分享出来,欢迎大家共同交流学习,有不对的地方还请指出来,免得误人子弟。
0x1.初探
题目:分析代码找出程序的flag
程序运行界面:



输入假码(程序显示"you can do it"并退出):



大致情况已经了解,仅凭外表和程序大小来判断是c++写的,并且应该是一个64位的可执行程序。

0x2.尝试
拖入OD,尝试破解。
搜索字符串,很明显地看到了失败的提示、疑似真码的文本(Qht_2019_Gsqzcjsf_Vszzc)、成功的提示。



尝试直接输入那一串文本,发现还是提示错误,说明这并不是。
不过不要灰心,分析代码。
来到失败处,代码如下:
0040107C|.F7D1          not ecx
0040107E|.49            dec ecx
0040107F|.BF B0404100   mov edi,welcome_.004140B0                ;Qht_2019_Gsqzcjsf_Vszzc
00401084|.8BD1          mov edx,ecx
00401086|.83C9 FF       or ecx,-0x1
00401089|.F2:AE         repne scas byte ptr es:
0040108B|.F7D1          not ecx
0040108D|.49            dec ecx
0040108E|.3BD1          cmp edx,ecx
00401090|.74 19         je short welcome_.004010AB
00401092|.68 18414100   push welcome_.00414118                   ;you.can.doit\n
00401097|.E8 B4390000   call welcome_.00404A50
0040109C|.83C4 04       add esp,0x4
0040109F|.83C8 FF       or eax,-0x1
004010A2|.5F            pop edi                                  ;welcome_.<ModuleEntryPoint>
004010A3|.5E            pop esi                                  ;welcome_.<ModuleEntryPoint>
004010A4|.81C4 00080000 add esp,0x800
004010AA|.C3            retn
004010AB|>8D72 FF       lea esi,dword ptr ds:
004010AE|.33C9          xor ecx,ecx
004010B0|.85F6          test esi,esi
004010B2|.7C 41         jl short welcome_.004010F5

有点乱,不过大概就是判断输入的文本长度是否与“Qht_2019_Gsqzcjsf_Vszzc”的长度相等,这里我们直接将ZFlag置1,使跳转实现,分析后面代码



接下来我们来到了一个循环体

004010AE|.33C9          xor ecx,ecx
004010B0|.85F6          test esi,esi
004010B2|.7C 41         jl short welcome_.004010F5
004010B4|>8A440C 08   /mov al,byte ptr ss:
004010B8|.3C 5A         |cmp al,0x5A
004010BA|.7F 17         |jg short welcome_.004010D3
004010BC|.3C 41         |cmp al,0x41
004010BE|.7C 13         |jl short welcome_.004010D3
004010C0|.0FBEC0      |movsx eax,al
004010C3|.83E8 33       |sub eax,0x33
004010C6|.BF 1A000000   |mov edi,0x1A
004010CB|.99            |cdq
004010CC|.F7FF          |idiv edi                              ;welcome_.004140C8
004010CE|.80C2 41       |add dl,0x41
004010D1|.EB 19         |jmp short welcome_.004010EC
004010D3|>3C 7A         |cmp al,0x7A
004010D5|.7F 19         |jg short welcome_.004010F0
004010D7|.3C 61         |cmp al,0x61
004010D9|.7C 15         |jl short welcome_.004010F0
004010DB|.0FBEC0      |movsx eax,al
004010DE|.83E8 53       |sub eax,0x53
004010E1|.BF 1A000000   |mov edi,0x1A
004010E6|.99            |cdq
004010E7|.F7FF          |idiv edi                              ;welcome_.004140C8
004010E9|.80C2 61       |add dl,0x61
004010EC|>88540C 08   |mov byte ptr ss:,dl
004010F0|>41            |inc ecx
004010F1|.3BCE          |cmp ecx,esi
004010F3|.^ 7E BF         \jle short welcome_.004010B4
004010F5|>53            push ebx
004010F6|.8D7424 0C   lea esi,dword ptr ss:

这里就是对输入的文本进行处理的地方了,我们后面再分析,直接循环尾F2下断,使程序运行到断点处



单步一下,发现了我们输入的假码,不过没变,应该是对数字不进行处理,继续分析
又来到一个循环体

004010FA|.B8 B0404100   mov eax,welcome_.004140B0                ;Qht_2019_Gsqzcjsf_Vszzc
004010FF|>8A10          /mov dl,byte ptr ds:
00401101|.8A1E          |mov bl,byte ptr ds:
00401103|.8ACA          |mov cl,dl
00401105|.3AD3          |cmp dl,bl
00401107|.75 1E         |jnz short welcome_.00401127
00401109|.84C9          |test cl,cl
0040110B|.74 16         |je short welcome_.00401123
0040110D|.8A50 01       |mov dl,byte ptr ds:
00401110|.8A5E 01       |mov bl,byte ptr ds:
00401113|.8ACA          |mov cl,dl
00401115|.3AD3          |cmp dl,bl
00401117|.75 0E         |jnz short welcome_.00401127
00401119|.83C0 02       |add eax,0x2
0040111C|.83C6 02       |add esi,0x2
0040111F|.84C9          |test cl,cl
00401121|.^ 75 DC         \jnz short welcome_.004010FF
00401123|>33C0          xor eax,eax
00401125|.EB 05         jmp short welcome_.0040112C

这里是挨个和处理后的文本与“Qht_2019_Gsqzcjsf_Vszzc”进行比较,如果有不一致就调到错误提示的地方



这里将跳转语句nop,运行



成功了,不过这当然不是我们需要的flag,不过可以得到一条信息:flag='love_'+str+"_"
其中,key为执行上面文本处理算法得到的(Qht_2019_Gsqzcjsf_Vszzc)
代码的执行流程为:
str->key->flag
所以我们要找到原始文本str,并和“love_”以及“_”进行拼接,就得到了答案
要找到str,就得将分析出生成key的算法,再将算法进行逆向操作

0x3.逆向
重新载入程序,来到算法的地方

004010B4|> /8A440C 08   /mov al,byte ptr ss:
004010B8|. |3C 5A         |cmp al,0x5A
004010BA|. |7F 17         |jg short welcome_.004010D3
004010BC|. |3C 41         |cmp al,0x41
004010BE|. |7C 13         |jl short welcome_.004010D3
004010C0|. |0FBEC0      |movsx eax,al
004010C3|. |83E8 33       |sub eax,0x33
004010C6|. |BF 1A000000   |mov edi,0x1A
004010CB|. |99            |cdq
004010CC|. |F7FF          |idiv edi
004010CE|. |80C2 41       |add dl,0x41
004010D1|. |EB 19         |jmp short welcome_.004010EC
004010D3|> |3C 7A         |cmp al,0x7A
004010D5|. |7F 19         |jg short welcome_.004010F0
004010D7|. |3C 61         |cmp al,0x61
004010D9|. |7C 15         |jl short welcome_.004010F0
004010DB|. |0FBEC0      |movsx eax,al
004010DE|. |83E8 53       |sub eax,0x53
004010E1|. |BF 1A000000   |mov edi,0x1A
004010E6|. |99            |cdq
004010E7|. |F7FF          |idiv edi
004010E9|. |80C2 61       |add dl,0x61
004010EC|> |88540C 08   |mov byte ptr ss:,dl
004010F0|> |41            |inc ecx
004010F1|. |3BCE          |cmp ecx,esi
004010F3|.^\7E BF         \jle short welcome_.004010B4
004010F5|>53            push ebx

这次假码不使用数字,用字母a

发现全部变成了o
大写字母也是一样,且为固定转换,严格对应(a->o,A->O.......)
我们当然可以挨个将a-z输入一遍,然后建立一个密码表,将key解密,理所应当的会得到str
不过这种方法显得有点儿蠢,万一别人用1k位的数据,或者随机转换,那就行不通了

老老实实分析代码,具体规则:
1.长度为23位
2.小写字母
(ASCII-0x53)/0x1A+0x61
3.大写字母
(ASCII-0x33)/0x1A+0x41

str--规则-->key(Qht_2019_Gsqzcjsf_Vszzc)
根据上面我们可以开始编写代码了,重装了系统,手头没用趁手的工具,用谷歌浏览器开发者工具里面的控制台写JS来实现
具体代码:

function calc() {
    var str = "";
    var key = "Qht_2019_Gsqzcjsf_Vszzc";
    var n, k, j;
    for (var i = 0; i < key.length; i++) {
      n = key.substring(i, i + 1).charCodeAt();
      if (n >= 65 && n <= 90) {
            n = n - 65;
            j = 0;
            while (true) {
                k = j * 26 + n;
                k = k + 51;
                j++;
                if (k >= 65 && k <= 90) {
                  break;
                }
            }
      } else if (n >= 97 && n <= 122) {
            n = n - 97;
            j = 0;
            while (true) {
                k = j * 26 + n;
                k = k + 83;
                j++;
                if (k >= 97 && k <= 122) {
                  break;
                }
            }
      } else {
            k = n;
      }
      str = str + String.fromCharCode(k);
    }
    console.log(str);
}



得到答案:Ctf_2019_Seclover_Hello
输入进去也反馈了正确结果



flag=love_Ctf_2019_Seclover_Hello_
网站结果也确认无误,至此,逆向就告一段落了。

0x4.总结
没什么需要总结的,知道了原理之后就没什么难度了。
不过这是我第一次尝试CTF,也是值得纪念的,希望以后共同学习交流。


最后附上样本,后缀改成exe就行,不保证安全无毒,建议虚拟机或影子系统。

闲月疏云 发表于 2019-7-20 22:29

我也刚开始接触CTF,用了一个小时多一点(捂脸)
主要是刚开始就踩坑orz 你输入的123456那里我直接随手打了个回车然后程序就直接关闭了,所以我以为这程序就直接让我暴力找flag的……
然后我就直接扔进IDA F5了,你爆破出flag之前的步骤用IDA能猜的差不多(要不是IDA把那个strlen给解析出来我就真的是从入门到放弃了)

那几段很鬼畜的汇编代码是inline后的strcpy和strlen
算key如下,取余26我是算了取值范围发现只能取1所以就分开计算了

Moon3r 发表于 2019-7-22 09:18

闲月疏云 发表于 2019-7-20 22:38
凯撒加密是不是通用思路就是直接跑密码表?

不用密码表,直接暴力遍历字母表就行,ascii码一直加然后模26,再去字母表里对应位置取出就行了

小2b 发表于 2019-7-20 21:01

强,ctf也挺烧脑

Moon3r 发表于 2019-7-20 21:57

一看到那串字符串就是凯撒啊,这都不用想的

闲月疏云 发表于 2019-7-20 22:38

Moon3r 发表于 2019-7-20 21:57
一看到那串字符串就是凯撒啊,这都不用想的

凯撒加密是不是通用思路就是直接跑密码表?

icezyf 发表于 2019-7-20 23:05

非常不错

zilvyyrs 发表于 2019-7-21 00:29

这个有点强{:301_1009:}

灰灰。 发表于 2019-7-21 00:32

给楼主推荐个工具吧,叫ida;www

Avenshy 发表于 2019-7-21 15:10

算法值得学习~

wang19940311 发表于 2019-7-21 15:36

不明觉厉
页: [1] 2 3 4
查看完整版本: 一道CTF入门题目