首页
论坛
课程
招聘
[原创]人生如戏,却只有一次,且行且珍惜PEDIY2017P.CTF03Writeup
2017-10-30 11:13 4132

[原创]人生如戏,却只有一次,且行且珍惜PEDIY2017P.CTF03Writeup

2017-10-30 11:13
4132

key的标准验证方案:

(0)key = input

(1)deckey1 = base64.b64decode(key)

(2)deckey2 = base64.b64decode(deckey1)

(3)deckey3 = morse_decode(deckey2)

(4)sm3_hash(deckey2,3)==tail of key

(5)deckey3 can step out game_map

 

即输出key讲过二次base64解密得到deckey2

deckey2讲过摩斯解码得到deckey3

其中需校验deckey2前三字符的"国密3"的杂凑(哈希)值是否与输出key尾部相等

其次deckey3表示在迷宫中行走,zlqp分别表示往下、往右、往上、往左行走,

此方式假定坐标从上到下,从左到右表示的迷宫图,最后" "空格表示停止。

 

能走的坐标必须是"0"标记值,而走过之后都会在原来坐标标记"4",

而迷宫又是全局静态变量,所以标准走法只能验证一次。

 

而又由于” “表示停止,而后续没有检测走过不数或目的坐标的检测,

导致其存在地图校验bug,bug可以知道导致不走迷宫而直接跳过迷宫校验,

同时也可以只完成部分迷宫路径走动,而不必全部走出迷宫就可以完成校验。

 

 

废话少说,清除反调试检测

下述函数都是反调试逆向的操作,需要返回0才能继续正常功能,否则退出。

它们是对后面所列地址函数的封装,这里我们批量修改为指令

xor eax,eax

ret

即机器码 "\x33\xC0\xC3",用pefile处理原pe程序另存为CTF03_dbg.exe即可。

 

 

Hi_check_reverse_info1_sub_42D681   .text 0042D681         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info2_sub_42E26B    .text 0042E26B         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info3_sub_42D7F8    .text 0042D7F8         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info4_sub_42D23F    .text 0042D23F         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info5_sub_42D334   .text 0042D334         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info6_sub_42DD66   .text 0042DD66        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info7_sub_42D92E   .text 0042D92E         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info8_sub_42DF7D   .text 0042DF7D        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info9_sub_42E31F    .text 0042E31F 00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoA_sub_42DA7D  .text 0042DA7D        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoB_sub_42D389   .text 0042D389         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoC_sub_42D6BD  .text 0042D6BD        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoD_sub_42D807   .text 0042D807         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoE_sub_42D39D   .text 0042D39D        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_infoF_sub_42D1CC   .text 0042D1CC        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info10_sub_42DA96 .text 0042DA96        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info11_sub_42D8CA .text 0042D8CA        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info12_sub_42DD48 .text 0042DD48        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info13_sub_42DBC7 .text 0042DBC7        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info15_sub_42E428  .text 0042E428         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info16_sub_42D825 .text 0042D825         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info17_sub_42E27F  .text 0042E27F 00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info18_sub_42E162  .text 0042E162         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info19_sub_42D4F6 .text 0042D4F6         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info1A_sub_42DA41 .text 0042DA41        00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info1B_sub_42D096 .text 0042D096         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info1C_sub_42E45A .text 0042E45A         00000005                            R       .        .        .        .        .        .

Hi_check_reverse_info1D_sub_42D203 .text 0042D203         00000005                            R       .        .        .        .        .        .

 

 

#反反调试处理脚本如下

#------- ------- ------- ------- ------- ------- -------

rvas = '''430B10

431190

430E80

430030

430170

4302A0

4303D0

4304F0

430610

431070

4313C0

431420

4314F0

4317C0

431C40

431CE0

431D80

431F20

431FD0

4338D0

42D0B4

42FA20

42FAF0

42FC90

4323B0

4325D0

4326C0

432840

4329C0

432B30

432D20'''

 

import pefile

pe = pefile.PE(r'.\CTF03.exe')

def cpy_rpl_nk_fn(pe,fn):

  xor_eax_eax_ret = "\x33\xC0\xC3"

  for eastr in rvas.split('\n'):

    rva = int(eastr,0x10)-0x400000

    pe.set_bytes_at_rva(rva,xor_eax_eax_ret)

  pe.write(r'.\CTF03\{}'.format(fn))

 

cpy_rpl_nk_fn(pe,"CTF03_dbg.exe")

#------- ------- ------- ------- ------- ------- -------

 

 

三个函数

base64 解码 Hi_base64decode_sub_42D267

莫斯码 解码 Hi_sgd_morse_xsym_decode_sub_42D96A

国密3  杂凑 Hi_sm3_hash_sub_42DA78

 

base64可用python的base64模块替换

国密3可以用网络开源测试,实际我找到的一份只有计算'/\x00\x00'三个字符时的杂凑值一样,

所以还是直接用样例程序的

 

莫斯码解码分三种,5字节长的0~9,4字节的a~z,7字节长的特殊符号,

4字节长的都是在原莫斯码基础上补*对齐,7字节长也是补*对齐7,然后再加上所代表的符合

 

莫斯码解码与编码功能模拟,mnstr,sgdstr,xsymstr可通过GetString获取,如

mnstr GetString(0x49B1E0,-1,ASCSTR_C)

sgdstr = GetString(0x49B218,-1,ASCSTR_C)

xsymstr = GetString(0x49B218,-1,ASCSTR_C)

 

mnstr = '-----.----..---...--....-.....-....--...---..----.'

morse_n = {}

for i in xrange(0,0xA):

  tmnstr = mnstr[i*5:i*5+5]

  morse_n[chr(0x30+i)] = tmnstr

  morse_n[tmnstr] = chr(0x30+i)

 

sgdstr='.-**-...-.-.-..*.***..-.--.*......**.----.-*.-..--**-.**---*.--.--.-.-.*...*-***..-*...-.--*-..--.----..'

sgd_az = {}

for i in xrange(0,0x1A):

  tsgdstr = sgdstr[i*4:i*4+4]

  sgd_az[chr(0x61+i)] = tsgdstr

  sgd_az[tsgdstr] = chr(0x61+i)

 

xsymstr='.-.-.-*.---...*:--..--*,-.-.-.*;..--..*?-...-**=.----.*\'-..-.**/-.-.--*!-....-*-..--.-*_.-..-.*"-.--.**(-.--.-*)...-..-$.-...**&.--.-.*@.-**-...-.-.-..*.***..-.--.*......**.----.-*.-..--**-.**---*.--.--.-.-.*...*-***..-*...-.--*-..--.----..'

xsym = {} #.:,;?='/!-_"()$&@

a = ""

for i in xrange(0,0x11):

  txsymstr = xsymstr[i*8:i*8+8]

  symch = txsymstr[7]

  xstr = txsymstr[:7]

  xsym[symch] = xstr

  xsym[xstr] = symch

  a += symch

 

def get_encodestr_of__sgd_az__morse_n__xsymstr(gmap_steps_key=''):

  global sgd_az,morse_n,xsym

  xstr = ''

  for ch in gmap_steps_key:

    if ch == ' ':

      xstr += '/'

      return xstr

    else:

      if ch in sgd_az:

        xstr += (sgd_az[ch]+' ')

      if ch in morse_n:

        xstr += (morse_n[ch]+' ')

      if ch in xsym:

        xstr += (xsym[ch]+' ')

 

因为deckey3用于走迷宫,通过迷宫分析我们得到(参考后续分析)

deckey3 = gmap_steps_key = 'zlzllllzzzppqppzzzlllzlllzll '

对deceky3进行摩斯编码即可得到deckey2

deckey2 = '--.. .-.. --.. .-.. .-.. .-.. .-.. --.. --.. --.. .--. .--. --.- .--. .--. --.. --.. --.. .-.. .-.. .-.. --.. .-.. .-.. .-.. --.. .-.. .-.. /'

对deckey2进行二次base64编码即可得到key的前面部分

import base64

deckey1 = base64.b64encode(deckey2)

key = base64.b64encode(deckey1)

 

其中key='TFMwdUxpQXVMUzR1SUMwdExpNGdMaTB1TGlBdUxTNHVJQzR0TGk0Z0xpMHVMaUF0TFM0dUlDMHRMaTRnTFMwdUxpQXVMUzB1SUM0dExTNGdMUzB1TFNBdUxTMHVJQzR0TFM0Z0xTMHVMaUF0TFM0dUlDMHRMaTRnTGkwdUxpQXVMUzR1SUM0dExpNGdMUzB1TGlBdUxTNHVJQzR0TGk0Z0xpMHVMaUF0TFM0dUlDNHRMaTRnTGkwdUxpQXY='

再加上deckey2的前三个字符的国密3杂凑值sm3_hash('--.')即可,

我用网络的sm3代码编译得到的是b92a72497b685c31013347a7276f371f8cf91085ab8322

而样例得到的是 b92a72497b685c31013347a7276f371f8cf91085ab8322009bfed2df41d94f94

当然我们用样例的,可以在 00435191 出断点断下,然后复制eax字符串指针指向的hash值即可。

sm3_hash = 'b92a72497b685c31013347a7276f371f8cf91085ab8322009bfed2df41d94f94'

于是我们得到最终的key=key+sm3_hash,

这个验证肯定是没问题的,由于闲的是字母和数字,上述key中有”=“号,所以我们对

deckey3 = gmap_steps_key = 'zlzllllzzzppqppzzzlllzlllzll '修改一下,追加一个空格符,这会导致deckey2追加一个"/"

最终二次base64加密会对齐没有出现”=“号,即最终标准结果为

deckey2 = '--.. .-.. --.. .-.. .-.. .-.. .-.. --.. --.. --.. .--. .--. --.- .--. .--. --.. --.. --.. .-.. .-.. .-.. --.. .-.. .-.. .-.. --.. .-.. .-.. //'

deckey1 = base64.b64encode(deckey2)

key = base64.b64encode(deckey1)+sm3_hash

即为"TFMwdUxpQXVMUzR1SUMwdExpNGdMaTB1TGlBdUxTNHVJQzR0TGk0Z0xpMHVMaUF0TFM0dUlDMHRMaTRnTFMwdUxpQXVMUzB1SUM0dExTNGdMUzB1TFNBdUxTMHVJQzR0TFM0Z0xTMHVMaUF0TFM0dUlDMHRMaTRnTGkwdUxpQXVMUzR1SUM0dExpNGdMUzB1TGlBdUxTNHVJQzR0TGk0Z0xpMHVMaUF0TFM0dUlDNHRMaTRnTGkwdUxpQXZMdz09b92a72497b685c31013347a7276f371f8cf91085ab8322009bfed2df41d94f94"

 

 

迷宫的路径怎么来的,即如何得到

deckey3 = gmap_steps_key = 'zlzllllzzzppqppzzzlllzlllzll '

 

下述为迷宫校验部分,其中Hi_game_map_49B000为迷宫的数据

.text:004351DB lea     eax, [ebp+loc_deckey3]

.text:004351E1 push    eax

.text:004351E2 push    offset Hi_game_map_49B000

.text:004351E7 call    Hi_zlqp_script_run_sub_42D9AB

 

 

迷宫为10*10的方形,通过下属IDAPython脚本可以得到迷宫的直观图像

#------- ------- ------- ------- ------- ------- -------

lstr = ''

for l in xrange(0,10):

  for c in xrange(0,10):

    f = Byte(0x49B000+l*10*4+c*4)

    if f == 0:

      lstr+='0'

    else:

      lstr+='1'

  print lstr

  lstr = ''

#------- ------- ------- ------- ------- ------- -------

0111111110

0011111000

1000001011

1111101001

1000101001

1010001011

1011111001

1000011100

1111000010

1111111000

#------- ------- ------- ------- ------- ------- -------

上述迷宫,左上坐标为[0,0],作者原意应该是走到右下[9,9]走出迷宫之后停止,

只能往"0"处走,且走过之后会设置为"4",即不能走回头路,我们可以通过

如前言所提zlqp四个字符分别表示往下、往右、往上、往左行走,通过下面python

脚本,我们可以自动获取迷宫走过的路径

deckey3 = gmap_steps_key = get_gmap_steps() + ' '

得到#deckey3 = 'zlzllllzzzppqppzzzlllzlllzll '

#------- ------- ------- ------- ------- ------- -------

gmap = '''

0111111110

0011111000

1000001011

1111101001

1000101001

1010001011

1011111001

1000011100

1111000010

1111111000

'''

 

gmap = gmap.replace('\n','')

 

def get_gmap_steps():

  LINE = 0

  COLUMN = 1

  steps = ''

  START_LC = [0,0]

  END_LC = [9,9]

  cur_lc = START_LC

  pre_lc = cur_lc

  while True:

    next_lc,cur2next_op = get_next_lc(cur_lc,pre_lc)

    pre_lc = cur_lc

    cur_lc = next_lc

    steps += cur2next_op

    if cur_lc == END_LC:

      break

  return steps

 

def get_next_lc(cur_lc = [0,0],pre_lc = [0,0]):

  LINE = 0

  COLUMN = 1

  next_lc = [cur_lc[LINE],cur_lc[COLUMN]]

  cur2next_op = None

  if step_zlqp('z',cur_lc,pre_lc):

    next_lc[LINE] += 1

    cur2next_op = 'z'

  elif step_zlqp('l',cur_lc,pre_lc):

    next_lc[COLUMN] += 1

    cur2next_op = 'l'

  elif step_zlqp('q',cur_lc,pre_lc):

    next_lc[LINE] -= 1

    cur2next_op = 'q'

  elif step_zlqp('p',cur_lc,pre_lc):

    next_lc[COLUMN] -= 1

    cur2next_op = 'p'

  return [next_lc,cur2next_op]

 

def step_zlqp(op='',cur_lc = [0,0],pre_lc = [0,0]):

  global gmap

  LINE = 0

  COLUMN = 1

  if 'z' == op:

    if [cur_lc[LINE]+1,cur_lc[COLUMN]]==pre_lc:

      return False

    if cur_lc[LINE]+1 < 10:

      if '0' == gmap[(cur_lc[LINE]+1)*10+cur_lc[COLUMN]]:

        return True

    return False

  elif 'l' == op:

    if [cur_lc[LINE],cur_lc[COLUMN]+1]==pre_lc:

      return False

    if cur_lc[COLUMN]+1 < 10:

      if '0' == gmap[cur_lc[LINE]*10+cur_lc[COLUMN]+1]:

        return True

    return False

  elif 'q' == op:

    if [cur_lc[LINE]-1,cur_lc[COLUMN]]==pre_lc:

      return False

    if cur_lc[LINE]-1 > 0:

      if '0' == gmap[(cur_lc[LINE]-1)*10+cur_lc[COLUMN]]:

        return True

    return False

  elif 'p' == op:

    if [cur_lc[LINE],cur_lc[COLUMN]-1]==pre_lc:

      return False

    if cur_lc[COLUMN]-1 > 0:

      if '0' == gmap[cur_lc[LINE]*10+cur_lc[COLUMN]-1]:

        return True

    return False

 

#------- ------- ------- ------- ------- ------- -------

 

 

由于bug的存在,deckey3 = 'zlzllllzzzppqppzzzlllzlllzll '

最极致的情形就是deckey3=" "

此时deckey2 = "/"

sm3_hash("/\x00\x00")='2f725aaf8d9fa538554e9f3589ddc785364d52ab1a6760c12caa2ec01ae4ba9e'

key = base64.b64encode(base64.b64encode('/')) = 'THc9PQ=='

即key = 'THc9PQ==2f725aaf8d9fa538554e9f3589ddc785364d52ab1a6760c12caa2ec01ae4ba9e’

由于不能有"="号,根据base64解码特性,这里直接去掉两个”=“号就可以,即

‘THc9PQ2f725aaf8d9fa538554e9f3589ddc785364d52ab1a6760c12caa2ec01ae4ba9e’

其它的情形,如只走到中间,或后续追加不定个数的停止符空格” “,都会产生不同的验证key



[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

收藏
点赞0
打赏
分享
最新回复 (3)
雪    币: 4900
活跃值: 活跃值 (244)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xie风腾 活跃值 2017-10-30 14:00
2
0
好强大的样纸,来学习了
雪    币: 991
活跃值: 活跃值 (96)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
陈jack 活跃值 2018-7-21 16:52
3
0
写的也很好
雪    币: 87
活跃值: 活跃值 (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
罗小墨 活跃值 2019-5-22 14:43
4
0
厉害厉害
游客
登录 | 注册 方可回帖
返回