iOS Developer程序员

逆向工程课题研究

2017-05-22  本文已影响193人  朴下柔

简介

本篇文章是个人对IDA逆向工程的研究,相关内容:逆向工程的简介、IDA基本使用和工作原理、反编译可执行文件部分源码、反编译的逆向代码逻辑分析、IDA反编译存在的缺陷、防止反编译的措施。小小心得,与大家分享。

逆向工程的简介

计算机软件逆向工程(Reversepengineering)也称为计算机软件还原工程,是指通过对他人软件的目标程序(可执行程序)进行“逆向分析、研究”工作,以推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,作为自己开发软件时的参考,或者直接用于自己的软件产品中。

在传统的软件开发模型中,程序员使用编译器、汇编器和链接器中的一个或者几个创建可执行的程序。为了回溯编程过程,我们使用各种工具来撤销汇编和编译过程。这个过程叫做反汇编和反编译,因此我们可以利用一些逆向工具将程序运行时的机器语言转化为汇编语言,或者更高级的语言。我们对程序进行逆向主要是为了分析恶意软件、分析闭源软件的漏洞和操作性、分析编译器生成的代码,验证编译器的性能和准确性、逆向生成程序的源代码。下面阐述我将以IDA逆向工具操作相结合。

反汇编和反编译简介:反汇编是指把可执行的二进制机器码反汇编成为基本可读的汇编语言,一般包括静态反汇编和动态反汇编技术两部分。静态反汇编是吧二进制代码一次性全部翻译为汇编代码,它的耗时与二进制文件大小称正比。动态反汇编是人为可读的汇编代码。反编译技术是吧汇编程序进一步反编译为可读性较高的高级语言代码。

基本的逆向原理和算法
(1)确定进行反编译的代码区域。因为指令和数据混杂在一起,所以区分起来很难。以反编译可执行文件为例,可执行文件必须要符合可执行文件的通用格式,如window所使用的可移植可执行格式(Protable Executable,PE)或者Unix系统下常用的可执行和链接格式(Executable and linking format ,ELF),这些格式通常含有一种机制,用来确定文件中包含代码和代码入口点(指令地址)的部分位置。
(2)知道指令的起始地址后,我们就可以进一步的读取该地址所包含的值,并执行一次表查找,将二进制操作码的值和它的汇编语言的助记符对应起。
(3)获取指令并解码任何所需要的操作数,需要对它的汇编语言等价形式进行格式匹配,并将其在反汇编代码中输出。如X86汇编语言所使用的的两种主要格式IntelAT&T格式。
(4)输出一条指令,继续反汇编下一条指令,并重复上述过程,知道反汇编完文件中的所有指令。这个过程主要采用两种算法:线性扫描和递归下降两种反汇编算法。

IDA的基本使用

主要的数据显示窗口:

次要的窗口。

调用相关

反编译的例子

反编译的oc底层语言
// TestViewController - (void)setDataFromDict:(id) 
void __cdecl -[TestViewController setDataFromDict:](struct TestViewController *self, SEL a2, id a3)
{
  struct TestViewController *v3; // r5@1                 
  int v4; // r4@1
  void *v5; // r0@1
  int v6; // r0@2
  int v7; // r0@2
  int v8; // r0@2
  int v9; // r0@2
  int v10; // r0@2
  int v11; // r6@2
  unsigned int v12; // r11@3
  void *v13; // r8@3
  int v14; // r0@4
  int v15; // ST28_4@4
  int v16; // r0@4
  int v17; // r10@4
  int v18; // r0@4
  int v19; // r6@4
  int v20; // r0@4
  int v21; // r4@4
  int v22; // r0@4
  int v23; // r4@4
  void *v24; // r5@7
  int v25; // r0@7
  int v26; // r8@7
  int v27; // r0@7
  int v28; // r10@7
  int v29; // r0@7
  int v30; // r4@7
  int v31; // [sp+4h] [bp-40h]@3
  int v32; // [sp+8h] [bp-3Ch]@2
  int v33; // [sp+Ch] [bp-38h]@2
  void *v34; // [sp+24h] [bp-20h]@2

v3 = self;       ;*v3指self本类对象
  v4 = objc_retain((int)a3, (int)a2); ;保留一份dict ,函数约定的调用方式是fastball,将objc_retain的两各参数放在cpu的寄存器中,分别放在位于ECX和EDX寄存器中。
                          
  v5 = objc_msgSend(&OBJC_CLASS___NSNull, "class"); 运用runtime的消息机制输出函数的返回值空或者非空。
  if ( !((unsigned int)objc_msgSend((void *)v4, "isKindOfClass:", v5) & 0xFF) ) ;判断v4(dict)是否为空,如果返回值为假,则执行do—while循环;否则return;
  {
    v6 = (int)objc_msgSend((void *)v4, "objectForKeyedSubscript:", CFSTR("d")); ;根据v4中的key“d”,返回一个字典v6。
    v7 = objc_retainAutoreleasedReturnValue(v6);
    v33 = v7;
    v8 = (int)objc_msgSend((void *)v7, "objectForKeyedSubscript:", CFSTR("information"));字典v7调用key“information”,返回v8数组。
    v9 = objc_retainAutoreleasedReturnValue(v8);
    v32 = v9;
    v10 = (int)objc_msgSend((void *)v9, "objectAtIndex:", 0);通过0索引位置找到数组v9,返回v10数组。
    v11 = objc_retainAutoreleasedReturnValue(v10);
v34 = (void *)v11;


// 执行for循环,先判断v11.count是否有值,如否有值,执行do-while循环
    if ( objc_msgSend((void *)v11, "count") )
    {
      v31 = v4;
      v12 = 0;
      v13 = v3;
      do
      {
        v14 = (int)objc_msgSend((void *)v11, "objectAtIndex:", v12);
        v15 = objc_retainAutoreleasedReturnValue(v14);
        v16 = (int)objc_msgSend((void *)v15, "objectForKeyedSubscript:", CFSTR("title"));
        v17 = objc_retainAutoreleasedReturnValue(v16);
        v18 = (int)objc_msgSend((void *)v15, "objectForKeyedSubscript:", CFSTR("url"));
        v19 = objc_retainAutoreleasedReturnValue(v18);
        v20 = (int)objc_msgSend(v3, "arrayTitle");
        v21 = objc_retainAutoreleasedReturnValue(v20);
        objc_msgSend((void *)v21, "addObject:", v17);
        objc_release(v21);
        v22 = (int)objc_msgSend(v3, "arrayUrl");
        v23 = objc_retainAutoreleasedReturnValue(v22);
        objc_msgSend((void *)v23, "addObject:", v19);
        objc_release(v23);
        objc_release(v19);
        objc_release(v17);
        v11 = (int)v34;
        objc_release(v15);
        ++v12;
      }
      while ( v12 < (unsigned int)objc_msgSend(v34, "count") );
    }
    else
    {
      v31 = v4;
      v13 = v3;
}


//执行 [self createUI:self.arrayTitle urlList:self.arrayUrl number:self.Number];
    v24 = v13;
    v25 = (int)objc_msgSend(v13, "arrayTitle");
    v26 = objc_retainAutoreleasedReturnValue(v25);
    v27 = (int)objc_msgSend(v24, "arrayUrl");
    v28 = objc_retainAutoreleasedReturnValue(v27);
    v29 = (int)objc_msgSend(v24, "Number");
    v30 = objc_retainAutoreleasedReturnValue(v29);
    objc_msgSend(v24, "createUI:urlList:number:", v26, v28, v30);
    objc_release(v30);
    objc_release(v28);
    objc_release(v26);
    objc_release(v11);
    objc_release(v32);
    objc_release(v33);
    v4 = v31;
  }
  j__objc_release(v4);
}
(10)分析得出的伪源代码
-(void)setDataFromDict:(NSDictionary *)v4{
    if ( [v4 isKindOfClass:[NSNull class]]) {
        return;
    }
    NSDictionary *v7 = dict[@"d"];
    NSMutableArray *v9 = v7[@"information"];
    NSMutableArray  *v11= [v9 objectAtIndex:0];

    
    for (int i=0; i< v11.count; i++) {
        
        NSDictionary * v15 =[ v11  objectAtIndex:i];
        
        NSString  *v17= v15 [@"title"];
        NSString  *v18= v15[@"url"];
        [self.arrayTitle   addObject: v17];
        [self.arrayUrl     addObject: v18];
        
    }
    
    [self createUI:self.arrayTitle urlList:self.arrayUrl number:self.Number];
}

上述分析对应的源码:

图片6.png

函数对应的汇编语言及其执行逻辑。




IDA反编译存在的缺陷

防止反编译措施

(1)代码混淆。
代码混淆是指将程序的代码,转换成一种功能上等价,但是难以阅读和和理解的形式行为。通常这种行为用于源代码,混淆原理是将代码中的各种元素和逻辑,如变量,函数,类的名称改为没有意义的名字,使得阅读的人无法根据名字猜测其用途,打乱代码的格式,尝试不同实现逻辑等。目前是市面上没有相应的IOS混淆工具,不过我们在开发中,可以事先约定一套命名规则,如使用完全没有意义的字符代替、对名称进行MD5加密等。

(2)加壳。
所谓的加壳,是一种通过一系列数学运算,将可执行程序文件的编码进行改变,已达到缩小文件体积或者加密程序编码的目的。当被加壳的程序运行时,外壳程序会被执行,然后由这个外壳程序负责将用户原有的程序在内存中解压缩,并把控制权交还给脱壳后的真正程序。目前常见到的壳有“UPX”“ASPack”“PePack”“PECompact”“UPack”“NsPack”“免疫007”“木马彩衣”等等,我们可以通过Fileinfo的工具来查看具体什么壳。加壳的本质就是隐藏程序的入口点(OEP),防止被破解。软件的壳分为:加密壳,压缩壳,伪装壳,多层壳等。

(3)采用更加高级的编程方式。
为了防止被反编译器反编译,我们可以使用比传统的命令式编程习惯更加高级而复杂的编程方式去编写程序,比如基于block高阶函数的响应式编程(ReactiveCocoa),响应式编程可以提高我们代码的抽象级别,也更好的处理事件之间相互依存的业务逻辑。IDA和其他反编译工具,是不能识别block函数的。

(4)网络层我们要采用Https超文本安全传输协议。
其实HTTPS从最终的数据解析的角度,与HTTP没有任何的区别,HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,就是普通的HTTP数据。HTTP和SSL/TSL都处于OSI模型的应用层。从HTTP切换到HTTPS是一个非常简单的过程
首先用这个是因为http的数据是裸露的(举个例子,之前微信朋友圈很火的红包看图功能,我们直接抓包,就可以看到图片url,就能查看原图,何必再去专门发个红包😄 -- 当然不差钱的童鞋除外)。 所以用https,一定程度上降低了传输速率(其实损耗并不大的),但是SSL/TSL层加密之后,加上证书的限制,可以解决50%的安全隐患。

(5)数据传输不仅要用Https,而且在传输过程中我们前段和后端进行自定加密。在Https的传输中在进行加密处理,token认证。

(6)App数据存储安全,主要指在磁盘做数据持久化的时候所做的加密。

上一篇 下一篇

猜你喜欢

热点阅读