闲来无事,勾栏解题,看了看香港的这个网安夺旗赛,感觉题目质量都挺高的,写一个博客记录一下我做的几题
IDA分析报错,需要首先查看具体什么原因 decompress是异或,其实从compress这个词我们就知道应该是对数据做一些处理了,处理的数据正好就是传参的verify函数 看到verify函数我们会发现有很多,所以一个个手动解混淆花费的时间有点多,根据decompress的传参我们可以发现每一个verify的字节大小都是86,但是每一个verify的传参不一致,但是写脚本获取传参过于麻烦,发现参数都在-128-128之间,正确的话前四个字节必然是F3 0F 1E FA,直接梭就完事了,就不需要读取decompress传入的参数了。
IDApython直接识别开始爆破,修复后可能IDA还识别不过来,这个时候可以保存好patch后的附件,或者跑下面的IDApython代码重新识别
弄好之后小时的代码都回来了 每一次都是前后两个位置的异或等于一个值,继续搓脚本输出他,把每一位结果记录下来,构建约束关系,然后z3梭哈
Script语句中把解密后的源码打印出来即可
解包 随便解密就好了:
IL2CPP的题目,正好之前有过一段时间的研究。 用户输入产生MBTI数据,需要产生一一对应的数据 首先还是利用dumper把符号表之类的dump下来 恢复符号表啥的操作就不细说了详情可以看https://bbs.kanxue.com/thread-282821.htm 先用Dnspy观察: 可以发现逻辑不多,分析起来难度不会很大 根据Il2cpp的特性,我们知道方法传入的a1其实就是这个类里面一些成员变量组成的结构体之类的,我们可以通过偏移来推断到底是个什么类: 像这个charList就是a1+0x30: 于是我们在GameBehaviour__OnClick中就可以发现如下逻辑了:
首先看到偏移0x48就是InputName,在onclick中查找 不难发现:
这一段就是获取了onClick和charList,做一些基础的对象的处理 下面就是关键逻辑了:
调试发现,这里利用我们输入的名字在table中取了索引,然后对索引做了一个似乎是0x24进制的操作,储存到一个值中,我们可以看到UnityEngine_Random__InitState这个是Unity的一个设置随机中的的方法,类似于srand。 接下来过了一些基础操作就可以看到下一个关键逻辑了:
这里的0xc其实就是对应题目的12个MBTI 那么MBTI的生成逻辑肯定在里面 发现就是利用seed产生的value做运算了 到这里比较就完事了。 我们新建一个Unity项目 编辑->资源->创建一个C#脚本 然后点击我们的Main Camera,给Main Camera添加一个组件 添加我们的脚本进去,就可以执行我们的脚本了,正好一个很巧的事情发生了,一开始随便输入了一个1,然后程序居然正确了 这样正好给我们提供了一个测试思路的用例,1的索引正好就是1,我们写脚本验证一下 这里脚本需要注意: 继承的类得是MonoBehavior
对应上了,我们就直接开始爆破了
这里我只写了六位数据的爆破,需要跑大概半个小时,前面1-5个长度的名字基本都在一分钟内,五个长度需要一分钟 写个脚本整理结果就好了
import
re
import
idautils
import
idaapi
import
idc
def
find_specific_verify_exports():
verify_exports
=
[]
pattern
=
re.
compile
(r
"^_Z(?:8|9)verify_(\d+)"
)
for
i
in
range
(idaapi.get_entry_qty()):
ordinal
=
idaapi.get_entry_ordinal(i)
func_name
=
idaapi.get_entry_name(ordinal)
if
func_name
and
pattern.match(func_name):
func_addr
=
idaapi.get_entry(ordinal)
verify_exports.append((func_name, func_addr))
return
verify_exports
def
xor_bytes(data, key):
return
bytes([b ^ key
for
b
in
data])
def
patch_memory(address, xor_key):
original_data
=
idc.get_bytes(address,
86
)
if
not
original_data:
print
(f
"无法读取地址 {hex(address)} 的数据"
)
return
False
patched_data
=
xor_bytes(original_data, xor_key)
for
offset, byte
in
enumerate
(patched_data):
idc.patch_byte(address
+
offset, byte)
print
(f
"已在内存中补丁地址范围 {hex(address)} 到 {hex(address+85)},使用异或键 {xor_key}"
)
return
True
def
process_address_range(address):
original_data
=
idc.get_bytes(address,
86
)
if
not
original_data:
print
(f
"无法读取地址 {hex(address)} 的数据"
)
return
None
for
xor_key
in
range
(
-
255
,
256
):
xor_key &
=
0xFF
test_data
=
xor_bytes(original_data[:
4
], xor_key)
if
test_data
=
=
b
'\xF3\x0F\x1E\xFA'
:
if
patch_memory(address, xor_key):
print
(f
"成功对地址 {hex(address)} 应用补丁,异或键为 {xor_key}"
)
return
xor_key
print
(f
"地址 {hex(address)} 没有找到满足条件的异或键"
)
return
None
filtered_exports
=
find_specific_verify_exports()
if
filtered_exports:
print
(
"开始处理符合条件的 'verify' 函数地址..."
)
for
func_name, func_addr
in
filtered_exports:
print
(f
"Processing function: {func_name} at Address: {hex(func_addr)}"
)
process_address_range(func_addr)
else
:
print
(
"No specific 'verify' functions found in export table."
)
import
re
import
idautils
import
idaapi
import
idc
def
find_specific_verify_exports():
verify_exports
=
[]
pattern
=
re.
compile
(r
"^_Z(?:8|9)verify_(\d+)"
)
for
i
in
range
(idaapi.get_entry_qty()):
ordinal
=
idaapi.get_entry_ordinal(i)
func_name
=
idaapi.get_entry_name(ordinal)
if
func_name
and
pattern.match(func_name):
func_addr
=
idaapi.get_entry(ordinal)
verify_exports.append((func_name, func_addr))
return
verify_exports
def
xor_bytes(data, key):
return
bytes([b ^ key
for
b
in
data])
def
patch_memory(address, xor_key):
original_data
=
idc.get_bytes(address,
86
)
if
not
original_data:
print
(f
"无法读取地址 {hex(address)} 的数据"
)
return
False
patched_data
=
xor_bytes(original_data, xor_key)
for
offset, byte
in
enumerate
(patched_data):
idc.patch_byte(address
+
offset, byte)
print
(f
"已在内存中补丁地址范围 {hex(address)} 到 {hex(address+85)},使用异或键 {xor_key}"
)
return
True
def
process_address_range(address):
original_data
=
idc.get_bytes(address,
86
)
if
not
original_data:
print
(f
"无法读取地址 {hex(address)} 的数据"
)
return
None
for
xor_key
in
range
(
-
255
,
256
):
xor_key &
=
0xFF
test_data
=
xor_bytes(original_data[:
4
], xor_key)
if
test_data
=
=
b
'\xF3\x0F\x1E\xFA'
:
if
patch_memory(address, xor_key):
print
(f
"成功对地址 {hex(address)} 应用补丁,异或键为 {xor_key}"
)
return
xor_key
print
(f
"地址 {hex(address)} 没有找到满足条件的异或键"
)
return
None
filtered_exports
=
find_specific_verify_exports()
if
filtered_exports:
print
(
"开始处理符合条件的 'verify' 函数地址..."
)
for
func_name, func_addr
in
filtered_exports:
print
(f
"Processing function: {func_name} at Address: {hex(func_addr)}"
)
process_address_range(func_addr)
else
:
print
(
"No specific 'verify' functions found in export table."
)
import
re
import
idautils
import
idaapi
import
idc
print
(
"Starting to undefine all existing functions..."
)
for
func_ea
in
idautils.Functions():
func_name
=
idc.get_func_name(func_ea)
print
(f
"Undefining function {func_name} at {hex(func_ea)}"
)
idc.del_func(func_ea)
print
(
"All functions have been undefined."
)
print
(
"Setting all instructions and data to undefined in code segments..."
)
for
seg_ea
in
idautils.Segments():
if
idc.get_segm_attr(seg_ea, idc.SEGATTR_TYPE) !
=
idc.SEG_CODE:
print
(f
"Skipping data segment at {hex(seg_ea)}"
)
continue
start
=
seg_ea
end
=
idc.get_segm_end(seg_ea)
for
head
in
idautils.Heads(start, end):
idc.del_items(head, idc.DELIT_SIMPLE)
print
(
"All code in code segments set to undefined."
)
print
(
"Marking all code segments for reanalysis..."
)
for
seg_ea
in
idautils.Segments():
if
idc.get_segm_attr(seg_ea, idc.SEGATTR_TYPE)
=
=
idc.SEG_CODE:
ida_auto.auto_mark_range(seg_ea, idc.get_segm_end(seg_ea), ida_auto.AU_CODE)
ida_auto.auto_wait()
print
(
"Reanalyzed all code segments, excluding data segments."
)
import
re
import
idautils
import
idaapi
import
idc
print
(
"Starting to undefine all existing functions..."
)
for
func_ea
in
idautils.Functions():
func_name
=
idc.get_func_name(func_ea)
print
(f
"Undefining function {func_name} at {hex(func_ea)}"
)
idc.del_func(func_ea)
print
(
"All functions have been undefined."
)
print
(
"Setting all instructions and data to undefined in code segments..."
)
for
seg_ea
in
idautils.Segments():
if
idc.get_segm_attr(seg_ea, idc.SEGATTR_TYPE) !
=
idc.SEG_CODE:
print
(f
"Skipping data segment at {hex(seg_ea)}"
)
continue
start
=
seg_ea
end
=
idc.get_segm_end(seg_ea)
for
head
in
idautils.Heads(start, end):
idc.del_items(head, idc.DELIT_SIMPLE)
print
(
"All code in code segments set to undefined."
)
print
(
"Marking all code segments for reanalysis..."
)
for
seg_ea
in
idautils.Segments():
if
idc.get_segm_attr(seg_ea, idc.SEGATTR_TYPE)
=
=
idc.SEG_CODE:
ida_auto.auto_mark_range(seg_ea, idc.get_segm_end(seg_ea), ida_auto.AU_CODE)
ida_auto.auto_wait()
print
(
"Reanalyzed all code segments, excluding data segments."
)
import
re
import
idaapi
import
idautils
import
idc
def
find_specific_verify_exports():
verify_exports
=
[]
pattern
=
re.
compile
(r
"^_Z(?:8|9)verify_(\d+)"
)
for
i
in
range
(idaapi.get_entry_qty()):
ordinal
=
idaapi.get_entry_ordinal(i)
func_name
=
idaapi.get_entry_name(ordinal)
if
func_name
and
pattern.match(func_name):
func_addr
=
idaapi.get_entry(ordinal)
verify_exports.append((func_name, func_addr))
return
verify_exports
def
extract_equation_from_verify(func_addr):
esi_values
=
[]
cmp_value
=
None
for
instr_addr
in
idautils.FuncItems(func_addr):
mnemonic
=
idc.print_insn_mnem(instr_addr)
if
mnemonic
=
=
"mov"
and
"esi"
in
idc.print_operand(instr_addr,
0
):
esi_value
=
idc.get_operand_value(instr_addr,
1
)
esi_values.append(esi_value)
elif
mnemonic
=
=
"cmp"
and
"al"
in
idc.print_operand(instr_addr,
0
):
cmp_value
=
idc.get_operand_value(instr_addr,
1
)
if
len
(esi_values)
=
=
2
and
cmp_value
is
not
None
:
break
if
len
(esi_values)
=
=
2
and
cmp_value
is
not
None
:
equation
=
f
"input[0x{esi_values[0]:X}] ^ input[0x{esi_values[1]:X}] == 0x{cmp_value:X}"
return
equation
else
:
return
None
filtered_exports
=
find_specific_verify_exports()
if
filtered_exports:
print
(
"生成 verify 函数的方程:"
)
for
func_name, func_addr
in
filtered_exports:
equation
=
extract_equation_from_verify(func_addr)
if
equation:
print
(f
"{func_name}: {equation}"
)
else
:
print
(f
"{func_name}: 无法生成方程"
)
else
:
print
(
"未找到符合条件的 'verify' 函数"
)
import
re
import
idaapi
import
idautils
import
idc
def
find_specific_verify_exports():
verify_exports
=
[]
pattern
=
re.
compile
(r
"^_Z(?:8|9)verify_(\d+)"
)
for
i
in
range
(idaapi.get_entry_qty()):
ordinal
=
idaapi.get_entry_ordinal(i)
func_name
=
idaapi.get_entry_name(ordinal)
if
func_name
and
pattern.match(func_name):
func_addr
=
idaapi.get_entry(ordinal)
verify_exports.append((func_name, func_addr))
return
verify_exports
def
extract_equation_from_verify(func_addr):
esi_values
=
[]
cmp_value
=
None
for
instr_addr
in
idautils.FuncItems(func_addr):
mnemonic
=
idc.print_insn_mnem(instr_addr)
if
mnemonic
=
=
"mov"
and
"esi"
in
idc.print_operand(instr_addr,
0
):
esi_value
=
idc.get_operand_value(instr_addr,
1
)
esi_values.append(esi_value)
elif
mnemonic
=
=
"cmp"
and
"al"
in
idc.print_operand(instr_addr,
0
):
cmp_value
=
idc.get_operand_value(instr_addr,
1
)
if
len
(esi_values)
=
=
2
and
cmp_value
is
not
None
:
break
if
len
(esi_values)
=
=
2
and
cmp_value
is
not
None
:
equation
=
f
"input[0x{esi_values[0]:X}] ^ input[0x{esi_values[1]:X}] == 0x{cmp_value:X}"
return
equation
else
:
return
None
filtered_exports
=
find_specific_verify_exports()
if
filtered_exports:
print
(
"生成 verify 函数的方程:"
)
for
func_name, func_addr
in
filtered_exports:
equation
=
extract_equation_from_verify(func_addr)
if
equation:
print
(f
"{func_name}: {equation}"
)
else
:
print
(f
"{func_name}: 无法生成方程"
)
else
:
print
(
"未找到符合条件的 'verify' 函数"
)
from
z3
import
*
input_vars
=
[BitVec(f
'input_{i}'
,
8
)
for
i
in
range
(
0x37
)]
solver
=
Solver()
constraints
=
[
input_vars[
0x0
] ^ input_vars[
0x1
]
=
=
0x3
,
input_vars[
0x1
] ^ input_vars[
0x2
]
=
=
0x8
,
input_vars[
0x2
] ^ input_vars[
0x3
]
=
=
0x6
,
input_vars[
0x3
] ^ input_vars[
0x4
]
=
=
0x17
,
input_vars[
0x4
] ^ input_vars[
0x5
]
=
=
0x6
,
input_vars[
0x5
] ^ input_vars[
0x6
]
=
=
0x46
,
input_vars[
0x6
] ^ input_vars[
0x7
]
=
=
0x6
,
input_vars[
0x7
] ^ input_vars[
0x8
]
=
=
0x4F
,
input_vars[
0x8
] ^ input_vars[
0x9
]
=
=
0x8
,
input_vars[
0x9
] ^ input_vars[
0xA
]
=
=
0x40
,
input_vars[
0xA
] ^ input_vars[
0xB
]
=
=
0x5F
,
input_vars[
0xB
] ^ input_vars[
0xC
]
=
=
0xA
,
input_vars[
0xC
] ^ input_vars[
0xD
]
=
=
0x39
,
input_vars[
0xD
] ^ input_vars[
0xE
]
=
=
0x32
,
input_vars[
0xE
] ^ input_vars[
0xF
]
=
=
0x5D
,
input_vars[
0xF
] ^ input_vars[
0x10
]
=
=
0x54
,
input_vars[
0x10
] ^ input_vars[
0x11
]
=
=
0x55
,
input_vars[
0x11
] ^ input_vars[
0x12
]
=
=
0x57
,
input_vars[
0x12
] ^ input_vars[
0x13
]
=
=
0x1F
,
input_vars[
0x13
] ^ input_vars[
0x14
]
=
=
0x48
,
input_vars[
0x14
] ^ input_vars[
0x15
]
=
=
0x5F
,
input_vars[
0x15
] ^ input_vars[
0x16
]
=
=
0x9
,
input_vars[
0x16
] ^ input_vars[
0x17
]
=
=
0x38
,
input_vars[
0x17
] ^ input_vars[
0x18
]
=
=
0x3C
,
input_vars[
0x18
] ^ input_vars[
0x19
]
=
=
0x53
,
input_vars[
0x19
] ^ input_vars[
0x1A
]
=
=
0x54
,
input_vars[
0x1A
] ^ input_vars[
0x1B
]
=
=
0x57
,
input_vars[
0x1B
] ^ input_vars[
0x1C
]
=
=
0x6C
,
input_vars[
0x1C
] ^ input_vars[
0x1D
]
=
=
0x2B
,
input_vars[
0x1D
] ^ input_vars[
0x1E
]
=
=
0x1C
,
input_vars[
0x1E
] ^ input_vars[
0x1F
]
=
=
0x58
,
input_vars[
0x1F
] ^ input_vars[
0x20
]
=
=
0x6F
,
input_vars[
0x20
] ^ input_vars[
0x21
]
=
=
0x2B
,
input_vars[
0x21
] ^ input_vars[
0x22
]
=
=
0x1C
,
input_vars[
0x22
] ^ input_vars[
0x23
]
=
=
0x59
,
input_vars[
0x23
] ^ input_vars[
0x24
]
=
=
0x4
,
input_vars[
0x24
] ^ input_vars[
0x25
]
=
=
0x6A
,
input_vars[
0x25
] ^ input_vars[
0x26
]
=
=
0x36
,
input_vars[
0x26
] ^ input_vars[
0x27
]
=
=
0x5C
,
input_vars[
0x27
] ^ input_vars[
0x28
]
=
=
0x6A
,
input_vars[
0x28
] ^ input_vars[
0x29
]
=
=
0x31
,
input_vars[
0x29
] ^ input_vars[
0x2A
]
=
=
0x5E
,
input_vars[
0x2A
] ^ input_vars[
0x2B
]
=
=
0x64
,
input_vars[
0x2B
] ^ input_vars[
0x2C
]
=
=
0xB
,
input_vars[
0x2C
] ^ input_vars[
0x2D
]
=
=
0x1E
,
input_vars[
0x2D
] ^ input_vars[
0x2E
]
=
=
0x1E
,
input_vars[
0x2E
] ^ input_vars[
0x2F
]
=
=
0x32
,
input_vars[
0x2F
] ^ input_vars[
0x30
]
=
=
0x59
,
input_vars[
0x30
] ^ input_vars[
0x31
]
=
=
0x58
,
input_vars[
0x31
] ^ input_vars[
0x32
]
=
=
0x1B
,
input_vars[
0x32
] ^ input_vars[
0x33
]
=
=
0x43
,
input_vars[
0x33
] ^ input_vars[
0x34
]
=
=
0x46
,
input_vars[
0x34
] ^ input_vars[
0x35
]
=
=
0x41
,
input_vars[
0x35
] ^ input_vars[
0x36
]
=
=
0x4E
]
for
constraint
in
constraints:
solver.add(constraint)
for
var
in
input_vars:
solver.add(var >
=
0x20
, var <
=
0x7E
)
solutions
=
[]
while
solver.check()
=
=
sat:
model
=
solver.model()
solution
=
[model[input_vars[i]].as_long()
for
i
in
range
(
0x37
)]
solutions.append(solution)
solver.add(Or([input_vars[i] !
=
solution[i]
for
i
in
range
(
0x37
)]))
for
i, solution
in
enumerate
(solutions,
1
):
readable_solution
=
''.join(
chr
(val)
for
val
in
solution)
print
(f
"解 {i}: {readable_solution}"
)
from
z3
import
*
input_vars
=
[BitVec(f
'input_{i}'
,
8
)
for
i
in
range
(
0x37
)]
solver
=
Solver()
constraints
=
[
input_vars[
0x0
] ^ input_vars[
0x1
]
=
=
0x3
,
input_vars[
0x1
] ^ input_vars[
0x2
]
=
=
0x8
,
input_vars[
0x2
] ^ input_vars[
0x3
]
=
=
0x6
,
input_vars[
0x3
] ^ input_vars[
0x4
]
=
=
0x17
,
input_vars[
0x4
] ^ input_vars[
0x5
]
=
=
0x6
,
input_vars[
0x5
] ^ input_vars[
0x6
]
=
=
0x46
,
input_vars[
0x6
] ^ input_vars[
0x7
]
=
=
0x6
,
input_vars[
0x7
] ^ input_vars[
0x8
]
=
=
0x4F
,
input_vars[
0x8
] ^ input_vars[
0x9
]
=
=
0x8
,
input_vars[
0x9
] ^ input_vars[
0xA
]
=
=
0x40
,
input_vars[
0xA
] ^ input_vars[
0xB
]
=
=
0x5F
,
input_vars[
0xB
] ^ input_vars[
0xC
]
=
=
0xA
,
input_vars[
0xC
] ^ input_vars[
0xD
]
=
=
0x39
,
input_vars[
0xD
] ^ input_vars[
0xE
]
=
=
0x32
,
input_vars[
0xE
] ^ input_vars[
0xF
]
=
=
0x5D
,
input_vars[
0xF
] ^ input_vars[
0x10
]
=
=
0x54
,
input_vars[
0x10
] ^ input_vars[
0x11
]
=
=
0x55
,
input_vars[
0x11
] ^ input_vars[
0x12
]
=
=
0x57
,
input_vars[
0x12
] ^ input_vars[
0x13
]
=
=
0x1F
,
input_vars[
0x13
] ^ input_vars[
0x14
]
=
=
0x48
,
input_vars[
0x14
] ^ input_vars[
0x15
]
=
=
0x5F
,
input_vars[
0x15
] ^ input_vars[
0x16
]
=
=
0x9
,
input_vars[
0x16
] ^ input_vars[
0x17
]
=
=
0x38
,
input_vars[
0x17
] ^ input_vars[
0x18
]
=
=
0x3C
,
input_vars[
0x18
] ^ input_vars[
0x19
]
=
=
0x53
,
input_vars[
0x19
] ^ input_vars[
0x1A
]
=
=
0x54
,
input_vars[
0x1A
] ^ input_vars[
0x1B
]
=
=
0x57
,
input_vars[
0x1B
] ^ input_vars[
0x1C
]
=
=
0x6C
,
input_vars[
0x1C
] ^ input_vars[
0x1D
]
=
=
0x2B
,
input_vars[
0x1D
] ^ input_vars[
0x1E
]
=
=
0x1C
,
input_vars[
0x1E
] ^ input_vars[
0x1F
]
=
=
0x58
,
input_vars[
0x1F
] ^ input_vars[
0x20
]
=
=
0x6F
,
input_vars[
0x20
] ^ input_vars[
0x21
]
=
=
0x2B
,
input_vars[
0x21
] ^ input_vars[
0x22
]
=
=
0x1C
,
input_vars[
0x22
] ^ input_vars[
0x23
]
=
=
0x59
,
input_vars[
0x23
] ^ input_vars[
0x24
]
=
=
0x4
,
input_vars[
0x24
] ^ input_vars[
0x25
]
=
=
0x6A
,
input_vars[
0x25
] ^ input_vars[
0x26
]
=
=
0x36
,
input_vars[
0x26
] ^ input_vars[
0x27
]
=
=
0x5C
,
input_vars[
0x27
] ^ input_vars[
0x28
]
=
=
0x6A
,
input_vars[
0x28
] ^ input_vars[
0x29
]
=
=
0x31
,
input_vars[
0x29
] ^ input_vars[
0x2A
]
=
=
0x5E
,
input_vars[
0x2A
] ^ input_vars[
0x2B
]
=
=
0x64
,
input_vars[
0x2B
] ^ input_vars[
0x2C
]
=
=
0xB
,
input_vars[
0x2C
] ^ input_vars[
0x2D
]
=
=
0x1E
,
input_vars[
0x2D
] ^ input_vars[
0x2E
]
=
=
0x1E
,
input_vars[
0x2E
] ^ input_vars[
0x2F
]
=
=
0x32
,
input_vars[
0x2F
] ^ input_vars[
0x30
]
=
=
0x59
,
input_vars[
0x30
] ^ input_vars[
0x31
]
=
=
0x58
,
input_vars[
0x31
] ^ input_vars[
0x32
]
=
=
0x1B
,
input_vars[
0x32
] ^ input_vars[
0x33
]
=
=
0x43
,
input_vars[
0x33
] ^ input_vars[
0x34
]
=
=
0x46
,
input_vars[
0x34
] ^ input_vars[
0x35
]
=
=
0x41
,
input_vars[
0x35
] ^ input_vars[
0x36
]
=
=
0x4E
]
for
constraint
in
constraints:
solver.add(constraint)
for
var
in
input_vars:
solver.add(var >
=
0x20
, var <
=
0x7E
)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)