首页
社区
课程
招聘
[抛砖引玉]NAGA & PIOWIND 2014 APP应用攻防竞赛第二阶段题目解析
发表于: 2014-11-5 17:19 21929

[抛砖引玉]NAGA & PIOWIND 2014 APP应用攻防竞赛第二阶段题目解析

2014-11-5 17:19
21929
简单说一下个人的想法
第一题 (http://bbs.pediy.com/showthread.php?t=193755)
基本就是纯粹考察ARM的逆向能力,IDA F5 + 部分ARM汇编,很容易就可以将算法部分分析出来。最后得出的结论就是,其实就是base64加了三条'-'
值得一提的是,通过第一题,对加密部分代码有了比较深入的了解之后,对后面两道题解答有很大帮助

第二题(http://bbs.pediy.com/showthread.php?t=193824)
so加了壳,静态分析会发现dynamic字段是空。
当时偷懒,没细看,直接通过gDvm->loadedClasses找到 "Lcom/crackme/MainActivity"类的ClassObject,然后枚举Method获取了crackme函数的地址。
然后静态分析,发现就是倒序之后base64加密
补充一下"静态分析会发现dynamic字段是空"的问题,其实就是利用了文件和内存中使用的偏移不同的方法,如下图,文件中会读0x3C000,而内存中会读0x3D000,实际上(此SO)内存中布局是完全按照文件中的布局映射的,只要读取0x3D000处,就可以得到正常的dynamic了


第三题(http://bbs.pediy.com/showthread.php?t=193877)
貌似是添加了AntiDebug,由于是周末,家里啥环境都没有,也就没有细搞。按照第二题的思路,写了一个程序,进程注入,把crackme的函数地址读出来了,然后就没有然后了~~
希望大神来给分析一下AntiDebug怎么实现的,又是怎么绕过的。
好吧,我承认我懒得分析了,目测单步测试so加载部分代码应该能有所收获。
结论就是前面 n-2个字符两两交换,然后算base64

第四题(http://bbs.pediy.com/showthread.php?t=193953)
真正考逆向水平的题目。方法就是单步~读ARM指令~对照IDA F5~单步~读ARM指令~对照IDA F5~~~~多尝试几遍就行了。还好so里面保留了部分des的符号,让那些不熟悉des加密的娃(例如me)可以猜到是用了des加密。
不知道是不是我找的源码的问题。测试的时候,发现自己程序的加密结果和crackme的不同,经过反复反复反复反复地测试,发现是S_BOX和另外一个初始化的数组值有差异,测试的方法就是逐过程对照结果,看看哪一步得到的结果不一样,然后对照参数。最后找到不同点。
然后就很简单了。必须吐槽一下,这道题测试好麻烦,手工输入32个字符还算好的。
哦~忘了一点,秘钥生成,是使用了用户名的前8个字节,然后依次异或了0x100个字符 =.= ,话说,异或不是有交换结合律么,结果就是直接异或0x93就行了。

以上就是个人的一些方法,欢迎来讨论或者提供更好的解题思路啊

================== YD的分割线===============
我x~~竟然总顶了,受宠若惊啊,所以决定还是把整个解题流程写一下,方便各位查阅

持续更新中~~~~~~
===================低调的分割线=================

前言
这次比赛,前三道题目的算法都是base64,最后一题是des。
由于dex部分十分简单,就是调用libcrackme.so中的crackme函数,所以这里就不赘述了。

Crackme1
目的是分析libcrackme.so,不过这个so如果用IDA打开的话,会卡一段时间,然后报错,同时生成6G+的数据文件。这种情况明显是文件格式有猫腻 ,处理这种问题,推荐010Editor的模板。
用010Editor打开libcrackme.so,然后执行ELF模板,会提示解析出错。然后就能找到出错的内容了

可以看到,是一个程序节内容错误,本着简(懒)单(惰)的原则,直接将此节内容清零,保存文件后,IDA就可以正常打开文件了。此问题在后续的so里面都会出现,就不再赘述了。
之后就是找到Java_com_crackme_MainActivity_crackme函数,F5分析代码。
然后发现。。。。。。原来是加密的。
处理加密,考虑到运行时必定已经解密,所以运行crackme1,然后再吧so文件dump出来,过程不多说了,这里提一下用IDA下面python插件dump内存的方法:
import idaapi

base = 0x74E71000
size = 266252

data = idaapi.dbg_read_memory(base,size)
if data != None:
    f = open("e:\\crackme1.so","wb")
    f.write(data)
    f.close()


base根据实际情况修改。size可以看本地的so文件大小获取。当然也可以用idaapi的dbg_get_memory_info,不过这个函数用起来有点蛋疼,而且麻烦 。
不管怎么样,把内存中的so dump出来之后,就能看到正常的代码了
int __fastcall Java_com_crackme_MainActivity_crackme(int a1, int a2, int a3, int a4)
{
  v4 = a4;
  v5 = a1;
  v6 = (*(int (**)(void))(*(_DWORD *)a1 + 676))();
  v7 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v5 + 676))(v5, v4, 0);
  sub_536C(&dword_15220[1], v6, v7);
  sub_597C(&dword_15220[1]);
  return (*(int (__fastcall **)(int, int *))(*(_DWORD *)v5 + 668))(v5, &dword_15220[1]);
}


之前的v6和v7以及最后return时调用的函数,猜也能知道,是用来读写Java里面的String类的。所以关键内容是sub_536C和sub_597C
PS:如果看不懂的可以尝试一边单步调试,一遍看

int __fastcall sub_536C(int a1, const char *a2, const char *a3)
{
  v3 = a3;
  v4 = a1;
  v5 = a2;
  result = sub_5328();
  if ( v3 )
  {
    if ( v5 )
    {
      v7 = strlen(v5);
      v13 = v7;
      v8 = v7;
      v9 = strlen(v3);
      v10 = v8 + 1;
      v14 = v9;
      n = v9 + 1;
      *(_DWORD *)(v4 + 52) = operator new[](v10);
      result = operator new[](n);
      v11 = *(void **)(v4 + 52);
      *(_DWORD *)(v4 + 56) = result;
      if ( v11 )
      {
        if ( result )
        {
          memset(v11, 0, v10);
          memset(*(void **)(v4 + 56), 0, n);
          memcpy(*(void **)(v4 + 52), v5, v13);
          result = (int)memcpy(*(void **)(v4 + 56), v3, v14);
        }
      }
    }
  }
  return result;
}

sub_536C的内容很简单,就是为用户名和密码各申请一块内存。然后把指针放在一个申请的结构体里面

signed int __fastcall sub_597C(int a1)
{
   v1 = a1;
  sub_53E4();
  v2 = *(_DWORD *)(v1 + 56);
  if ( *(_BYTE *)(v2 + 3) != 45 || *(_BYTE *)(v2 + 7) != 45 || *(_BYTE *)(v2 + 11) != 45 )
  {
    *(_BYTE *)_cxa_allocate_exception(1) = 1;
    _cxa_throw();
  }
  sub_5430(v1);
  v3 = *(const char **)(v1 + 56);
  v4 = (unsigned __int8)byte_15120;
  if ( !byte_15120 )
  {
    do
      byte_15121[v4++] = -128;
    while ( v4 != 256 );
    v5 = 0;
    do
    {
      byte_15121[v5 + 65] = v5;
      ++v5;
    }
    while ( v5 != 26 );
    v6 = &byte_15182;
    do
    {
      *v6 = v5;
      v5 = (v5 + 1) & 0xFF;
      ++v6;
    }
    while ( v5 != 52 );
    v7 = &byte_15151;
    do
    {
      *v7 = v5;
      v5 = (v5 + 1) & 0xFF;
      ++v7;
    }
    while ( v5 != 62 );
    byte_1514C = 62;
    byte_15150 = 63;
    byte_1515E = 0;
    byte_15120 = 1;
  }
  if ( v3 )
  {
    v8 = strlen(v3);
    v9 = (const void *)operator new[](v8 + 1);
  }
  else
  {
    v9 = 0;
  }
  v10 = strlen(v3);
  v11 = 0;
  v12 = v9;
  v13 = 0;
  v14 = v3;
  while ( v11 < (signed int)(v10 - 3) )
  {
    v15 = 0;
    do
    {
      v16 = byte_15121[*(&v14[v11] + v15)];
      *(&v21 + v15) = v16;
      if ( v16 & 0x80 )
        *(&v21 + v15) = 0;
      ++v15;
    }
    while ( v15 != 4 );
    v13 += 3;
    v11 += 4;
    v17 = v22;
    *(_BYTE *)v12 = ((signed int)v22 >> 4) | 4 * v21;
    v18 = v23;
    *((_BYTE *)v12 + 1) = 16 * v17 | ((signed int)v23 >> 2);
    *((_BYTE *)v12 + 2) = (v18 << 6) | v24;
    v12 = (char *)v12 + 3;
  }
  v19 = (void *)operator new[](v13);
  memmove(v19, v9, v13);
  if ( v9 )
    operator delete[]((void *)v9);
  memcpy((void *)(v1 + 60), v19, v13);
  if ( v19 )
    operator delete[](v19);
  sub_548C(v1);
  return 1;
}


sub_597C的内容有点多,分段来分析
首先调用sub_53E4,sub_53E4内容如下:
signed int __fastcall sub_53E4(int a1)
{
  if ( !*(_DWORD *)(a1 + 52)
    || (v1 = *(const char **)(a1 + 56)) == 0
    || (v2 = strlen(*(const char **)(a1 + 52)) - 6, v3 = strlen(v1), v2 > 0xE)
    || v3 - 12 > 0x12 )
  {
    *(_BYTE *)_cxa_allocate_exception(1) = 1;
    _cxa_throw();
  }
  return 1;
}

可以看到,主要是用来判断输入的用户名和密码长度的。用户名长度为6-20,密码长度为12-30

v2 = *(_DWORD *)(v1 + 56);
  if ( *(_BYTE *)(v2 + 3) != 45 || *(_BYTE *)(v2 + 7) != 45 || *(_BYTE *)(v2 + 11) != 45 )
  {
    *(_BYTE *)_cxa_allocate_exception(1) = 1;
    _cxa_throw();
  }

这一段是用来判断字符串的第4,8,12个字符是不是'-',不是就返回失败

sub_5430(v1);

然后是sub_5430,这个函数不能被F5,所以看不到C代码,只能看ARM,因为不是太重要,就不贴ARM代码了。这个函数的功能就是把密码里面的‘-’符号去掉,剩下的组成一段字符串。

v4 = (unsigned __int8)byte_15120;
  if ( !byte_15120 )
  {
    do
      byte_15121[v4++] = -128;
    while ( v4 != 256 );
    v5 = 0;
    do
    {
      byte_15121[v5 + 65] = v5;
      ++v5;
    }
    while ( v5 != 26 );
    v6 = &byte_15182;
    do
    {
      *v6 = v5;
      v5 = (v5 + 1) & 0xFF;
      ++v6;
    }
    while ( v5 != 52 );
    v7 = &byte_15151;
    do
    {
      *v7 = v5;
      v5 = (v5 + 1) & 0xFF;
      ++v7;
    }
    while ( v5 != 62 );
    byte_1514C = 62;
    byte_15150 = 63;
    byte_1515E = 0;
    byte_15120 = 1;
  }

之后这一段,可以发现,是跟我们输入的用户名密码没有任何关系的。其实是生成一个table,用这个table来变换字符串。所以逆向的时候,这部分代码可以直接照抄。
中间的一些条件判断就不提了

 while ( v11 < (signed int)(v10 - 3) )
  {
    v15 = 0;
    do
    {
      v16 = byte_15121[*(&v14[v11] + v15)];
      *(&v21 + v15) = v16;
      if ( v16 & 0x80 )
        *(&v21 + v15) = 0;
      ++v15;
    }
    while ( v15 != 4 );
    v13 += 3;
    v11 += 4;
    v17 = v22;
    *(_BYTE *)v12 = ((signed int)v22 >> 4) | 4 * v21;
    v18 = v23;
    *((_BYTE *)v12 + 1) = 16 * v17 | ((signed int)v23 >> 2);
    *((_BYTE *)v12 + 2) = (v18 << 6) | v24;
    v12 = (char *)v12 + 3;
  }


这一部分是关键的代码。把输入的密码通过一定的变换规则,变换为另一段字符串。
之后的代码就是把变换得到的字符串和输入的用户名做比较,相同则成功,不同则失败。

到这里,代码分析完了,剩下的就是怎么写注册机。
都知道,注册机要把这段过程逆过来,我个人建议,在逆这段代码的时候,最好先用C代码把这段代码实现一遍,然后根据自己实现的代码进行逆转。
剩下的就是考验代码分析能力和编码能力了。这点教不来,只能靠大家锻炼
附上我提交的代码
keygen1.cpp.txt

+++++++++++++++++下面这货是crackme2+++++++++++++++++++++++

Crackme2:
上来二话不说,先把运行时的libcrackme.so文件dump出来。
但是IDA Attach上去之后,竟然发现Modules里面没有libcrackme.so,对于这种情况,可以通过python脚本调用idaapi.dbg_get_memory_info()来获取内存布局信息,当然根本方法就是读取/proc/<pid>/maps文件,通过读取该文件,可以获取libcrackme.so的基址,然后就能很容易地把文件dump出来了。


Dump出来的libcrackme.so的ELF文件头全是'\0',面对这种情况,我的方法是用原始的libcrackme.so的ELF头填充到dump出来的so中。
之后用IDA打开修复后的libcrackme.so文件,会发现导入函数和导出函数都是空的。
面对这种情况呢,实际上有两种解决方法。
第一种呢,就是本文最开始提到的,造成这种情况的原因,是因为dynamic节做了手脚,只要恢复了,就能看到导出函数了。
第二种是我做题的时候采取的笨方法,在libdvm.so中,导出了gDvm这个全局变量,gDvm里面有一项叫做loadedClasses,以哈希表的形式记录了已经加载的Class,通过遍历这个Class,可以找到com.crackme.MainActivity类的ClassObject结构体,在ClassObject里面又有一项叫directMethod,以数组的形式记录了该类的方法对象,遍历Method,可以找到crackme函数的Method对象,由于crackme是一个JNI调用。在Android4.4.4下(其他版本过程可能不太一样),第一次调用crackme函数,会掉用Method->nativeFunc,这个函数实际上是指向dvmResolveNativeMethod,在该函数中进行处理,处理完毕后,将crackme的实际地址,写入Method->jniArgInfo。也就是说,只要执行一次crackme,就能从Method->jniArgInfo获取crackme函数在libcrackme.so里面的偏移了。(再次重申,不同环境下可能不一样)
写了一个脚本遍历class:
import idaapi
from binascii import *
gDvm = 0x415A81F0
loadedClassesOffset = 0xAC

def get_uint32_of_pointer(addr):
    x = idaapi.dbg_read_memory(addr, 4)
    y = get_addr(x)
    return int(y,16)
    
def get_addr(data):
    s = b2a_hex(data)
    addr = "0x"
    try:
        for i in range(3,-1,-1):
            addr += s[2*i]
            addr += s[2*i+1]
    except:
        print s
        
    return addr

tmp = gDvm + loadedClassesOffset
hashtable = get_uint32_of_pointer(tmp)

size = get_uint32_of_pointer(hashtable)
addr = get_uint32_of_pointer(hashtable + 0xC)

xx(addr, size)

def xx(addr, size):
    table = idaapi.dbg_read_memory(addr, size*8)
    for i in range(0x2000):
        tmp = table[i*8 + 4:i*8+8]
        addr = get_addr(tmp)
        if addr != "00000000":
            iaddr = int(addr,16)
            clazz = idaapi.dbg_read_memory(iaddr, 28)
            if clazz != None:
                name = get_addr(clazz[4*6:4*6+4])
                iname = int(name,16)
                sname = idaapi.dbg_read_memory(iname, 50)
                if sname != None:
                    print addr + ' : ' + sname

里面用了一些硬编码。仅供参考,切勿直接copy使用
由于com.crackme.MainActivity类只有7(印象中)个直接方法,所以剩下的手动找就行了。

总之,获取到了crackme函数的地址,剩下的就是F5了
可以发现,算法的代码和crackme1出奇得一致。直到看到用户名比较那一段
记得之前可以F5的,但是这次不行了,所以把关键点标注了一下,凑活看吧,看不懂的单步走走。在sub_5718里面

不解释了,总之就是逆序比较,类似于
for(int i =0; i < n; i++){
    if(s1[i] != s2[n - i -1]){
        return false;
    }
}


由于考虑到base64加解密顺序是不变的,所以只有在加解密前逆序一次就行了。
crackme2提交的代码,其实就是crackme1添加了点内容
keygen2.cpp.txt

*******************我才不是crackme3呢****************************

Crackme3
相比较于Crackme2,添加了反调试的内容。
依旧是两种解决方案。
第一种就是crackme2中提到的方案二。写个注入程序,把crackme的地址找出来,并把内存中的So dump一份。不多说了,附上我写的代码,代码注入部分源码来源于网上。
inject.zip

另外一种就是靠实力进行分析了。首先要把so修复一下,然后分析找到DT_INIT

然后需要以DEBUG模式启动App进行分析。(不会的请翻阅http://bbs.pediy.com/showthread.php?t=178659
Debug模式启动后,在DT_INIT的偏移处下断点。然后就是单步测试。
最后可以找到
signed int __fastcall start()
{
  signed int v0; // r3@2
  pthread_t newthread; // [sp+4h] [bp-58h]@7
  int v3; // [sp+8h] [bp-54h]@5
  int v4; // [sp+Ch] [bp-50h]@9
  int v5; // [sp+10h] [bp-4Ch]@11
  int v6; // [sp+14h] [bp-48h]@3
  int v7; // [sp+4Ch] [bp-10h]@1

  v7 = 0;
  if ( pthread_mutex_init((pthread_mutex_t *)&mutex, 0) != 0 )
  {
    v0 = -1;
  }
  else
  {
    v7 = pthread_create((pthread_t *)&v6, 0, (void *(*)(void *))debuggdb_scan, 0);
    if ( v7 )
    {
      v0 = -1;
    }
    else
    {
      v7 = pthread_create((pthread_t *)&v3, 0, (void *(*)(void *))file_pro_thread_strengthen, 0);
      if ( v7 )
      {
        v0 = -1;
      }
      else
      {
        v7 = pthread_create(&newthread, 0, (void *(*)(void *))prevent_attach_one, 0);
        if ( v7 )
        {
          v0 = -1;
        }
        else
        {
          v7 = pthread_create((pthread_t *)&v4, 0, (void *(*)(void *))prevent_attach_two, 0);
          if ( v7 )
          {
            v0 = -1;
          }
          else
          {
            v7 = pthread_create((pthread_t *)&v5, 0, (void *(*)(void *))prevent_attach_three, 0);
            if ( v7 )
            {
              v0 = -1;
            }
            else
            {
              pthread_mutex_destroy((pthread_mutex_t *)&mutex);
              v0 = 0;
            }
          }
        }
      }
    }
  }
  return v0;
}


以及不停在pthread_kill的循环
 while ( 1 )
  {
    for ( i = 0; i < *(_DWORD *)(v3 + 12); ++i )
    {
      v2 = pthread_kill(*(_DWORD *)(v3 + 4 * (i + 8)), 0);
      if ( v2 == 3 )
        exit(0);
      if ( v2 == 22 )
        exit(0);
    }
    sleep(1u);
  }


然后就没准备继续分析下去了,有兴趣的可以自己看看这些函数。

总之就是想办法把解密后的So Dump出来,然后IDA就可以静态分析了。
过程依旧和前两个一样,只不过在最后比较的时候变成了每两个字符交换次序,然后比较
代码在sub_57D4里面

很明显在交换字符次序

其实就是
for(i = 0; i < len - 2; i += 2)
    {
        n[i] = name[i+1];
        n[i+1] = name[i];
    }


至此crackme3也算结束了。AntiDebug部分暂时不深究了。
keygen3.cpp.txt

##################让我缓一缓吧,crackme4没什么新内容,就看逆向能力,有空再跟新##############################

附件汇总:
keygen1.cpp.txt
keygen2.cpp.txt
keygen3.cpp.txt
inject.zip

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
免费 3
支持
分享
最新回复 (18)
雪    币: 21
活跃值: (254)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
2
醉了。我居然没发现是base64.....................
2014-11-5 17:22
0
雪    币: 22
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
醉了
2014-11-5 18:00
0
雪    币: 4443
活跃值: (2066)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
醉了 有详细的分析没哦
2014-11-5 19:28
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
5
没有做题的话,估计是看不懂的。这个帖子是为了讨论思路。详细的分析等公布提交的解题报告吧。
2014-11-5 21:41
0
雪    币: 367
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
没看过第一题
第二题只要dump出so,用ida就可以看到完整的函数名,dump出的文件是没有加密代码的,
第三题加了anti,对xpose/android_server/debug_server有检测,对Traceid是否是0有检测,对/proc/pid/stat的task_state状态有检测
第四题anti和第三题一样,dump出so后发现,crackme函数有一部分代码是动态加密的0xf0字节,跑完解密部分就可以得到完整的代码,用ida就可以看了
算法就看了第二题,其他没看
2014-11-5 21:57
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
7
我第三题IDA一Attach上去,程序就退出,第四题却没有这个问题啊,可以正常单步调试。
2014-11-6 09:37
0
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
8
http://bbs.pediy.com/forumdisplay.php?f=122

答案提交区权限开放
各题的成绩出来了,各位参赛者,若有异议,请在今天提出,明天将公布名次。
2014-11-6 14:38
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
9
还是没看明白分数是怎么算的,怎么只有零点几分?

我算出来了~~~是算的8次方?不是1/8次方??
2014-11-6 15:09
0
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
10
公式以这帖为准:http://bbs.pediy.com/showthread.php?t=193953

s=[(2880-t)/2880][sup]1/8[/sup]×100

你那个我联系玩命重新看看。
2014-11-6 15:30
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
11
[QUOTE=kanxue;1328898]公式以这帖为准:http://bbs.pediy.com/showthread.php?t=193953

s=[(2880-t)/2880][sup]1/8[/sup]×100

你那个我联系玩命重新看看。[/QUOTE]

我看了一下别人的,好像都是按照8次方算的。
2014-11-6 15:45
0
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
12
稍等,会重新算一遍。
目前公式目的:提交的时间不是最重要的权重系数,正确性是最重要的,即在都答对的情况下,参考一下时间。
2014-11-6 16:00
0
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
13
用excel表格重新计算了一遍,将有误的修正过了。
2014-11-6 16:32
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
前段时间没时间做 看看大牛的思路
2014-11-8 12:10
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
没看明白怎么绕过 反调试的...
2014-11-19 13:51
0
雪    币: 236
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
顶,大牛做事就是能直达要点。一招一式都那么干练直接,弱弱的问下 010Editor 工具哪里有的下载,论坛里有人分享了,http://bbs.pediy.com/showthread.php?t=194253,不知道跟LZ大牛的相同么?不好意思,自己不仔细 http://bbs.pediy.com/showthread.php?t=177160&highlight=010Editor这个帖子已经破解了,穷人,先用着,此问就不劳烦LZ大牛了。
2014-11-19 14:25
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
Crackme3 图 所用工具是啥?
2014-11-19 14:58
0
雪    币: 233
活跃值: (285)
能力值: ( LV12,RANK:270 )
在线值:
发帖
回帖
粉丝
18
我没有调试啊,注入了一个so,把内容dump出来的。
2014-11-19 16:23
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
v6 v7虽然知道是调用的java里的string,但是怎么来确定使用的是什么还输,完成的是什么操作呢?
2015-10-12 14:51
0
游客
登录 | 注册 方可回帖
返回
//