X-CTF(REVERSE高级) asong

asong加密程序、out加密结果、that_girl加密引用的数据

out:一堆乱码二进制

that_girl:66行英语文字

asong:

一、函数功能解析

main函数

获取用户输入的值,引用that_girl文件做词频统计,对用户的输入值和统计结果进行一通操作后将结果保存输出到out文件

sub_400AAA函数

打开that_girl文件逐字读取,读取的内容给sub_400936函数,计算得到v2,地址(a2+v2)的值加1

 sub_400936函数,用case语法判断读取的数字

 sub_400E54函数

根据flag获取数组v5,改变v5的数组顺序,对v5的数据进行位移和或操作,将v5写入到out文件

 sub_400936函数,用case语法判断读取的数字

 sub_400D33函数,改变v5数组的顺序,改变规则是通过index的变换,index的变换由s数组决定

 

 

 

 s数组的值

 sub_400DB4函数,对v5数组的值进行移位和或运算

 sub_400CC0函数,v5写入out文件

二、调试获取统计数据

开始调试,获取that_girl文件词频统计的结果。ida报错“Bochs executable "bochsdbg.exe" is not found:

Please install Bochs and/or specify the location of "bochsdbg.exe" in the dbg_bochs.cfg file.”

进入官网:http://sourceforge.net/projects/bochs/files/bochs/,下载bochs,安装。

进入本地\IDA_Pro_v7.5_Portable\cfg目录找到“dbg_bochs.cfg”文件,修改bochs安装路径,取消注释

 报错,“Please ensure that Bochs is installed and configured properly.

Bochs output can be checked in the message window”

是因为boot配置问题,“ROM: couldn't open ROM image file '(null)/BIOS-bochs-latest'.”

 对文件“asong.bochsrc”进行配置

 

 还是有问题,算了,自己统计

1.不区分大小写

 2.数字和字母的数组index,0开始为数字,10开始为字母

 26个英文字母加10个数字,从36开始的index也安排好了

统计词频

s = """there's_a_girl_but_i_let_her_get_away
it's_all_my_fault_cause_pride_got_in_the_way
and_i'd_be_lying_if_i_said_i_was_ok
about_that_girl_the_one_i_let_get_away
i_keep_saying_no
this_can't_be_the_way_we're_supposed_to_be
i_keep_saying_no
there's_gotta_be_a_way_to_get_you_close_to_me
now_i_know_you_gotta
speak_up_if_you_want_somebody
can't_let_him_get_away_oh_no
you_don't_wanna_end_up_sorry
the_way_that_i'm_feeling_everyday
no_no_no_no
there's_no_hope_for_the_broken_heart
no_no_no_no
there's_no_hope_for_the_broken
there's_a_girl_but_i_let_her_get_away
it's_my_fault_cause_i_said_i_needed_space
i've_been_torturing_myself_night_and_day
about_that_girl_the_one_i_let_get_away
i_keep_saying_no
this can't be the way we're supposed to be
i keep saying no
there's gotta be a way to get you
there's gotta be a way
to_get_you_close_to_me
you_gotta
speak_up_if_you_want_somebody
can't_let_him_get_away_oh_no
you_don't_wanna_end_up_sorry
the_way_that_i'm_feeling_everyday
no_no_no_no
there's_no_hope_for_the_broken_heart
no no no no
there's no hope for the broken
no home for me
no home cause i'm broken
no room to breathe
and i got no one to blame
no home for me
no_home_cause_i'm_broken
about_that_girl
the_one_i_let_get_away
so_you_better
speak_up_if_you_want_somebody
you_can't_let_him_bet_away_no_no
you_don't_wanna_end_up_sorry
the_way_that_i'm_feeling_everyday
don't_you_know
no_no_no_no
there's_no_hope_for_the_broken_hearty
don't you know
no no no no
there's no hope for the broken
oh
you don't wanna lose at love
it's only gonna hurt too much
i'm telling you
you_don't_wanna_lose_at_love
it's_only_gonna_hurt_too_much
i'm_telling_you
you_don't_wanna_lose_at_love
cause_there's_no_hope_for_the_broken_heart
that_girl
the_one_i_let_get_away
"""
out = {}
for i in s:
    out.update({i:s.count(i)})
out = sorted(out.items())
print(out)
        

'''
[('\n', 66), (' ', 71), ("'", 40), ('_', 245), ('a', 104), ('b', 30), ('c', 15), ('d', 29), ('e', 169), ('f', 19), ('g', 38), ('h', 67), ('i', 60), ('k', 20), ('l', 39), ('m', 28), ('n', 118), ('o', 165), ('p', 26), ('r', 61), ('s', 51), ('t', 133), ('u', 45), ('v', 7), ('w', 34), ('y', 62)]
没有数字,没有case以外的统计,根据规则重新整理一下数组排序:

[('a', 104), ('b', 30), ('c', 15), ('d', 29), ('e', 169), ('f', 19), ('g', 38), ('h', 67), ('i', 60), ('k', 20), ('l', 39), ('m', 28), ('n', 118), ('o', 165), ('p', 26), ('r', 61), ('s', 51), ('t', 133), ('u', 45), ('v', 7), ('w', 34), ('y', 62),(' ', 71),('_', 245)]
'''

三、解密

对“out”文件的数据进行反向操作,先做移位和或运算,再改变顺序,最后和词频统计换算

 这道题算法的小细节很多,仿佛回到了高中做数学奥数题,最简洁精妙的解法还是官方的wp,所以放上添加了我注释的官方wp吧,之后讲讲为什么精妙。

s = [22, 0, 6, 2, 30, 24, 9, 1, 21, 7, 18, 10, 8, 12, 17, 23, 13, 4, 3, 14, 19, 11, 20, 16, 15, 5, 25, 36, 27, 28, 29, 37, 31, 32, 33, 26, 34, 35]
#从程序data区复制出来的s数组值

mapp={' ': 71, "'": 40, '_': 245, 'a': 104, 'c': 15, 'b': 30, 'e': 169, 'd': 29, 'g': 38, 'f': 19, 'i': 60, 'h': 67, 'k': 20, 'm': 28, 'l': 39, 'o': 165, 'n': 118, 'p': 26, 's': 51, 'r': 61, 'u': 45, 't': 133, 'w': 34, 'v': 7, 'y': 62}
#that_girl词频统计

def decypt():
    enc = open("out","rb").read()
    d0 = []
    temp = enc[len(enc)-1] & 0x7
    for i in range(len(enc)):
        d0.append((temp << 5) | enc[i] >> 3)
        temp = enc[i] & 0x7
    #移位、或运算的解
    
    i = 37
    temp = d0[37]
    while s.index(i) != 37:
        d0[i] = d0[s.index(i)]
        i = s.index(i)
    #改变顺序的解
    
    d0[i] = temp
    flag = []
    for i in d0:
        flag.append(list(mapp.keys())[list(mapp.values()).index(i)])
        #和词频统计换算的解
    return "QCTF{%s}" % ''.join(flag)
print(decypt())

官方wp代码解析:

一、为什么移位和或运算可以用&0x7去逆推

仿照程序 sub_400DB4函数,对v5数组的值进行移位和或运算,写一个s数组模仿对s的加密解密,发现能通过&0x7去逆推位移和或运算,太神奇了吧。但是逆推回去的最后一位不准,s数组的值超过255会造成多位值不准。正好对应了程序加密保留原v5数组的最后一位值不变,“out”文件的byte读取值范围是0到0xff。

那为什么&0x7可以逆推移位和或运算呢,<<是向左移动,>>是向右移动,|或运算(有1则1),&与运算(都1则1)

加密:s[i]<<3,左移三位,为保留本i的二进制后七位到当前值。s[i+1]>>5,右移5位,为保留下一i的二进制前三位到当前值。

解密:&0x7,与运算0000 0111,为取二进制后三位的值。temp<<5,为恢复当前i的二进制前三位的值。enc[i] >> 3,为恢复当前i的二进制后七位的值。

所以,整体逻辑是,加密运算本i保留后七位和下一i的前三位,逆运算把本i七位退回去,用temp去前i找保留的本i的前三位。

 

想了一晚上,谁逆向放二进制加密谁是🐕,平常要多多积累啊

二、为什么s数组是根据index改变数组顺序

*(_DWORD *)是强制类型转化,然后再提领指针。*(int *) 也是强行转换后,再提领指针。

因为v2等于a1的地址,v2[1]为数组的值,那么&v2[1]为获取元素v2[1]在数组v2中的索引,然后*(int *)强制类型转换为int

v2[1]就是while循环的变量i,因为out的长度是37,所以其逆推其初始长度为37

最后词频统计的逻辑很简单,逆推就是根据上面算出result数组的值,去查找对应字典的key

 

posted @ 2022-07-25 23:26  黑冰Lisa  阅读(660)  评论(0编辑  收藏  举报