vmp入门(一):android dex vmp还原和安全性论述

07-19 1892阅读

看了一下,目前市面上关于dex vmp还原就我兄弟写的这一个,我不得不佩服他巨强的二级制分析能力。关于dex vmp 他的大部分都写了,但是,他搞的实在是太复杂了,他的分析基本都是基于静态的数据流向分析,这种对于二进制vmp都有杀伤力的,只能说杀鸡焉用牛刀,而且他的牛刀还没发借给我,这是最气的。

另外一个360大佬在看雪峰会上发的关于dex vmp的ppt演讲,他这个我没看得很懂,技术有限。前面都是大佬,一看就会,一学就废。

vmp宝宝入门知识讲解

vmp目前基本可以说是逆向分析中代码混淆的巅峰,顶点,最高里程碑,dex vmp在我看来是比较简单一点的vmp了,所以我们从这里入门。

vmp设计理念

vmp是一种虚拟机保护壳,如果你第一次听过,估计直接蒙了,他其实就是一个虚拟的执行机器,就比如java虚拟机,他们的原理是一样的。java虚拟机可以叫vmp是保护的意思,所以叫vmp。这种虚拟机一般有两部分:vmp入门(一):android dex vmp还原和安全性论述

vmp执行实体运作原理

vmp虽然设计理念上跟别的虚拟机没什么差别,但是他很简陋,基本只有能转换虚拟机的bytecode然后进行运行的能力。写个伪代码示意一下

1

2

3

4

5

while(1){                               

    //一旦进入虚拟机就会开始读取字节码,除非遇到退出指令,否则一直执行下去

    bytecode_ins = read(bytecode_buff);  //不断的读取指令

    exe(bytecode_ins)                   //运行指令

}

就是这样一直循环

vmp加固软件

在vmp执行实体中,可以看到有个bytecode_buff,虚拟机为什么能识别他,因为加固软件将源码,转变成虚拟机的bytecode,然后将虚拟机和bytecode一起打包到需要加固的软件中。

逆向目标

对于一个vmp,我们的目标只有一个,那就是还原,还原成c也好,汇编也好,java也好,反正就是去除vmp化。这就是最务实的目标,所以这篇博客的目的就是如何将dex vmp 还原成java代码。

程序分析

Java层函数入口

首先看入口函数,如果看过我上面提到过的dex vmp 你就该知道,这个pro.getxxx,其实就是调用的dex vmp函数的入口,而第一个参数,其实就是代表了当前这个函数,用这个数字来索引已经被虚拟化的代码来执行。
vmp入门(一):android dex vmp还原和安全性论述

然后再看看这个函数的定义,这个如果你经验丰富,经常写java代码和别的代码的交互,你就会发现,这是一个java 和c的交互代码,写了这么多类型,就是可以从c 层返回任何一种类型的java对象,看不出来也没事。vmp入门(一):android dex vmp还原和安全性论述

寻找java方法的native 地址

这个本来不想写的,但是其实其实比较简单,有人多人去hook NativeRegister,很多加固去通过各种奇葩的手段绕过NativeRegister进行注册,但是其实没必要,java方法的native地址根本无法隐藏。分享一个frida脚本寻找native方法的地址。

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

var offset = -1

//这个函数是用来做偏移矫正的

function get_android_api_jnioffset() {

    if(offset != -1){

        return offset

    }

    var native_addr = Module.findExportByName("libandroid_runtime.so", "_Z32android_os_Process_getUidForNameP7_JNIEnvP8_jobjectP8_jstring")

    // console.log("native_addr:",native_addr)

    var className = "android.os.Process";

    var classResult = Java.use(className).class;

    var methodArr = classResult.getDeclaredMethods();

    for (var i = 0; i

        var methodName = methodArr[i].toString();

        var flags = methodArr[i].getModifiers()

        if (flags & 256) {

            if(methodName.indexOf("getUidForName")!= -1){

                var artmethod = methodArr[i].getArtMethod();

                for (var i = 0; i

p      case 'B':/p p        v72 = (v55-functions->NewByteArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'C':

        v72 = (v55->functions->NewCharArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'D':

        v72 = (v55->functions->NewDoubleArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'F':

        v72 = (v55->functions->NewFloatArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'I':

        v72 = (v55->functions->NewIntArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'J':

        v72 = (v55->functions->NewLongArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'S':

        v72 = (v55->functions->NewShortArray)(v55, v57, a3, a4, a5, a6, a7, a8);

        goto LABEL_24;

      case 'Z':

        v72 = (v55->functions->NewBooleanArray)(v55, v57, a3, a4, a5, a6, a7, a8);

LABEL_24:

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

  cls_name = v58->functions->FindClass(v58, v100);

  functions = v58->functions;

  v103 = v58;

  cls_name_2 = cls_name;

  v105 = v58;

  obj = *(a39 + 8 * a22);

  functions->ExceptionClear(&v105->functions);

  free(v100);

  method_id = v103->functions->GetMethodID(&v103->functions, cls_name_2, method_name, fun_sign);

  free(fun_sign);

  if ( method_id )

  {

    a40 = 0.0;

    v57 = a32;

    if ( v95 )

    {

      switch ( *return_type )

      {

        case 'B':

          v110 = env->functions->CallNonvirtualByteMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_74;

        case 'C':

          v115 = env->functions->CallNonvirtualCharMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_87;

        case 'D':

          env->functions->CallNonvirtualDoubleMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_89;

        case 'F':

          env->functions->CallNonvirtualFloatMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_78;

        case 'I':

          v112 = env->functions->CallNonvirtualIntMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_81;

        case 'J':

          v113 = COERCE_DOUBLE(env->functions->CallNonvirtualLongMethodA(env, obj, cls_name_2, method_id, args));

          goto LABEL_84;

        case 'L':

        case '[':

          v108 = env;

          v109 = COERCE_DOUBLE(env->functions->CallNonvirtualObjectMethodA(env, obj, cls_name_2, method_id, args));

          goto LABEL_68;

        case 'S':

          v117 = env->functions->CallNonvirtualShortMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_92;

        case 'V':

          env->functions->CallNonvirtualVoidMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_93;

        case 'Z':

          v114 = env->functions->CallNonvirtualBooleanMethodA(env, obj, cls_name_2, method_id, args);

          goto LABEL_96;

        default:

          goto LABEL_97;

      }

    }

    switch ( *return_type )

    {

      case 'B':

        v110 = CallNonvirtualByteMethodV(env, obj, cls_name_2, method_id);

LABEL_74:

        v44 = v110;

        goto LABEL_75;

      case 'C':

        v115 = CallNonvirtualCharMethodV(env, obj, cls_name_2, method_id);

LABEL_87:

        v44 = v115;

很多很多,在运行中分析这些数据发现他调用很多java的代码,各种各样,有很多我甚至都没有用过。

  • 第一个特征使用了大量的jni函数而且,没有什么别的函数
  • 第二就是上面贴的这个函数,正常的函数调用,谁没事搞这么多参数,各种各种的jni方法调用啊,摆明了不就是一个通用的c成调用任何java方法的功能吗
  • 第三特征,在前面分析入口的时候,遇到一个常量函数列表,在调用中我经过函数交叉引用我发现很多函数都在这个常量函数列表vmp入门(一):android dex vmp还原和安全性论述我觉得不对劲,然后我数了数,刚好256个。这个数字很微妙,而且不正常。
  • 第四特征,我对上面的这些函数的功能进行了分析,我发现更像是在执行单条Dalvik 指令(本人对于dalivk分非常熟悉,手写过一段时间smali)。

    到这里基本破案了,256条指令对应了256个Dalvik 指令的opcode,虽然很多没用。

    vmp框架分析

    前面我们说了我们的目标,就是将某一段代码还原。而vmp,还原的方法只能去分析整个vmp的框架,进行功能含义分析,然后通过某一段vm的指令,将他手工转化成java代码。说白了就是一个vmp的反向过程。

    • vmp基本单元指令

      上文提到的那个256个函数地址的数组,我第一时间就判断为vmp的基本单元指令,毕竟太巧了,而且试试也无妨(对逆向来说猜测并测试也是一种极为重要的方法)

    • vm运行方式

      1

      2

      3

      4

      5

      while(1){                               

          //一旦进入虚拟机就会开始读取字节码,除非遇到退出指令,否则一直执行下去

          bytecode_ins = read(bytecode_buff);  //不断的读取指令

          exe(bytecode_ins)                   //运行指令

      }

      我签名写的这个,就是vmp简单的执行示意,它必然需要不断的循环运行,来执行基本单元指令,除非基本单元指令退出,否则会无线循环。

      但是实际分析过程中,我发现没有while循环,也没有这种wile里读指令,然后根据不同的指令跳转到不同的vmp基本单元指令。(猜测失败的代价就是对自己的技术产生怀疑)

      然后我去研究了一下trace,看一下他这个指令之间是如何关联起来的,上一条指令如何跳转到下一条指令。(研究过多条指令,很长的函数还没有间隔,完全不知道出口在哪里,但是很幸运的是有比较短的)

      1

      2

      3

      4

      5

      6

      .text:00000076F68DBDD4                         nop                                     ; DATA XREF: .data.rel.ro:00000076F6938940↓o

      .text:00000076F68DBDD4 F7 0A 00 91             ADD             X23, X23, #2                                                              ; monitor:loc_76F690C534↓j

      .text:00000076F68DBDD8 F4 02 40 79             LDRH            W20, [X23]

      .text:00000076F68DBDDC 88 1E 40 92             AND             X8, X20, #0xFF

      .text:00000076F68DBDE0 C8 7A 68 F8             LDR             X8, [X22,X8,LSL#3]

      .text:00000076F68DBDE4 00 01 1F D6             BR              X8

      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

      .text:00000076F68E0C0C C9 02 00 B0             ADRP            X9, #translator_8_12_2@PAGE

      .text:00000076F68E0C10 CB 02 00 B0             ADRP            X11, #translator_12_16_2@PAGE

      .text:00000076F68E0C14 29 01 47 F9             LDR             X9, [X9,#translator_8_12_2@PAGEOFF]

      .text:00000076F68E0C18 6B 45 47 F9             LDR             X11, [X11,#translator_12_16_2@PAGEOFF]

      .text:00000076F68E0C1C 88 2E 48 D3             UBFX            X8, X20, #8, #4

      .text:00000076F68E0C20 8A 3E 4C D3             UBFX            X10, X20, #0xC, #4

      .text:00000076F68E0C24 28 69 68 38             LDRB            W8, [X9,X8]

      .text:00000076F68E0C28 69 69 6A 38             LDRB            W9, [X11,X10]

      .text:00000076F68E0C2C 28 6D 1C 33             BFI             W8, W9, #4, #0x1C

      .text:00000076F68E0C30 13 1D 00 13             SXTB            W19, W8

      .text:00000076F68E0C34 F3 00 F8 36             TBZ             W19, #0x1F, loc_76F68E0C50

      .text:00000076F68E0C34

      .text:00000076F68E0C38 28 03 40 39             LDRB            W8, [X25]

      .text:00000076F68E0C3C A8 00 20 36             TBZ             W8, #4, loc_76F68E0C50

      .text:00000076F68E0C3C

      .text:00000076F68E0C40 E8 5B 40 F9             LDR             X8, [SP,#arg_B0]

      .text:00000076F68E0C44 E0 27 40 F9             LDR             X0, [SP,#arg_48]

      .text:00000076F68E0C48 17 01 1F F8             STUR            X23, [X8,#-0x10]

      .text:00000076F68E0C4C F4 C8 00 94             BL              sub_76F691301C

      .text:00000076F68E0C4C

      .text:00000076F68E0C50

      .text:00000076F68E0C50                         loc_76F68E0C50                          ; CODE XREF: goto+28↑j

      .text:00000076F68E0C50                                                                 ; goto+30↑j

      .text:00000076F68E0C50 F7 C6 33 8B             ADD             X23, X23, W19,SXTW#1

      .text:00000076F68E0C54 F4 02 40 79             LDRH            W20, [X23]

      .text:00000076F68E0C58 88 1E 40 92             AND             X8, X20, #0xFF

      .text:00000076F68E0C5C C8 7A 68 F8             LDR             X8, [X22,X8,LSL#3]

      .text:00000076F68E0C60 00 01 1F D6             BR              X8

      不多贴了,他们的尾部都是一样的,x20 当前指令,x22 data_rel,x23 指令数据表。他是通过上一条指令的内容计算下一条指令的地址,然后读取数据数据,和指令处理的地址,用x8跳过去,他不是一个while模式。写个运行的伪代码的例子

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      data_rel= 指令数据数组(fun1,fun2,fun3)

      fun1(){

        XXXXXX正文内容

        下一套指令地址=read(data_rel(当前的数据)+fun1函数这个指令的长度)

         bl  下一套指令地址

      }

      fun2(){

        XXXXXX正文内容

        下一套指令地址=read(data_rel(当前的数据)+fun1函数这个指令的长度)

         bl  下一套指令地址

      }

      fun3(){

        XXXXXX正文内容

        下一套指令地址=read(data_rel(当前的数据)+fun1函数这个指令的长度)

         bl  下一套指令地址

      }

      call fun1

      这样一旦开始执行,除非遇到退出指令,否则函数会一直执行下去。android dvm虚拟机以前读源码的时候见过这种模式,这种计算指令长度的方式,davlik也确实可以这样搞。他每个指令长度都是可以提前写死的,没有变长的可能。

      指令函数分析

      我们知道了他的基本单元指令,以及基本单元指令的函数,这个时候我们对基本单元指令进行一个一个的分析,然后将某一个vmp化的java函数vmp指令进行反向转化。

      • 裸函数

        所有的函数都是裸函数,纯用汇编写的可能性不打,毕竟还有jni函数,这些裸函数直接打乱了ida的反汇编

      • 函数代码乱序

        很多指令函数其实都是正常顺序的,但是大部分中间都有个错误处理,这些错误处理都会调向同一个函数的位置。直白点就是,这个指令处理的函数开始的内存地址和结束的内存地址是连续的,但是中间有一条错误处理指令,跳到了远方地址,让ida进行函数识别的时候出错了,直接把这个错误处理nop就可以正常分析了。

      • dex字符编码支持

        这个其实我是没想到了,分析了好久,搞不懂里面有一段很繁琐的代码,到底是干嘛,对于追求优雅和有强迫症的我,这是不能接受的。然后我搞出跑了一下,发现跟一个以前的字符编码格式很像。不知道没关系,也不影响。如果想了解可以看看

        1

        2

        3

        4

        v62 = (*(*a30 + 0x58LL) + *(*(*a30 + 16LL) + 4 * v59));

          do

            v64 = *v62++;

          while ( (v64 & 0x80000000) != 0 );

        • vmp指令函数匹配davlik指令

          这个有很多人说叫指令对照表,不过无所谓,我兄弟的博客中已经说了很多还原的方法,具体啥意思,没完全懂,他好像是数据流向分析+vmp指令分析,但是我在实际还原的过程中使用的是人肉+推倒,不算快,但是速度客观,不算太难。算了,这部分不具体写了,感觉没啥难的,主要还是对于davlik指令要熟悉,后面跟别的一起写为什么这个不难。

        • 指令参数匹配

          vmp对于寄存器原来的含义,进行了加密,在运行的时候进行解密。寄存器标示着这个寄存器位移栈的位置,这种vmp是有自己定义栈的,但是他的指令的参数所在的位置在运行过程中是和真正davlik相同的,加了一道转化手续而已。下面的代码中有translator_8_12,这个类似的就是做转化的。

          vmp指令还原

          以前听说360还原用的是对照表。但是逆向到我这个地步,写对照表已经属于浪费时间了,而且对照表还要对着opcode一个一个的改,懒得搞了。我直接写了一个小型的opcode 转smali语言的ida脚本,将vmp指令转变为smali指令,然后写道对应的dex中反编译的smali工程中,进行回编译。直接贴脚本,这是一个的半自动化+手工来进行指令还原的脚本,扔给你们做参考吧。

          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

          229

          230

          231

          232

          233

          234

          235

          236

          237

          238

          239

          240

          241

          242

          243

          244

          245

          246

          247

          248

          249

          250

          251

          252

          253

          254

          255

          256

          257

          258

          259

          260

          261

          262

          263

          264

          265

          266

          267

          268

          269

          270

          271

          272

          273

          274

          275

          276

          277

          278

          279

          280

          281

          282

          283

          284

          285

          286

          287

          288

          289

          290

          291

          292

          293

          294

          295

          296

          297

          298

          299

          300

          301

          302

          303

          304

          305

          306

          307

          308

          309

          310

          311

          312

          313

          314

          315

          316

          317

          318

          319

          320

          321

          322

          323

          324

          325

          326

          327

          328

          329

          330

          331

          332

          333

          334

          335

          336

          337

          338

          339

          340

          341

          342

          343

          344

          345

          346

          347

          348

          349

          350

          351

          352

          353

          354

          355

          356

          357

          358

          359

          360

          361

          362

          363

          364

          365

          366

          367

          368

          369

          370

          371

          372

          373

          374

          375

          376

          377

          378

          379

          380

          381

          382

          383

          384

          385

          386

          387

          388

          389

          390

          391

          392

          393

          394

          395

          396

          397

          398

          399

          400

          401

          402

          403

          404

          405

          406

          407

          408

          409

          410

          411

          412

          413

          414

          415

          416

          417

          418

          419

          420

          421

          422

          423

          424

          425

          426

          427

          428

          429

          430

          431

          432

          433

          434

          435

          436

          437

          438

          439

          440

          441

          442

          443

          444

          445

          446

          447

          448

          449

          450

          451

          452

          453

          454

          455

          456

          457

          458

          459

          460

          461

          462

          463

          464

          465

          466

          467

          468

          469

          470

          471

          472

          473

          474

          475

          476

          477

          478

          479

          480

          481

          482

          483

          484

          485

          486

          487

          488

          489

          490

          491

          492

          493

          494

          495

          496

          497

          498

          499

          500

          501

          502

          503

          504

          505

          506

          507

          508

          509

          510

          511

          512

          513

          514

          515

          516

          517

          518

          519

          520

          521

          522

          523

          524

          525

          526

          527

          528

          529

          530

          531

          532

          533

          534

          535

          536

          537

          538

          539

          540

          541

          542

          543

          544

          545

          546

          547

          548

          549

          550

          551

          552

          553

          554

          555

          556

          557

          558

          559

          560

          561

          562

          563

          564

          565

          566

          567

          568

          569

          570

          571

          572

          573

          574

          575

          576

          577

          578

          579

          580

          581

          582

          583

          584

          585

          586

          587

          588

          589

          590

          591

          592

          593

          594

          595

          596

          597

          598

          599

          600

          601

          602

          603

          604

          605

          606

          607

          608

          609

          610

          611

          612

          613

          614

          615

          616

          617

          618

          619

          620

          621

          622

          623

          624

          625

          626

          627

          628

          629

          630

          631

          632

          633

          634

          635

          636

          637

          638

          639

          640

          641

          642

          643

          644

          645

          646

          647

          648

          649

          650

          651

          652

          653

          654

          655

          656

          657

          658

          659

          660

          661

          662

          663

          664

          665

          666

          667

          668

          669

          670

          671

          672

          673

          674

          675

          676

          677

          678

          679

          680

          681

          682

          683

          684

          685

          686

          687

          688

          689

          690

          691

          692

          693

          694

          695

          696

          697

          698

          699

          700

          701

          702

          703

          704

          705

          706

          707

          708

          709

          710

          711

          712

          713

          714

          715

          716

          717

          718

          719

          720

          import idaapi

          global_table = 0x78D05D8E80

          fun_id = 158

          fun_addr_ref = 0x0

          instr_fun_table = 0x0

          # 0x1b4

          translator_8_12=[3,2,1,0,7,6,5,4,0xb,0xA ,9, 8, 0xF, 0xE, 0xD, 0xC]

          translator_12_16=[0xB, 0xA, 9, 8, 0xF, 0xE, 0xD, 0xC, 3, 2, 1, 0, 7, 6, 5, 4]

          def aget(instr_addr,x10):

              print("aget","arg:")

              return 4

          def const_4(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              arg0 = translator_8_12[(instr_0_2 >> 8) & 0xF]

              arg1 = translator_12_16[instr_0_2 >> 12]

              print("const/4 v%d, %d"%(arg0,arg1))

              return 2

          def const_16(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              arg0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              print("const/16 v%d, %d"%(arg0,instr_2_4))

              return 4

          def const(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              arg0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              print("const v%d, %d"%(arg0,instr_2_4))

              return 6

          def const_string(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              v60 = instr_0_2 >> 12

              v61 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF

              v63 = 16 * (translator_12_16[v60] & 0xF)

              v75 = v63 | v61

              #

              a38_index_11 = read_mem_to_Int(x10 + 0x58, 8)

              a38_index_2 = read_mem_to_Int(x10 + 0x10, 8)

              v3 = read_mem_to_Int(instr_2_4 * 4 + a38_index_2, 4)

              string =  v3+ a38_index_11

              # print("string addr",hex(string),hex(instr_addr))

              # cla_len =  read_mem_to_Int(string, 1)

              # return_type = idaapi.dbg_read_memory(string+1, cla_len).decode('utf-8')

              #

              # print("const-string ",v75,return_type)

              print("const-string v%d, %s"%(v75,hex(string)))

              return 4

          def xor(instr_addr,x10):

              print("xor","arg:")

              return 4

          def move_16(instr_addr,x10):

              print("move-16","arg:")

              return 2

          def iput_object(instr_addr,x10):

              print("iput_object","arg:")

              return 4

          def aput_object(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v37 = (instr_0_2 >> 8) & 0xF

              v38 = instr_0_2 >> 12

              v40 = translator_8_12[v37];

              v41 = translator_12_16[v38];

              arg_v1 = v40 & 0xF | (16 * (v41 & 0xF))

              arg_v2 = read_mem_to_Int(instr_addr+2, 1)

              arg_v3 = read_mem_to_Int(instr_addr+3, 1)

              print("aput-object v%d, v%d, v%d"%(arg_v1,arg_v2,arg_v3))

              return 4

          def goto(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v3 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              if v3 >= 128:

                  int_num = (v3 - 256)*2

              else:

                  int_num = v3 *2

              print("goto","arg:",hex(instr_addr+int_num),int_num)

              return 2

          def rsub(instr_addr,x10):

              print("rsub","arg:")

              return 4

          def cmp(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v1 = read_mem_to_Int(instr_addr+2, 1)

              v2 = read_mem_to_Int(instr_addr+3, 1)

              v3 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("cmp","arg:",v3,v1,v2)

              return 4

          def invoke_super(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_add_4 = read_mem_to_Int(instr_addr+4, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              register_size = v63 >> 4

              arg1 = instr_add_4   & 0xF

              arg2 = instr_add_4   >> 4 & 0xF

              arg3 = (instr_add_4   >> 8) & 0xf

              arg4 = instr_add_4   >> 12

              arg5 = v63   & 0xF

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              classname_str = get_class_name(x10,ref_index)

              method_name_str = get_method_name(x10,ref_index)

              type_name_str = get_proto_type_name(x10, ref_index)

              # arg_name_str = get_method_arg(x10,ref_index)

              # print("invoke_direct class:",classname_str,"method:",method_name_str,"method_ref_index",hex(ref_index),"type:",type_name_str,"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              print("invoke-super ",classname_str+"->"+method_name_str+type_name_str,"method_ref_index",hex(ref_index),"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              return 6

          def invoke_direct(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_add_4 = read_mem_to_Int(instr_addr+4, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              register_size = v63 >> 4

              arg1 = instr_add_4   & 0xF

              arg2 = instr_add_4   >> 4  & 0xF

              arg3 = (instr_add_4   >> 8) & 0xf

              arg4 = instr_add_4   >> 12

              arg5 = v63   & 0xF

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              classname_str = get_class_name(x10,ref_index)

              method_name_str = get_method_name(x10,ref_index)

              type_name_str = get_proto_type_name(x10, ref_index)

              # arg_name_str = get_method_arg(x10,ref_index)

              # print("invoke-direct class:",classname_str,"method:",method_name_str,"method_ref_index",hex(ref_index),"type:",type_name_str,"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              print("invoke-direct ",classname_str+"->"+method_name_str+type_name_str,"method_ref_index",hex(ref_index),"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              return 6

          def invoke_static(instr_addr, x10):    # 第一个字节是指令,主导函数偏移,第二个字节是寄存器,三四字节是方法偏移

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_add_4 = read_mem_to_Int(instr_addr+4, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              register_size = v63 >> 4

              arg1 = instr_add_4   & 0xF

              arg2 = instr_add_4   >> 4 & 0xF

              arg3 = (instr_add_4   >> 8) & 0xf

              arg4 = instr_add_4   >> 12

              arg5 = v63   & 0xF

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              classname_str = get_class_name(x10,ref_index)

              method_name_str = get_method_name(x10,ref_index)

              type_name_str = get_proto_type_name(x10, ref_index)

              # arg_name_str = get_method_arg(x10,ref_index)

              # print("invoke-static class:",classname_str,"method:",method_name_str,"method_ref_index",hex(ref_index),"type:",type_name_str,"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              print("invoke-static ",classname_str+"->"+method_name_str+type_name_str,"method_ref_index",hex(ref_index),"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              # print("CallStaticVoidMethod arg_name_str:",arg_name_str)

              return 6

          def invoke_interface(instr_addr,x10):    # 第一个字节是指令,主导函数偏移,第二个字节是寄存器,三四字节是方法偏移

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_add_4 = read_mem_to_Int(instr_addr+4, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              register_size = v63 >> 4

              arg1 = instr_add_4   & 0xF

              arg2 = instr_add_4   >> 4 & 0xF

              arg3 = (instr_add_4   >> 8) & 0xf

              arg4 = instr_add_4   >> 12

              arg5 = v63   & 0xF

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              classname_str = get_class_name(x10,ref_index)

              method_name_str = get_method_name(x10,ref_index)

              type_name_str = get_proto_type_name(x10, ref_index)

              # arg_name_str = get_method_arg(x10,ref_index)

              print("invoke-interface ",classname_str+"->"+method_name_str+type_name_str,"method_ref_index",hex(ref_index),"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              # print("CallStaticVoidMethod arg_name_str:",arg_name_str)

              return 6

          def invoke_virutal(instr_addr,x10):    # 第一个字节是指令,主导函数偏移,第二个字节是寄存器,三四字节是方法偏移

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_add_4 = read_mem_to_Int(instr_addr+4, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              register_size = v63 >> 4

              arg1 = instr_add_4   & 0xF

              arg2 = instr_add_4   >> 4 & 0xF

              arg3 = (instr_add_4   >> 8) & 0xf

              arg4 = instr_add_4   >> 12

              arg5 = v63   & 0xF

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              classname_str = get_class_name(x10,ref_index)

              method_name_str = get_method_name(x10,ref_index)

              type_name_str = get_proto_type_name(x10, ref_index)

              # arg_name_str = get_method_arg(x10,ref_index)

              print("invoke-virtual ",classname_str+"->"+method_name_str+type_name_str,"method_ref_index",hex(ref_index),"register:",register_size,"arg1:",arg1,"arg2:",arg2,"arg3:",arg3,"arg4:",arg4,"arg5:",arg5)

              # print("CallStaticVoidMethod arg_name_str:",arg_name_str)

              return 6

          def rem_doule(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              instr_2_4 = read_mem_to_Int(instr_addr + 2, 2)

              print("rem-doule",hex(v_arg_0),hex(instr_2_4))

              return 4

          def move_result_object(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 =     translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              instr_2_4 = read_mem_to_Int(instr_addr + 2, 2)

              print("move-result-object v%d"%v_arg_0)

              return 2

          def MonitorEnter(instr_addr,x10):

              print("MonitorEnter","arg:")

              return 2

          def return_object(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("return-object v%d"%v_arg_0)

              return 2

          def move_object(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF];

              v_arg_1 = translator_12_16[instr_0_2 >> 12]

              print("move-object v%d, v%d"%(v_arg_0,v_arg_1))

              return 2

          def new_instance(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              classname_str = byIndexGet_dex_type_name(dex_base_addr,dex_type_off,dex_string_list_off,ref_index)

              print("new-instance v%d, %s"%(v_arg_0,classname_str))

              return 4

          def move_result(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("move-result v%d"%v_arg_0)

              return 2

          def new_array(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_size = translator_12_16[instr_0_2 >> 12]

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF];

              ref_index = read_mem_to_Int(instr_addr + 2, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              classname_str = byIndexGet_dex_type_name(dex_base_addr,dex_type_off,dex_string_list_off,ref_index)

              print("new-array v%d, v%d, %s"%(v_arg_0,v_arg_size,classname_str))

              return 4

          def if_ne(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              arg1 = translator_8_12[(instr_0_2 >> 8) & 0xF];

              arg2 = translator_12_16[instr_0_2 >> 12];

              print("if-ne v%d, v%x, %x"%(arg1,arg2,instr_addr+instr_2_4*2))

              return 4

          def if_eqz(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              print("if-eqz v%d, %x"%(v63,instr_addr+instr_2_4*2))

              return 4

          def if_gt(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              arg1 = translator_8_12[(instr_0_2 >> 8) & 0xF];

              arg2 = translator_12_16[instr_0_2 >> 12];

              print("if-gt v%d, v%x, %x"%(arg1,arg2,instr_addr+instr_2_4*2))

              return 4

          def if_lt(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              arg1 = translator_8_12[(instr_0_2 >> 8) & 0xF];

              arg2 = translator_12_16[instr_0_2 >> 12];

              print("if-lt v%d, v%x, %x"%(arg1,arg2,instr_addr+instr_2_4*2))

              return 4

          def if_lez(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              print("if-lez v%d, %x"%(v63,instr_addr+instr_2_4*2))

              return 4

          def if_nez(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              instr_2_4 = read_mem_to_Int(instr_addr+2, 2)

              v63 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF));   #处理一个字节(8 bit)的 数据,作为寄存器转换

              print("if-nez v%d, %x"%(v63,instr_addr+instr_2_4*2))

              return 4

          def iput(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              filed_index = read_mem_to_Int(instr_addr+2, 2)

              v_arg_0 = translator_12_16[instr_0_2 >> 12]

              v_arg_1 = translator_8_12[(instr_0_2 >> 8) & 0xF]

              dex_filed_off = read_mem_to_Int(x10 + 0x20, 8)

              class_index = read_mem_to_Int(8 * filed_index + dex_filed_off, 2)

              filed_type_index = read_mem_to_Int(8 * filed_index + dex_filed_off+2, 2)

              name_string_index = read_mem_to_Int(8 * filed_index + dex_filed_off+4, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              class_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, class_index)

              filed_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, filed_type_index)

              name_string = byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,name_string_index)

              # print("iput-object ",v_arg_0,v_arg_1,class_type_string,"->",name_string,";",filed_type_string,"filed_index:",filed_index)

              print("iput-object v%d, v%d, %s"%(v_arg_1,v_arg_0,class_type_string+"->"+name_string+":"+filed_type_string+" filed_index: "+filed_index))

              return 4

          def aget_object(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              arg_1 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              arg_v2 = read_mem_to_Int(instr_addr+2, 1)

              arg_v3 = read_mem_to_Int(instr_addr+3, 1)

              print("aget-object v%d, v%d, v%d"%(arg_1,arg_v2,arg_v3))

              return 4

          def iget_object(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              filed_index = read_mem_to_Int(instr_addr+2, 2)

              v_arg_0 = translator_12_16[instr_0_2 >> 12]

              v_arg_1 = translator_8_12[(instr_0_2 >> 8) & 0xF]

              dex_filed_off = read_mem_to_Int(x10 + 0x20, 8)

              class_index = read_mem_to_Int(8 * filed_index + dex_filed_off, 2)

              filed_type_index = read_mem_to_Int(8 * filed_index + dex_filed_off+2, 2)

              name_string_index = read_mem_to_Int(8 * filed_index + dex_filed_off+4, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              class_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, class_index)

              filed_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, filed_type_index)

              name_string = byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,name_string_index)

              print("iget-object v%d, v%d, %s"%(v_arg_1,v_arg_0,class_type_string+"->"+name_string+":"+filed_type_string+" filed_index: "+filed_index))

              return 4

          def check_cast(instr_addr,x10):

              type_index = read_mem_to_Int(instr_addr+2, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, type_index)

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("check-cast v%d, %s"%(v_arg,type_string))

              return 4

          def sget_object(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              filed_index = read_mem_to_Int(instr_addr+2, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              dex_filed_off = read_mem_to_Int(x10 + 0x20, 8)

              class_index = read_mem_to_Int(8 * filed_index + dex_filed_off, 2)

              filed_type_index = read_mem_to_Int(8 * filed_index + dex_filed_off+2, 2)

              name_string_index = read_mem_to_Int(8 * filed_index + dex_filed_off+4, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              class_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, class_index)

              filed_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, filed_type_index)

              name_string = byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,name_string_index)

              print("sget-object v%d, %s"%(v_arg_0,class_type_string+"->"+name_string+":"+filed_type_string))

              return 4

          def sget_boolean(instr_addr, x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              filed_index = read_mem_to_Int(instr_addr+2, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              dex_filed_off = read_mem_to_Int(x10 + 0x20, 8)

              class_index = read_mem_to_Int(8 * filed_index + dex_filed_off, 2)

              filed_type_index = read_mem_to_Int(8 * filed_index + dex_filed_off+2, 2)

              name_string_index = read_mem_to_Int(8 * filed_index + dex_filed_off+4, 2)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              class_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, class_index)

              filed_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, filed_type_index)

              name_string = byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,name_string_index)

              print("sget-boolean v%d, %s"%(v_arg_0,class_type_string+"->"+name_string+":"+filed_type_string))

              return 4

          def nop(instr_addr,x10):

              print("nop")

              return 2

          def return_void(instr_addr,x10):

              print("return-void")

              return 2

          def throw(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("throw v%d"%v_arg_0)

              return 2

          def throw_2(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              v_arg_0 = translator_8_12[(instr_0_2 >> 8) & 0xF] & 0xF | (16 * (translator_12_16[instr_0_2 >> 12] & 0xF))

              print("move-exception v%d"%v_arg_0)

              return 2

          def array_length(instr_addr,x10):

              instr_0_2 = read_mem_to_Int(instr_addr, 2)

              arg_0 = translator_12_16[instr_0_2 >> 12]

              arg_1 =  translator_8_12[(instr_0_2 >> 8) & 0xF]

              print("array-length v%d, v%d"%(arg_1,arg_0))

              return 2

          switch = {0x0:iput, 0x1:invoke_super, 0x2:aget, 0x3:xor, 0x4:move_16, 0x5:iput_object, 0x5a:nop, 0xd:goto, 0xda:aput_object , 0x7:rsub, 0x8:cmp, 0x1e:if_lez, 0x2b:if_nez, 0x3e:if_eqz, 0x3c:if_gt,0x21:if_lt,0x44:const_16, 0xc3:const,

                    0x5e:move_result , 0x68:invoke_direct, 0xA6:invoke_virutal, 0xAA:new_instance, 0xc7:move_result_object, 0xbb:return_object, 0x92:return_void, 0xfc:move_object, 0x79:invoke_interface, 0xf9:check_cast,

                    0xf0:const_4, 0xfa:if_ne, 0x93:sget_object, 0xc2:sget_boolean, 0x9a:invoke_static, 0x9b:const_string, 0x1f:new_array, 0x19:iget_object, 0x9d:MonitorEnter,0xc4:array_length,0x6b:aget_object,0xf5:throw,0xee:throw_2}

          def ByFunIDgetFunAddr(id):

              offset_mem = int.from_bytes(idaapi.dbg_read_memory(4*id+global_table,4),byteorder='little')

              method_addr = global_table+offset_mem

              print("fun_id:",hex(id),"method_addr:",hex(offset_mem),"method_addr:",hex(method_addr))

              return method_addr

          def read_mem_to_String(addr,size):

              mem = idaapi.dbg_read_memory(addr,size)

              return mem

          def read_mem_to_Int(addr, size):

              mem = int.from_bytes(idaapi.dbg_read_memory(addr,size),byteorder='little')

              return mem

          def get_proto_type_name(x10, type_index):

              dex_method_off = read_mem_to_Int(x10 + 0x28, 8)

              dex_string_list_off = read_mem_to_Int(x10 + 0x10, 8)

              dex_type_off = read_mem_to_Int(x10 + 0x18, 8)

              dex_base_addr = read_mem_to_Int(x10 + 0x58, 8)

              dex_proto_off = read_mem_to_Int(x10 + 0x30, 8)

              proto_index = read_mem_to_Int(8 * type_index + dex_method_off + 2, 2)

              return_type_index = read_mem_to_Int(dex_proto_off + 12 * proto_index + 4,4)

              pararm_off = read_mem_to_Int(dex_proto_off + 12 * proto_index + 8,4)

              sign_type_string_list = "("

              if pararm_off != 0:

                  pararm_size = read_mem_to_Int(dex_base_addr+pararm_off,4)

                  # pararm_mem_total =  pararm_size * 2

                  # for i in pararm_size:

                  for i in range(pararm_size):

                      pararm_type_index = read_mem_to_Int(dex_base_addr + pararm_off+4+i*2, 2)

                      pararm_type_string = byIndexGet_dex_type_name(dex_base_addr, dex_type_off, dex_string_list_off, pararm_type_index)

                      sign_type_string_list =sign_type_string_list+pararm_type_string

              return_type_string = byIndexGet_dex_type_name(dex_base_addr,dex_type_off,dex_string_list_off,return_type_index)

              sign_type_string_list=sign_type_string_list+")"+return_type_string

              return sign_type_string_list

          def byIndexGet_dex_type_name(dex_base_addr,dex_type_off,dex_string_list_off,pararm_type_index):

              string_index = read_mem_to_Int(pararm_type_index * 4 + dex_type_off, 4)

              type_string = byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,string_index)

              return type_string

          def byIndexGet_dex_string_name(dex_base_addr,dex_string_list_off,string_index):

              v3 = read_mem_to_Int(string_index * 4 + dex_string_list_off, 4)

              cla_len = read_mem_to_Int(dex_base_addr+v3,1)

              class_str = idaapi.dbg_read_memory(dex_base_addr+v3+1, cla_len).decode('utf-8')

              print("byIndexGet_dex_string_name:",hex(dex_base_addr+v3))

              return class_str

          def get_class_name(x10,class_index):

              a38_index_5 = read_mem_to_Int(x10 + 0x28, 8)

              a38_index_2 = read_mem_to_Int(x10 + 0x10, 8)

              a38_index_3 = read_mem_to_Int(x10 + 0x18, 8)

              a38_index_11 = read_mem_to_Int(x10 + 0x58, 8)

              v1 = read_mem_to_Int(8 * class_index + a38_index_5, 2)

              v2 = read_mem_to_Int(v1 * 4 + a38_index_3, 4)

              v3 = read_mem_to_Int(v2 * 4 + a38_index_2, 4)

              class_str_addr = v3+a38_index_11

              cla_len =  read_mem_to_Int(class_str_addr, 1)

              class_str = idaapi.dbg_read_memory(class_str_addr+1, cla_len).decode('utf-8')

              # print("classname len:",cla_len)

              return class_str

          def get_method_name(x10, method_index):

              a38_index_5 = read_mem_to_Int(x10 + 0x28, 8)

              a38_index_2 = read_mem_to_Int(x10 + 0x10, 8)

              a38_index_3 = read_mem_to_Int(x10 + 0x18, 8)

              a38_index_11 = read_mem_to_Int(x10 + 0x58, 8)

              v1 = read_mem_to_Int(8 * method_index + a38_index_5 + 4, 2)

              v3 = read_mem_to_Int(v1 * 4 + a38_index_2, 4)

              method_name_str_addr =  v3 + a38_index_11

              method_name_str_len =  read_mem_to_Int(method_name_str_addr, 1)

              method_name_str = idaapi.dbg_read_memory(method_name_str_addr+1, method_name_str_len).decode('utf-8')

              return method_name_str

          def get_method_arg(x10, index):

              # (v70 + * (v69 + 4LL * * (v68 + 4LL * * (v81 + v84 + 2))));

              a38_index_5 = read_mem_to_Int(x10 + 0x28, 8)

              a38_index_2 = read_mem_to_Int(x10 + 0x10, 8)

              a38_index_3 = read_mem_to_Int(x10 + 0x18, 8)

              a38_index_11 = read_mem_to_Int(x10 + 0x58, 8)

              a38_index_6 = read_mem_to_Int(x10 + 0x30, 8)

              v0 = read_mem_to_Int(8 * index + a38_index_5 + 2, 2)

              v80 = read_mem_to_Int(a38_index_6 + 12 * v0 + 8,4)

              print("arg_addr a38_index_5:",hex(a38_index_5))

              print("arg_addr a38_index_2:",hex(a38_index_2))

              print("arg_addr a38_index_3:",hex(a38_index_3))

              print("arg_addr a38_index_11:",hex(a38_index_11))

              print("arg_addr a38_index_6:",hex(a38_index_6))

              print("arg_addr v80:",v80)

              v81 = v80 + a38_index_11

              # print("arg_addr v81:",v81)

              v1 = read_mem_to_Int(v81+2, 4)

              v2 = read_mem_to_Int(v1 * 4 + a38_index_3, 4)

              v3 = read_mem_to_Int(v2 * 4 + a38_index_2, 4)

              return_type =  v3+a38_index_11

              # cla_len =  read_mem_to_Int(return_type, 1)

              # return_type = idaapi.dbg_read_memory(return_type+1, cla_len).decode('utf-8')

              print("arg_addr:",return_type)

              return return_type

          # def instr_handle(instr_addr,x10):

          #     offset_mem = read_mem(instr_addr,2) & 0xff

          #     instr_len = dexinstr[offset_mem](instr_addr,x10)

          #

          #     return instr_len

          def get_segment_address(segment_name):

              seg = idaapi.get_segm_by_name(segment_name)

              if seg is not None:

                  return seg.start_ea, seg.end_ea

              else:

                  return None

          # 使用方法

          def main():

              method_addr = ByFunIDgetFunAddr(fun_id)

              register_size = read_mem_to_Int(method_addr, 2)

              ins_size = read_mem_to_Int(method_addr+2, 2)

              insns_size = read_mem_to_Int(method_addr+12, 4)

              print("method registers_size:",hex(register_size))

              print("method ins_size:",hex(ins_size))

              print("method insns_size:",hex(insns_size))

              method_addr_2 = ByFunIDgetFunAddr(fun_id+1)

              funSize = method_addr_2 - method_addr

              print("method_size:",hex(funSize))

              instr_addr = method_addr+0x10

              print("method_end:",hex(instr_addr+insns_size*2 ))

              while(1):

                  offset = read_mem_to_Int(instr_addr, 2) & 0xff  #第一个字节

                  instr_fun_addr = instr_fun_table+offset*8

                  print("-----------------------------------")

                  print("instr_fun_addr:", hex(instr_fun_addr),hex(offset))

                  instr_handle = switch[offset]

                  print("instr_addr:", hex(instr_addr))

                  print(hex(offset))

                  if(instr_handle == None):

                      break

                  instr_len = instr_handle(instr_addr, fun_addr_ref)

                  instr_addr = instr_addr + instr_len

              print(hex(offset))

          def init_var():

              global global_table

              global instr_fun_table

              global fun_addr_ref

              bss_start, bss_end = get_segment_address(".bss")

              qword_78B9BFCDC8 = read_mem_to_Int(bss_start + 0x2B8, 8)

              global_table = read_mem_to_Int(bss_start + 0x270, 8)

              yaq2__sec = read_mem_to_Int(bss_start + 0x2B0, 8)

              v6 = yaq2__sec+ fun_id *12

              v6_index_1 = read_mem_to_Int(v6 + 4, 4)

              v6_index_2 = read_mem_to_Int(v6 + 8, 4)

              fun_addr_ref = read_mem_to_Int(8 * (v6_index_2 - 1) + qword_78B9BFCDC8, 8)

              instr_fun_table, data_rel_end = get_segment_address(".data.rel.ro")

              print("qword_78B9BFCDC8", hex(qword_78B9BFCDC8))

              print("global_table", hex(global_table))

              print("v6_index_1", hex(v6_index_1))

              print("v6_index_2", hex(v6_index_2))

              print("fun_addr_ref", hex(fun_addr_ref))

          if __name__ == '__main__':

              init_var()

              main()

          效果图,我就不贴

          大致讲一下这个脚本原理思路和使用

          这个脚本需要在vmp已经装载完成以后调用,找到还原的dex和对应的vmp指令地址,可以直接循环翻译成smali指令
          • dex文件的骨架(内存地址),来解析辅助davlik指令反编译
          • 具体某个函数的vmp指令,进行转化,通过函数index和vmp规则进行计算
          • 函数长度,人工分析+寻找return类指令

            这个脚本需要在vmp已经装载完成以后调用,找到还原的dex和对应的vmp指令地址,可以直接循环翻译成smali指令。具体的指令是有具体的长度写在代码里的。smali函数结束都有smali指令的返回指令(return类指令),可以随便找个smali工程看一下,函数结束都有一个return类指令.也有可能是其中的一个返回,通过跳转指令结合分析

            dex vmp安全性论述

            易攻击的缺点

            dex vmp 虽然跟二进制vmp的原理一样,但是他的逆向难度和vmp却不是一个量级的。为什么会出现这种情况

            • dex vmp的基础支撑其实是jni函数,而这些函数熟悉人的人过于的多,能进行分析和追踪的方法也很多,有经验和耐心的甚至可以通过jni方法追踪来分析代码。
            • dex vmp的框架设计和运行原理跟我早起读过的dvm虚拟机的解释模式基本差不多。
            • dex vmp的指令设计是跟davlik指令有着一对一的关系,可以说只是一个简单的对照关系,这导致如果对于davlik指令足够熟悉,相当于对于dex vmp的指令熟悉。

              对于一个vmp虚拟机,如果你熟悉他的程序,熟悉他的指令,知道如何测试他(通过jni将dex vmp当作黑河测试),这种情况下,即使不去完全的人肉分析每一条指令,完全通过测试分析,也是可以慢慢进行还原的。

              已知的保护措施

              这个已知保护措施是dex vmp在可被逆向的情况下,如果提高一些安全性

              • 指令对照加密进行动态版本变更
              • 指令参数再加一层对照加密

                目前就知道这两个,如果dex vmp对照表已经被破解,如果有这两种保护措施,也无法被自动换还原。

                不知道有没有一种通讯协议进行加解密破解的感觉,而dex vmp是一种拥有一定规律,可被暴力分析和破解的算法

                易分析的davlik指令

                分析vmp指令,对他进行还原的时候,并没有非常具体的写。在这里一切写,这是一些写davlik指令,或者说反编译分析davlik的一些经验。

                davlik指令是非常容易分析的,基本不太存在保护的价值,因为一般的防护扛不住静态分析工具的分析,这个他的设计是有很大关系的。

                上下文关联性强,基本都是编译器自动生成,不存在dalvik底层对抗问题

                dalvik中的部分指令有很强的关联性。比如

                1

                2

                0000: invoke-static       {}, Lcom/hepta/davlik/JvmRuntime;->getClassLoaderList()[Ljava/lang/ClassLoader; # method@0012

                0003: move-result-object  v0

                前面这个invoke-static ,如果有返回值,必会跟这后面这条指令,甚至可以说他们是一条指令

                1

                2

                001e: const-string        v2, "/libjvmRuntime.so" # string@0006

                0020: invoke-virtual      {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; # method@0028

                变量在定义之后,有大概率在下一条就会使用,当然不是绝对

                1

                2

                000b: new-instance        v1, Ljava/lang/StringBuilder; # type@001f

                000d: invoke-direct       {v1}, Ljava/lang/StringBuilder;->()V # method@0026

                这是new一个对象的代码,会立即调用init方法

                1

                2

                3

                return-void

                return-object

                return vAA

                函数结束必有return指令,当然遇到return不一定是结束。但是进行人工还原的时候通过上下文分析函数结束位置,还是非常有价值的。

                变量类型明确,指令基本很少有歧义性

                有很多操作相同,但是操作类型却不同的指令
                vmp入门(一):android dex vmp还原和安全性论述

                这一条aget指令,针对于各种不同的类型有不同的指令。在指令中有个对应的索引,我们甚至可以直接查看这个类型来反向推理这个指令操作的是什么类型。这种设计也从侧面给说明了,dalvik一个指令只有一个解释,没有歧义性,不想汇编,c,不知道类型,更不知道操作具体是什么,只是数据进行了操作。即使是一些虚函数,java中你可能分不出来他具体调用的是那个类的,但是在dalvik中,这个类的类型已经写死在索引中了。

                真正的功能指令少

                虽然dalvik号称256个opcode,但是有很多不用的,还有一些是同一个功能,但是不同类型

                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

                switch (opcode.format) {

                           case Format10t:

                               return new FixDexBackedInstruction10t(dexFile, opcode, instructionStartOffset,reader);

                           case Format10x:

                               return new DexBackedInstruction10x(dexFile, opcode, instructionStartOffset);  //

                           case Format11n:

                               return new FixDexBackedInstruction11n(dexFile, opcode, instructionStartOffset,reader);

                           case Format11x:

                               return new FixDexBackedInstruction11x(dexFile, opcode, instructionStartOffset,reader);

                           case Format12x:

                               return new FixDexBackedInstruction12x(dexFile, opcode, instructionStartOffset,reader);

                           case Format20bc:

                               return new FixDexBackedInstruction20bc(dexFile, opcode, instructionStartOffset,reader);

                           case Format20t:

                               return new FixDexBackedInstruction20t(dexFile, opcode, instructionStartOffset,reader);

                           case Format21c:

                               return new FixDexBackedInstruction21c(dexFile, opcode, instructionStartOffset,reader);

                           case Format21ih:

                               return new FixDexBackedInstruction21ih(dexFile, opcode, instructionStartOffset,reader);

                           case Format21lh:

                               return new FixDexBackedInstruction21lh(dexFile, opcode, instructionStartOffset,reader);

                           case Format21s:

                               return new FixDexBackedInstruction21s(dexFile, opcode, instructionStartOffset,reader);

                           case Format21t:

                               return new FixDexBackedInstruction21t(dexFile, opcode, instructionStartOffset,reader);

                           case Format22b:

                               return new FixDexBackedInstruction22b(dexFile, opcode, instructionStartOffset,reader);

                           case Format22c:

                               return new FixDexBackedInstruction22c(dexFile, opcode, instructionStartOffset,reader);

                           case Format22cs:

                               return new FixDexBackedInstruction22cs(dexFile, opcode, instructionStartOffset,reader);

                           case Format22s:

                               return new FixDexBackedInstruction22s(dexFile, opcode, instructionStartOffset,reader);

                           case Format22t:

                               return new FixDexBackedInstruction22t(dexFile, opcode, instructionStartOffset,reader);

                           case Format22x:

                               return new FixDexBackedInstruction22x(dexFile, opcode, instructionStartOffset,reader);

                           case Format23x:

                               return new FixDexBackedInstruction23x(dexFile, opcode, instructionStartOffset,reader);

                           case Format30t:

                               return new FixDexBackedInstruction30t(dexFile, opcode, instructionStartOffset,reader);

                           case Format31c:

                               return new FixDexBackedInstruction31c(dexFile, opcode, instructionStartOffset,reader);

                           case Format31i:

                               return new FixDexBackedInstruction31i(dexFile, opcode, instructionStartOffset,reader);

                           case Format31t:

                               return new FixDexBackedInstruction31t(dexFile, opcode, instructionStartOffset,reader);

                           case Format32x:

                               return new FixDexBackedInstruction32x(dexFile, opcode, instructionStartOffset,reader);

                           case Format35c:

                               return new FixDexBackedInstruction35c(dexFile, opcode, instructionStartOffset,reader);

                           case Format35ms:

                               return new FixDexBackedInstruction35ms(dexFile, opcode, instructionStartOffset,reader);

                           case Format35mi:

                               return new FixDexBackedInstruction35mi(dexFile, opcode, instructionStartOffset,reader);

                           case Format3rc:

                               return new FixDexBackedInstruction3rc(dexFile, opcode, instructionStartOffset,reader);

                           case Format3rmi:

                               return new FixDexBackedInstruction3rmi(dexFile, opcode, instructionStartOffset,reader);

                           case Format3rms:

                               return new FixDexBackedInstruction3rms(dexFile, opcode, instructionStartOffset,reader);

                           case Format45cc:

                               return new FixDexBackedInstruction45cc(dexFile, opcode, instructionStartOffset,reader);

                           case Format4rcc:

                               return new FixDexBackedInstruction4rcc(dexFile, opcode, instructionStartOffset,reader);

                           case Format51l:

                               return new FixDexBackedInstruction51l(dexFile, opcode, instructionStartOffset,reader);

                           case PackedSwitchPayload:

                               return new FixDexBackedPackedSwitchPayload(dexFile, instructionStartOffset,reader);

                           case SparseSwitchPayload:

                               return new FixDexBackedSparseSwitchPayload(dexFile, instructionStartOffset,reader);

                           case ArrayPayload:

                               return new FixDexBackedArrayPayload(dexFile, instructionStartOffset,reader);

                           default:

                               throw new ExceptionWithContext("Unexpected opcode format: %s", opcode.format.toString());

                这是smali解析的dalvik指令,就上面这些功能,不算多。

                指令以方法为单位,封闭性好

                dalvik的指令如果想要运行,必须在一个method里,不可能超出一个函数的范围,更不可能先汇编语言那样全局的大跳,动态跳转地址,每一个函数都是一个密封的黑盒,当然可能会有一些类变量什么的,但是就代码含义分析这一块基本没问题。

                Future

                一般在这个位置都是要吹牛逼的,但是我发现牛逼可能成真,就留个悬念。

                最后

                如果真的觉得可以就关注一下公众号吧。也不发图推广了。

                哦,对了,我这脚本配合人工半自动换,是可以做到一个函数一个函数还原的,我经过测试了,图,就不贴,怕人找,虽然目前没人找过。

                我随便找的其中的某个样本,发的照片,很多样本好像都差不多,都是这个逆向思路

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]