逆向工程课题研究

简介

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

逆向工程的简介

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

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

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

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

IDA的基本使用

  • IDA的启动。NewGoPrevious三种方式加载文件。

    图片1.png

  • PC端拖拽文件到文件拖拽区域

    图片2.png

  • 弹出加载新页面的窗口。


    图片3.png
  • IDA数据库文件。当点击OK后,开始加载文件,IDA将选择指定的可执行文件加载到内存中,并对相关部分进行分析。随后IDA会创建一个数据库,其组件分别保存在4个文件中,这些文件的扩展名分别为.id0, .id1 , .nam.til.id0文件是一个二叉树形式的数据库,.id1文件包含描述每个程序字节的标注,.name文件包含与IDAName窗口中显示的给定程序位置有关的索引信息,.til文件用于存储与每个数据库本地化定义有关的信息。

    图片4.png

  • IDA数据库的保存。

    图片5.png

  • IDA桌面介绍:
    工具栏介绍
    导航彩带:是被加载文件的地址空间的线性视图。默认情况下,它会呈现二进制文件的整个地址范围。不同的颜色表示不同类型的文件内容,如数据块、代码块等,黄色小光标指向与当前反汇编窗口中显示的地址范围对应的导航带地址。
    颜色标签栏:显示导航彩带上不同彩带对应的二进制信息内容。
    反汇编视图:主要呈现视图数据。有两种不同的形式:图形视图和列表视图。图形视图中IDA显示的是某个函数在某个一时间的流程图。通过与导航彩带的结合使用我们就可以了解函数的运行情况。
    图形概况视图:因为使用图形视图很难显示某个函数的完整视图。我们是可以使用图形概况视图显示整个函数的概况。
    消息窗口:显示文件进程相关的信息,用户操作输出的信息,相当于设备的输出设备。

主要的数据显示窗口:

  • IDA View:图形视图:将函数分解成许多基本块,生动的显示该函数由一个块到另一个块的控制流程。每个基本快都有唯一的入口点和退出点。

  • 视图列表:窗口中的汇编代码分行显示,虚拟地址默认显示:格式:【区域名称】:【虚拟地址】,如:text:0040110C0.

  • 箭头窗口:用于描述函数中非线性流程。实线:表示非条件跳转,虚线:表示条件跳转。粗线:表示一个跳转将控制权转交给程序的某个地址,这时候要使用粗线。出现这种类的情况说明程序中有循环。
    注释:以分好开头,属于交叉引用情况,表示所注释的位置的指令。

  • Name窗口:列举二进制的所有全局名称,每个名称对应一个程序的虚拟地址。通过Name窗口可以快速定位程序列表中已知的位置,双击虚拟地址跳到反编译视图。

  • F:表示常规函数,IDA认为这些函数不属于库函数,属于自定义函数。

  • L:库函数,IDA通过签名匹配算法识别库函数,如果某个库函数的签名不存在该函数就会标记为常规函数。

  • I:导入的名称,通常为共享库导入的函数名称。他与库函数的区别:导入的函数没有代码,而库函数的主体将在反编译代码清单上显示。

  • G:命名代码,这些是已经命名的程序指令位置,IDA认为他们不属于任何函数。

  • D:数据,已命名数据的位置通常表示全局变量。

  • A:字符串数据,这是一个被引用的数据位置,其中包含的一串字符符合IDA的某种已知的字符串数据类型。

次要的窗口。

  • Hex View – A:十六进制窗口显示程序内容和列表的标准十六进制代码,每行16个字符,以及对应的ASCII字符。这个窗口是和视图窗口是对应同步的。十六进制窗口中显示问好,这表示IDA无法识别给定的虚拟地址的范围,如果程序中包含了bss节就会出现这种情况。Bss由编译器创建,用于保存的所有未初始化的静态变量。当程序执行时,加载器会为其分配所需要的空间,并将整个数据块的初始值设置为0.

  • Import窗口:

  • Export窗口:

  • 函数窗口(functionName):由于列出数据库中IDA识别的所有函数。用户可以根据二进制中的虚拟地址对应的.text部分找到函数,还可以显示函数在十六进制下的字节数,它返回调用方法(R)并使用EBP寄存器(B)引用它的变量。

  • Stuctures(结构体窗口):显示IDA在二进制数据中识别复杂数据结构的布局。

  • Enums(枚举窗口):如果IDA检测到标准的枚举数据类型,它将被在这个窗口被列举出来。

调用相关

  • 调用约定:创建栈帧时最重要的是通过调用函数,将函数函数参数存入栈中。调用函数必须存储被调用函数所需要的参数,否则会导致严重的错误。各个函数会选择并遵守特定的调用约定,以表明他们希望以何种方式接受参数。

  • Cdecl调用的约定:调用方法按从右到左的顺序将函数参数放入栈中,在被调用的函数完成其操作时,调用方负责从栈中清楚参数。这样无论函数需要多少参数,我们都可以找到第一个参数。Cedecl调用约定非常适用于那些参数数量可变的函数。

  • _stdcall调用约定:按照从右向左的顺序将函数参数放入栈中,程序执行结束后,由被调用的函数负责删除栈中的函数参数。对于被调用的函数而言,要完成这个任务,它必须清楚知道栈上有多少个参数,这只有在函数接受的参数两固定时才有可能。优点:在函数每次调用完后,不需要通过代码从栈中清除参数,因此生成体积小,速度快的程序。

  • Fastcall调用约定:是stdcall函数约定的变体,它向cpu寄存器(非程序栈)最多传递两个参数。如果指定使用fastcall约定,则传递函数的前两个参数将分别位于ECXEDX寄存器中。剩余的其他参数将按照类似与stdcall的方式从右到左放入栈上。调用完成后,由fastcall函数负责从栈中删除参数。

反编译的例子

反编译的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反编译存在的缺陷

  • IDA工具对block信息体被识别不了。由于block的实质是用一些结构体来保存调用信息,而结构体信息在release版的C++编译结果中是无明文的,所以在反汇编中block的传参看起来就是直接传到一块内存区域中,需要自己理解其排放顺序是参照那些结构体的。

  • 反编译的最终代码是Object-CRuntime运行时语言,而不我们正常编码的OC语言。对运行时机制和语言不了解的程序员是很难理解的,研究反编译难度大,不过这是一个学习Runtime运行时语言的好工具哦。

  • IDA反编译工具相对现在流行的反编译工具hopper disassemblerclass_dump,过于古老,功能和目的性略显不足。

防止反编译措施

(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数据存储安全,主要指在磁盘做数据持久化的时候所做的加密。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容