MINIRE
有upx壳,并且把upx改为gcc了
两种解法 一种是hook运算操作,尝试还原算法,我比赛时就是用的这种,但是这题校验非常难找,导致比赛时一直没找到,另一种是单字节爆破
方法一
脱壳,f5发现没法反编译
但是ghidra是可以的,它没有栈限制
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 | case 'X' :
local_20 = 0;
while (local_1c = local_1c + 1, *( char *)(param_1 + local_1c) != 'x' ) {
(&stack0xffffffffffc24328)[local_20] = *(undefined *)(param_1 + local_1c);
local_20 = local_20 + 1;
}
(&stack0xffffffffffc24328)[local_20] = 0;
if (1 < local_20) {
if (in_stack_ffffffffffc24328 != 'V' ) {
for (local_24 = 1;
((local_24 < local_20 && ( '/' < ( char )(&stack0xffffffffffc24328)[local_24])) &&
(( char )(&stack0xffffffffffc24328)[local_24] < ':' )); local_24 = local_24 + 1) {
}
}
iVar7 = ( int )in_stack_ffffffffffc24328;
if (iVar7 == 0x20) {
bVar1 = (&DAT_005175a0)[DAT_00768090];
bVar5 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = bVar1 ^ bVar5;
iVar6 = DAT_00768094;
}
else if (((0x1f < iVar7) && (iVar7 < 0x6b)) && (0x40 < iVar7)) {
switch (iVar7) {
case 0x41:
cVar2 = (&DAT_005175a0)[DAT_00768090];
cVar4 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = cVar2 * cVar4;
iVar6 = DAT_00768094;
break ;
case 0x4a:
iVar7 = FUN_00405c10(&stack0xffffffffffc24329);
iVar6 = DAT_00768094;
if (iVar7 != 0) {
cVar2 = (&DAT_005175a0)[DAT_00768090];
cVar4 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = cVar2 + cVar4;
iVar6 = DAT_00768094;
}
break ;
case 0x51:
cVar2 = (&DAT_005175a0)[DAT_00768090];
cVar4 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = cVar2 - cVar4;
iVar6 = DAT_00768094;
break ;
case 0x54:
bVar1 = (&DAT_005175a0)[DAT_00768090];
bVar5 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = bVar1 | bVar5;
iVar6 = DAT_00768094;
break ;
case 0x55:
uVar3 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = uVar3;
iVar6 = DAT_00768094;
break ;
case 0x5f:
cVar2 = (&DAT_005175a0)[DAT_00768090];
cVar4 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = cVar2 + cVar4;
iVar6 = DAT_00768094;
break ;
case 0x61:
iVar7 = FUN_00405c10(&stack0xffffffffffc24329);
iVar6 = DAT_00768094;
if (iVar7 != 0) {
bVar1 = (&DAT_005175a0)[DAT_00768090];
bVar5 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = bVar1 / bVar5;
iVar6 = DAT_00768094;
}
break ;
case 0x6a:
bVar1 = (&DAT_005175a0)[DAT_00768090];
bVar5 = FUN_00405c10(&stack0xffffffffffc24329);
(&DAT_005175a0)[DAT_00768090] = bVar1 & bVar5;
iVar6 = DAT_00768094;
|
对应的找到各个运算的位置下断点,很容易就能得到加密逻辑
xor: 0x66,0x4
add: 0xC,0x62,0xC
xor: 0x6E,0xB
xor: 0x65,0xC
sub: 0x69,0x2
sub: 0x67,0x1
xor: 0x66,0x6
add: 0xA,0x6C,0xA
xor: 0x76,0xC
add: 0x5,0x7A,0x5
add: 0x4,0x61,0x4
add: 0xE,0x65,0xE
sub: 0x67,0xA
sub: 0x5D,0x3
add: 0xD,0x5A,0xD
add: 0x7,0x67,0x7
sub: 0x6E,0x9
xor: 0x65,0xE
sub: 0x7B,0x7
sub: 0x74,0xF
xor: 0x30,0xE
xor: 0x31,0x2
add: 0x0,0x33,0x0
xor: 0x33,0x2
add: 0x9,0x31,0x9
add: 0x3,0x3A,0x3
sub: 0x3D,0x2
add: 0xD,0x3B,0xD
xor: 0x48,0x1
add: 0x8,0x32,0x8
add: 0xC,0x3A,0xC
xor: 0x46,0xF
add: 0x5,0x49,0x5
xor: 0x4E,0x3
add: 0x8,0x4D,0x8
add: 0x8,0x33,0x8
add: 0x2,0x3B,0x2
xor: 0x3D,0x8
sub: 0x35,0xC
add: 0xB,0x29,0xB
sub: 0x34,0x3
xor: 0x31,0x9
sub: 0x38,0x2
add: 0x8,0x36,0x8
sub: 0x34,0x0
sub: 0x34,0x2
add: 0xB,0x35,0xB
sub: 0x36,0x2
xor: 0x34,0x7
sub: 0x33,0x1
sub: 0x32,0x5
add: 0x1,0x37,0x1
sub: 0x38,0xD
add: 0xC,0x2B,0xC
sub: 0x38,0x2
sub: 0x36,0xE
sub: 0x28,0x9
xor: 0x1F,0xE
add: 0x1,0x11,0x1
add: 0xA,0x12,0xA
sub: 0x1C,0x7
sub: 0x15,0x1
add: 0x9,0x14,0x9
sub: 0x39,0x0
sub: 0x39,0xB
xor: 0x2E,0x4
add: 0x9,0x2A,0x9
xor: 0x61,0xE
add: 0x5,0x6F,0x5
sub: 0x74,0xC
sub: 0x68,0x4
sub: 0x64,0x4
add: 0xF,0x60,0xF
add: 0xE,0x6F,0xE
sub: 0x7D,0x4
xor: 0x79,0x5
sub: 0x7C,0xC
xor: 0x62,0xB
add: 0xD,0x69,0xD
sub: 0x76,0xB
xor: 0x6B,0x4
add: 0xA,0x6F,0xA
sub: 0x79,0x8
add: 0xA,0x63,0xA
add: 0xF,0x6D,0xF
add: 0x2,0x7C,0x2
sub: 0x7E,0x6
sub: 0x78,0x9
sub: 0x6F,0x9
add: 0x3,0x66,0x3
add: 0x5,0x69,0x5
sub: 0x6E,0xF
add: 0x2,0x5F,0x2
sub: 0x64,0xE
sub: 0x56,0xD
xor: 0x65,0x7
sub: 0x62,0xF
sub: 0x53,0x1
xor: 0x52,0x3
sub: 0x51,0x3
add: 0xB,0x4E,0xB
add: 0xA,0x59,0xA
add: 0xA,0x63,0xA
add: 0x8,0x66,0x8
sub: 0x6E,0x6
sub: 0x68,0x7
xor: 0x61,0x4
sub: 0x65,0x1
xor: 0x64,0x3
add: 0x5,0x30,0x5
add: 0x2,0x35,0x2
add: 0xA,0x37,0xA
xor: 0x41,0x0
sub: 0x31,0x7
add: 0x0,0x2A,0x0
add: 0x8,0x2A,0x8
add: 0x1,0x32,0x1
add: 0x1,0x33,0x1
xor: 0x34,0xA
sub: 0x3E,0xE
xor: 0x30,0xF
sub: 0x3F,0x7
xor: 0x38,0x8
xor: 0x32,0x4
sub: 0x36,0x9
add: 0xF,0x2D,0xF
add: 0xC,0x33,0xC
xor: 0x3F,0x2
xor: 0x3D,0x4
sub: 0x39,0x7
add: 0x1,0x32,0x1
add: 0x0,0x34,0x0
add: 0x9,0x34,0x9
xor: 0x3D,0x2
add: 0xE,0x3F,0xE
xor: 0x4D,0xB
xor: 0x46,0x1
add: 0x2,0x47,0x2
add: 0x9,0x49,0x9
sub: 0x52,0x9
add: 0x6,0x35,0x6
xor: 0x36,0xA
sub: 0x3C,0x4
sub: 0x38,0xD
xor: 0x2B,0x8
sub: 0x23,0xE
add: 0xE,0x15,0xE
xor: 0x23,0xE
add: 0x1,0x37,0x1
xor: 0x38,0x4
sub: 0x3C,0x9
add: 0x9,0x33,0x9
sub: 0x3C,0x5
add: 0xD,0x37,0xD
add: 0x1,0x44,0x1
xor: 0x45,0xA
xor: 0x38,0x9
add: 0xE,0x39,0xE
add: 0xC,0x47,0xC
xor: 0x53,0x0
sub: 0x53,0x0
sub: 0x53,0xF
xor: 0x44,0xB
sub: 0x4F,0xB
add: 0x8,0x44,0x8
add: 0xB,0x61,0xB
add: 0x6,0x6C,0x6
add: 0x1,0x72,0x1
xor: 0x73,0xC
sub: 0x7F,0x6
sub: 0x79,0xE
sub: 0x62,0xB
xor: 0x57,0xD
add: 0x5,0x5A,0x5
xor: 0x5F,0x2
sub: 0x5D,0x2
add: 0xD,0x63,0xD
xor: 0x70,0x6
add: 0xF,0x76,0xF
xor: 0x85,0x0
sub: 0x85,0x3
sub: 0x82,0xC
sub: 0x76,0xF
xor: 0x67,0xB
add: 0x9,0x64,0x9
add: 0xA,0x6D,0xA
add: 0xA,0x77,0xA
xor: 0x81,0xA
sub: 0x65,0xD
xor: 0x66,0x7
sub: 0x61,0x0
sub: 0x7D,0xB
add: 0xE,0x72,0xE
sub: 0x80,0x8
add: 0x4,0x78,0x4
add: 0x3,0x7C,0x3
add: 0xD,0x7F,0xD
xor: 0x8C,0x3
xor: 0x8F,0xA
这里打印的时候可以格式化一下,便于后续写脚本,因为没想到能直接写z3()
参考0xcafebabe
师傅的题解给出的脚本格式,会非常方便搞成z3,但是这里需要看出来eax - 46 就是index
1 2 3 4 5 | import ida_dbg
index = ida_dbg.get_reg_value( 'RAX' ) - 0x46
EDX_value = ida_dbg.get_reg_val( "EDX" )
print (f "x[{value}]^= 0x{EDX_value:X}" )
|
接着就是找密文或者校验逻辑了,但是比赛卡这地方卡了一下午ww
后来将多个点全hook,并根据已知正确的前五个字符'flag{'的加密结果,可以发现其读的方式
猜测应该是存于某一个连续内存的,hook方式如下
1 2 3 | import ida_dbg
EAX_value = ida_dbg.get_reg_val( "EAX" )
print (f "case 69: 0x{EAX_value:X}" )
|
1 2 3 4 5 | import ida_dbg
EAX_value = ida_dbg.get_reg_val( "EAX" )
print (f "case 69: 0x{EAX_value:X}" )
if EAX_value = = 0X99 :
fuck
|
通过上述脚本可断于特定位置,跟以下发现了以下内存块存储了enc
然后即可用z3解了
脚本可参考https://blog.hxzzz.asia/,属实是懒得写z3了
方法二
比赛的时候原本是有想用的,但是看波动范围比较大就没用,下来试了下发现没问题
插桩点分别是handler与数据清理出
hook.js
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 | var number = 0
function main()
{
var base = Module.findBaseAddress( "minire" )
if (base){
/ / MemoryAccessMonitor.enable([{ base: base.add( 0x1175Ec ), size: 6 }], {
/ / onAccess: function(details) {
/ / number + = 1
/ / }
/ / });
Interceptor.attach(base.add( 0x2B57 ), {
onEnter: function(args) {
number + = 1
}
});
Interceptor.attach(base.add( 0x60F0 ), {
onEnter: function(args) {
send(number)
Thread.sleep( 0.001 )
}
});
}
}
setImmediate(main);
|
brute.py
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 | import subprocess
import frida
import sys
import time
visible_chars = [
' ' , '!' , '"' , '#' , '$' , '%' , '&' , " '", ' ( ', ' ) ', ' * ', ' + ', ' , ', ' - ', ' . ', ' / ',
'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
':' , ';' , '<' , '=' , '>' , '?' , '@' ,
'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' ,
'[' , '\\', ' ] ', ' ^ ', ' _ ', ' `',
'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
'{' , '|' , '}' , '~'
]
number = 0
flaglen = 32
filename = "./minire"
flag = bytearray(b 'flag{' + b '!' * 32 + b '}' )
jscode = open ( "hook.js" , "rb" ).read().decode()
new_number = 0
result = 0
def test(F):
def on_message(message, data):
global result
if message[ 'type' ] = = 'send' :
result = message[ 'payload' ]
else :
print (message)
process = subprocess.Popen([filename, "root" ], stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True )
time.sleep( 0.1 )
session = frida.attach( "minire" )
script = session.create_script(jscode)
script.on( 'message' , on_message)
script.load()
process.stdin.write(F.decode())
output, error = process.communicate()
process.terminate()
session.detach()
return result
max_number = 0
right_chr = 0
flag = bytearray(b 'flag{' + b '!' * 32 + b '}' )
for i in range (flaglen):
for j in visible_chars:
flag[ 5 + i] = ord (j)
number = test(flag)
if number > = max_number:
max_number = number
right_chr = ord (j)
flag[ 5 + i] = right_chr
max_number = 0
right_chr = 0
print (flag)
print (number)
|