一些比较有意思的题 实际上最近的比赛都有在打,只是没有整理了,一方面是时间不够,二是觉得做起的题的难度也就那样,没必要整理,就把最近感觉比较有含金量的题整理整理。最近也在出题,好忙。。。不过值得高兴的是,原本以为之前打的两个比赛已经没什么希望了,结果一个还有奖金,一个还进了线下,昨天永劫无间还用免费宝箱开了个冰魄皮肤,哈哈哈,欧皇竟是我自己。
这篇文章就整理最近觉得比较好的题。
HaHaHaHa 比赛时,搞了一个下午,java爆破太慢了。
先下载apk打开看看,发现是要输入8个字符串,然后没有小写字符。
用jeb打开。
发现使用了一个for循环来处理8个字符串。 然后下面有一个处理字符串的函数,也就是a类中的c函数。 然后下面使用修改后的数据去进行各种hash加密。 接下来看如何通过传入的第一个参数从而决定到底是哪种hash。 然后直接去解就会发现不对劲,原来是在开始的时候,修改了a类中的数据。 然后先得到8个字符串的加密方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int [] b; int i; b = new int []{0xAF , 0xA1 , 0xA4 , 170 , 0xA5 , 0xAE , 0xA0 , 0xA3 }; for (i=0 ;i<b.length;i++) { System.out.print(b[i]); System.out.print(" " ); System.out.print((b[i]^0xab ) >>> 3 & 1 ); System.out.print(" " ); System.out.println((b[i]^0xab )&7 ); }
然后开始解。
1 2 3 4 5 6 7 8 9 10 11 12 13 import hashlibfor a in range (0 ,128 ): for b in range (0 ,128 ): for c in range (0 ,128 ): for d in range (0 ,128 ): str = chr (a)+chr (b)+chr (c)+chr (d) print(str ) flag = hashlib.sha384(str .encode('utf-8' )).hexdigest() if flag[0 :16 ] == 'f2dda5fc021fe2bf' : print(str , end='' ) exit(0 )
HmacSha512比较特殊,就直接用了类中的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 package com.company;import javax.crypto.spec.SecretKeySpec;import java.math.BigInteger;import java.security.GeneralSecurityException;import java.security.InvalidKeyException;import java.security.Key;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import java.util.*;public class Main { public static void main (String[] args) { byte [][] a; String[] c; a = new byte [][]{"WIgD1ZNZ0ilJqFpw" .getBytes(), "4811tjOZjoiXpjdq" .getBytes(), "ALFjcgztxnUaC89v" .getBytes(), "ZgHzTu79Zwhoi0PB" .getBytes(), "UYBfajKYrDFE1zJs" .getBytes(), "yr4PBIjlJg89FpP3" .getBytes(), "SFHqaTYDf7EeEevX" .getBytes(), "gUwrqaE3nCxKr4Du" .getBytes()}; c = new String[]{"fc7466e55fbf37b1" , "78b0be39e63b6837" , "c2f9c805d0442203" , "c11a61bb60d79dab" , "869e650ee55bd9f6" , "f2dda5fc021fe2bf" , "305044db48fe6174" , "d6659b5e2d1059f8" }; int v4 = 0 ; MessageDigest hhh = null ; while (v4 < a.length) { System.out.print(" " ); try { hhh = MessageDigest.getInstance("MD5" ); } catch (NoSuchAlgorithmException v1_1) { v1_1.printStackTrace(); } hhh.update(a[v4]); a[v4] = hhh.digest(); System.out.println(a[v4]); ++v4; } String v0_1; byte [] s; int a1, a2, a3, a4, calc = 0 ; s = new byte []{0x1 , 0x2 , 0x3 , 0x4 }; for (a1 = 71 ; a1 < 127 ; a1++) { for (a2 = 65 ; a2 < 127 ; a2++) { for (a3 = 76 ; a3 < 127 ; a3++) { for (a4 =70 ; a4 < 127 ; a4++) { s[0 ] = (byte ) a1; s[1 ] = (byte ) a2; s[2 ] = (byte ) a3; s[3 ] = (byte ) a4; v0_1 = func(s, a[0 ]); System.out.println("no " + s[0 ] + " " + s[1 ] + " " + s[2 ] + " " + s[3 ] + " " + v0_1.substring(0 , 16 ) + " " + c[2 ]); if (v0_1.substring(0 , 16 ).equals(c[7 ])) { System.out.println("yes" ); for (int i = 0 ; i < 4 ; i++) { System.out.print(s[i]+" " ); } System.exit(0 ); } } } } } int [] b; int i; b = new int []{0xAF , 0xA1 , 0xA4 , 170 , 0xA5 , 0xAE , 0xA0 , 0xA3 }; for (i=0 ;i<b.length;i++) { System.out.print(b[i]); System.out.print(" " ); System.out.print((b[i]^0xab ) >>> 3 & 1 ); System.out.print(" " ); System.out.println((b[i]^0xab )&7 ); } } public static String func (byte [] arg2, byte [] arg3) { String v0 = "HmacSha512" ; String v2_2 = null ; try { SecretKeySpec v1 = new SecretKeySpec(arg3, v0); Mac v3 = Mac.getInstance(v0); v3.init(((Key) v1)); v3.update(arg2); for (v2_2 = new BigInteger(1 , v3.doFinal()).toString(16 ); v2_2.length() < 0x20 ; v2_2 = "0" + v2_2) { } } catch (InvalidKeyException v2) { ((GeneralSecurityException) v2).printStackTrace(); return null ; } catch (NoSuchAlgorithmException v2_1) { return null ; } return v2_2; } }
最后会得到8组数据。
0x5f,0x38,0x40,0x50,
0x35,0x31,0x5f,0x48,
0x7d,0x50,0x31,0x31,
0x7b,0x48,0x40,0x35,
0x43,0x5f,0x35,0x35,
0x50,0x31,0x4e,0x33,
0x33,0x48,0x37,0x5f,
0x47,0x41,0x4c,0x46,
这就设计到&0x7f的问题了。一些数需要加0x80,一些数有不需要,所以我直接手动控制,来得到那个v10,看哪种情况满足条件和题目中a.b数组会产生一样的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.company;import java.util.*;public class Main { public static void main (String[] args) { int v9; int v10; byte [] v7_1; v7_1=new byte []{0x47 ,0x41 ,0x4c ,0x46 }; v7_1[0 ]+=0x80 ; v9 = 0 ; v10 = 0 ; while (v9 < v7_1.length) { v10 = v10 << 1 | (v7_1[v9] & 0x80 ) >>> 7 ; v7_1[v9] = ((byte )(v7_1[v9] & 0x7F )); ++v9; } System.out.println(v10); System.out.print(v10 >>> 3 & 1 ); System.out.print(" " ); System.out.println(v10&7 ); } }
得到,输入后就会得到flag。
# 0x5f,0x38+0x80,0x40,0x50 5FB84050
#
# 0x35+0x80,0x31,0x5f+0x80,0x48 B531DF48
#
# 0x7d,0x50,0x31,0x31, +0x80 FDD0B1B1
#
# 0x7b,0x48,0x40,0x35+0x80 7B4840B5
#
# 0x43+0x80,0x5f+0x80,0x35+0x80,0x35 C3DFB535
#
# 0x50,0x31+0x80,0x4e,0x33+0x80 50B14EB3
#
# 0x33+0x80,0x48,0x37+0x80,0x5f+0x80 B348B7DF
#
# 0x47+0x80,0x41,0x4c,0x46 C7414C46
fastjs 赛后来做的,这种题型已经是第三次见了,所以准备来搞一搞。
看雪,和Mas0n师傅的博客里面都有详细讲解如何解这种题。
看雪
Mas0n
实际上就是利用quickjs来将一个js文件弄成.c文件,然后将用gcc编译c文件成为exe。
比如说,GitHub上quickjs项目example的hello.js,弄成c文件后就是如下的结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include "quickjs-libc.h" const uint32_t qjsc_hello_size = ???;const uint8_t qjsc_hello[size] = {}; static JSContext *JS_NewCustomContext (JSRuntime *rt) { JSContext *ctx = JS_NewContextRaw(rt); if (!ctx) return NULL ; JS_AddIntrinsicBaseObjects(ctx); JS_AddIntrinsicDate(ctx); JS_AddIntrinsicEval(ctx); JS_AddIntrinsicStringNormalize(ctx); JS_AddIntrinsicRegExp(ctx); JS_AddIntrinsicJSON(ctx); JS_AddIntrinsicProxy(ctx); JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); JS_AddIntrinsicBigInt(ctx); return ctx; } int main (int argc, char **argv) { JSRuntime *rt; JSContext *ctx; rt = JS_NewRuntime(); js_std_set_worker_new_context_func(JS_NewCustomContext); js_std_init_handlers(rt); JS_SetModuleLoaderFunc(rt, NULL , js_module_loader, NULL ); ctx = JS_NewCustomContext(rt); js_std_add_helpers(ctx, argc, argv); js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0 ); js_std_loop(ctx); JS_FreeContext(ctx); JS_FreeRuntime(rt); return 0 ; }
这和我们将题目文件拖到ida如出一辙。 然后我们采用文章中的方法来将题目的opcode弄成字节码。
得到,有点长,不贴完了。
0000: 02 3a 58 atom indexes {
1"long2str"
1"str2long"
1"sdfsfsdf"
1"str2Hex"
1"hex2str"
···
···
···
put_loc0 0: ““ get_var_undef arguments typeof_is_undefined if_true8 203 get_var arguments dup put_var args put_loc0 0: ““ goto8 215 203: push_atom_value error dup put_var args put_loc0 0: ““ 215: get_var main get_var args call1 1 set_loc0 0: ““ return
}
然后手搓还原,只不过也太多了,暂时不知道能用什么工具反编译不,实际上可以半猜半分析。
可以看到里面有base64表 推测base64加密,有dealt,有e,xxtea。
然后尝试去解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include<stdio.h> void decrypt(unsigned int *code ,unsigned int *key ,unsigned int n) { unsigned int next,end,sum; unsigned int rounds,e,delta=2654435769; int i; rounds=6+52/n; sum=rounds*delta; next=code[0];//设置next为code的第一个 do { e=(sum>>2)&3; for(i=n-1;i>0;i--)//解密最后一个到第二个 { end=code[i-1]; code[i]-=(( (end>>5^next<<2) + (next>>3^end<<4) ) ^ ( (sum^next) + (key[(i&3)^e]^end) )); next=code[i]; } end=code[n-1]; code[0]-=(( (end>>5^next<<2) + (next>>3^end<<4) ) ^ ( (sum^next) +(key[i&3^e]^end) )); next=code[0]; sum-=delta; }while(--rounds); } int main() { unsigned int flag[2]={1,2},key[4]={0x745f6f6e, 0x676e6968, 0x5f73695f, 0x65757274}; unsigned int n=14; unsigned int code[14]={0xced0ae05,0xb5801f44,0x4caf36bc,0xfc098569,0x71c9c36c,0xe53d3546,0xbe6a5ca9,0xa7d47fa0,0xd8320907,0x622dc36a,0x91a57286,0x2397e523,0xff5ddb31,0x627305e7}; int i; decrypt(code,key,n); printf("%x %x\n",code[0],code[1]); for(i=0;i<14;i++) { printf("%c%c%c%c",*((char*)&code[i]+0),*((char*)&code[i]+1),*((char*)&code[i]+2),*((char*)&code[i]+3)); } } //ZmxhZ3tmYzVlMDM4ZDM4YTU3MDMyMDg1NDQxZTdmZTcwMTBiMH0=4
解下base64得到flag{fc5e038d38a57032085441e7fe7010b0}
showyourflag 初步分析可以知道,这个程序是加密文件,然后生成一个文件。
命令行参数
showyourflag [flie] [encoedfile]
当时和师傅一起做题时,只分析出来了部分,没分析完,可以确定是个png文件,但还需要分析那个主要加密函数,后面有时间来细致分析(看情况)。
gogogog 一个华容道的题,开始是蓝帽杯决赛的一个题,后面也遇到了两次,还是一模一样的。。。。所以直接秒了。
一个解华容道的脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 import heapqimport copyimport timeimport mathimport argparseS0 = [[5 , 1 , 0 , 2 ], [9 , 6 , 3 , 8 ], [13 , 15 , 10 , 11 ], [14 , 4 , 7 , 12 ]] SG = [[1 , 2 , 3 , 4 ], [5 , 6 , 7 , 8 ], [9 , 10 , 11 , 12 ], [13 , 14 , 15 , 0 ]] MOVE = {'up' : [1 , 0 ], 'down' : [-1 , 0 ], 'left' : [0 , -1 ], 'right' : [0 , 1 ]} OPEN = [] SUM_NODE_NUM = 0 class State (object ): def __init__ (self, deepth=0 , rest_dis=0.0 , state=None , hash_value=None , father_node=None ): ''' 初始化 :参数 deepth: 从初始节点到目前节点所经过的步数 :参数 rest_dis: 启发距离 :参数 state: 节点存储的状态 4*4的列表 :参数 hash_value: 哈希值,用于判重 :参数 father_node: 父节点指针 ''' self.deepth = deepth self.rest_dis = rest_dis self.fn = self.deepth + self.rest_dis self.child = [] self.father_node = father_node self.state = state self.hash_value = hash_value def __lt__ (self, other ): return self.fn < other.fn def __eq__ (self, other ): return self.hash_value == other.hash_value def __ne__ (self, other ): return not self.__eq__(other) def cal_M_distence (cur_state ): ''' 计算曼哈顿距离 :参数 state: 当前状态,4*4的列表, State.state :返回: M_cost 每一个节点计算后的曼哈顿距离总和 ''' M_cost = 0 for i in range (4 ): for j in range (4 ): if cur_state[i][j] == SG[i][j]: continue num = cur_state[i][j] if num == 0 : x, y = 3 , 3 else : x = num / 4 y = num - 4 * x - 1 M_cost += (abs (x - i) + abs (y - j)) return M_cost def cal_E_distence (cur_state ): ''' 计算曼哈顿距离 :参数 state: 当前状态,4*4的列表, State.state :返回: M_cost 每一个节点计算后的曼哈顿距离总和 ''' E_cost = 0 for i in range (4 ): for j in range (4 ): if cur_state[i][j] == SG[i][j]: continue num = cur_state[i][j] if num == 0 : x, y = 3 , 3 else : x = num / 4 y = num - 4 * x - 1 E_cost += math.sqrt((x - i)*(x - i) + (y - j)*(y - j)) return E_cost def generate_child (sn_node, sg_node, hash_set, open_table, cal_distence ): ''' 生成子节点函数 :参数 sn_node: 当前节点 :参数 sg_node: 最终状态节点 :参数 hash_set: 哈希表,用于判重 :参数 open_table: OPEN表 :参数 cal_distence: 距离函数 :返回: None ''' if sn_node == sg_node: heapq.heappush(open_table, sg_node) print('已找到终止状态!' ) return for i in range (0 , 4 ): for j in range (0 , 4 ): if sn_node.state[i][j] != 0 : continue for d in ['up' , 'down' , 'left' , 'right' ]: x = i + MOVE[d][0 ] y = j + MOVE[d][1 ] if x < 0 or x >= 4 or y < 0 or y >= 4 : continue state = copy.deepcopy(sn_node.state) state[i][j], state[x][y] = state[x][y], state[i][j] h = hash (str (state)) if h in hash_set: continue hash_set.add(h) global SUM_NODE_NUM SUM_NODE_NUM += 1 deepth = sn_node.deepth + 1 rest_dis = cal_distence(state) node = State(deepth, rest_dis, state, h, sn_node) sn_node.child.append(node) heapq.heappush(open_table, node) def show_block (block, step ): print("------" , step, "--------" ) for b in block: print(b) def print_path (node ): ''' 输出路径 :参数 node: 最终的节点 :返回: None ''' print("最终搜索路径为:" ) steps = node.deepth stack = [] while node.father_node is not None : stack.append(node.state) node = node.father_node stack.append(node.state) step = 0 while len (stack) != 0 : t = stack.pop() show_block(t, step) step += 1 return steps def A_start (start, end, distance_fn, generate_child_fn ): ''' A*算法 :参数 start: 起始状态 :参数 end: 终止状态 :参数 distance_fn: 距离函数,可以使用自定义的 :参数 generate_child_fn: 产生孩子节点的函数 :返回: 最优路径长度 ''' root = State(0 , 0 , start, hash (str (S0)), None ) end_state = State(0 , 0 , end, hash (str (SG)), None ) if root == end_state: print("start == end !" ) OPEN.append(root) heapq.heapify(OPEN) node_hash_set = set () node_hash_set.add(root.hash_value) while len (OPEN) != 0 : top = heapq.heappop(OPEN) if top == end_state: return print_path(top) generate_child_fn(sn_node=top, sg_node=end_state, hash_set=node_hash_set, open_table=OPEN, cal_distence=distance_fn) print("无搜索路径!" ) return -1 if __name__ == '__main__' : parser = argparse.ArgumentParser(description='选择距离计算方法' ) parser.add_argument('--method' , '-m' , help ='method 选择距离计算方法(cal_E_distence or cal_M_distence)' , default = 'cal_M_distence' ) args = parser.parse_args() method = args.method time1 = time.time() if method == 'cal_E_distence' : length = A_start(S0, SG, cal_E_distence, generate_child) else : length = A_start(S0, SG, cal_M_distence, generate_child) time2 = time.time() if length != -1 : if method == 'cal_E_distence' : print("采用欧式距离计算启发函数" ) else : print("采用曼哈顿距离计算启发函数" ) print("搜索最优路径长度为" , length) print("搜索时长为" , (time2 - time1), "s" ) print("共检测节点数为" , SUM_NODE_NUM)
根据程序的方向,换一下,得到###$@%#$$@@@%#%#%@$@%##$$@@%##%#
1 2 3 4 5 table={'a' :'$' ,'d' :'%' ,'w' :'@' ,'s' :'#' } s='sssawdsaawwwdsdsdwawdssaawwdssds' for i in s: print(table[i],end='' )
然后md5加密。
Faker.exe 题目名称不叫Faker.exe,绿盟杯的一道500分的题,动态分可能更高,比赛时没做起,最后发现是脚本,我愚蠢的吧if判断条件后面加了个分号,低级错误,导致没能进线下,我是傻逼。
一道类似于病毒的题,以隐藏文件形式创建了个dll文件,并且在exe中用命令行执行来调用dll文件的导出函数,也就是cherk,传入的参数是26字节,也就是flag{}中间部分,然后进行了一些操作。
可以根据弹出的字符串和GetWindowsText函数来定位到关键位置。 接下来就会以隐藏模式创建FakerDll和Faker.dll文件。 接下来会自解密两个字符串,并将flag{}内的内容和两个字符串拼接起来。 我们来看看最后的字符串会变成什么样子。
1 2 3 4 5 6 7 8 9 10 11 12 13 s1="D;]Xjoepxt]TztXPX75]svoemm43/fyf GblfsEmm/emm-Difdl" s2="D;]Xjoepxt]TztXPX75]svoemm43/fyf GblfsEmm/emm-Difdl" for i in range (len (s1)): print(chr (ord (s1[i])-1 ),end='' ) print(end='\n' ) for i in range (len (s2)): print(chr (ord (s2[i])-1 ),end='' ) print(end='\n' )
然后拼接上flag,所以已经可以想到了,这个是想用windows自带的rundll32.exe来启动dll文件来检测flag是否正确,接下来,看sub_402680函数。 Faker.dll分析。
check函数 字符串处理函数 然后写脚本,先base64解,再异或,当然,因为个人的低级错误,分号的问题,导致没得到正确答案,也是非常可惜。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <string.h> int main () { char s3[]="sbVnXr_G`By<Dp]5gIUmGk6t4x" ; int i,j; for (i=0 ;i<13 ;i++) { if ( ((s3[i]^0xf ) <= 'z' ) && ( (s3[i]^ 0xf ) >= '0' )) { s3[i] ^= 0xf ; } } for (i=13 ;i<26 ;i++) { if ( ((s3[i]^5 ) <= 'z' )&& ( (s3[i]^5 ) >= '0' )) { s3[i] ^= 5 ; } } for (i=0 ;i<26 ;i++) { printf ("%c" ,s3[i]); } }