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
import
idautils
import
idc
import
idaapi
import
ida_ua
from
keystone
import
*
g_reg
=
[
0
]
*
40
reg_base
=
129
g_cond_info
=
list
()
ldr_reg
=
-
1
add_reg
=
-
1
ks
=
keystone.Ks(keystone.KS_ARCH_ARM64, keystone.KS_MODE_LITTLE_ENDIAN)
def
get_opcode(ea):
opcode
=
None
disasm
=
idc.GetDisasm(ea)
if
disasm.find(
'LT'
) !
=
-
1
:
opcode
=
'blt'
elif
disasm.find(
'EQ'
) !
=
-
1
:
opcode
=
'beq'
elif
disasm.find(
'CC'
) !
=
-
1
:
opcode
=
'bcc'
elif
disasm.find(
'GT'
) !
=
-
1
:
opcode
=
'bgt'
elif
disasm.find(
'NE'
) !
=
-
1
:
opcode
=
'bne'
elif
disasm.find(
'GE'
) !
=
-
1
:
opcode
=
'bge'
elif
disasm.find(
'HI'
) !
=
-
1
:
opcode
=
'bhi'
return
opcode
def
do_patch(patch_1, patch_2, opcode, cond_jmp_addr, uncond_jmp_addr):
print
(
"patch_1=0x%x patch_1=0x%x opcode=%s cond_jmp_addr=0x%x uncond_jmp_addr=0x%x"
%
(patch_1, patch_2, opcode, cond_jmp_addr, uncond_jmp_addr))
jump_offset
=
" ({:d})"
.
format
(cond_jmp_addr
-
patch_1)
repair_opcode
=
opcode
+
jump_offset
encoding, count
=
ks.asm(repair_opcode)
idaapi.patch_byte(patch_1, encoding[
0
])
idaapi.patch_byte(patch_1
+
1
, encoding[
1
])
idaapi.patch_byte(patch_1
+
2
, encoding[
2
])
idaapi.patch_byte(patch_1
+
3
, encoding[
3
])
jump_offset
=
" ({:d})"
.
format
(uncond_jmp_addr
-
patch_2)
repair_opcode
=
'b'
+
jump_offset
encoding, count
=
ks.asm(repair_opcode)
idaapi.patch_byte(patch_2, encoding[
0
])
idaapi.patch_byte(patch_2
+
1
, encoding[
1
])
idaapi.patch_byte(patch_2
+
2
, encoding[
2
])
idaapi.patch_byte(patch_2
+
3
, encoding[
3
])
def
do_deobf(ea):
opcode
=
get_opcode(ea)
if
opcode
is
None
:
print
(
"opcode:unknown opcode 0x%x"
%
ea)
return
ea
cond_reg
=
-
1
uncond_reg
=
-
1
cond_data
=
-
1
uncond_data
=
-
1
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem
=
=
'CSEL'
:
cond_reg
=
idc.get_operand_value(ea,
1
)
uncond_reg
=
idc.get_operand_value(ea,
2
)
elif
mnem
=
=
'CSET'
:
cond_data
=
1
uncond_data
=
0
ea
=
idc.next_head(ea)
ldr_reg
=
-
1
lsl_value
=
-
1
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem
=
=
'LSL'
:
lsl_value
=
idc.get_operand_value(ea,
2
)
ea
=
idc.next_head(ea)
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem !
=
'LDR'
:
print
(
"LDR:0x%x -> %s"
%
(ea, mnem))
return
ea
operand_type
=
idc.get_operand_type(ea,
1
)
if
operand_type
=
=
idc.o_phrase:
insn
=
ida_ua.insn_t()
ida_ua.decode_insn(insn, ea)
ldr_reg
=
insn.Op2.reg
if
lsl_value
=
=
-
1
:
lsl_value
=
insn.Op2.value
else
:
return
ea
ea
=
idc.next_head(ea)
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem
=
=
'MOV'
:
ea
=
idc.next_head(ea)
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem !
=
'ADD'
:
print
(
"ADD:0x%x -> %s"
%
(ea, mnem))
return
ea
op_3
=
idc.print_operand(ea,
2
)
op_3
=
op_3[
1
:]
ea
=
idc.next_head(ea)
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem !
=
'BR'
:
print
(
"BR:0x%x -> %s"
%
(ea, mnem))
return
ea
if
cond_data !
=
-
1
and
uncond_data !
=
-
1
:
print
(lsl_value)
cond_jmp_addr
=
(idc.get_qword(g_reg[ldr_reg
-
reg_base]
+
(cond_data << lsl_value))
+
g_reg[
int
(op_3)]) &
0xffffffffffffffff
uncond_jmp_addr
=
(idc.get_qword(g_reg[ldr_reg
-
reg_base]
+
(uncond_data << lsl_value))
+
g_reg[
int
(op_3)]) &
0xffffffffffffffff
else
:
cond_jmp_addr
=
(idc.get_qword(g_reg[ldr_reg
-
reg_base]
+
(g_reg[cond_reg
-
reg_base] << lsl_value))
+
g_reg[
int
(op_3)]) &
0xffffffffffffffff
uncond_jmp_addr
=
(idc.get_qword(g_reg[ldr_reg
-
reg_base]
+
(g_reg[uncond_reg
-
reg_base] << lsl_value))
+
g_reg[
int
(op_3)]) &
0xffffffffffffffff
do_patch(idc.prev_head(ea), ea, opcode, cond_jmp_addr, uncond_jmp_addr)
return
ea
def
deobf(ea):
off_reg
=
-
1
off_data
=
-
1
while
True
:
mnem
=
idc.ida_ua.ua_mnem(ea)
if
mnem
=
=
'RET'
:
break
elif
mnem
=
=
'MOV'
:
op_1_type
=
idc.get_operand_type(ea,
0
)
op_2_type
=
idc.get_operand_type(ea,
1
)
if
(op_1_type
=
=
idc.o_reg)
and
(op_2_type
=
=
idc.o_imm):
op_1
=
idc.get_operand_value(ea,
0
)
op_2
=
idc.get_operand_value(ea,
1
)
g_reg[op_1
-
reg_base]
=
op_2
elif
mnem
=
=
'MOVK'
:
op_1_type
=
idc.get_operand_type(ea,
0
)
op_2_type
=
idc.get_operand_type(ea,
1
)
op_3_type
=
idc.get_operand_type(ea,
2
)
if
(op_1_type
=
=
idc.o_reg)
and
(op_2_type
=
=
idc.o_imm):
op_1
=
idc.get_operand_value(ea,
0
)
op_2
=
idc.get_operand_value(ea,
1
)
g_reg[op_1
-
reg_base]
=
(op_2 <<
16
) | (g_reg[op_1
-
reg_base] &
0xffff
)
elif
mnem
=
=
'ADRP'
:
op_1
=
idc.get_operand_value(ea,
0
)
op_2
=
idc.get_operand_value(ea,
1
)
off_reg
=
op_1
off_data
=
op_2
elif
mnem
=
=
'ADD'
:
op_1
=
idc.get_operand_value(ea,
0
)
op_2
=
idc.get_operand_value(ea,
1
)
op_3
=
idc.get_operand_value(ea,
2
)
op_3_type
=
idc.get_operand_type(ea,
2
)
if
(op_1
=
=
off_reg)
and
(op_2
=
=
off_reg)
and
(op_3_type
=
=
idc.o_imm):
off_data
=
off_data
+
op_3
ldr_reg
=
off_reg
-
reg_base
g_reg[ldr_reg]
=
off_data
elif
(mnem
=
=
'CSEL'
)
or
(mnem
=
=
'CSINC'
)
or
(mnem
=
=
'CSET'
)
or
(mnem
=
=
'CINC'
):
ea
=
do_deobf(ea)
continue
ea
=
idc.next_head(ea)
def
test():
for
i
in
range
(
len
(g_reg)):
print
(
"%d:0x%x"
%
(i, g_reg[i]))
def
main():
ea
=
idc.get_screen_ea()
func
=
idaapi.get_func(ea)
ea
=
func.start_ea
print
(
"start deobf fun:0x%x"
%
(ea))
deobf(ea)
print
(
"deobf ok!"
)
pass
if
__name__
=
=
"__main__"
:
main()
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
import
idautils
import
idc
import
idaapi
decrypt_fun_list
=
dict
()
def
get_code_refs_to_list(addr):
result
=
list
(idautils.CodeRefsTo(addr,
True
))
return
result
def
do_decrypt(src_addr, dst_addr, offset, eor_data, add_data, call_addr):
flag
=
idc.get_wide_byte(src_addr)
str_len
=
flag ^ (idc.get_wide_byte(src_addr
+
1
))
final_str
=
''
for
i
in
range
(str_len):
v4
=
idc.get_wide_byte(src_addr
+
2
+
i)
v5
=
(flag
+
i) ^ eor_data
final_str
+
=
chr
((v4 ^ flag) &
0xff
)
flag
=
v5
+
add_data
decrypt_fun_list[call_addr]
=
final_str
+
" "
+
str
(
hex
(offset))
print
(
'decrypt_addr=0x%x offset=0x%x, final_str=%s'
%
(call_addr, offset, final_str))
def
decrpt_str():
decrypt_fun_list.clear()
tmp_list
=
[
7
,
6
,
5
,
4
,
3
,
2
,
1
]
fun_list
=
get_code_refs_to_list(
0x106C54
)
if
not
fun_list:
return
for
i
in
range
(
len
(fun_list)):
call_addr
=
fun_list[i]
call_addr_start
=
idc.get_func_attr(call_addr, idc.FUNCATTR_START)
caller_fun_list
=
get_code_refs_to_list(call_addr_start)
for
j
in
range
(
len
(caller_fun_list)):
call_decryptStr_addr
=
caller_fun_list[j]
arg_addr
=
idc.prev_head(call_decryptStr_addr)
mnem
=
idc.ida_ua.ua_mnem(arg_addr)
register
=
idc.get_operand_value(arg_addr,
0
)
while
True
:
if
(register
=
=
129
)
and
(mnem
=
=
'MOV'
):
break
arg_addr
=
idc.prev_head(arg_addr)
mnem
=
idc.ida_ua.ua_mnem(arg_addr)
register
=
idc.get_operand_value(arg_addr,
0
)
offset
=
idc.get_operand_value(arg_addr,
1
)
if
offset
=
=
0xa0
:
offset
=
0
src_addr
=
0x1250D8
+
offset
dst_addr
=
0x15E778
+
offset
index
=
offset
%
100
eor_data
=
index
add_data
=
tmp_list[(index
%
7
)]
do_decrypt(src_addr, dst_addr, offset, eor_data, add_data, call_decryptStr_addr)
pass
def
main():
decrpt_str()
if
__name__
=
=
"__main__"
:
main()
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
import
idautils
import
idc
import
idaapi
decrypt_fun_list
=
dict
()
def
get_code_refs_to_list(addr):
result
=
list
(idautils.CodeRefsTo(addr,
True
))
return
result
def
do_decrypt(src_addr, dst_addr, offset, eor_data, add_data, call_addr):
flag
=
idc.get_wide_byte(src_addr)
str_len
=
flag ^ (idc.get_wide_byte(src_addr
+
1
))
final_str
=
''
for
i
in
range
(str_len):
v10
=
idc.get_wide_byte(src_addr
+
2
+
i) ^ flag
flag
=
((i
+
flag) ^ eor_data)
+
add_data
final_str
+
=
chr
(v10 &
0xff
)
decrypt_fun_list[call_addr]
=
final_str
+
" "
+
str
(
hex
(offset))
print
(
'call_addr=0x%x, offset=0x%x, eor_data=0x%x, add_data=0x%x, final_str=%s'
%
(call_addr, offset, eor_data, add_data, final_str))
def
decrpt_str():
decrypt_fun_list.clear()
tmp_list
=
[
7
,
6
,
5
,
4
,
3
,
2
,
1
]
fun_list
=
get_code_refs_to_list(
0x27E724
)
if
not
fun_list:
return
for
i
in
range
(
len
(fun_list)):
arg_addr
=
fun_list[i]
call_addr
=
arg_addr
fun_start
=
idc.get_func_attr(arg_addr, idc.FUNCATTR_START)
mnem
=
idc.ida_ua.ua_mnem(arg_addr)
register
=
idc.get_operand_value(arg_addr,
0
)
while
arg_addr >
=
fun_start:
if
(register
=
=
129
)
and
(mnem
=
=
'MOV'
):
offset
=
idc.get_operand_value(arg_addr,
1
)
break
arg_addr
=
idc.prev_head(arg_addr)
mnem
=
idc.ida_ua.ua_mnem(arg_addr)
register
=
idc.get_operand_value(arg_addr,
0
)
if
offset
=
=
0xa0
:
offset
=
0
index
=
offset
%
100
eor_data
=
index
add_data
=
tmp_list[(index
%
7
)]
src_addr
=
0x3880E4
+
offset
dst_addr
=
0x4C9B78
+
offset
do_decrypt(src_addr, dst_addr, offset, eor_data, add_data, call_addr)
def
main():
decrpt_str()
if
__name__
=
=
"__main__"
:
main()