看雪CTF 2019Q3 第十题 传家之宝
2019-09-25 11:41:17 Author: bbs.pediy.com(查看原文) 阅读量:236 收藏

一、前言

(本来已经放弃了,感谢kanxue、作者卓桐、小编的鼓励。使得能够在快结束时提交答案)

本题使用了一些android加固技术:包括

1)多种反调试

2)ollvm混淆

3)native函数多次动态注册

4) 简单的dex vmp虚拟机(advmp)

5) java层字符混淆

6) java部分函数插花指令

7)so 字符串加密(应该是ollvm实现的)

8)java字符串加密

遇到的一些困难:
1、java混淆使用0 O o之类的字符,使得分析java源码非常费劲。
2、在so中加入了很多反调试功能,尤其是vmp引擎函数的部分指令码中也加入了,过掉这些反调试虽然简单,但是比较繁琐。

         3、程序中使用了大量的ollvm混淆,其中dexvmp引擎就有26K多,程序只提供arm指令so库,由于angr反混淆对arm支持的不够友好,没时间利用符号执行去掉ollvm.稍微分析下程序,可得到部分垃圾指令,将其去掉后,使用F5反汇编这个函数,花费了6个小时才完成。当然这需要更改ida配置使得max_funsition size由64k 改为1024K或者更多。即使不F5,直接分析汇编代码,IDA动态调试时其自动分析功能也需要大概15到20分钟(此时单步调试会非常卡顿,需要等IDA分析完成后才混顺畅)。也就是是说分析一个虚拟机引擎从程序开始启动,到15分钟后才能流畅的跟踪。

         4、将VMP大部分指令识别后,还原出加解密代码,发现输入竟然是随机的,因为其java层将输入的flag通过一些查表变换后使用了base64进行加密,但是这个base64加密的基准字符,使用了一个random随机生成的。当再次加密时这个基准又重新随机生成,所以2次输入相同的sn,base64的结果是不样的。此时整个人都不好了。以为掉了一个坑里面,这个vmp引擎是假的,再次分析程序,包括另外的一个libmyqtest.so,也没发现端倪,果断放弃。

         5、作者在群里提示说 random的随机种子是固定的,我理解的是“对于正确的sn无论输入多少次,结果都应该是正确的“,看来作者的意图是第一次输入正确就算通过。再次开始分析。这里是造成多解的一个原因:第一次可以不正确,后面几次是可能正确的。

         6、由于base64的基准是随机生成的,难免会有重复的,一般base64的基准字符是不重复的, 这样加密和解密才能对应。但是随机生成的基准字符有重复的造成加密和解密的结果是不一致的,因此对于重复的字符需要进行分别尝试,这也是可能造成多解的一个原因。

         7、在java层对输入的flag使用了多种编码格式的转换,使得反推flag的时候花费了点时间。

二、流程分析

         (一)java部分代码分析

          1 java字符串解密

         反编译后可以看到大部分字符串都是加密的,加密函数为:OooOO0OOli.d,为了快速定位到检测SN入口,需要将所有加密的字符串进行解密。使用C对dex文件中的加密字符串进行解密如下():

        
int readUnsignedLeb128(unsigned char** pStream)
 {
	unsigned char* ptr = *pStream;
	int result = *(ptr++);

	if (result > 0x7f) {
		int cur = *(ptr++);
		result = (result & 0x7f) | ((cur & 0x7f) << 7);
		if (cur > 0x7f) {
			cur = *(ptr++);
			result |= (cur & 0x7f) << 14;
			if (cur > 0x7f) {
				cur = *(ptr++);
				result |= (cur & 0x7f) << 21;
				if (cur > 0x7f) {
					/*
					* Note: We don't check to see if cur is out of
					* range here, meaning we tolerate garbage in the
					* high four-order bits.
					*/
					cur = *(ptr++);
					result |= cur << 28;
				}
			}
		}
	}

	*pStream = ptr;
	return result;
}
unsigned char* GetFileNameBuffer(unsigned char* fileName, int* size)
{
	FILE *fd;
	unsigned char *pName;
	int sign = 0;
	int filesize;

	if (NULL == fileName)
	{
		return NULL;
	}

	fd = fopen((char*)fileName, "rb");
	if (NULL == fd)
	{
		return NULL;
	}
	fseek(fd, 0, SEEK_END);
	filesize = ftell(fd);
	pName = (unsigned char*)malloc(filesize + 100);
	if (NULL == pName)
	{
		fclose(fd);
		return NULL;
	}
	memset(pName, 0, filesize + 100);
	fseek(fd, 0, SEEK_SET);
	fread(pName, 1, filesize, fd);
	fclose(fd);
	*size = filesize;
	return pName;
}

bool WriteFileNameBuffer(const char* fileName, unsigned char* buf, int size)
{
	FILE *fd;

	if (NULL == fileName)
	{
		return false;
	}

	unlink((const char*)fileName);

	fd = fopen((char*)fileName, "wb");
	if (NULL == fd)
	{
		return NULL;
	}

	fwrite(buf, 1, (size_t)size, fd);

	fclose(fd);

	return true;
}
char dd1(char key)
{
	if (key <= 0x39 && key >= 0x30)
		return key - 0x30;
	else if (key <= 'F' && key >= 'A')
	{
		return key - 0x37;
	}
	else
	{
		return -1;
	}
}
bool isnNeedDecryptString(char* arg8,unsigned int len)
{
	for (unsigned int i = 0; i < len; i++)
	{
		if (dd1(arg8[i]) < 0)
		{
			return false;
		}
	}
	return true;

}
bool dexStringDecrypt(char* fileName, char* outFile)
{
	int size;
	//打开dex文件
	unsigned char* pbuf1 = GetFileNameBuffer((unsigned char*)"fileName", &size);
	if (NULL == pbuf1)
		return false;

	unsigned int stringCnt = *(unsigned int*)(pbuf1 + 0x38);
	unsigned int stringOffset = *(unsigned int*)(pbuf1 + 0x3c);
	unsigned int* stringId = (unsigned int*)(pbuf1 + stringOffset);
	for (unsigned int i = 0; i < 2488;  i++)
	{
		if (i == 144 || i == 2145 || i == 2146 || i == 2147 || i == 2148 || i == 2308 || i == 2309 || i == 1900)
			continue;
	
		unsigned int off = stringId[i];
		unsigned char * pbuf = pbuf1 + off;
		int len = readUnsignedLeb128(&pbuf);
		if (len <3)
			continue;
		if (false == isnNeedDecryptString((char*)pbuf, len))
			continue;
		char* outString = decryptString((char*)pbuf, len);
		if (NULL == outString)
			continue;
		printf("i = %d, out = %s\n",i, outString);
		memcpy(pbuf, outString, strlen(outString));
	}
	WriteFileNameBuffer(outFile, (unsigned char*)pbuf1, size);

	return true;
}
i = 117, out = No keylines defined for
i = 193, out = Keyline index
i = 194, out = Keyframe
i = 195, out = Key error!
i = 297, out = Fragment no longer exists for key
i = 302, out = FLAG_REQUEST_FILTER_KEY_EVENTS
i = 476, out = CAPABILITY_CAN_FILTER_KEY_EVENTS
i = 482, out = Bad fragment at key
i = 681, out = The key is correct and the decryption begins!
i = 758, out = Result key can't be null
i = 933, out = keyframe
i = 934, out = key == null
i = 935, out = key == null || value == null
i = 941, out = intent_extra_data_key
i = 1149, out = android.arch.lifecycle.ViewModelProvider.DefaultKey:
i = 1182, out = android.support.groupKey
i = 1201, out = android.support.sortKey
i = 1241, out = action_key
i = 1397, out = resultKey

 2 manifest分析

 从manifest文件可知:

         a)没有application

 b)主activity为:android.support.v4.app.o000000o

 c)注册了一个provider:android.support.v4.app.OO0OOOO0   

         因为 provider会在入口类走之前运行,因此第一个执行的类为 android.support.v4.app.OO0OOOO0,正其父类android.support.v4.app.O0OO00O的init函数中加载‘libmydvp.so’ 

System.loadLibrary(OooOO0OOli.d("mydvpB393F"));

          3、 android.support.v4.app.o000000o入口类

           1)其init函数会加载 “libmyqtest.so

System.loadLibrary(OooOO0OOli.d("myqtestB2A433B"));

           2)后面的流程就比较复杂,而且字符混淆非常严重,可以从输入提示入手,即 "Key error!", 可以检测函数在类android.support.v4.app.O000000o中,如下:

 public void onClick(View arg3) {
                    String v1;
                    O0OOOOO v3;
                    if(this.O000000o.O000oOo) {
                        v3 = this.O000000o.O0000oo0();
                        v1 = OooOO0OOli.d("The decryption has already started! Please don\'t touch it!083D1B0A2B6E101F2309083C0A4F2B205E683B4C1D201A0C276F593B6E");
                    }
                    else if(OO0o0.O00000Oo(o000000o.g_string2, this.O000000o.O0000O0o.getText().toString())) {
                        this.O000000o.O000oOo = true;
                        Toast.makeText(this.O000000o.O0000oo0(), OooOO0OOli.d("The key is correct and the decryption begins!F3B27556F2B090A3D161F3B265F216F0E0C2806013C6E"), 0).show();
                        this.O000000o.mybutton.setText(OooOO0OOli.d("In decryptionD361C1D260001"));
                        return;
                    }
                    else {
                        v3 = this.O000000o.O0000oo0();
                        v1 = OooOO0OOli.d("Key error!423D201E48");
                    }

                    Toast.makeText(((Context)v3), ((CharSequence)v1), 0).show();
                }

        可以看出OO0o0.O00000Oo(o000000o.g_string2, this.O000000o.O0000O0o.getText().toString())函数返回真即为正确。其中参数 this.O000000o.O0000O0o.getText().toString()为输入的flag。下面继续跟进函数“ OO0o0.O00000Oo ”
       2)检测函数 OO0o0.O00000Oo

public static boolean O00000Oo(String randomString, String flag) {
        byte[] key;
        int index = 0;
        if(TextUtils.isEmpty(((CharSequence)flag))) {
            return 0;
        }

        int v2 = 1;
        if((flag.startsWith(OooOO0OOli.d("flag{E2834"))) && (flag.endsWith(OooOO0OOli.d("32")))) {
            flag = flag.substring(flag.indexOf(123) + 1, flag.length() - 1);
        }

        flag.getBytes();
        flag = OO0o0.O0000O0o(flag) + "";
        System.out.println(flag);
        if(flag.startsWith(OooOO0OOli.d("62"))) {
            flag = flag.substring(1);
        }

        new byte[0];
        try {
            key = flag.getBytes(OooOO0OOli.d("utf-896277"));
        }
        catch(UnsupportedEncodingException v3) {
            v3.printStackTrace();
        }

        StringBuilder v3_1 = new StringBuilder();
        int len = key.length;
        while(index < len) {
            int keyValue = key[index];
            int v6 = keyValue - 48;
            if(v6 > 9) {
                v6 = keyValue - 65;
                if(v6 <= 25) {
                    v6 += 10;
                }
                else if(keyValue == 125) {
                    v6 = 62;
                }
                else {
                    v6 += -61;
                }
            }

            char v5_1 = OO0o0.O0000OOo[v6];
            v6 = v2 % 9;
            if(v6 == 0) {
                v5_1 = '+';
            }

            v3_1.append(v5_1);
            if(v6 == 0) {
                keyValue = v2 / 9;
                v3_1.append(flag.substring((keyValue - 1) * 9, keyValue * 9));
            }

            ++v2;
            ++index;
        }

        return OO0o0.O000000o(randomString, OO0o0.O00000Oo(v3_1.toString().getBytes()));
    }

        这部分流程比较简单

        1)分析输入的sn是否是flag{xxx}格式的如果是就截取xxx。

        2)调用OO0o0.O0000O0o(flag)函数。该函数实际上就是用输入的sn索引下面的表,然后转换成一个long型的字符串。

               OO0o0.O0000O0o = new char[]{'d', 'c', '2', 'l', '{', '}', 'f', 'g', 'e', 'm', 'a', 'b', 'h', '0', '8', 'y'}; 

        3)再次使用long型的字符串进行chartobyte后索引一个常量表:

               OO0o0.O0000OOo = new char[]{'加', '载', '本', '件', '请', '卸', '要', '微', '软', '不', '可', '以', '来', '去', '安', 'a', '人', '7', '减', '好', 'l', '卓', '测', 'p', '试', 'p', '3', '7', '乘', '吗', 'b', '桐', 'c', 'e', '眼', 'q', '6', '4', '以', '为', '神', 'd', '无', 'f', '功', '圣', '名', '至', '己', '0', '何', '解', '忧', 'g', '唯', '有', '1', '杜', '2', '康', 'h', '}', '{'};

               并且当时9的倍数是使用'+'字符代替,并且将原始的9个字符串拷贝到之后。

        4) 调用 OO0o0.O00000Oo(v3_1.toString().getBytes())

              这个函数实际上是个变形的base64。但是其基准字符是随机生成的,随机种子固定。部分代码如下:

            
for(inputLen = 0; inputLen < OO0o0.seed.length; ++inputLen) {
            try {
                OO0o0.seed[inputLen] = ((byte)(OO0o0.O0000OoO.nextInt() % 256));
            }
            catch(Throwable v2_3) {
                v2_3.printStackTrace();
            }
            catch(NumberFormatException v2_1) {
                v2_1.printStackTrace();
            }
        }

       对应第一次的生成的基准如下:

unsigned char base64char[65] =
	{ 0xa9, 0x06, 0xab, 0x48, 0x03, 0x8b, 0x08, 0xf0, 0xe5, 0x38, 0x29, 0xe9, 0x2b, 0x7e, 0x26, 0x57,
	0x36, 0x75, 0xa2, 0x4d, 0x10, 0x35, 0x98, 0x3a, 0xd4, 0x63, 0x5b, 0xa3, 0x4c, 0x0e, 0xd7, 0x12,
	0xdb, 0xbb, 0x1c, 0x4e, 0x9d, 0xbe, 0x1d, 0xcb, 0xcc, 0xcb, 0xb4, 0x63, 0x65, 0xcc, 0xe8, 0x46,
	0x0b, 0xf4, 0x72, 0x2f, 0x2a, 0x13, 0x7d, 0xd8, 0x5e, 0x2b, 0xae, 0xb0, 0x16, 0x44, 0x63, 0xca,
	0x74 };

其中最后一个字符为站位字符其数值为0x74,这里注意的是再次调用次base64函数后,这个基准数值将发生变化,重新随机生成。

5)调用OO0o0.O000000o(randomString, OO0o0.O00000Oo(v3_1.toString().getBytes()))

     以一个随机产生的字符串(无用的),以及base64结果作为参数调用函数 OO0o0.O000000o。其返回如果为true则flag正确。

4、函数 :public static boolean O000000o(String paramString1, String paramString2)

public static boolean O000000o(String paramString1, String paramString2)
  {
    int j;
    try
    {
      paramString2 = paramString2.getBytes(OooOO0OOli.d("ISO-8859-1087A764158"));
      j = paramString2.length;
      i = 0;
    }
    catch (UnsupportedEncodingException paramString1)
    {
      paramString1.printStackTrace();
    }
    paramString1 = new OO0OOOO().OO0OOOO(paramString2, paramString1.getBytes());
    paramString2 = new OO00OO();
    paramString2.O000000o = "";
    int i = 0;
    for (;;)
    {
      if (i < paramString1.length)
      {
        StringBuilder localStringBuilder = new StringBuilder();
        localStringBuilder.append(paramString2.O000000o);
        localStringBuilder.append(OO00OO.O00000Oo(paramString1[i]));
        paramString2.O000000o = localStringBuilder.toString();
        i += 1;
      }
      else
      {
        boolean bool = "820e52333de3bcb42467f0a20564c145af5edbf2e923df33be21f0af159710c92cbc43f79f94ec930a7ae86021af5b3ae263369299de5436b85f297be08a032a28dc357391961ecc26931bfc97d67a5e74d8781fb4105b9afbe613a2041dd8c3".equals(paramString2.O000000o);
        return bool;
      }
    }
    while (i < j)
    {
      int k = paramString2[i];
      i += 1;
    }
  }
  

从上面的函数可以知道,最后结果为“820e52333de3bcb42467f0a20564c145af5edbf2e923df33be21f0af159710c92cbc43f79f94ec930a7ae86021af5b3ae263369299de5436b85f297be08a032a28dc357391961ecc26931bfc97d67a5e74d8781fb4105b9afbe613a2041dd8c3”时flag正确。

 函数开始是进行了编码转换后执行如下语句

        paramString1 = new OO0OOOO().OO0OOOO(paramString2, paramString1.getBytes());

其是一个native函数,原型如下:

       public native byte[] OO0OOOO(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2);

 这个native函数输入的是一个base64加密后的结果。将其返回的结果进行bytetochar转换后与上面的标定字符串比较相等即为正确。因此后续核心分析转为native函数 OO0OOOO。首先要定位到此函数。

三      libmydvp.so 初始化函数分析

         1、初始化数组如下:

init_array:000E0AD8 75 4A 01 00                                                     DCD init_sshho+1
.init_array:000E0ADC 09 2A 02 00                                                     DCD init_tracePidStringDecode+1
.init_array:000E0AE0 91 31 02 00                                                     DCD init_nop+1
.init_array:000E0AE4 29 5B 07 00                                                     DCD init_decryptString+1
.init_array:000E0AE8 E9 87 07 00                                                     DCD init_decryptString2+1
.init_array:000E0AEC A1 5D 08 00                                                     DCD init_decrypt3+1
.init_array:000E0AF0 F5 EF 08 00                                                     DCD init_decrypt4+1
.init_array:000E0AF4 ED 04 09 00                                                     DCD init_decrypt5+1
.init_array:000E0AF8 B9 05 09 00                                                     DCD int_nop1+1
.init_array:000E0AFC E9 4A 09 00                                                     DCD init_decrypt6+1
.init_array:000E0B00 C9 1B 01 00                                                     DCD init_g_sspbahh1+1
.init_array:000E0B04 E9 AE 01 00                                                     DCD init_antiDebug1_CreateThread_AndSet_g_antiDebugGlobal+1
.init_array:000E0B08 55 ED 01 00                                                     DCD init_antiDebug2_fork+1
.init_array:000E0B0C CD 1B 01 00                                                     DCD sub_11BCC+1
.init_array:000E0B10 A9 1C 01 00                                                     DCD sub_11CA8+1
.init_array:000E0B14 85 1D 01 00                                                     DCD sub_11D84+1
.init_array:000E0B18 C1 1D 01 00                                                     DCD sub_11DC0+1
.init_array:000E0B1C D1 1D 01 00                                                     DCD sub_11DD0+1

前面的几个函数都是解密字符串的,其中包括对native函数  OO0OOOO原型的解密。

2、反调试函数init_antiDebug1_CreateThread_AndSet_g_antiDebugGlobal(1AEE8)

由于此函数使用ollvm混淆,并且加入了一些垃圾代码使得F5失效,简单写个脚本去掉相关垃圾代码,F5即可成功。如下

#coding=utf-8

import struct
from idaapi import *
from idc import *
from idautils import *

def patchNopCode(ea, endAddr, data, len):
    while ea < endAddr:
        ea = find_binary(ea, SEARCH_DOWN| SEARCH_NEXT, data, radix=16)
        if BADADDR == ea:
            break
        patch_bytes(ea,  '\xC0\x46' * len)
        print 'ea = ' + str(hex(ea))
        ea = ea + len

def KillNop():
    lajiData = '2D E9 F0 47 BD E8 F0 47 13 E0 BD E8 F0 47 05 E0 00 F1 01 00 0A E0 1B 46 0E E0 10 E0 B1 B5 01 E0 12 46 01 E0 82 B0 FB E7 02 B0 F1 E7 A0 F1 01 00 F1 E7 2D E9 F0 47 E8 E7 BD E8 B1 40 ED E7 2D E9 F0 47 BD E8 F0 47'
    nopData = '\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46\xC0\x46'
    nopCode1 = 'B1 B5 82 B0 12 46  02 B0 00 F1 01 00 A0 F1 01 00 1B 46 BD E8 B1 40  01 F1 01 01 A1 F1 01 01'
    lajiData2 = '2D E9 F0 40 BD E8 F0 40 13 E0 BD E8 F0 47 05 E0 00 F1 01 00 0A E0 1B 46 14 E0 16 E0 B1 B5 01 E0 12 46 01 E0 82 B0 FB E7 02 B0 F1 E7 A0 F1 01 00 F1 E7 2D E9 F0 47 EF F3 00 84 10 B4 10 BC 84 F3 00 89 E2 E7 BD E8 B1 40 E7 E7 2D E9 F0 47 BD E8 F0 47 FF E7'

    libBase = GetLibAddr('libmydvp.so')
    startAddr = libBase
    endAddr = libBase + 0x8916A
    print 'startAddr= ' + str(hex(startAddr))
    print 'endAddr = ' + str(hex(endAddr))
    patchNopCode(startAddr, endAddr, lajiData, 35)

KillNop()

F5之后的代码也很多,将近2000行,只说下核心代码

pthread_create((pthread_t *)&g_antiDebug_thread, 0, (void *(*)(void *))antidebugThread1, 0);
  pid = getpid();
  sub_B7BB0(&v230);
  buf_1 = std::operator|(16, 8);
  sub_A83CC((int)&v249, buf_1);
  sub_A37CC((int)&v250, pid);
  sub_951B4(&v249, &v230);

         1、函数开始创建了一个线程antidebugThread1(174CC)这个线程后面在说。

         2、然后剩下的将近1800多行就是个tracepid反调试。

         3、如果tracepid的值不为0 ,则执行如下代码:

  if ( v161 != -942489079 )
        break;
      LODWORD(v181) = fd;                       // 反调试设置为1
      HIDWORD(v181) = pid;
      *(_QWORD *)&g_antiDebug_Global = v181;
      v161 = -1048192996;

          如果在调试状态下 全局变量g_antiDebug_Global(E3550)将被设置为文件句柄值,在 antidebugThread1会访问在这个值,实际上其应该是一个类似于jvm,env的指针,被设置成文件句柄后,会使程序访问其时发生异常崩溃。知道这些后过掉次函数反调试就比较容易了。(后面会有一个过掉所有反调试的脚步)

       3、反调试线程 antidebugThread1(174CC)

          这个函数看上去依然很大(2000多行),起始代码也很简单,贴一下核心代码。

           

         1)其会检索 g_antiDebug_Global(E3550)如果其值为0,则调用usleep随眠。

         2)如果不为0,而是之前反调试的结果为文件句柄,将发生异常程序退出。

         3)如果不为0,而是一个合法的值,执行如下代码:

         

nativeRegisterFlag = __PAIR__(realNativeFun, decryptString50((int)&v294));

                  (*(void (**)(void))(*(_DWORD *)((unsigned int)&dword_E3554 & ~dword_E3554 | dword_E3554 & ~(unsigned int)&dword_E3554)

                                    + 860))();

        其中 realNativeFun(14A9C)实际上就是之前我们要寻找的native函数。但是由于 g_antiDebug_Global = 0,所以次部分代码没有执行。

        4)剩下其他1800多号代码又是个tracepid反调试功能。与 init_antiDebug1_CreateThread_AndSet_g_antiDebugGlobal类似过掉方法也一样。

         4、初始化函数init_antiDebug2_fork(1ED54)

         核心代码如下:

    
newthread = &v410;
      haystack = (char *)&v409;
      v417 = (char *)&v409;
      v416 = (char *)&v406;
      v413 = &v405;
      v421 = &v404;
      v418 = (char *)&v404;
      needle = (char *)&v403;
      v419 = &v402;
      v426 = &v401;
      v410 = pthread_self();
      pipe(&g_pipe);
      antidebugptraceFork();
      v392 = pthread_create(newthread, 0, (void *(*)(void *))sub_1D404, 0);
      v393 = &v411;
      v394 = &v409;
      v395 = &savedregs;
      v396 = -1357197669;
      v22 = getpid();

2)  创建一个管道,同时调用函数 antidebugptraceFork();

3)依然tracepid反调试,采用相同方法 bypass

4 antidebugptraceFork(1D580)

核心代码如下:

sprintf((char *)&v125, v24, pid);
  sonPid = fork();

1)fork一个子进程

2)子进程调用 ptrace(0, 0, 0, 0, ststusBuf_2);

3)子进程读取tracepid,并将其值通过管道write给父进程:write(dword_E356C, &buf, 4u);

4)父进程流程比较简单,直接函数返回了。

5)父进程的线程 sub_1D404为读取管道,代码如下:

int sub_1D404()
{
  signed int v0; // r0
  int v1; // r1
  __pid_t v2; // r0
  int result; // r0
  int v4; // [sp+8h] [bp-20h]
  int v5; // [sp+Ch] [bp-1Ch]

  v4 = -1;
  close(dword_E356C);
  read(g_pipe, &v4, 4u);
  sleep(1u);
LABEL_3:
  v0 = 1819773075;
  while ( 1 )
  {
    v1 = v0 & 0x7FFFFFFF;
    if ( (v0 & 0x7FFFFFFF) == 340946481 )
    {
      v4 = -1;
      goto LABEL_3;
    }
    if ( v1 == 1314826255 )
      break;
    if ( v1 == 1819773075 )
    {
      read(g_pipe, &v4, 4u);
      v0 = 1314826255;
      if ( !v4 )
        v0 = 340946481;
    }
  }
  kill(g_sonPid, 9);
  v2 = getpid();
  kill(v2, 9);
  result = _stack_chk_guard - v5;
  if ( _stack_chk_guard == v5 )
    result = 0;
  return result;
}

了解了以上反调试功能后:

首先可以直接将父进程的线程 sub_1D404杀掉,让其直接返回。然后nop掉fork指令,并使其结果r0=0就可以。

至此,初始化函数就分析完了。

 四、jni_onload函数

         这个函数代码量相对较小,贴出来吧

int __fastcall JNI_OnLoad(int a1)
{
  char v1; // r1
  char v2; // r1
  unsigned int v3; // r2
  signed int v4; // r1
  signed int v5; // r0
  unsigned int v6; // r6
  int v7; // r0
  unsigned int v8; // r12
  signed int v9; // r3
  signed int v10; // r4
  bool v11; // zf
  signed int v12; // r3
  int v13; // r0
  struct globalInfo *v14; // r0
  int v15; // r0
  signed int v16; // r1
  signed int v17; // r3
  signed int v18; // r3
  clock_t v19; // r0
  signed int v20; // r0
  signed int v21; // r2
  struct _JNIEnv **v22; // r0
  int *v23; // r1
  struct _JNIEnv **v24; // r2
  struct _JNIEnv *v25; // r0
  clock_t clockTime2; // r0
  int size; // r0
  signed int v28; // r1
  int result; // r0
  int v30; // [sp-1Ch] [bp-84h]
  unsigned int v31; // [sp+4h] [bp-64h]
  int *g_dword_E35501; // [sp+8h] [bp-60h]
  _DWORD *v33; // [sp+Ch] [bp-5Ch]
  struct _JavaVM *JVM; // [sp+10h] [bp-58h]
  char v35; // [sp+16h] [bp-52h]
  char v36; // [sp+17h] [bp-51h]
  struct _JNIEnv **v37; // [sp+18h] [bp-50h]
  int *v38; // [sp+1Ch] [bp-4Ch]
  char v39; // [sp+23h] [bp-45h]
  clock_t clockTime1; // [sp+24h] [bp-44h]
  int v41; // [sp+28h] [bp-40h]

  v33 = &_stack_chk_guard;
  v1 = 0;
  if ( y_47 < 10 )
    v1 = 1;
  v36 = v1;
  v2 = 0;
  if ( !((~-x_46 * x_46 ^ 0xFFFFFFFE) & ~-x_46 * x_46) )
    v2 = 1;
  v35 = v2;
  JVM = (struct _JavaVM *)a1;
  v3 = (unsigned int)&g_antiDebug_Global & 0x1A04BD11;
  g_dword_E35501 = &g_antiDebug_Global;
  v31 = (~(unsigned int)&g_antiDebug_Global & 0xE5FB42EE | (unsigned int)&g_antiDebug_Global & 0x1A04BD11) ^ (a1 & 0x1A04BD11 | ~a1 & 0xE5FB42EE);
  v4 = 1974858797;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          v5 = v4;
          v6 = v3;
          if ( v4 <= 286463644 )
            break;
          if ( v4 > 1387993772 )
          {
            if ( v4 > 1974858796 )
            {
              if ( v4 == 2007388415 )
              {
                v4 = 102368831;
              }
              else if ( v4 == 1974858797 )
              {
                v4 = 1146459914;
                if ( v36 )
                  v4 = -217046703;
                if ( !v35 )
                  v4 = 1146459914;
                if ( v35 != v36 )
                  v4 = -217046703;
              }
            }
            else if ( v4 == 1387993773 )
            {
              v3 = -1;
              v4 = -1667146202;
            }
            else if ( v4 == 1813294739 )
            {
              v16 = 0;
              if ( y_47 < 10 )
                v16 = 1;
              v17 = 0;
              if ( (~(x_46 * (x_46 - 1)) | 0xFFFFFFFE) == -1 )
                v17 = 1;
              v11 = v16 == v17;
              v18 = 407986737;
              if ( !v11 )
                v18 = -1082095904;
              v4 = v18;
              if ( (~(x_46 * (x_46 - 1)) | 0xFFFFFFFE) == -1 )
                v4 = -1082095904;
              if ( y_47 >= 10 )
                v4 = v18;
            }
          }
          else if ( v4 <= 719945599 )
          {
            if ( v4 == 286463645 )
            {
              v4 = 0xEFC89CB9;
              if ( v39 )
                v4 = 0x52BB1AAD;
            }
            else if ( v4 == 407986737 )
            {
              v4 = -1082095904;
            }
          }
          else
          {
            v3 = 65540;
            v4 = -1667146202;
            if ( v5 != 719945600 )
            {
              if ( v5 != 1146459914 )
              {
                v28 = 795111493;
                goto LABEL_92;
              }
              v30 = 0;
              getENV(JVM);
              v3 = v6;
              v4 = -217046703;
            }
          }
        }
        if ( v4 <= -770792315 )
          break;
        if ( v4 > -123417836 )
        {
          if ( v4 == 0xF8A4CB15 )
          {
            size = readSSSAndGetSize(g_apkPath, (int)algn_E353C);
            v4 = -1557264936;
            if ( !size )
              v4 = 1813294739;
            g_sssSize = size;
            v3 = v6;
          }
          else if ( v4 == 102368831 )
          {
            v19 = clock();
            v4 = 0x2AE97F80;
            if ( (double)(signed int)(v19 - clockTime1) / 1000000.0 > 5.0 )
              v4 = 0xD20EA486;
            v3 = v6;
          }
        }
        else if ( v4 == -272065351 )
        {
          clockTime1 = clock();
          callnativeRegister(*v37);
          v22 = v37;
          v23 = g_dword_E35501;
          *g_dword_E35501 = v31;
          v24 = v37;
          v23[1] = (unsigned int)(v23 + 1) & ~(unsigned int)*v22 | (unsigned int)*v22 & ~(unsigned int)(v23 + 1);
          v25 = *v24;
          g_apkPath = (int)GetApkPath();
          clockTime2 = clock();
          v4 = 0xF8A4CB15;
          if ( (double)(signed int)(clockTime2 - clockTime1) / 1000000.0 > 2.0 )
            v4 = 0x2F647045;
          v3 = v6;
        }
        else if ( v4 == -217046703 )
        {
          v37 = (struct _JNIEnv **)&v30;
          v38 = &v30;
          v30 = 0;
          v7 = getENV(JVM);
          v8 = (x_46 * ~-x_46 ^ 0xFFFFFFFE) & x_46 * ~-x_46;
          v9 = 0;
          if ( !v8 )
            v9 = 1;
          v10 = 0;
          if ( y_47 < 10 )
            v10 = 1;
          v11 = v10 == v9;
          v12 = 1146459914;
          if ( !v11 )
            v12 = 286463645;
          v4 = v12;
          if ( !v8 )
            v4 = 286463645;
          if ( v7 )
            LOBYTE(v7) = 1;
          v39 = v7;
          if ( y_47 >= 10 )
            v4 = v12;
          v3 = v6;
        }
        else
        {
          v28 = -770792314;
LABEL_92:
          v11 = v5 == v28;
          v3 = v6;
          v4 = v5;
          if ( v11 )
            abort();
        }
      }
      if ( v4 <= -1509585911 )
        break;
      v4 = 102368831;
      if ( v5 != -1509585910 )
      {
        v4 = v5;
        if ( v5 == -1082095904 )
        {
          v4 = 407986737;
          if ( !(x_46 * (x_46 - 1) & (x_46 * (x_46 - 1) ^ 0xFFFFFFFE)) )
            v4 = -1509585910;
          if ( y_47 >= 10 )
            v4 = 407986737;
          v20 = 0;
          if ( !(x_46 * (x_46 - 1) & (x_46 * (x_46 - 1) ^ 0xFFFFFFFE)) )
            v20 = 1;
          v21 = 0;
          if ( y_47 < 10 )
            v21 = 1;
          if ( v21 != v20 )
            v4 = -1509585910;
          v3 = v6;
        }
      }
    }
    if ( v4 == -1667146202 )
      break;
    if ( v4 == 2737702360 )
    {
      v13 = operator new(0x84u);
      v14 = (struct globalInfo *)sub_85FB0(v13);
      g_globalInfo = v14;
      v15 = initSSSGlobal((int *)v14, *(int *)algn_E353C, g_sssSize);
      v4 = 2007388415;
      if ( v15 )
        v4 = 102368831;
      v3 = v6;
    }
  }
  result = *v33 - v41;
  if ( *v33 == v41 )
    result = v3;
  return result;
}

     1、第一部分 

     1)  获得env:etENV(JVM);

      2 ) 获得当前clockTime1 = clock();

      3 ) 调用函数 callnativeRegister(*v37); 

      4 ) 调用g_apkPath = (int)GetApkPath();

      5 ) 调用clockTime2 = clock();   

      6 ) 执行  clockTime2- clockTime1

      貌似有个 时间反调试,为了方便,直接修改clock函数返回值即可。

    2、 callnativeRegister函数(16CFC

unsigned int __fastcall callnativeRegister(struct _JNIEnv *env)
{
  char v1; // r2
  char v2; // r1
  unsigned int result; // r0
  signed int v4; // r1
  signed int v5; // r2
  unsigned int v6; // r12
  signed int v7; // r3
  signed int v8; // r1
  bool v9; // zf
  signed int v10; // r3
  struct _JNIEnv *env1; // [sp+Ch] [bp-24h]
  unsigned __int8 v12; // [sp+11h] [bp-1Fh]
  char v13; // [sp+12h] [bp-1Eh]
  unsigned __int8 v14; // [sp+13h] [bp-1Dh]

  env1 = env;
  v1 = 0;
  v2 = 0;
  if ( y_45 < 10 )
    v2 = 1;
  v13 = v2;
  result = (~((x_44 - 1) * x_44) | 0xFFFFFFFE) + 1;
  if ( (~((x_44 - 1) * x_44) | 0xFFFFFFFE) == -1 )
    v1 = 1;
  v12 = v1;
  v4 = -1230185785;
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( v4 <= 1146544159 )
        {
          switch ( v4 )
          {
            case -1230185785:
              result = v12;
              v5 = 626878466;
              if ( v12 != (unsigned __int8)v13 )
                v5 = 1146544160;
              v4 = v5;
              if ( v13 )
                v4 = 1146544160;
              if ( !v12 )
                v4 = v5;
              break;
            case -299872326:
              result = v14;
              v4 = 1603580720;
              if ( v14 )
                v4 = 1673631727;
              break;
            case 626878466:
              result = nativeRegister(env1);
              v4 = 1146544160;
              break;
          }
        }
        if ( v4 != 1146544160 )
          break;
        result = nativeRegister(env1);
        v6 = (x_44 * ~-x_44 ^ 0xFFFFFFFE) & x_44 * ~-x_44;
        v7 = 0;
        if ( !v6 )
          v7 = 1;
        v8 = 0;
        if ( y_45 < 10 )
          v8 = 1;
        v9 = v8 == v7;
        v10 = 626878466;
        if ( !v9 )
          v10 = -299872326;
        v4 = v10;
        if ( !v6 )
          v4 = -299872326;
        v14 = result;
        if ( y_45 >= 10 )
          v4 = v10;
      }
      if ( v4 != 1603580720 )
        break;
      v4 = 1673631727;
    }
  }
  while ( v4 != 1673631727 );
  return result;
}

        看出是一个native注册函数,其又调用152C0进行真正的nativeregister,其部分代码如下:

v73 = NewGlobalRef(&env1->functions, v107);
      dword_E3558 = (unsigned int)&dword_E3558 & ~v73 | v73 & ~(unsigned int)&dword_E3558;
      RegisterNatives(&env1->functions, v107, v106, 1);

       其实这个函数是个虚假的native函数。这里就不分析了。

       3、第二部分

        创建一个结构体 ,bin进行初始化

 v13 = operator new(0x84u);
      v14 = (struct globalInfo *)sub_85FB0(v13);
      g_globalInfo = v14;
      v15 = initSSSGlobal((int *)v14, *(int *)algn_E353C, g_sssSize);

      整个这部分代码的功能就是分析apk中的assets\ssspbahh.so文件。这个文件实际上是个变形的dex文件。其格式如下:

      

偏移	       大小	                                                                  功能
+0x00          8byte                                                                      magic
+0x08          int                                                                        文件头大小
+0x18          int                                                                       method个数
+0x1c          int                                                                       field type个数
+0x20          int                                                                       field	offset
+0x5c          int[0x0e]t                                                                 每个filed type字符串长度
+0x94          char[0x0e*2]                                                               filed type字符串 offset
+0xb0          int[0x0e]                                                                  field type value
+0xC8          int 									  method对象
+0xd0          int                                                                       accessflag
+0xd4          int                                                                       函数输入参数个数
+0xd8          int                                                                       寄存器个数
+0xe0          char[2]                                                                    输入参数类型字符串
+0xe4          short[0x574]                                                               指令码(变形的smali
              

     当然在jni_onload中会将env\jvm指针赋给 g_antiDebug_Global  ,从而使得线程 antidebugThread1 再次注册jni函数:14A9C

      五、native函数 14A9C 虚拟机引擎

       1、反调试:

       再次吐槽下这个函数的混淆。之前的反调试已经可以了,没必要再在虚拟机里面加反调试了,只是徒增工作量而已。这里增加了2中新的反跳检测:

       TCP端口和 /proc/pid/wchan文件检测。下面的脚本是过掉所有反调试的

#coding=utf-8

import struct
from idaapi import *
from idc import *
from idautils import *

'''====================================================
函数名:GetLibAddr
用  途:根据模块名获得模块起始地址
备  注:此函数在SHT被破坏时将不起作用
====================================================='''
def GetLibAddr(targetName):
    targetBase = 0
    for i in Modules():
        #print i.name
        if targetName in i.name:
            targetBase = int("%x"%(i.base), 16)
            #print 'targetName:=' + targetName + 'targetBase:=' + str(targetBase)
            break
    if targetBase == 0:
        #print 'targetBase None!!!'
        return False

    return targetBase

'''====================================================
函数名:GetSegAddrByName
用  途:根据段名获得段的起始地址
备  注:
====================================================='''
def GetSegAddrByName(targetName):
    tempAddr = FirstSeg()
    while tempAddr!=0:
        if tempAddr == 0:
            break
        name = SegName(tempAddr)
        if targetName == name:
            return tempAddr
        tempAddr = NextSeg(tempAddr)
    return 0

'''====================================================
函数名:writeMemoryForAddr
用  途:向模块地址写入指定shellcode
备  注:
====================================================='''
def writeMemoryForAddr(targetName, offset, buf):
    targetBase = 0
    targetBase = GetSegAddrByName(targetName)
    if targetBase == 0:
        #print 'targetBase None!!!'
        return False

    addr = targetBase + offset
    if not dbg_write_memory(addr, buf):
        return False

    refresh_debugger_memory()
    return True

'''====================================================
函数名:addBptForAddr
用  途:给模块指定偏移下断点
备  注:
====================================================='''
def addBptForAddr(targetName, offset, comm):
    soAddr = GetSegAddrByName(targetName)
    if soAddr > 0 :
        AddBpt(soAddr + offset)
        SetBptAttr(soAddr + offset, BPTATTR_FLAGS, BPT_ENABLED | BPT_BRK)
        MakeComm(soAddr + offset, comm)
    else :
        print 'create point fail'
    return True

'''====================================================
函数名:DelBptForAddr
用  途:删除模块指定偏移的断点
备  注:
====================================================='''
def DelBptForAddr(targetName, offset):
    soAddr = GetSegAddrByName(targetName)
    if soAddr > 0 :
        AddBpt(soAddr + offset)
        DelBpt(soAddr + offset)
    return True

'''====================================================
函数名:makenameForAddr
用  途:给指定模块函数重命名
备  注:
====================================================='''
def makenameForAddr(targetName, offset, comm):
    soAddr = GetSegAddrByName(targetName)
    if soAddr > 0 :
        MakeName(soAddr + offset, comm)
    else :
        print 'makenameForAddr fail'
    return True

'''====================================================
函数名:makeCommForAddr
用  途:给指定模块地址下注释
备  注:
====================================================='''
def makeCommForAddr(targetName, offset, comm):
    soAddr = GetSegAddrByName(targetName)
    if soAddr > 0 :
        MakeComm(soAddr + offset, comm)
    else :
        print 'makeCommForAddr fail'
    return True


'''====================================================
函数名:dumpMem
用  途:dump 指定大小的内存数据
备  注:
====================================================='''
def dumpMem(start, l, target):
    block = 1024
    c = l / block
    left = l % block
    fd = open(target, 'wb')
    if c > 0:
        for i in range(c):
            rawdex = idaapi.dbg_read_memory(start, block)
            fd.write(rawdex)
            start += block

    if left > 0:
        rawdex = idaapi.dbg_read_memory(start, left)
        print rawdex == True
        fd.write(rawdex)
    fd.close()

'''====================================================
函数名:doDump
用  途:通过窗口dump内存数据
备  注:
====================================================='''
def doDump():
    start = AskAddr(0, 'Input Addr start in hex: ')
    print('start is ' + str(hex(start)))

    end = AskAddr(0, 'Input Addr end in hex: ')
    print('end is ' + str(hex(end)))

    l = end - start
    target = AskStr('./dump.mem', 'Input the dump file path')

    if l > 0 and start > 0x0 and target and AskYN(1, 'start is 0x%0x, len is %d, dump to %s' % (start, l, target)) == 1:
        dumpMem(start, l, target)
        print('Dump Finish')

'''====================================================
函数名:readIntFromMemory
用  途:从内存中读取一个int
备  注:
====================================================='''
def readIntFromMemory(addr):
    flag=0
    temp = addr
    temp1= temp
    if  temp%2:
        temp1= temp-1
        flag=1
    else:
        temp1=temp
    m4=dbg_read_memory(temp1, 4+flag)
    return  struct.unpack('i', m4)[0+flag]

'''====================================================
函数名:dbg_read_every_memory
用  途:从内存中读取一个int
备  注:优化过的
====================================================='''
def dbg_read_every_memory(start, len):

    tempStart=start - (start%0x1000)
    maxLen = (start%0x1000) + len
    #print maxLen
    #print tempStart
    rawdex = idaapi.dbg_read_memory(tempStart, maxLen)
    #print rawdex
    left = start%0x1000
    #print left
    #print rawdex[left:maxLen]
    return bytearray(rawdex[left:maxLen])

'''====================================================
函数名:readShortFromMemory
用  途:从内存中读取一个short
备  注:
====================================================='''
def readShortFromMemory(addr):
    m2=dbg_read_memory(addr, 2)
    return  struct.unpack('h', m2)[0]

'''====================================================
函数名:readCharFromMemory
用  途:从内存中读取一个char
备  注:
====================================================='''
def readCharFromMemory(addr):
    m1=dbg_read_memory(addr, 1)
    return  struct.unpack('c', m2)[0]

'''====================================================
函数名:readUIntFromMemory
用  途:从内存中读取一个uint
备  注:
====================================================='''
def readUIntFromMemory(addr):
    m4=dbg_read_memory(addr, 4)
    return  struct.unpack('I', m4)[0]

'''====================================================
函数名:readUShortFromMemory
用  途:从内存中读取一个ushort
备  注:
====================================================='''
def readUShortFromMemory(addr):
    m2=dbg_read_memory(addr, 2)
    return  struct.unpack('H', m2)[0]

'''====================================================
函数名:readUCharFromMemory
用  途:从内存中读取一个uchar
备  注:
====================================================='''
def readUCharFromMemory(addr):
    m1=dbg_read_memory(addr, 1)
    return  struct.unpack('C', m1)[0]

'''====================================================
函数名:copyMem
用  途:拷贝内存到另外一个区域r
备  注:
====================================================='''
def copyMem(start, l, target):
    addrTemp = target
    print 'copy start'
    block = 1024
    c = l / block
    left = l % block
    if c > 0:
        for i in range(c):
            print str(hex(start))
            print str(hex(addrTemp))
            rawdex = idaapi.dbg_read_memory(start, block)
            dbg_write_memory(addrTemp, rawdex)
            refresh_debugger_memory()
            start += block
            addrTemp += block

    if left > 0:
        rawdex = idaapi.dbg_read_memory(start, left)
        dbg_write_memory(addrTemp, rawdex)
        refresh_debugger_memory()
    print 'copy end'

'''====================================================
函数名:get_uleb128
用  途:
备  注:
====================================================='''
def get_uleb128(content):
    value = 0
    for i in xrange(0,5):
        tmp = ord(content[i]) & 0x7f
        value = tmp << (i * 7) | value
        if (ord(content[i]) & 0x80) != 0x80:
            break
    if i == 4 and (tmp & 0xf0) != 0:
        print "parse a error uleb128 number"
        return -1
    return i+1, value

'''====================================================
函数名:get_uleb128_forIda
用  途:
备  注:
====================================================='''
def get_uleb128_forIda(addr):
    flag=0
    temp = addr
    if  temp%2:
        temp1= temp-1
        flag=1
    else:
        temp1=temp
    staticFieldSizeAddr=dbg_read_memory(temp1, 5+flag)
    s,v1 = get_uleb128(staticFieldSizeAddr[flag:])
    return s,v1

'''====================================================
函数名:FindData
用  途:从指定位置查找指定字节的数据
备  注:返回第一个找到的位置
====================================================='''
def FindData(addr, target):
    a = -1
    segStart = SegStart(addr)
    segEnd = SegEnd(addr)
    len=segEnd-segStart
    for i in range(len):
        rawdex1 = idaapi.dbg_read_memory(segStart, 1024)
        a = rawdex1.find(target)
        if a>= 0:
            a = a + segStart
            break;
        segStart = segStart+512
    return a

'''====================================================
函数名:FindDataEx
用  途:从指定位置查找指定字节的数据
备  注:返回第一个找到的位置
====================================================='''
def FindDataEx(addr, target,offset, target2):
    a = -1
    segStart = SegStart(addr)
    segEnd = SegEnd(addr)
    len=segEnd-segStart
    for i in range(len):
        rawdex1 = idaapi.dbg_read_memory(segStart, 1024)
        a = rawdex1.find(target)
        if a>= 0:
            rawdex2 = dbg_read_every_memory(segStart+a+offset, 512)
            b = rawdex2.find(target2)
            if b == 0:
                a = a + segStart
                break;
        segStart = segStart+512
    return a



#set debug breakpoint
C_Linker_InitAddr0=0x18B9A
C_Linker_InitAddr=0x1892E
C_Dvm_JniOnloadAddr=0x22988C
C_Dvm_IsDebugAddr=0x2D5768
C_Dvm_NativeRegisterAddr = 0x29AD1C
C_Dvm_JarLoadedAddr=0x29EE44

'''-------------------------------------------------------------
在调试器开始时,只执行一次
-------------------------------------------------------------'''
if not 'setflag' in dir():
    print 'set breakpoint'
    #addBptForAddr('linker', C_Linker_InitAddr0, 'C_Linker_InitAddr0')
    addBptForAddr('linker', C_Linker_InitAddr, 'C_Linker_InitAddr')
    #addBptForAddr('libart.so', C_Dvm_JniOnloadAddr,'jni_onload')
    #addBptForAddr('libart.so', C_Dvm_NativeRegisterAddr, 'native register')  # set nativeregister



    setflag = "tmp"

'''-------------------------------------------------------------
执行init 处理
-------------------------------------------------------------'''
pcValue=GetRegValue('pc')
libBase=GetLibAddr('linker')
if  pcValue-libBase == C_Linker_InitAddr:
    print 'C_Linker_InitAddr start'
    #bypass tracepid
    writeMemoryForAddr('libmydvp.so', 0x1C888, '\xc0\x46\xc0\x46\xc0\x46\xc0\x46\xc0\x46') # bypass traspid1
    writeMemoryForAddr('libmydvp.so', 0x22456, '\x00\x20\xc0\x46')  # bypass traspid2
    writeMemoryForAddr('libmydvp.so', 0x18DFC, '\x00\x20\xc0\x46')  # bypass traspidThread
    writeMemoryForAddr('libmydvp.so', 0x1D404, '\x70\x47\xc0\x46')  #bypass antidebugThread2
    writeMemoryForAddr('libmydvp.so', 0x1D906, '\xf8\x20\xc0\x46')  # bypass fork
    writeMemoryForAddr('libmydvp.so', 0x49D52, '\x00\x20\xc0\x46')  # bypass ooooo tracepid    
    writeMemoryForAddr('libmydvp.so', 0x5F09C, '\x00\x20\xc0\x46')  # bypass ooooo wchan 
    writeMemoryForAddr('libmydvp.so', 0x67290, '\x00\x20\xc0\x46')  # bypass ooooo 23946     
    writeMemoryForAddr('libmydvp.so', 0x68318, '\x00\x20\xc0\x46')  # bypass ooooo wchan 2   
    writeMemoryForAddr('libmydvp.so', 0x60C96, '\x01\x20\xc0\x46')  # bypass ooooo wchan 3 
    writeMemoryForAddr('libmydvp.so', 0x44878, '\x00\x20\xc0\x46')  # bypass ooooo tracepid 2 
    writeMemoryForAddr('libmydvp.so', 0x4FD1A, '\x00\x20\xc0\x46')  # bypass ooooo clock     
    writeMemoryForAddr('libmydvp.so', 0x34BD6, '\x00\x20\xc0\x46')  # bypass ooooo clock 2        
             
    addBptForAddr('libmydvp.so', 0x242E0, 'ooooooooo')
    addBptForAddr('libmydvp.so', 0x14A9C, 'native')
    KillNop()


         使用方法:

        1) 在程序载入时,执行一次脚本,会在 linker调用so的初始化函数位置设置断点,这个是米8的,不同机型修改C_Linker_InitAddr=0x1892E这个值即可

        2)当断在初始化位置时,再次执行脚本,会bypss所有反调试,同时bypass掉部分垃圾指令,使得F5可以成功。 

        3)   然后就虚拟机入口设置断点。

        2、关于虚拟机的分析

         分析dex的vmp虚拟机核心是找到不同opcode的处理分支,这个分支在data段off_E0B20位置具体如下:

.data.rel.ro:000E0B20                                                                                         ; sub_242E0+4552A↑w
.data.rel.ro:000E0B21 37                                                              DCB 0x37 ; 7
.data.rel.ro:000E0B22 03                                                              DCB    3
.data.rel.ro:000E0B23 00                                                              DCB    0
.data.rel.ro:000E0B24 1E 3F 03 00                                                     DCD loc_33F1E
.data.rel.ro:000E0B28 F0 41 03 00                                                     DCD loc_341F0
.data.rel.ro:000E0B2C FE 75 03 00                                                     DCD loc_375FE
.data.rel.ro:000E0B30 4A 3D 03 00                                                     DCD loc_33D4A
.data.rel.ro:000E0B34 F2 A8 03 00                                                     DCD loc_3A8F2
.data.rel.ro:000E0B38 E2 A9 03 00                                                     DCD loc_3A9E2
.data.rel.ro:000E0B3C BE D5 02 00                                                     DCD loc_2D5BE
.data.rel.ro:000E0B40 54 AA 03 00                                                     DCD loc_3AA54
.data.rel.ro:000E0B44 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B48 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B4C 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B50 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B54 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B58 2C 62 02 00                                                     DCD loc_2622C
.data.rel.ro:000E0B5C 78 46 03 00                                                     DCD loc_34678
.data.rel.ro:000E0B60 48 AE 03 00                                                     DCD loc_3AE48
.data.rel.ro:000E0B64 76 AE 03 00                                                     DCD loc_3AE76
.data.rel.ro:000E0B68 48 96 03 00                                                     DCD loc_39648
.data.rel.ro:000E0B6C AE AE 03 00                                                     DCD loc_3AEAE

        找到这个分支就好办了,直接在每个分支上设置断点,分析各个opcode的具体功能就可以恢复了

addBptForAddr('libmydvp.so', 0x3377C, '0x00	iput-wide vx,vy, field_id')
addBptForAddr('libmydvp.so', 0x33F1E, 'ins 0x01')
addBptForAddr('libmydvp.so', 0x341F0, 'ins 0x02')
addBptForAddr('libmydvp.so', 0x375FE, 'ins 0x03	if-nez vx,target')
addBptForAddr('libmydvp.so', 0x33D4A, '0x04	aget-byte vx,vy,vz')
addBptForAddr('libmydvp.so', 0x3A8F2, 'ins 0x05')
addBptForAddr('libmydvp.so', 0x3A9E2, 'ins 0x06')
addBptForAddr('libmydvp.so', 0x2D5BE, '0x07	iget-wide vx,vy,field_id')
addBptForAddr('libmydvp.so', 0x3AA54, '0x08	iget vx, vy, field_id')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x09')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x0a')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x0b')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x0c')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x0d')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0x0e')
addBptForAddr('libmydvp.so', 0x34678, '0x0f	?????')
addBptForAddr('libmydvp.so', 0x3AE48, 'ins 0x10')
addBptForAddr('libmydvp.so', 0x3AE76, 'ins 0x11')
addBptForAddr('libmydvp.so', 0x39648, 'ins 0x12')
addBptForAddr('libmydvp.so', 0x3AEAE, '0x13	if-ne vx,vy,target')
addBptForAddr('libmydvp.so', 0x3AEDC, 'ins 0x14')
addBptForAddr('libmydvp.so', 0x26302, 'ins 0x15')
addBptForAddr('libmydvp.so', 0x3AF34, 'ins 0x16')
addBptForAddr('libmydvp.so', 0x3AF7E, '0x17	if-eq vx,vy,target	')
addBptForAddr('libmydvp.so', 0x3AFBE, 'ins 0x18')
addBptForAddr('libmydvp.so', 0x3AFFE, 'ins 0x19')
addBptForAddr('libmydvp.so', 0x2E49A, 'ins 0x1a')
addBptForAddr('libmydvp.so', 0x3B048, 'ins 0x1b')
addBptForAddr('libmydvp.so', 0x3B0FC, 'ins 0x1c')
addBptForAddr('libmydvp.so', 0x39576, 'ins 0x1d')
addBptForAddr('libmydvp.so', 0x3147E, 'ins 0x1e')
addBptForAddr('libmydvp.so', 0x32FE6, 'ins 0x1f')
addBptForAddr('libmydvp.so', 0x3B19C, 'ins 0x20')
addBptForAddr('libmydvp.so', 0x2D26E, 'ins 0x21')
addBptForAddr('libmydvp.so', 0x3B1F4, 'ins 0x22')
addBptForAddr('libmydvp.so', 0x3B27A, '0x23	goto/16 target')
addBptForAddr('libmydvp.so', 0x37F20, '0x24	goto target	')
addBptForAddr('libmydvp.so', 0x3E14E, 'ins 0x25')
addBptForAddr('libmydvp.so', 0x3B2E0, '0x26	get-quick vx,vy,offset')
addBptForAddr('libmydvp.so', 0x37870, 'ins 0x27')
addBptForAddr('libmydvp.so', 0x3E946, 'ins 0x28')
addBptForAddr('libmydvp.so', 0x33FA2, '0x29	new-array vx,vy,type_id')
addBptForAddr('libmydvp.so', 0x3B3FC, 'ins 0x2a')
addBptForAddr('libmydvp.so', 0x3B434, '0x2B	array-length vx,vy')
addBptForAddr('libmydvp.so', 0x2524A, 'ins 0x2c')
addBptForAddr('libmydvp.so', 0x2F532, 'ins 0x2d')
addBptForAddr('libmydvp.so', 0x3E494, 'ins 0x2e')
addBptForAddr('libmydvp.so', 0x3B48E, 'ins 0x2f')
addBptForAddr('libmydvp.so', 0x3B4EE, 'ins 0x30')
addBptForAddr('libmydvp.so', 0x3948C, 'ins 0x31')
addBptForAddr('libmydvp.so', 0x3B546, 'ins 0x32')
addBptForAddr('libmydvp.so', 0x2B056, 'ins 0x33')
addBptForAddr('libmydvp.so', 0x3E32C, '0x34	const-wide vx, lit64')
addBptForAddr('libmydvp.so', 0x3B5B4, 'ins 0x35')
addBptForAddr('libmydvp.so', 0x3B620, '0x36	const/16 vx,lit16')
addBptForAddr('libmydvp.so', 0x27908, 'ins 0x37')
addBptForAddr('libmydvp.so', 0x30C46, 'ins 0x38')
addBptForAddr('libmydvp.so', 0x3B6A4, '0x39	const/16 vx,lit16')
addBptForAddr('libmydvp.so', 0x3B720, '0x3A 	const/4 vx,lit4')
addBptForAddr('libmydvp.so', 0x3B7E6, 'ins 0x3b')
addBptForAddr('libmydvp.so', 0x3B836, 'ins 0x3c')
addBptForAddr('libmydvp.so', 0x3B896, 'ins 0x3d')
addBptForAddr('libmydvp.so', 0x3B906, 'ins 0x3e')
addBptForAddr('libmydvp.so', 0x3E7CA, 'ins 0x3f')
addBptForAddr('libmydvp.so', 0x275F0, 'ins 0x40')
addBptForAddr('libmydvp.so', 0x3B96C, 'ins 0x41')
addBptForAddr('libmydvp.so', 0x3B9EC, 'ins 0x42')
addBptForAddr('libmydvp.so', 0x27E20, 'ins 0x43')
addBptForAddr('libmydvp.so', 0x37D52, '0x44 	move/from16 vAA, vBBBB')
addBptForAddr('libmydvp.so', 0x39F3A, 'ins 0x45')
addBptForAddr('libmydvp.so', 0x3E4F4, 'ins 0x46')
addBptForAddr('libmydvp.so', 0x3BA96, 'ins 0x47')
addBptForAddr('libmydvp.so', 0x3E1DC, '0x48 	move vx,vy')
addBptForAddr('libmydvp.so', 0x3BB1A, 'ins 0x49')
addBptForAddr('libmydvp.so', 0x3BB9A, '0x44 	move/from16 vAA, vBBBB')
addBptForAddr('libmydvp.so', 0x3BC26, '0x4B	move vx,vy int to int')
addBptForAddr('libmydvp.so', 0x3BC8C, 'ins 0x4c')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x4d')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x4e')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x4f')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x50')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x51')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x52')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x53')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x54')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x55')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x56')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x57')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x58')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x59')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5a')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5b')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5c')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5d')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5e')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x5f')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x60')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x61')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x62')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x63')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x64')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x65')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x66')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x67')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x68')
addBptForAddr('libmydvp.so', 0x2515C, 'ins 0x69')
addBptForAddr('libmydvp.so', 0x31746, 'ins 0x6a')
addBptForAddr('libmydvp.so', 0x3C180, 'ins 0x6b')
addBptForAddr('libmydvp.so', 0x3C1DA, 'ins 0x6c')
addBptForAddr('libmydvp.so', 0x3C246, 'ins 0x6d')
addBptForAddr('libmydvp.so', 0x3C2AE, 'ins 0x6e')
addBptForAddr('libmydvp.so', 0x3C354, 'ins 0x6f')
addBptForAddr('libmydvp.so', 0x3E9B6, '0x70	div-long vx, vy, vz	')
addBptForAddr('libmydvp.so', 0x2D138, 'ins 0x71')
addBptForAddr('libmydvp.so', 0x2D42C, '0x72	mul-int vx, vy, vz')
addBptForAddr('libmydvp.so', 0x3C3B8, 'ins 0x73')
addBptForAddr('libmydvp.so', 0x3EA38, '0x74	add-int/lit8 vx,vy,lit8	')
addBptForAddr('libmydvp.so', 0x3E3EC, 'ins 0x75')
addBptForAddr('libmydvp.so', 0x3C42C, 'ins 0x76')
addBptForAddr('libmydvp.so', 0x397AC, '0x77	and-int/lit16 vx,vy,lit16')
addBptForAddr('libmydvp.so', 0x3054E, 'ins 0x78')
addBptForAddr('libmydvp.so', 0x3C4F6, 'ins 0x79')
addBptForAddr('libmydvp.so', 0x3A4EE, 'ins 0x7a')
addBptForAddr('libmydvp.so', 0x2A9AE, 'ins 0x7b')
addBptForAddr('libmydvp.so', 0x3C550, 'ins 0x7c')
addBptForAddr('libmydvp.so', 0x3C550, 'ins 0x7d')
addBptForAddr('libmydvp.so', 0x3C66C, 'ins 0x7e')
addBptForAddr('libmydvp.so', 0x3C71C, 'ins 0x7f')
addBptForAddr('libmydvp.so', 0x3C7C2, 'ins 0x80')
addBptForAddr('libmydvp.so', 0x3C880, 'ins 0x81')
addBptForAddr('libmydvp.so', 0x313A2, 'ins 0x82')
addBptForAddr('libmydvp.so', 0x3C8DA, 'ins 0x83')
addBptForAddr('libmydvp.so', 0x34EEE, 'ins 0x84')
addBptForAddr('libmydvp.so', 0x3C97C, 'ins 0x85')
addBptForAddr('libmydvp.so', 0x3E72C, 'ins 0x86')
addBptForAddr('libmydvp.so', 0x3CA24, 'ins 0x87')
addBptForAddr('libmydvp.so', 0x3CAE0, '0x88	shr-int vx, vy, vz')
addBptForAddr('libmydvp.so', 0x29B88, '0x89	shl-long/2addr vx, vy	')
addBptForAddr('libmydvp.so', 0x3E838, 'ins 0x8a')
addBptForAddr('libmydvp.so', 0x35932, 'ins 0x8b')
addBptForAddr('libmydvp.so', 0x3CBA8, 'ins 0x8c')
addBptForAddr('libmydvp.so', 0x3E57A, 'ins 0x8d')
addBptForAddr('libmydvp.so', 0x373C6, 'ins 0x8e')
addBptForAddr('libmydvp.so', 0x3CC84, 'ins 0x8f')
addBptForAddr('libmydvp.so', 0x3E64E, 'ins 0x90')
addBptForAddr('libmydvp.so', 0x3E388, 'ins 0x91')
addBptForAddr('libmydvp.so', 0x2D048, 'ins 0x92')
addBptForAddr('libmydvp.so', 0x3CD3A, 'ins 0x93')
addBptForAddr('libmydvp.so', 0x32D22, '0x94	shl-int/2addr vx, vy')
addBptForAddr('libmydvp.so', 0x3CDE4, 'ins 0x95')
addBptForAddr('libmydvp.so', 0x30D26, 'ins 0x96')
addBptForAddr('libmydvp.so', 0x3CE8A, 'ins 0x97')
addBptForAddr('libmydvp.so', 0x3CF32, '0x98	div-int vx,vy,vz')
addBptForAddr('libmydvp.so', 0x307C4, 'ins 0x99')
addBptForAddr('libmydvp.so', 0x3EADE, 'ins 0x9a')
addBptForAddr('libmydvp.so', 0x2EF5A, 'ins 0x9b')
addBptForAddr('libmydvp.so', 0x3CFA8, '0x9c	add-int/2addr vx,vy')
addBptForAddr('libmydvp.so', 0x3CFA8, '0x9c	add-int/2addr vx,vy')
addBptForAddr('libmydvp.so', 0x3D002, 'ins 0x9e')
addBptForAddr('libmydvp.so', 0x3D0CC, 'ins 0x9f')
addBptForAddr('libmydvp.so', 0x327A4, 'ins 0xa0')
addBptForAddr('libmydvp.so', 0x260EE, 'ins 0xa1')
addBptForAddr('libmydvp.so', 0x3D18C, 'ins 0xa2')
addBptForAddr('libmydvp.so', 0x3D246, 'ins 0xa3')
addBptForAddr('libmydvp.so', 0x3D304, 'ins 0xa4')
addBptForAddr('libmydvp.so', 0x3D3C6, 'ins 0xa5')
addBptForAddr('libmydvp.so', 0x37BCE, 'ins 0xa6')
addBptForAddr('libmydvp.so', 0x387F8, 'ins 0xa7')
addBptForAddr('libmydvp.so', 0x3D494, '0xA8	shr-long vx,vy,vz')
addBptForAddr('libmydvp.so', 0x26E2E, '0xA9	shl-long vx, vy, vz	')
addBptForAddr('libmydvp.so', 0x39B4E, '0xAA	xor-long vx, vy, vz	')
addBptForAddr('libmydvp.so', 0x28F88, '0xAB	or-long/2addr vx, vy')
addBptForAddr('libmydvp.so', 0x3D574, '0xAC	and-long vx, vy, vz')
addBptForAddr('libmydvp.so', 0x344F6, 'ins 0xad')
addBptForAddr('libmydvp.so', 0x3D67C, 'ins 0xae')
addBptForAddr('libmydvp.so', 0x3D70C, 'ins 0xaf')
addBptForAddr('libmydvp.so', 0x3E27C, 'ins 0xb0')
addBptForAddr('libmydvp.so', 0x3D764, 'ins 0xb1')
addBptForAddr('libmydvp.so', 0x3E5F4, 'ins 0xb2')
addBptForAddr('libmydvp.so', 0x3E2D4, 'ins 0xb3')
addBptForAddr('libmydvp.so', 0x3308E, 'ins 0xb4')
addBptForAddr('libmydvp.so', 0x3D838, 'ins 0xb5')
addBptForAddr('libmydvp.so', 0x3581A, 'ins 0xb6')
addBptForAddr('libmydvp.so', 0x38AD6, 'ins 0xb7')
addBptForAddr('libmydvp.so', 0x342FA, 'ins 0xb8')
addBptForAddr('libmydvp.so', 0x361C2, 'ins 0xb9')
addBptForAddr('libmydvp.so', 0x3D8E8, 'ins 0xba')
addBptForAddr('libmydvp.so', 0x2AF9C, 'ins 0xbb')
addBptForAddr('libmydvp.so', 0x3D97E, 'ins 0xbc')
addBptForAddr('libmydvp.so', 0x3982A, 'ins 0xbd')
addBptForAddr('libmydvp.so', 0x312CC, 'ins 0xbe')
addBptForAddr('libmydvp.so', 0x3D9EC, '0xBF	move vx,vy byte to int')
addBptForAddr('libmydvp.so', 0x3DA44, 'ins 0xc0')
addBptForAddr('libmydvp.so', 0x33E50, 'ins 0xc1')
addBptForAddr('libmydvp.so', 0x2D97C, 'ins 0xc2')
addBptForAddr('libmydvp.so', 0x3DAD0, 'ins 0xc3')
addBptForAddr('libmydvp.so', 0x29652, 'ins 0xc4')
addBptForAddr('libmydvp.so', 0x368E4, 'ins 0xc5')
addBptForAddr('libmydvp.so', 0x3DB68, 'ins 0xc6')
addBptForAddr('libmydvp.so', 0x3DBFE, 'ins 0xc7')
addBptForAddr('libmydvp.so', 0x3DC5A, '0xC8	move-wide  long to int')
addBptForAddr('libmydvp.so', 0x3DCF4, 'ins 0xc9')
addBptForAddr('libmydvp.so', 0x36CBE, 'ins 0xca')
addBptForAddr('libmydvp.so', 0x3DD58, '0xCB	move-wide')
addBptForAddr('libmydvp.so', 0x3DDB0, 'ins 0xcc')
addBptForAddr('libmydvp.so', 0x3DE52, 'ins 0xcd')
addBptForAddr('libmydvp.so', 0x3DEE6, 'ins 0xce')
addBptForAddr('libmydvp.so', 0x2F446, 'ins 0xcf')
addBptForAddr('libmydvp.so', 0x399CA, 'ins 0xd0')
addBptForAddr('libmydvp.so', 0x3DF40, 'ins 0xd1')
addBptForAddr('libmydvp.so', 0x3DF40, 'ins 0xd2')
addBptForAddr('libmydvp.so', 0x3DF40, 'ins 0xd3')
addBptForAddr('libmydvp.so', 0x3E06E, 'ins 0xd4')
addBptForAddr('libmydvp.so', 0x39E2E, 'ins 0xd5')
addBptForAddr('libmydvp.so', 0x3E0E0, 'ins 0xd6')
addBptForAddr('libmydvp.so', 0x324A6, 'ins 0xd7')
addBptForAddr('libmydvp.so', 0x26546, 'ins 0xd8')
addBptForAddr('libmydvp.so', 0x26546, 'ins 0xd9')
addBptForAddr('libmydvp.so', 0x3E008, 'ins 0xda')
addBptForAddr('libmydvp.so', 0x3DF98, 'ins 0xdb')
addBptForAddr('libmydvp.so', 0x3394A, 'ins 0xdc')
addBptForAddr('libmydvp.so', 0x3C5F6, 'ins 0xdd')
addBptForAddr('libmydvp.so', 0x2622C, 'ins 0xde')
addBptForAddr('libmydvp.so', 0x391E6, 'ins 0xdf')
addBptForAddr('libmydvp.so', 0x39BC8, 'ins 0xe0')
addBptForAddr('libmydvp.so', 0x3C128, 'ins 0xe1')
addBptForAddr('libmydvp.so', 0x3C0E6, 'ins 0xe2')
addBptForAddr('libmydvp.so', 0x3C076, 'ins 0xe3')
addBptForAddr('libmydvp.so', 0x2A662, 'ins 0xe4')
addBptForAddr('libmydvp.so', 0x3BFCE, 'ins 0xe5')
addBptForAddr('libmydvp.so', 0x2E7B0, 'ins 0xe6')
addBptForAddr('libmydvp.so', 0x2EBCA, 'ins 0xe7')
addBptForAddr('libmydvp.so', 0x3BF68, 'ins 0xe8')
addBptForAddr('libmydvp.so', 0x3BF0E, 'ins 0xe9')
addBptForAddr('libmydvp.so', 0x37206, 'ins 0xea')
addBptForAddr('libmydvp.so', 0x27770, 'ins 0xeb')
addBptForAddr('libmydvp.so', 0x3BEB4, 'ins 0xec')
addBptForAddr('libmydvp.so', 0x2CB42, 'ins 0xed')
addBptForAddr('libmydvp.so', 0x3BE50, 'ins 0xee')
addBptForAddr('libmydvp.so', 0x3A190, 'ins 0xef')
addBptForAddr('libmydvp.so', 0x25B6E, 'ins 0xf0')
addBptForAddr('libmydvp.so', 0x36B28, 'ins 0xf1')
addBptForAddr('libmydvp.so', 0x2A78E, 'ins 0xf2')
addBptForAddr('libmydvp.so', 0x377D6, 'ins 0xf3')
addBptForAddr('libmydvp.so', 0x3BDBC, 'ins 0xf4')
addBptForAddr('libmydvp.so', 0x27502, 'ins 0xf5')
addBptForAddr('libmydvp.so', 0x2EE7E, 'ins 0xf6')
addBptForAddr('libmydvp.so', 0x3A2FC, 'ins 0xf7')
addBptForAddr('libmydvp.so', 0x38610, 'ins 0xf8')
addBptForAddr('libmydvp.so', 0x3BD3C, 'ins 0xf9')
addBptForAddr('libmydvp.so', 0x3BCC6, 'ins 0xfa')
addBptForAddr('libmydvp.so', 0x3AD66, 'ins 0xfb')
addBptForAddr('libmydvp.so', 0x37DF6, 'ins 0xfc')
addBptForAddr('libmydvp.so', 0x3ACEC, '0xFD	aput-char vx,vy,vz')
addBptForAddr('libmydvp.so', 0x3AC14, 'ins 0xfe')
addBptForAddr('libmydvp.so', 0x3AB38, 'ins 0xff')

         真个变形的smali大概有40多个指令,具体如下:(可能有部分不准确,不过不影响分析):

指令		含义									
0x00	iput-wide vx,vy, field_id		
0x03	if-nez vx,target						
0x04	aget-byte vx,vy,vz					
0x07	iget-wide vx,vy,field_id		
0x08	iget vx, vy, field_id				
0x0f	?????
0x13	if-ne vx,vy,target					
0x17	if-eq vx,vy,target					
0x23	goto/16 target							
0x24	goto target									
0x26	get-quick vx,vy,offset			
0x29	new-array vx,vy,type_id			
0x2B	array-length vx,vy					
0x34	const-wide vx, lit64				
0x36	const-wide/16 vx, lit16			
0x39	const/16 vx,lit16						
0x3A 	const/4 vx,lit4							
0x48 	move vx,vy									
0x44 	move/from16 vAA, vBBBB      
0x4A 	move/from16 vAA, vBBBB      
0x4B	move vx,vy									
0x70	div-long vx, vy, vz					
0x72	mul-int vx, vy, vz					
0x74	add-int/lit8 vx,vy,lit8			
0x77	and-int/lit16 vx,vy,lit16		
0x88	shr-int vx, vy, vz					
0x89	shl-long/2addr vx, vy				
0x94	shl-int/2addr vx, vy				
0x98	div-int vx,vy,vz						
0x9c	add-int/2addr vx,vy					
0x9d	add-int/2addr vx,vy					
0xA8	shr-long vx,vy,vz						
0xA9	shl-long vx, vy, vz					
0xAA	xor-long vx, vy, vz					
0xAB	or-long vx, vy, vz					
0xAC	and-long vx, vy, vz					
0xBF	move vx,vy									
0xC8	move-wide 									
0xCB	move-wide										
0xFD	aput-char vx,vy,vz					

           分析到这里一种是直接将还原smali,但是其实能够还原也表示看懂了算法了,直接还原算法就可以了。

六、虚拟机算法还原

      1、生成一个long g_constTable[0x20]

       程序前部分代码是生成一个long型的数组。

       1)是生成一个16个byte 数组:21 26 23 28 25 2A 27 2C  29 2E 2B 30 2D 32 2F 34 

for(int i = 0; i < 8; i++)
{
	p3[i]   = 0x21 + i*2;
	p3[i+1] = 0x25+i*2;
}		

2)与 C6 BA B2 A3 50 33 AA 16  97 91 4D 67 DC 32 70 B2 进行亦或‘

3)然后再次与一个 long g_constTable[0x40]  与 char g_constTable1[256]进行异或及索引后生成如下常量table

8F1A4404 FFF2E7BB
1E937DC8 00708BD4
7F7F73C3 0074F871
B328B155 FFE90BBF
1009F1C3 FFDCFEB9
FFE4254A 00330BFC
F2BA3ED7 0021DED8
FE2D5F02 FF9A6DF5
C917FCF0 FFFF7EED
1A19EDF0 00329985
33E7164F 0026EF98
8B35A805 FFB28997
BB562A98 FF849AFA
C92B0A64 00693E49
BCC4EAC9 0004C9FE
C451397E FF812002
223F99D9 FFA6F1DE
713992DA 0007FC82
0B4E883C 005F48FA
6106622D FFC51D0A
E0B88ACA FFE720EF
03996681 0072D9F4
0A7CBE18 003B4522
81836FEA FF8C0AF6
062A5D20 FFE3A7D6
14BAC6DF 004C7B9E
FF98252C 005BF4AD
CE471681 FF9405EF
BD0958EC FFE4EFA2
03A69C77 00317701
1C0A19C3 004F960B
D6334DF1 FFFDF083

然后开始对输入的sn进行加密,算法如下:

long long constkey[0x20] =
{
	0xFFF2E7BB8F1A4404,
	0x00708BD41E937DC8,
	0x0074F8717F7F73C3,
	0xFFE90BBFB328B155,
	0xFFDCFEB91009F1C3,
	0x00330BFCFFE4254A,
	0x0021DED8F2BA3ED7,
	0xFF9A6DF5FE2D5F02,
	0xFFFF7EEDC917FCF0,
	0x003299851A19EDF0,
	0x0026EF9833E7164F,
	0xFFB289978B35A805,
	0xFF849AFABB562A98,
	0x00693E49C92B0A64,
	0x0004C9FEBCC4EAC9,
	0xFF812002C451397E,
	0xFFA6F1DE223F99D9,
	0x0007FC82713992DA,
	0x005F48FA0B4E883C,
	0xFFC51D0A6106622D,
	0xFFE720EFE0B88ACA,
	0x0072D9F403996681,
	0x003B45220A7CBE18,
	0xFF8C0AF681836FEA,
	0xFFE3A7D6062A5D20,
	0x004C7B9E14BAC6DF,
	0x005BF4ADFF98252C,
	0xFF9405EFCE471681,
	0xFFE4EFA2BD0958EC,
	0x0031770103A69C77,
	0x004F960B1C0A19C3,
	0xFFFDF083D6334DF1,
};

unsigned char constkey0[256] = 
{
	0x37, 0x23, 0xEC, 0x34, 0xA2, 0x4B, 0x65, 0xA1, 0x35, 0x9C, 0x2B, 0x46, 0x8A, 0xD2, 0x54, 0xF9,
	0x43, 0xCE, 0x4A, 0x47, 0xCD, 0x4F, 0x5B, 0x78, 0xC9, 0x30, 0x69, 0xFC, 0x56, 0x95, 0xDA, 0x50,
	0x74, 0x1C, 0xC4, 0xE7, 0xA3, 0x1E, 0xAE, 0xF4, 0x09, 0xDF, 0x55, 0x83, 0x6B, 0xAC, 0x5F, 0xD8,
	0xFF, 0x7D, 0x6A, 0x57, 0xC3, 0x0E, 0xE9, 0xFE, 0x28, 0x03, 0x71, 0x42, 0x61, 0x68, 0xD1, 0x52,
	0x01, 0xED, 0xE1, 0xA8, 0x04, 0xB3, 0x82, 0x49, 0x29, 0xB2, 0xB5, 0xC1, 0x88, 0x84, 0x86, 0x1B,
	0x87, 0xB6, 0xBE, 0x58, 0x0F, 0xA0, 0x20, 0xB8, 0x3E, 0x2C, 0x63, 0xD0, 0x99, 0x9D, 0xF0, 0xAB,
	0x31, 0xBD, 0x91, 0x18, 0xE0, 0x5E, 0xFD, 0x26, 0x19, 0x15, 0x67, 0xB4, 0x48, 0x92, 0x79, 0xB7,
	0xF1, 0x44, 0x93, 0xC0, 0xD9, 0x77, 0x41, 0xD7, 0xF8, 0xC7, 0x60, 0xFB, 0xEB, 0x7B, 0xAF, 0xB9,
	0xA5, 0x80, 0xCC, 0x24, 0x7E, 0x9B, 0xDD, 0x1D, 0x0D, 0x97, 0x4C, 0xBF, 0xEE, 0x13, 0x22, 0x45,
	0x8F, 0xF3, 0x9F, 0xA7, 0x11, 0x64, 0xC6, 0x3A, 0x59, 0x89, 0xAD, 0x6F, 0xF5, 0x8B, 0xF2, 0x75,
	0xFA, 0x06, 0x5C, 0x70, 0xEF, 0x2A, 0xEA, 0xCF, 0x2F, 0x08, 0x6D, 0x00, 0x73, 0xE3, 0xBC, 0x2E,
	0x07, 0xE4, 0xA9, 0x33, 0xC2, 0x9E, 0x36, 0xBA, 0x85, 0x6C, 0x2D, 0x3C, 0x5A, 0x05, 0x8D, 0x25,
	0x7A, 0x0C, 0x16, 0x17, 0x7C, 0x02, 0xD3, 0x5D, 0x96, 0xE2, 0x3B, 0x90, 0x53, 0x21, 0xDE, 0x76,
	0x4E, 0xF6, 0xDC, 0xCB, 0x51, 0x3D, 0x0B, 0x94, 0x8E, 0xE6, 0x72, 0xC5, 0x8C, 0xA6, 0x6E, 0x7F,
	0xD5, 0x9A, 0x1A, 0xA4, 0x39, 0x27, 0x1F, 0xB1, 0xE5, 0x98, 0xC8, 0x4D, 0x10, 0xDB, 0x40, 0x66,
	0x3F, 0xCA, 0xF7, 0x12, 0xAA, 0xD6, 0xE8, 0x62, 0xBB, 0x38, 0x32, 0x14, 0x0A, 0xD4, 0xB0, 0x81,
};


unsigned char*  encode(unsigned char* key, int keylen, int* outLen)
{

	//补齐
	unsigned char left = 0x10 - keylen % 0x10;
	int key1Len = keylen + left;
	unsigned char* key1 = (unsigned char*)malloc(key1Len);
	unsigned char* outKey = (unsigned char*)malloc(key1Len);
	memset(outKey, 0x00, key1Len);
	memset(key1, 0x00, key1Len);
	long long inputKey[0x24];
	memset((char*)&inputKey, 0x00, sizeof(inputKey));

	for (int i = 0; i < key1Len; i++)
	{	
		if (i < keylen)
			key1[i] = key[i];
		else
			key1[i] = left;
	}

	

	int maxCnt = key1Len / 0x10;

	for (int m = 0; m < maxCnt; m++)
	{
		memset((char*)&inputKey, 0x00, sizeof(inputKey));
		for (int j = 0; j < 4; j++)
		{
			inputKey[j]  = (long long)key1[m * 0x10 + j * 4 + 0] << 24;
			inputKey[j] |= (long long)key1[m * 0x10 + j * 4 + 1] << 16;
			inputKey[j] |= (long long)key1[m * 0x10 + j * 4 + 2] << 8;
			inputKey[j] |= (long long)key1[m * 0x10 + j * 4 + 3] << 0;
		}
		for (int i = 0; i < 0x20; i++)
		{
			if (i == 0x1f)
			{
				int xxx = 0;
			}
			long long r15 = inputKey[i + 1] ^ inputKey[i + 2] ^ inputKey[i + 3];

			long long r19 = constkey[i] ^ r15;

			unsigned char tempkey4[4];
			tempkey4[0] = (r19 >> 24) & 0xff;
			tempkey4[1] = (r19 >> 16) & 0xff;
			tempkey4[2] = (r19 >> 8) & 0xff;
			tempkey4[3] = (r19)& 0xff;

			unsigned char tempkey5[4];
			tempkey5[0] = constkey0[tempkey4[0]];
			tempkey5[1] = constkey0[tempkey4[1]];
			tempkey5[2] = constkey0[tempkey4[2]];
			tempkey5[3] = constkey0[tempkey4[3]];

			//将 tempkey5 按字节置换
			long long r4 = ((long long)tempkey5[0] << 24);
			r4 |= ((long long)tempkey5[1] << 16);
			r4 |= ((long long)tempkey5[2] << 8);
			r4 |= (long long)tempkey5[3];


			long long r13 = (r4 << 2) | (r4 >> 30);
			long long r16 = r13 ^ r4;

			r13 = (r4 << 0x0a) | (r4 >> 0x16);
			long long r17 = r13 ^ r16;

			r13 = (r4 << 0x12) | (r4 >> 0x0e);
			long long r18 = r13 ^ r17;

			r13 = (r4 << 0x18) | (r4 >> 0x8);
			r18 = r13 ^ r18;

			inputKey[i + 4] = inputKey[i] ^ r18;
		}

		for (int j = 0; j < 4; j++)
		{
			outKey[m * 0x10 + j * 4 + 0] = (inputKey[0x23 - j] >> 24) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 1] = (inputKey[0x23 - j] >> 16) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 2] = (inputKey[0x23 - j] >> 8) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 3] = (inputKey[0x23 - j]) & 0x0ff;
		}

		int xx = 1;

	}

	*outLen = key1Len;
	return outKey;

}

unsigned char*  decode(unsigned char* key, int keylen, int* outLen)
{

	//补齐
	unsigned char left = 0;
	if (keylen % 0x10)
		left = 0x10 - keylen % 0x10;
	int key1Len = keylen + left;
	unsigned char* key1 = (unsigned char*)malloc(key1Len+100);
	unsigned char* outKey = (unsigned char*)malloc(key1Len + 100);
	memset(outKey, 0x00, key1Len + 100);
	memset(key1, 0x00, key1Len + 100);
	long long inputKey[0x24];
	memset((char*)&inputKey, 0x00, sizeof(inputKey));

	for (int i = 0; i < key1Len; i++)
	{
		if (i < keylen)
			key1[i] = key[i];
		else
			key1[i] = left;
	}

	int maxCnt = key1Len / 0x10;

	for (int m = 0; m < maxCnt; m++)
	{
		memset((char*)&inputKey, 0x00, sizeof(inputKey));
		for (int j = 0; j < 4; j++)
		{
			inputKey[0x23 - j] = (long long)key1[m * 0x10 + j * 4 + 0] << 24;
			inputKey[0x23 - j] |= (long long)key1[m * 0x10 + j * 4 + 1] << 16;
			inputKey[0x23 - j] |= (long long)key1[m * 0x10 + j * 4 + 2] << 8;
			inputKey[0x23 - j] |= (long long)key1[m * 0x10 + j * 4 + 3] << 0;
		}
		for (int i = 0x1F; i >=0; i--)
		{
			long long r15 = inputKey[i+1] ^ inputKey[i+2] ^ inputKey[i+ 3];

			long long r19 = constkey[i] ^ r15;

			unsigned char tempkey4[4];
			tempkey4[0] = (r19 >> 24) & 0xff;
			tempkey4[1] = (r19 >> 16) & 0xff;
			tempkey4[2] = (r19 >> 8) & 0xff;
			tempkey4[3] = (r19)& 0xff;

			unsigned char tempkey5[4];
			tempkey5[0] = constkey0[tempkey4[0]];
			tempkey5[1] = constkey0[tempkey4[1]];
			tempkey5[2] = constkey0[tempkey4[2]];
			tempkey5[3] = constkey0[tempkey4[3]];

			//将 tempkey5 按字节置换
			long long r4 = ((long long)tempkey5[0] << 24);
			r4 |= ((long long)tempkey5[1] << 16);
			r4 |= ((long long)tempkey5[2] << 8);
			r4 |= (long long)tempkey5[3];


			long long r13 = (r4 << 2) | (r4 >> 30);
			long long r16 = r13 ^ r4;

			r13 = (r4 << 0x0a) | (r4 >> 0x16);
			long long r17 = r13 ^ r16;

			r13 = (r4 << 0x12) | (r4 >> 0x0e);
			long long r18 = r13 ^ r17;

			r13 = (r4 << 0x18) | (r4 >> 0x8);
			r18 = r13 ^ r18;

			inputKey[i] = inputKey[i+4] ^ r18;
		}

		for (int j = 0; j < 4; j++)
		{
			outKey[m * 0x10 + j * 4 + 0] = (inputKey[j] >> 24) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 1] = (inputKey[j] >> 16) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 2] = (inputKey[j] >> 8) & 0x0ff;
			outKey[m * 0x10 + j * 4 + 3] = (inputKey[j]) & 0x0ff;
		}

		int xx = 1;

	}

	*outLen = key1Len;
	outKey[key1Len] = 0;
	return outKey;

}

其中 encode为加密,decode为解密。


unsigned char realKey[96] = {
		0x82, 0x0e, 0x52, 0x33, 0x3d, 0xe3, 0xbc, 0xb4, 0x24, 0x67, 0xf0, 0xa2, 0x05, 0x64, 0xc1, 0x45,
		0xaf, 0x5e, 0xdb, 0xf2, 0xe9, 0x23, 0xdf, 0x33, 0xbe, 0x21, 0xf0, 0xaf, 0x15, 0x97, 0x10, 0xc9,
		0x2c, 0xbc, 0x43, 0xf7, 0x9f, 0x94, 0xec, 0x93, 0x0a, 0x7a, 0xe8, 0x60, 0x21, 0xaf, 0x5b, 0x3a,
		0xe2, 0x63, 0x36, 0x92, 0x99, 0xde, 0x54, 0x36, 0xb8, 0x5f, 0x29, 0x7b, 0xe0, 0x8a, 0x03, 0x2a,
		0x28, 0xdc, 0x35, 0x73, 0x91, 0x96, 0x1e, 0xcc, 0x26, 0x93, 0x1b, 0xfc, 0x97, 0xd6, 0x7a, 0x5e,
		0x74, 0xd8, 0x78, 0x1f, 0xb4, 0x10, 0x5b, 0x9a, 0xfb, 0xe6, 0x13, 0xa2, 0x04, 0x1d, 0xd8, 0xc3 };
	int outLen = 0;
	unsigned char* inSn = decode(realKey, 96, &outLen);

	

 执行上述代码得到如下数据:

ae 29 63 d8 2b cb 72 65 2b cb 72 65 ae e9 7d 44 
2b e9 1c 7e 2b e9 1c 7e ae e9 7d 44 ae 29 63 d8 
29 2f 36 72 2b 4e 03 2b 26 4d 03 2a 7e 63 1c 44 
63 63 a2 5e 4e d7 a2 5e 4e d7 1c 44 63 63 98 7e 
e8 26 98 63 63 e8 1c 46 cc 63 1c 1d db 36 74 74 

当java调用native函数是传入上述数据,将会提示sn正确。

七 base64解密

1、由于base64的输入是  OO0o0.O0000OOo = new char[]{'加', '载', '本', '件', '请', '卸', '要', '微', '软', '不', '可', '以', '来', '去', '安', 'a', '人', '7', '减', '好', 'l', '卓', '测', 'p', '试', 'p', '3', '7', '乘', '吗', 'b', '桐', 'c', 'e', '眼', 'q', '6', '4', '以', '为', '神', 'd', '无', 'f', '功', '圣', '名', '至', '己', '0', '何', '解', '忧', 'g', '唯', '有', '1', '杜', '2', '康', 'h', '}', '{'};中的一个。虽然能限制部分多解,但是由于base64本身每次加密的基准字符不同,而且基准字符还存在重合的现象。所有不可避免会出现多解。

1、对于第一次输入,base64解码后得到一个字符串“ 请本本不不载请微+422991479不卸请要 ”;

2、再次逆推,得到一个long型字符串“ 42219914789985746 ”

3、可得到一个解:m}y8hm8yecc002

[培训]《安卓高级研修班》彻底搞定函数抽取型壳!现在报名得源码和安卓8.1脱壳机!10月20日深圳专场不见不散!

最后于 14小时前 被oooAooo编辑 ,原因:


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