一道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就行,不保证安全无毒,建议虚拟机或影子系统。
我也刚开始接触CTF,用了一个小时多一点(捂脸)
主要是刚开始就踩坑orz 你输入的123456那里我直接随手打了个回车然后程序就直接关闭了,所以我以为这程序就直接让我暴力找flag的……
然后我就直接扔进IDA F5了,你爆破出flag之前的步骤用IDA能猜的差不多(要不是IDA把那个strlen给解析出来我就真的是从入门到放弃了)
那几段很鬼畜的汇编代码是inline后的strcpy和strlen
算key如下,取余26我是算了取值范围发现只能取1所以就分开计算了
闲月疏云 发表于 2019-7-20 22:38
凯撒加密是不是通用思路就是直接跑密码表?
不用密码表,直接暴力遍历字母表就行,ascii码一直加然后模26,再去字母表里对应位置取出就行了 强,ctf也挺烧脑 一看到那串字符串就是凯撒啊,这都不用想的 Moon3r 发表于 2019-7-20 21:57
一看到那串字符串就是凯撒啊,这都不用想的
凯撒加密是不是通用思路就是直接跑密码表? 非常不错 这个有点强{:301_1009:} 给楼主推荐个工具吧,叫ida;www 算法值得学习~ 不明觉厉