[原创]某手游xqtd的简单分析
2023-5-31 15:25:44 Author: bbs.pediy.com(查看原文) 阅读量:14 收藏

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()


文章来源: https://bbs.pediy.com/thread-277428.htm
如有侵权请联系:admin#unsafe.sh