例题
babyLoginPlus
第七届山东省大学生网络安全技能大赛.Bugku
int __cdecl main(int argc, const char **argv, const char **envp)
{
sub_401510();
sub_4013C0(&unk_40B0B0);
return (int)sub_401540(&byte_408240, count_0, 1);
}sub_4013C0跟进
int __cdecl sub_4013C0(int a1)
{
int buf; // eax
*(_DWORD *)a1 = 0;
*(_DWORD *)(a1 + 4) = 0;
*(_DWORD *)(a1 + 8) = 0;
*(_DWORD *)(a1 + 12) = 0;
*(_DWORD *)(a1 + 16) = 0;
*(_BYTE *)(a1 + 24) = -48;
*(_DWORD *)(a1 + 28) = sub_401000;
*(_BYTE *)(a1 + 32) = -47;
*(_DWORD *)(a1 + 36) = sub_401180;
*(_BYTE *)(a1 + 40) = -46;
*(_DWORD *)(a1 + 44) = sub_401210;
*(_BYTE *)(a1 + 48) = -44;
*(_DWORD *)(a1 + 52) = sub_401270;
*(_BYTE *)(a1 + 56) = -43;
*(_DWORD *)(a1 + 60) = sub_4012A0;
*(_BYTE *)(a1 + 64) = -45;
*(_DWORD *)(a1 + 68) = nullsub_1;
*(_BYTE *)(a1 + 72) = -42;
*(_DWORD *)(a1 + 76) = sub_4012E0;
*(_BYTE *)(a1 + 80) = -41;
*(_DWORD *)(a1 + 84) = sub_4011A0;
*(_BYTE *)(a1 + 88) = -40;
*(_DWORD *)(a1 + 92) = sub_4011F0;
buf = (int)malloc(0x400u);
memset((void *)buf, 0, 0x400u);
qmemcpy((void *)(buf + 100), sub_401540(&::a1, count, 0), count);
qmemcpy((void *)(buf + 200), sub_401540(&a1_0, count_1, 0), count_1);
qmemcpy((void *)(buf + 300), &src_, count_2);
buf = buf;
qmemcpy((void *)(buf + 400), &src__0, count_3);
return buf;
}sub_401540跟进,有对unk_40B0B0的引用,发现opcode unk_40853C
char *__cdecl sub_401540(_BYTE *p_a1, signed int count, int a3)
{
char *buf; // esi
signed int i; // eax
char *v6; // edx
char v7; // cl
buf = (char *)malloc(count + 2);
memset(buf, 0, count);
for ( i = 0; i < count; *v6 = v7 )
{
v6 = &buf[i];
v7 = (*p_a1 ^ 0x66) - 1;
++i;
p_a1 += 2;
}
*(_WORD *)&buf[i] = 0;
if ( a3 )
{
sub_401600(buf);
sub_401390(&unk_40B0B0, &unk_40853C);
sub_401600(&buf_);
}
return buf;
}对sub_4013C0函数进行结构体优化,变量类型按y修改
typedef struct _opcode {
unsigned int opcode;
unsigned char p_fun;
} _opcode;
typedef struct _cpu {
unsigned int _eax; // 4 bytes
unsigned int _ebx; // 4 bytes
unsigned int _ecx; // 4 bytes
unsigned int _edx; // 4 bytes
unsigned int _tmp; // 4 bytes
unsigned int _eip; // 4 bytes
_opcode oplist[10];
} _cpu;
效果如下
int __cdecl sub_4013C0(_cpu *a1)
{
int buf; // eax
a1->_eax = 0;
a1->_ebx = 0;
a1->_ecx = 0;
a1->_edx = 0;
a1->_tmp = 0;
LOBYTE(a1->oplist[0].opcode) = -48;
*(_DWORD *)&a1->oplist[0].p_fun = sub_401000;
LOBYTE(a1->oplist[1].opcode) = -47;
*(_DWORD *)&a1->oplist[1].p_fun = sub_401180;
LOBYTE(a1->oplist[2].opcode) = -46;
*(_DWORD *)&a1->oplist[2].p_fun = sub_401210;
LOBYTE(a1->oplist[3].opcode) = -44;
*(_DWORD *)&a1->oplist[3].p_fun = sub_401270;
LOBYTE(a1->oplist[4].opcode) = -43;
*(_DWORD *)&a1->oplist[4].p_fun = sub_4012A0;
LOBYTE(a1->oplist[5].opcode) = -45;
*(_DWORD *)&a1->oplist[5].p_fun = nullsub_1;
LOBYTE(a1->oplist[6].opcode) = -42;
*(_DWORD *)&a1->oplist[6].p_fun = sub_4012E0;
LOBYTE(a1->oplist[7].opcode) = -41;
*(_DWORD *)&a1->oplist[7].p_fun = sub_4011A0;
LOBYTE(a1->oplist[8].opcode) = -40;
*(_DWORD *)&a1->oplist[8].p_fun = sub_4011F0;
::buf = (int)malloc(0x400u);
memset((void *)::buf, 0, 0x400u);
qmemcpy((void *)(::buf + 100), sub_401540(&p_a1, count, 0), count);
qmemcpy((void *)(::buf + 200), sub_401540(&a1_0, count_1, 0), count_1);
qmemcpy((void *)(::buf + 300), &src_, count_2);
buf = ::buf;
qmemcpy((void *)(::buf + 400), &src__0, count_3);
return buf;
}buf有smc处理,动调提一下
char *__cdecl sub_401540(_BYTE *p_a1, signed int count, int a3)
{
char *buf; // esi
signed int i; // eax
char *v6; // edx
char v7; // cl
buf = (char *)malloc(count + 2);
memset(buf, 0, count);
for ( i = 0; i < count; *v6 = v7 )
{
v6 = &buf[i];
v7 = (*p_a1 ^ 0x66) - 1;
++i;
p_a1 += 2;
}
*(_WORD *)&buf[i] = 0;
if ( a3 )
{
sub_401600(buf);
sub_401390(&cpu, &opcode);
sub_401600(&buf_);
}
return buf;
}照着随便异或-1,对比try again能确定为两个反馈,应该还有其他还原处理,然后动调还能看出welcome,和大小写字母表,比较与处理判断在最开始的sub_401540,中间不断调用vm函数
Resulting ASCII string:
""--<""Gslkpavunavislw!"">
""--<""Vpy"akail!"">
::buf = (int)malloc(0x400u);
memset((void *)::buf, 0, 0x400u);
qmemcpy((void *)(::buf + 100), sub_401540(&p_a1, count, 0), count);// success
qmemcpy((void *)(::buf + 200), sub_401540(&a1_0, count_1, 0), count_1);// wrong
qmemcpy((void *)(::buf + 300), welcome, count_2);// welcome
buf = ::buf;
qmemcpy((void *)(::buf + 400), &src__0, count_3);提取opcode,对相应处理函数重命名
D0_mv
_DWORD *__cdecl sub_401000(_DWORD *cpu)
{
// cpu[0] = _eax, cpu[1] = _ebx, cpu[2] = _ecx, cpu[3] = _edx, cpu[5] = _eip
unsigned int eip = cpu[5];
unsigned char op_type = *(unsigned char *)(eip + 1);
unsigned int imm32 = *(unsigned int *)(eip + 2);
switch (op_type) {
case 0x20: // ' ': mov eax, imm32
cpu[0] = imm32;
break;
case 0x21: // '!': mov ebx, imm32
cpu[1] = imm32;
break;
case 0x22: // '"': mov ecx, imm32
cpu[2] = imm32;
break;
case 0x23: // '#': mov edx, imm32
cpu[3] = imm32;
break;
case 0x41: // 'A': mov eax, byte ptr [buf + imm32]
cpu[0] = *(unsigned char *)(buf + imm32);
break;
case 0x42: // 'B': mov ebx, byte ptr [buf + imm32]
cpu[1] = *(unsigned char *)(buf + imm32);
break;
case 0x51: // 'Q': mov dword ptr [buf + ecx], imm32
*(unsigned int *)(buf + cpu[2]) = imm32;
break;
case 0x52: // 'R': mov dword ptr [buf + ecx], eax
*(unsigned int *)(buf + cpu[2]) = cpu[0];
break;
case 0x61: // 'a': mov eax, byte ptr [buf + eax]
cpu[0] = *(unsigned char *)(buf + cpu[0]);
break;
case 0x62: // 'b': mov ebx, byte ptr [buf + ebx]
cpu[1] = *(unsigned char *)(buf + cpu[1]);
break;
default:
// 未知指令,仅推进 eip
break;
}
cpu[5] += 6; // 所有指令长度为 6 字节
return cpu;
}D1_xor
_DWORD *__cdecl xor(_DWORD *a1)
{
_DWORD *result; // eax
int v2; // ecx
result = a1;
v2 = a1[5] + 1;
*a1 ^= a1[1];
a1[5] = v2;
return result;
}
//优化后
_DWORD *__cdecl sub_401180(_DWORD *cpu)
{
// 执行: EAX ^= EBX
cpu[0] ^= cpu[1];
// 指令长度为 1 字节,推进 EIP
cpu[5] += 1;
return cpu;
}opcode
opcode=[0xD0, 0x20, 0x0, 0x0, 0x0, 0x0,
0xD0,0x21, 0x25, 0x0, 0x0, 0x0,
0xD4,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x9, 0x0, 0x0, 0x0,
0xD8,
0xD0, 0x21, 0x26, 0x0, 0x0, 0x0,
0xD1,
0xD0, 0x21, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x22, 0x2C, 0x1, 0x0, 0x0,
0xD7, 0x2,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD1,
0xD0, 0x21, 0x6, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x21, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x22, 0x90, 0x1, 0x0, 0x0,
0xD7, 0x2,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x0, 0x20,
0xD0, 0x20, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x22, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x52, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x22, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x52, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x25, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x1, 0xB8,
0xD0, 0x20, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x25, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x0, 0xE,
0xD0, 0x20, 0x64, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1F, 0x0, 0x0, 0x0,
0xD5, 0xD3,
0xD0, 0x20, 0xC8, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x19, 0x0, 0x0, 0x0,
0xD5, 0xD3,
0xD0, 0x22, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x51, 0x61, 0x62, 0x63, 0x64,
0xD0, 0x20, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x4, 0x0, 0x0, 0x0,
0xD5, 0xD3]其余同理,最后如下
int __cdecl vm_init(_cpu *p__cpu)
{
int buf; // eax
p__cpu->_eax = 0;
p__cpu->_ebx = 0;
p__cpu->_ecx = 0;
p__cpu->_edx = 0;
p__cpu->_tmp = 0;
LOBYTE(p__cpu->oplist[0].opcode) = 0xD0;
*(_DWORD *)&p__cpu->oplist[0].p_fun = mov;
LOBYTE(p__cpu->oplist[1].opcode) = 0xD1;
*(_DWORD *)&p__cpu->oplist[1].p_fun = eax_xor_ebx;
LOBYTE(p__cpu->oplist[2].opcode) = 0xD2;
*(_DWORD *)&p__cpu->oplist[2].p_fun = cmp;
LOBYTE(p__cpu->oplist[3].opcode) = 0xD4;
*(_DWORD *)&p__cpu->oplist[3].p_fun = read;
LOBYTE(p__cpu->oplist[4].opcode) = 0xD5;
*(_DWORD *)&p__cpu->oplist[4].p_fun = print;
LOBYTE(p__cpu->oplist[5].opcode) = 0xD3;
*(_DWORD *)&p__cpu->oplist[5].p_fun = nop;
LOBYTE(p__cpu->oplist[6].opcode) = 0xD6;
*(_DWORD *)&p__cpu->oplist[6].p_fun = jz;
LOBYTE(p__cpu->oplist[7].opcode) = 0xD7;
*(_DWORD *)&p__cpu->oplist[7].p_fun = add;
LOBYTE(p__cpu->oplist[8].opcode) = 0xD8;
*(_DWORD *)&p__cpu->oplist[8].p_fun = sub;
::buf = (int)malloc(0x400u);
memset((void *)::buf, 0, 0x400u);
qmemcpy((void *)(::buf + 100), obf_smc(&p_a1, count, 0), count);// success
qmemcpy((void *)(::buf + 200), obf_smc(&a1_0, count_1, 0), count_1);// wrong
qmemcpy(
(void *)(::buf + 300),
welcome, // " "
count_2); // welcome
buf = ::buf;
qmemcpy((void *)(::buf + 400), &enc, count_3);
return buf;
}opcode->asm
def disasm_vm(opcode_bytes):
i = 0
asm_lines = []
while i < len(opcode_bytes):
op = opcode_bytes[i]
if op == 0xD0:
if i + 5 >= len(opcode_bytes):
break
typ = opcode_bytes[i + 1]
imm32 = int.from_bytes(opcode_bytes[i+2:i+6], 'little')
if typ == 0x20:
asm = f"mov eax, 0x{imm32:X}"
elif typ == 0x21:
asm = f"mov ebx, 0x{imm32:X}"
elif typ == 0x22:
asm = f"mov ecx, 0x{imm32:X}"
elif typ == 0x23:
asm = f"mov edx, 0x{imm32:X}"
elif typ == 0x41:
asm = f"mov eax, byte [buf + 0x{imm32:X}]"
elif typ == 0x42:
asm = f"mov ebx, byte [buf + 0x{imm32:X}]"
elif typ == 0x51:
asm = f"mov dword [buf + ecx], 0x{imm32:X}"
elif typ == 0x52:
asm = f"mov dword [buf + ecx], eax"
elif typ == 0x61:
asm = "mov eax, byte [buf + eax]"
elif typ == 0x62:
asm = "mov ebx, byte [buf + ebx]"
else:
asm = f"mov ??? (type=0x{typ:02X}), 0x{imm32:X}"
asm_lines.append(asm)
i += 6
elif op == 0xD1:
asm_lines.append("xor eax, ebx")
i += 1
elif op == 0xD2:
if i + 2 >= len(opcode_bytes):
break
mode = opcode_bytes[i + 1]
imm8 = opcode_bytes[i + 2]
if mode == 0:
asm = f"cmp eax, 0x{imm8:02X}"
elif mode == 1:
asm = "cmp eax, ebx"
elif mode == 2:
asm = "cmp ebx, ecx"
elif mode == 3:
asm = "cmp ecx, edx"
else:
asm = f"cmp ??? (mode={mode})"
asm_lines.append(asm)
i += 3
elif op == 0xD3:
asm_lines.append("nop")
i += 1
elif op == 0xD4:
asm_lines.append("read") # read(_eax, _ebx)
i += 1
elif op == 0xD5:
asm_lines.append("print") # print(_ebx chars)
i += 1
elif op == 0xD6:
if i + 2 >= len(opcode_bytes):
break
mode = opcode_bytes[i + 1]
imm8 = opcode_bytes[i + 2]
if mode == 1:
asm = f"jz backward 0x{imm8:02X}" # 实际是 eip -= imm8
else:
asm = f"jz forward 0x{imm8:02X}" # 实际是 eip += imm8 + 3
asm_lines.append(asm)
i += 3
elif op == 0xD7:
if i + 1 >= len(opcode_bytes):
break
mode = opcode_bytes[i + 1]
if mode == 1:
asm = "add eax, ebx"
elif mode == 2:
asm = "add ebx, ecx"
elif mode == 3:
asm = "add ecx, edx"
else:
asm = f"add ??? (mode={mode})"
asm_lines.append(asm)
i += 2
elif op == 0xD8:
asm_lines.append("sub eax, ebx")
i += 1
else:
asm_lines.append(f"unknown opcode 0x{op:02X}")
i += 1
return asm_lines
# -----------------------------
# 输入你的 opcode 列表
opcode = [
0xD0, 0x20, 0x0, 0x0, 0x0, 0x0,
0xD0,0x21, 0x25, 0x0, 0x0, 0x0,
0xD4,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x9, 0x0, 0x0, 0x0,
0xD8,
0xD0, 0x21, 0x26, 0x0, 0x0, 0x0,
0xD1,
0xD0, 0x21, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x22, 0x2C, 0x1, 0x0, 0x0,
0xD7, 0x2,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD1,
0xD0, 0x21, 0x6, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x21, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x22, 0x90, 0x1, 0x0, 0x0,
0xD7, 0x2,
0xD0, 0x62, 0x0, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x0, 0x20,
0xD0, 0x20, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x22, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x52, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1, 0x0, 0x0, 0x0,
0xD7, 0x1,
0xD0, 0x22, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x52, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x20, 0x0, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x25, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x1, 0xB8,
0xD0, 0x20, 0x8, 0x2, 0x0, 0x0,
0xD0, 0x61, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x25, 0x0, 0x0, 0x0,
0xD2, 0x1, 0x0, 0xD6, 0x0, 0xE,
0xD0, 0x20, 0x64, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x1F, 0x0, 0x0, 0x0,
0xD5, 0xD3,
0xD0, 0x20, 0xC8, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x19, 0x0, 0x0, 0x0,
0xD5, 0xD3,
0xD0, 0x22, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x51, 0x61, 0x62, 0x63, 0x64,
0xD0, 0x20, 0x0, 0x0, 0x0, 0x0,
0xD0, 0x21, 0x4, 0x0, 0x0, 0x0,
0xD5, 0xD3,
# 后面大量数据可能是字符串或填充,非指令
]
# 只取前面的有效指令部分(到 0xD5, 0xD3 结束即可)
# 但为了安全,我们让脚本能自动停在未知指令前
asm = disasm_vm(opcode)
for idx, line in enumerate(asm):
print(f"{idx+1:2d}: {line}")1: mov eax, 0x0
2: mov ebx, 0x25
3: read
4: mov eax, 0x200
5: mov eax, byte [buf + eax]
6: mov eax, byte [buf + eax]
7: mov ebx, 0x9
8: sub eax, ebx
9: mov ebx, 0x26
10: xor eax, ebx
11: mov ebx, 0x200
12: mov ebx, byte [buf + ebx]
13: mov ecx, 0x12C
14: add ebx, ecx
15: mov ebx, byte [buf + ebx]
16: xor eax, ebx
17: mov ebx, 0x6
18: add eax, ebx
19: mov ebx, 0x200
20: mov ebx, byte [buf + ebx]
21: mov ecx, 0x190
22: add ebx, ecx
23: mov ebx, byte [buf + ebx]
24: cmp eax, ebx
25: jz forward 0x20
26: mov eax, 0x208
27: mov eax, byte [buf + eax]
28: mov ebx, 0x1
29: add eax, ebx
30: mov ecx, 0x208
31: mov dword [buf + ecx], eax
32: mov eax, 0x200
33: mov eax, byte [buf + eax]
34: mov ebx, 0x1
35: add eax, ebx
36: mov ecx, 0x200
37: mov dword [buf + ecx], eax
38: mov eax, 0x200
39: mov eax, byte [buf + eax]
40: mov ebx, 0x25
41: cmp eax, ebx
42: jz backward 0xB8
43: mov eax, 0x208
44: mov eax, byte [buf + eax]
45: mov ebx, 0x25
46: cmp eax, ebx
47: jz forward 0x0E
48: mov eax, 0x64
49: mov ebx, 0x1F
50: print
51: nop
52: mov eax, 0xC8
53: mov ebx, 0x19
54: print
55: nop
56: mov ecx, 0x0
57: mov dword [buf + ecx], 0x64636261
58: mov eax, 0x0
59: mov ebx, 0x4
60: print
61: nop解密
转成高级语言
L = 0x25
t1 = [...]
t2 = [...]
for i in range(1, L):
flag[i] = (((t2[i] - 6) ^ t1[i]) ^ 0x26) + 9
print(flag.decode())enc=[0x32, 0x26, 0x18, 0x21, 0x41, 0x23, 0x2A, 0x57, 0x44, 0x29,
0x35, 0x12, 0x20, 0x17, 0x45, 0x1C, 0x68, 0x2D, 0x7A, 0x79,
0x47, 0x7F, 0x44, 0x9, 0x1E, 0x75, 0x41, 0x2A, 0x19, 0x34,
0x76, 0x47, 0x14, 0x50, 0x52, 0x76, 0x58]
key = [0x57, 0x65, 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x5F, 0x74, 0x6F,
0x5F, 0x73, 0x64, 0x6E, 0x69, 0x73,0x63, 0x5F, 0x32, 0x30,
0x31, 0x38, 0x5F, 0x42, 0x79, 0x2E, 0x5A, 0x65, 0x72, 0x6F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
flag=''
for i in range(len(enc)):
tmp=((enc[i] - 6) ^ key[i] ^ 0x26) + 9
flag+=chr(tmp)
print(flag)
# flag{_p1us_babyL0gin_pPpPpPpPp_p1us_}