本帖最后由 kiseyzed 于 2019-7-20 21:07 编辑
0x0.写在前面
萌新,以前没有接触过CTF,今天同学给我分享了个入门的题目,几经波折还是做出来了,具体就只涉及到代码分析和简单的算法编写。在这里把经验分享出来,欢迎大家共同交流学习,有不对的地方还请指出来,免得误人子弟。
0x1.初探
题目:分析代码找出程序的flag
程序运行界面:
输入假码(程序显示"you can do it"并退出):
大致情况已经了解,仅凭外表和程序大小来判断是c++写的,并且应该是一个64位的可执行程序。
0x2.尝试
拖入OD,尝试破解。
搜索字符串,很明显地看到了失败的提示、疑似真码的文本(Qht_2019_Gsqzcjsf_Vszzc)、成功的提示。
尝试直接输入那一串文本,发现还是提示错误,说明这并不是。
不过不要灰心,分析代码。
来到失败处,代码如下:
[Asm] 纯文本查看 复制代码 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:[edi]
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:[edx-0x1]
004010AE |. 33C9 xor ecx,ecx
004010B0 |. 85F6 test esi,esi
004010B2 |. 7C 41 jl short welcome_.004010F5
有点乱,不过大概就是判断输入的文本长度是否与“Qht_2019_Gsqzcjsf_Vszzc”的长度相等,这里我们直接将ZFlag置1,使跳转实现,分析后面代码
接下来我们来到了一个循环体
[Asm] 纯文本查看 复制代码 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:[esp+ecx+0x8]
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:[esp+ecx+0x8],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:[esp+0xC]
这里就是对输入的文本进行处理的地方了,我们后面再分析,直接循环尾F2下断,使程序运行到断点处
单步一下,发现了我们输入的假码,不过没变,应该是对数字不进行处理,继续分析
又来到一个循环体
[Asm] 纯文本查看 复制代码 004010FA |. B8 B0404100 mov eax,welcome_.004140B0 ; Qht_2019_Gsqzcjsf_Vszzc
004010FF |> 8A10 /mov dl,byte ptr ds:[eax]
00401101 |. 8A1E |mov bl,byte ptr ds:[esi]
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:[eax+0x1]
00401110 |. 8A5E 01 |mov bl,byte ptr ds:[esi+0x1]
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.逆向
重新载入程序,来到算法的地方
[Asm] 纯文本查看 复制代码 004010B4 |> /8A440C 08 /mov al,byte ptr ss:[esp+ecx+0x8]
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:[esp+ecx+0x8],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来实现
具体代码:
[JavaScript] 纯文本查看 复制代码 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就行,不保证安全无毒,建议虚拟机或影子系统。
welcome_CTF.txt
(96 KB, 下载次数: 61)
|