题目
看雪.TSRC 2017CTF秋季赛
地址:https://ctf.pediy.com/game-fight-47.htm
入坑
初始程序
打开国际惯例,查看字符串,找到引用,
#main函数
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 push offset aCrackmeForCtf2 ; "\n Crackme for CTF2017 @Pediy.\n"
.text:00401005 call printf
.text:0040100A add esp, 4
.text:0040100D mov dword ptr unk_41B034, 2
.text:00401017 call sub_401050
.text:0040101C call sub_401090
.text:00401021 call sub_4010E0
.text:00401026 mov eax, dword ptr unk_41B034
.text:0040102B test eax, eax
.text:0040102D jnz short loc_40103F
.text:0040102F push offset aYouGetIt ; "You get it!\n"
.text:00401034 call printf
.text:00401039 add esp, 4
.text:0040103C xor eax, eax
.text:0040103E retn
main函数,很简单,不过不能F5,看来是作者自己汇编,(因为三连call),很简单的流程在text:00401017
里面进行输入
#sub_401050函数
.text:00401050 var_C = dword ptr -0Ch #注意这个地方
.text:00401050
.text:00401050 sub esp, 0Ch
.text:00401053 push offset aCodedByFpc ; " Coded by Fpc.\n\n"
.text:00401058 call printf
.text:0040105D add esp, 4
.text:00401060 push offset aPleaseInputYou ; " Please input your code: "
.text:00401065 call printf
.text:0040106A add esp, 4
.text:0040106D lea eax, [esp+0Ch+var_C]
.text:00401071 push eax
.text:00401072 push offset aS ; "%s" #这里没有确定输入大小,可以导致溢出
.text:00401077 call _scanf
.text:0040107C lea eax, [esp+14h+var_C]
.text:00401080 add esp, 14h
.text:00401083 retn
.text:00401083 sub_401050 endp
经过测试,输入9位没有问题,输入12位会跳转到401000地址处,但是不能输入10位或11位,会导致地址错误,堆栈不平衡。可以自己试验下。
直接跳转到You get it
因为有溢出,我们直接覆盖ret地址,也就是这种aaaabbbbcccc+目标地址十六进制
input:aaaabbbbcccc/@
但是不符合解题要求,这个值输入不了,只能复制粘贴上
看看下面两个call
#sub_401090()
if ( v1 && v0 && v1 != v0 && 5 * (v1 - v0) + v1 == 2404399682 && 13 * (v1 - v0) + v0 == 4015012418 )
--unk_41B034;
#sub_4010E0()
if ( v1 && v0 && v1 != v0 && 17 * (v1 - v0) + v1 == -207009661 && 7 * (v1 - v0) + v0 == 866732163 )
--unk_41B034;
可能ida翻译有问题,根据汇编把他两化简出来
401090:
ecx=5678 x
edx=1234 y
eax=eax-edx = x-y
l.3 = eax = x-y
eax = eax*5 = (x-y)*5
ecx = eax+ecx = (x-y)*5+x
(x-y)*5+x = 0x8f503a42
4010e0:
ecx=5678 x
edx=1234 y
eax=ecx=x
eax=eax-edx=x-y
l.3=eax=x-y
eax=eax*0x11=(x-y)*0x11
ecx=ecx+eax=(x-y)*0x11+x
(x-y)*0x11+x = 0xf3a94883
z3 check一下,发现无解
而且看了一下,只能通过这两个地址减少unk_41B034
的值所以这个地址是不行的
别人的write up
https://bbs.pediy.com/thread-222372.htm
这个人写的很好,很接地气,让我学会了不少。
我自己再来记录一下
找到一块可输入地址
因为我们可以输入flag是数字和字母,而0x40是@,说一不行,我们排除0x40xxxx地址,然后开始从0x410000开始找,发先0x413131有个很奇怪的不能识别的,管他那么多,跳转到这试试。
跳转到0x413131
根据前面的溢出
我们前面12位随意填,因为 我们输入值在内存是以十六进制反向保存 所以我们输入11A 保存到内存就是413131
所以我们输入aaaabbbbcccc11A 就可以跳转到这里了。然后发现全都是跳转,可以看出好像是故意留的花指令。
od run 跟踪去花指令
这题也是让我学会了od的跟踪,确实好用啊。
下断413131
这里是花指令开始的地方,我们先在40102D地址下短,打开od->查看->run跟踪
,然后打开调试->跟踪步入,然后od会记录下从413131运行到40102D这之间的地址,方便我们查看。
更改花指令
因为我们输入的肯定是错误的flag 所以花指令肯定是不全的,而且肯定是不全的。我们查看花指令的413420 ,将jnz改为jz,让他跳转,继续验证,后面的也是同理,在 413420 0x41362e 都下断,修改指令,最后可以把有用指令清理出来
ecx=41414141 x
ebx=42424242 y
edx=43434343 z
eax=ecx-ebx=x-y
eax=eax<<2 = (x-y)*4
eax=eax+ecx = (x-y)*4+x
eax=eax+edx = (x-y)*4+x+z
(x-y)*4+x+z = 0xeaf917e2
eax=0+ecx=x
eax=eax-ebx = x-y
ebx=eax=x-y
eax=eax*2=(x-y)*2
eax=eax+ebx=(x-y)*3
eax=eax+ecx=(x-y)*3+x
ecx=eax=(x-y)*3+x
eax=eax+edx=(x-y)*3+x+z
(x-y)*3+x+z =0xe8f508c8
eax=ecx=(x-y)*3+x
eax=eax-edx=(x-y)*3+x-z
(x-y)*3+x-z = 0xc0a3c68
总结出来就是三个方程
(x-y)*4+x+z = 0xeaf917e2
(x-y)*3+x+z = 0xe8f508c8
(x-y)*3+x-z = 0xc0a3c68
Z3启动
三个未知数,三个方程,应该是能有解
from z3 import *
import binascii
s = Solver()
x = Int('x')
y = Int('y')
z = Int('z')
s.add((x-y)*4+x+z == 0xeaf917e2)
s.add((x-y)*3+x+z == 0xe8f508c8)
s.add((x-y)*3+x-z == 0xc0a3c68)
print(s.check())
res = s.model()
a2 = '' + binascii.a2b_hex(hex(int(('%s'%res[x]))).replace('0x','')).decode('utf-8')[::-1]
a2 += binascii.a2b_hex(hex(int(('%s'%res[y]))).replace('0x','')).decode('utf-8')[::-1]
a2 += binascii.a2b_hex(hex(int(('%s'%res[z]))).replace('0x','')).decode('utf-8')[::-1]
print(a2)
总的来说还是很有收获,很喜欢这种题。