我迷迷糊糊地跟随着信的指引来到俊男坊。
一个少年站在门口,一直盯着我的方向看,看起来像是在等人。
等我走近,还没说话,他便从我手里夺过那封信狂奔而去。
这时,耳边响起熟悉的系统提示:“恭喜你!完成第一关送信任务,你已升级为二级鸡米花。”
我看到周围的环境瞬息之间已经改变,我站在一座雪山上,脚下是漫漫云海,雪与云相互缠绕,只要踏错一步,就可能坠山而亡。
一只鹌鹑停在我手上,它居然开口说话了:“万金斯,跟我来。”
我这时候依然搞不清楚状况,只好跟着它走。
远方的建筑逐渐清晰,像是一个寺庙,又有点儿道观的风格,门口的扫地僧和我隔着200米,不知他用了什么功夫,空中居然轻飘飘地传来一句话:“施主,打尖还是住店啊?”
系统的声音再次传来:“友情提示,本次任务是找到隐藏的宗教分裂者。否则,雪山下就是你的归宿。”
这时我才意识到事情的严重性,系统会一直分配任务而我必须接受,只有达到最高级别才能重回现实世界。
看来要认真起来了,佛海无边,道法自然,这个夜晚注定会充满惊险。
本题解题思路看雪论坛 HHHso 提供:
dt nt!_TEB32 nttib.
ntdll!_TEB32
+0x000 NtTib :
+0x000 ExceptionList : Uint4B
+0x004 StackBase : Uint4B
+0x008 StackLimit : Uint4B
+0x00c SubSystemTib : Uint4B
+0x010 FiberData : Uint4B
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Uint4B
+0x018 Self : Uint4B
main
-------------esp
.-04hww tmpvar
VirtualProtect from main.start.0x401210 to 0x505678:=start+0x104468
VirtualProtect from base.0x400000 to 0x401000:=start+0x1000
st0 = image.20hww 0x508000
m_stack{
m_StackBase.bottom = NtTib.StackBase
m_StackLimit.top = NtTib.StackLimit
image.24hww = m_StackSize = m_StackLimit-m_StackBase = 0x3000
}
m_esp,m_ebp
g_esp = g_StackLimit+(m_esp-m_StackLimit)
g_ebp = g_StackLimit+(m_ebp-m_StackLimit)
g_stack{align:4;g_StackLimit,g_StackBase;m_StackSize}:=m_stack
NtTib.StackBase = g_StackBase
c_src = 0x4014E2
username: 4FF49B
userkey: 4FF65C
c_size = 0x104196
c_end = c_src+c_size 0x505678
cs_s = st0 0x508000 随机代码执行区起始基址(非执行入口),不变
cs_e = cs_s + 0x6400000 随机代码执行区尾部,不变
rdtsc_mod = rdtsc % 0x6400000 随机代码释放点偏移量,也是执行入口,
r_s = cs_s+rdtsc_mod 解密释放代码到随机地址,也作为入口,每次释放由于rdtsc计算的不同而不同
r_e = r_s + c_size
savedata "C:\CTF2019\Q4\CTF06\last_3B34.dec",0x53c3A8C,0x3b34
savedata "C:\CTF2019\Q4\CTF06\last_9e56.dec",0x66988A,0x9e56
IDAPython执行 loadfile(r"C:\CTF2019\Q4\CTF06\last_9e56.dec",0,0x4016AC,0x9e56)
from keystone import *
ks = Ks(KS_ARCH_X86, KS_MODE_32)
import sark,idc
def sim_call_pop_sub_add(ea = 0,basm=False):
global ks
if ea == 0:
ea = idc.here()
cl_call = sark.Line(ea)
cl_pop =cl_call.next
cl_sub =cl_pop.next
cl_add =cl_sub.next
if cl_call.insn.mnem!='call' or cl_pop.insn.mnem!='pop' or cl_sub.insn.mnem!= 'sub' or cl_add.insn.mnem!= 'add':
raise Exception("not call pop sub add")
iv = cl_pop.ea
vs = cl_sub.insn.operands[1].imm
va = cl_add.insn.operands[1].imm
r = cl_add.insn.operands[0].reg
print("{:X} {:X} {:X}".format(iv,vs,va))
asmstr = "MOV {},0x{:X}".format(r,iv-vs+va)
encoding, count = ks.asm(asmstr)
if basm:
i4size = sum([ins.size for ins in [cl_call,cl_pop,cl_sub,cl_add]])
#asmc = ''.join(chr(c) for c in encoding)
#asmc+= '\x90'*(i4size-asmc.__len__())
asmc = encoding + [0x90]*(i4size-len(encoding))
pea = cl_call.ea
for i in range(0,len(asmc)):
idc.PatchByte(pea+i,asmc[i])
return [encoding,count,asmstr]
void decrypt2(int a2){
__int16 K0f;
__int16 K21;
__int16 K43;
__int16 K65;
__int16 K87;
__int16 Ka9;
__int16 Kcb;
__int16 Ked;
__int16 wTX;
__int16 Kr1;
__int16 Kr2;
int wS;
int Ks;
int wX;
int wA;
int wT1;
int wT3;
int wST;
int wR;
unsigned int A53;
unsigned int x6587;
K0f=*(unsigned __int8*)(a2+0xF)+(*(unsigned __int8*)(a2+0x0)<<8);
K21=*(unsigned __int8*)(a2+0x1)+(*(unsigned __int8*)(a2+0x2)<<8);
K43=*(unsigned __int8*)(a2+0x3)+(*(unsigned __int8*)(a2+0x4)<<8);
K65=*(unsigned __int8*)(a2+0x5)+(*(unsigned __int8*)(a2+0x6)<<8);
K87=*(unsigned __int8*)(a2+0x7)+(*(unsigned __int8*)(a2+0x8)<<8);
Ka9=*(unsigned __int8*)(a2+0x9)+(*(unsigned __int8*)(a2+0xA)<<8);
Kcb=*(unsigned __int8*)(a2+0xB)+(*(unsigned __int8*)(a2+0xC)<<8);
Ked=*(unsigned __int8*)(a2+0xD)+(*(unsigned __int8*)(a2+0xE)<<8);
wS=(unsigned __int16)(Kcb-Ka9);
wX=(K21^K43)&0xFFFF;
x6587 = K65^K87;
A53=(unsigned __int16)((((x6587&0x5555)+(((unsigned __int16)x6587>>1)&0x5555))&0x3333)
+(((unsigned __int16)((x6587&0x5555)
+(((unsigned __int16)x6587>>1)&0x5555))>>2)&0x3333));
Ks=((unsigned __int16)((A53&0xF0F)+((A53>>4)&0xF0F))>>8)
+(unsigned __int8)((A53&0xF)+((A53>>4)&0xF));
wA=(K0f+Ked);
wT1=(unsigned __int16)(wS&~(_WORD)wX|wX&wA);
printf("wS:%X\twX:%X\twA:%X\twT1:%X\n",wS,wX,wA,wT1);
wTX=wT1^K65;
Kr1=__ROL2__(wTX,Ks);
Kr2=__ROL2__(wT1^K87,Ks);
printf("x6587:%X\tks:%d\n",x6587,Ks);
wT3=(unsigned __int16)((wX*(unsigned int)(unsigned __int16)wT1>>Ks)+24);
wST=wS^wT3;
wR=wT1&wT3|(unsigned __int16)wST&(wT1|wT3);
printf("wT3:%X\twST:%X\twR:%X\n",wT3,wST,wR);
printf("wTX:%X\tKr1:%X\tKr2:%X\n",wTX,Kr1,Kr2);
*(_WORD*)(a2+0x0)=K43^wR;
*(_WORD*)(a2+0x2)=K21^wR;
*(_WORD*)(a2+0x4)=wST+K0f;
*(_WORD*)(a2+0x6)=Ked-wST;
*(_WORD*)(a2+0x8)=wT3+Kcb;
*(_WORD*)(a2+0xA)=wT3+Ka9;
*(_WORD*)(a2+0xC)=Kr2;
*(_WORD*)(a2+0xE)=Kr1;
}
x6587 = K65^K87;
wTX=wT1^K65;
Kr1=__ROL2__(wTX,Ks);
Kr2=__ROL2__(wT1^K87,Ks);
*(_WORD*)(a2+0xC)=Kr2; //Udc or Ddc
*(_WORD*)(a2+0xE)=Kr1; //Ufe or Dfe
因为
Kr1^Kr2=Ufe^Udc=__ROL2__(wTX,Ks) ^ __ROL2__(wT1^K87,Ks);
=__ROL2__(wTX ^ wT1 ^ K87,Ks)
=__ROL2__(wT1 ^ K65 ^ wT1 ^ K87,Ks)
=__ROL2__(K65 ^ K87,Ks)
=__ROL2__(x6587,Ks)
定义
An = Kr1^Kr2= = Ufe ^ Udc
即
An = x6587 <<< Ks //可以直接由D或U的 Ufe ^ Udc 计算得到
那么如何由An得到x6587和Ks呢?
又因为
A53=(unsigned __int16)((((x6587&0x5555)+(((unsigned __int16)x6587>>1)&0x5555))&0x3333)
+(((unsigned __int16)((x6587&0x5555)
+(((unsigned __int16)x6587>>1)&0x5555))>>2)&0x3333));
Ks=((unsigned __int16)((A53&0xF0F)+((A53>>4)&0xF0F))>>8)
+(unsigned __int8)((A53&0xF)+((A53>>4)&0xF));
其中A53由x6587计算得到,我们不需要关注细节
而Ks实际是计算A53 类型是双字节0x****四个“Half-Byte"的和,作为循环左移的次数。
实际也不需关注细节,因为双字节循环左移的结果只有16种,我们简单枚举下,就可以由An得到x6587和Ks
An = c_ushort(Ufe ^ Udc)
x6587 = c_ushort(0)
A53 = c_ushort(0)
_Ks = None
for Ks in range(0x10):# x6587<<Ks | x6587 >> (16-Ks) An=x6587 <<< Ks --> x6587=An >>> Ks
x6587.value = ((An.value >> Ks)|(An.value << (16-Ks)))&0xFFFF
A53.value=((((x6587.value&0x5555)+((x6587.value>>1)&0x5555))&0x3333)+((((x6587.value&0x5555)+((x6587.value>>1)&0x5555))>>2)&0x3333))&0xFFFF;
cKs = (((A53.value&0xF0F)+((A53.value>>4)&0xF0F))>>8)+((A53.value&0xF)+((A53.value>>4)&0xF))
if cKs==Ks:
_Ks = Ks
_x6587 = x6587
print("x6587=0x{:04X};Ks={} from D-to-K python".format(x6587.value,Ks))
Ks = _Ks
x6587 = _x6587.value
主要利用D或U直接的加减和异或计算中间观察量:
因为,
*(_WORD*)(a2+0x0)=K43^wR;
*(_WORD*)(a2+0x2)=K21^wR;
*(_WORD*)(a2+0x4)=wST+K0f;
*(_WORD*)(a2+0x6)=Ked-wST;
*(_WORD*)(a2+0x8)=wT3+Kcb;
*(_WORD*)(a2+0xA)=wT3+Ka9;
又因为,
wS=(unsigned __int16)(Kcb-Ka9);
wX=(K21^K43)&0xFFFF;
wA=(K0f+Ked);
所以,
wS=U98-Uba
wX=U32^U10
wA=U54+U76
wT1=(wS&~wX|wX&wA)
继续有
wT3=(((wX*wT1)>>Ks)+24)&0xFFFF
wST=wS^wT3
wR=(wT1&wT3)|(wST&(wT1|wT3))
Kr1=Ufe
Kr2=Udc
wTX=((Kr1 >> Ks)|(Kr1 << (16-Ks)))&0xFFFF
有了中间观察量,由观察量到K就简单多了
K65=wTX^wT1 #ok
K87=x6587^K65 #ok
K21=U32^wR #ok
K43=U10^wR #ok
Ked=(U76+wST)&0xFFFF #ok
K0f=c_ushort(U54-wST).value
Ka9=c_ushort(Uba-wT3).value
Kcb=c_ushort(U98-wT3).value
from ctypes import *
import struct
#D=7B E6 79 E0 89 E3 D3 6B 83 ED 81 EB 7A EA 5A 0A
Ufe=0x0a5A
Udc=0xEA7A
U98=0xed83
Uba=0xeb81
U32=0xe079
U10=0xe67b
U54=0xe389
U76=0x6bd3
An = c_ushort(Ufe ^ Udc)
x6587 = c_ushort(0)
A53 = c_ushort(0)
_Ks = None
for Ks in range(0x10):# x6587<<Ks | x6587 >> (16-Ks) An=x6587 <<< Ks --> x6587=An >>> Ks
x6587.value = ((An.value >> Ks)|(An.value << (16-Ks)))&0xFFFF
A53.value=((((x6587.value&0x5555)+((x6587.value>>1)&0x5555))&0x3333)+((((x6587.value&0x5555)+((x6587.value>>1)&0x5555))>>2)&0x3333))&0xFFFF;
cKs = (((A53.value&0xF0F)+((A53.value>>4)&0xF0F))>>8)+((A53.value&0xF)+((A53.value>>4)&0xF))
if cKs==Ks:
_Ks = Ks
_x6587 = x6587
print("x6587=0x{:04X};Ks={} from D-to-K python".format(x6587.value,Ks))
Ks = _Ks
x6587 = _x6587.value
#---check ok---
#x6587=0x8080;Ks=2
#x6587=0x0E02;Ks=4
wS=U98-Uba
wX=U32^U10
wA=U54+U76
wT1=(wS&~wX|wX&wA)
print("wS:{:X}\twX:{:X}\twA:{:X}\twT1:{:X} from D-to-K python\n".format(wS,wX,wA,wT1))
#wS:202 wX:602 wA:14F5C wT1:600
#wS:202 wX:602 wA:FFFF4F5C wT1:600
#---check ok---
wT3=(((wX*wT1)>>Ks)+24)&0xFFFF
wST=wS^wT3
wR=(wT1&wT3)|(wST&(wT1|wT3))
print("wT3:{:X}\twST:{:X}\twR:{:X} from D-to-K python\n".format(wT3,wST,wR))
#wT3:40D8 wST:42DA wR:42D8
#wT3:40D8 wST:42DA wR:42D8
#---check ok---
Kr1=Ufe
Kr2=Udc
wTX=((Kr1 >> Ks)|(Kr1 << (16-Ks)))&0xFFFF
print("wTX:{:X}\tKr1:{:X}\tKr2:{:X} from D-to-K python\n".format(wTX,Kr1,Kr2))
#wTX:FFFFA0A5 Kr1:A5A Kr2:FFFFEA7A
#wTX:A0A5 Kr1:A5A Kr2:EA7A
#---check ok---
K65=wTX^wT1 #ok
K87=x6587^K65 #ok
K21=U32^wR #ok
K43=U10^wR #ok
Ked=(U76+wST)&0xFFFF #ok
K0f=c_ushort(U54-wST).value
Ka9=c_ushort(Uba-wT3).value
Kcb=c_ushort(U98-wT3).value
struct.pack("HHHHHHHH",K21,K43,K65,K87,Ka9,Kcb,Ked,K0f).encode('hex').upper()
得到 K='A1A2A3A4A5A6E466A9AAABACADAEAFA0',即对应我们的参考
userkey=A0A1A2A3A4A5A6A7A8A9AAABACADAEAF (注意其中的A0位置)
#D: 4B 43 54 46 00 1A 19 18 17 16 15 14 13 12 11 10
U10=0x434B
U32=0x4654
U54=0x1A00
U76=0x1819
U98=0x1617
Uba=0x1415
Udc=0x1213
Ufe=0x1011
替换D,运行上述python得到
K:=CDE9D2EC1D469DC67C647E66B4C5656C
则userkey为置换K最后一字节到开头得到 6CCDE9D2EC1D469DC67C647E66B4C565
C 直接编译:del decryptTest.exe & cl decryptTest.c & decryptTest.exe
python 直接执行:python keygen.py KCTF
得到
x6587=0x8080;Ks=2 from D-to-K python
wS:202 wX:51F wA:3219 wT1:219 from D-to-K python
wT3:AF99 wST:AD9B wR:AF99 from D-to-K python
wTX:4404 Kr1:1011 Kr2:1213 from D-to-K python
username:KCTF
userkey :6CCDE9D2EC1D469DC67C647E66B4C565
【上海第五空间信息科技研究院】(简称:第五空间)是经上海市社会组织管理局批准成立,上海市科协作为业务主管部门的新型研发机构,由翼盾智能科技创始人积聚社会力量发起成立,立足科技事业,支撑国家战略,开展科技研究,推进协同创新。
【杭州安恒信息技术股份有限公司】(简称:安恒信息)成立于2007年,科创板股票代码:688023,一直专注于网络信息安全领域,公司主营业务为网络信息安全产品的研发、生产及销售,并为客户提供专业的网络信息安全服务。公司的产品及服务涉及应用安全、大数据安全、云安全、物联网安全、工业控制安全及工业互联网安全等领域。