精华内容
下载资源
问答
  • X64位游戏软件安全逆向入门 ? ? ? ? 随着64位系统的普及,计算机软件、游戏也在朝64位进发面对64位游戏软件的逆向很多人一脸懵逼。? ? ? ? 本部教程立足64位游戏软件的逆向入门,64位逆向工具使用等使新手少走弯路不...
  • 用于逆向新手的一个源码。Eclipse运行。
  • 逆向入门

    万次阅读 多人点赞 2017-04-06 17:38:39
    从本篇起,逆向工厂带大家从程序起源讲起,领略计算机程序逆向技术,了解程序的运行机制,逆向通用技术手段和软件保护技术,更加深入地去探索逆向的魅力。 一、程序如何诞生? 1951年4月开始在英国牛津郡哈维尔...

    前沿

    从本篇起,逆向工厂带大家从程序起源讲起,领略计算机程序逆向技术,了解程序的运行机制,逆向通用技术手段和软件保护技术,更加深入地去探索逆向的魅力。

    一、程序如何诞生?

    这里写图片描述

    1951年4月开始在英国牛津郡哈维尔原子能研究基地正式投入使用的英国数字计算机“哈维尔·德卡特伦”,是当时世界上仅有的十几台电脑之一。图中两人手持的“纸带”即是早期的程序,纸带通过是否穿孔记录1或0,而这些正好对应电子器件的开关状态,这便是机器码,是一种早期计算机程序的存储形式。 
    这里写图片描述

    计算机程序是用来实现某特定目标功能,所以需要将人类思维转换为计算机可识别的语言,从人类语言到电子器件开关的闭合,这中间的媒介便是“编程语言”。

    “编程语言”大致分为三类:

    1、机器语言,又称机器码、原生码,电脑CPU可直接解读,因该语言与运行平台密切相关,故通用性很差,上面提到的利用卡带记录的便属于该类语言;

    2、汇编语言,是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在不同的设备中,汇编语言对应着不同的机器语言指令集, 运行时按照设备对应的机器码指令进行转换,所以汇编语言可移植性也较差; 
    这里写图片描述

    3、高级语言,与前两种语言相比,该类语言高度抽象封装,语法结构更接近人类语言,逻辑也与人类思维逻辑相似,因此具有较高的可读性和编程效率。但是高级语言与汇编语言相比,因编译生成的辅助代码较多,使运行速度相对“较慢”。 Java,c,c++,C#,pascal,Python,lisp,prolog,FoxPro,易语言等等 均属于高级语言。

    学会编程语言各种基本语义语法后,就可以实战了,而实战场所由IDE提供。IDE(集成开发环境Integrated Development Environment)是用于提供程序开发环境的应用程序,目前IDE的种类繁多,不再敖述,只要自己用得顺手、开发效率高、你开心就好。 
    这里写图片描述

    通过IDE可快速生成程序,根据程序的生成和运行过程,程序大致可分为两类:编译型程序解释型程序

    编译型程序:程序在执行前编译成机器语言文件,运行时不需要重新翻译,直接供机器运行,该类程序执行效率高,依赖编译器,跨平台性差,如C、C++、Delphi等;

    解释型程序:程序在用编程语言编写后,不需要编译,以文本方式存储原始代码,在运行时,通过对应的解释器解释成机器码后再运行,如BasiC语言,执行时逐条读取解释每个语句,然后再执行。由此可见解释型语言每执行一句就要翻译一次,效率比较低,但是相比较编译型程序来说,优势在于跨平台性好。

    Q : Java属于编译型语言OR解释型语言?

    这里写图片描述

    Java首先将源代码通过编译器编译成.class类型文件(字节码),这是java自定义的一种类型,只能由JAVA虚拟机(JVM)识别。程序运行时JVM从.class文件中读一行解释执行一行。另外JAVA为实现跨平台,不同操作系统对应不同的JVM。从这个过程来看JAVA程序前半部分经过了编译,而后半部分又经过解析才能运行,可以说是一种混合型程序,由于该类程序运行依赖虚拟机,一些地方称其为“虚拟机语言”。下图展现各语言之间关系。

    这里写图片描述

    硬件->机器语言->汇编语言->系统语言(C和C++)->解释型语言(python)和虚拟机语言(java),语言的封装程度越来越高,也更加抽象,贴近于人类思维,即“造车前不用再考虑车轮怎么造”。同时,层次越高意味着程序在执行时经历的转化步骤越多,毕竟都要转换为机器语言才能被硬件直接运行,这也是一些高级语言无法应用在效率要求较苛刻场景的原因之一。

    Java为了对运行效率进行优化,提出“JIT (Just-In-Time Compiliation)”优化技术,中文为“即时编译”。JVM会分析Java应用程序的函数调用并且达到内部一些阀值后将这些函数编译为本地更高效的机器码,当执行中遇到这类函数,直接执行编译好的机器码,从而避免频繁翻译执行的耗时。

    重点看看C\C++语言生成程序的过程及程序是以怎样的形态存储。 
    这里写图片描述

    上图为c语言程序的生成过程,主要经过编译链接两大过程。

    编译是指编译器将源代码进行词法和语法的分析,将高级语言指令转换为汇编代码。主要包含3个步骤:

    1、预处理。正式编译前,根据已放置在文件中的预处理指令来修改源文件的内容,包含宏定义指令,条件编译指令,头文件包含指令,特殊符号替换等。

    2、编译、优化。编译程序通过词法分析和语法分析,将其翻译成等价的中间代码表示或汇编代码。

    3、目标代码生成。将上面生成的汇编代码译成目标机器指令的过程。目标文件中所存放着与源程序等效的目标的机器语言代码。

    链接是指将有关的目标文件彼此相连接生成可加载、可执行的目标文件,其核心工作是符号表解析重定位链接按照工作模式分静态动态链接两类

    静态链接:链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中,整个过程在程序生成时完成。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码,静态链接则是把相关代码拷贝到源码相关位置处参与程序的生成。

    动态链接:动态链接库在编译链接时只提供符号表和其他少量信息用于保证所有符号引用都有定义,保证编译顺利通过。程序执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间,根据可执行程序中记录的信息找到相应的函数地址并调用执行。

    经过编译链接后,程序生成,windows程序则都已PE文件形式存储。

    PE文件全称Portable Executable,意为可移植可执行文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件。 PE文件以段的形式存储代码和相关资源数据,其中数据段和代码段是必不可少的两个段。

    Windows NT 预定义的段分别为 
    .text、.bss、.rdata、.data、.rsrc、.edata、.idata、.pdata和.debug。这些段并不是都是必须的,另外也可以根据需要定义更多的段,常见的一些加壳程序则拥有自己命名的段。

    在应用程序中最常出现的段有以下6种:

    1、执行代码段,.text命名; 
    2、数据段,.data、.rdata 命名; 
    3、资源段,.rsrc命名; 
    4、导出表,.edata命名; 
    5、导入表,.idata命名; 
    6、调试信息段,.debug命名。

    下图为一个标准的PE文件结构。

    这里写图片描述

    [NOTE] 
    到此为止,程序就诞生了,如果你对文件形态足够了解,就完全可以向网上的某些大牛一样,纯手工打造一个PE文件

    二、程序如何运行

    程序诞生后,我们就可以运行了,也就是双击程序后的事儿(本节重点描述windows平台程序)。需要说明的是,上面产生的程序文件是存储在硬盘(外存)里的二进制数据,当你双击程序后,windows系统会根据后缀名进行注册表查找相应的启动程序,这里我们编译出的是以exe后缀的可执行程序,则系统对程序进行运行。

    Q:系统如何运行可执行程序?

    系统并非在硬盘上直接运行程序,而是将其装载进内存里,包括其中的代码段、数据段等。

    Q:为什么在这会多此一举,把程序复制到内存再执行呢?

    内存直接由CPU控制,享受与CPU通信的最优带宽,然而硬盘是通过主板上的桥接芯片与CPU相连,所以速度比较慢。再加上传统机械式硬盘靠电机带动盘片转动来读写数据,磁头寻道等机械操作耗费时间,而内存条通过电路来读写数据,显然电机的转速肯定没有电的传输速度快。后来的固态硬盘则大大提升了读写速度,但是由于控制方式依旧不同于内存,读写速度任然慢于内存。

    为了程序运行速率,任何程序在运行时,都是有一个叫做“装载器”的程序先将硬盘上的数据复制到内存,然后才让CPU来处理,这个过程就是程序的装载。装载器根据程序的PE头中的各种信息,进行堆栈的申请和代码数据的映射装载,在完成所有的初始化工作后,程序从入口点地址进入,开始执行代码段的第一条指令。 


    这里写图片描述

    程序从入口点开始顺序执行,CPU直接与内存中的程序打交道,读取内存中的数据进行处理,并将结果保存到内存,除非代码段中还有保存数据到硬盘的代码,否则程序全程都不会在硬盘中存储任何数据。这就好比我们打开文档编辑器去编译文档,不管输入多少内容,在我们点击“保存”前,硬盘上的程序文件都没有变动,输入的数据都只是存储在内存上,如果此时很不幸断电了,内存上的数据会立刻丢失。为了应对这种尴尬局面,一些编辑软件会定期自动保存新数据至硬盘上,以防意外丢失数据的情况发生。

    既然程序在运行时需要加载到内存中才能运行,那么问题来了,对于目前体积越来越庞大的游戏来说,岂不是要把40~50G(可见使命召唤系列)的数据全塞进内存里。在某猫上搜索某品牌电脑,按价格排序后,某款3w RMB的移动工作站的内存也只是32G,这显然不满足一下子装载一款游戏的需求。而查看该游戏的运行配置需求,内存需求也只是几个G而已,这是怎么回事呢?

    原来,操作系统为解决此问题:当程序运行需要的空间大于内存容量时,会将内存中暂时不用的数据写回硬盘;需要时再从硬盘中读取,并将另外一部分不用的数据写入硬盘。这样,硬盘中部分空间会用于存储内存中暂时不用的数据,这一部分空间就叫做虚拟内存(Virtual Memory)。其中内存交换、内存管理等详细过程,感兴趣的同学可以查阅操作系统相关书籍。 


    这里写图片描述 

    一些同学看到这,就单纯的认为,调整虚拟内存空间即可变向提高内存空间,从而提升运行速度。硬盘的读写速度远远慢于内存,所以虚拟内存和内存频繁进行数据交换会浪费很多时间,严重影响计算机的运行速度。所以同学们还是要努力学习,早日当上高富帅白富美,换高配置电脑吧。

    这里写图片描述

    三、逆向目的和原理

    简要了解计算机程序基础知识后,我们进入【逆向工厂】的正题——逆向。

    Q:为什么要逆向?

    1、破解正版软件的授权


    这里写图片描述

    这里写图片描述

    由于一些软件采用商业化运营模式,并不开源,同时需要付费使用。为此这些软件采用各种保护技术对使用做了限制,而一些想享受免费的童鞋则对这些保护技术发起进攻,其中的主要技术便是逆向,通过逆向梳理出保护技术的运行机制,从而寻找突破口。

    2、挑战自我、学习提高

    这里写图片描述

    crackme是一些公开给别人尝试破解的小程序,制作 crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 cracker,想挑战一下其它 cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破,不管是什么目的,都是通过crackme提高了自身能力。另外, 一些互联网安全公司也会在面试中采取这种形式对应聘者进行测试。

    3、挖掘漏洞与安全性检测

    一些安全性要求较高的行业,为确保所用软件的安全,而又无法获取源码时,也需逆向还原软件的运行过程,确保软件的安全可靠。另外,挖洞高手在挖掘漏洞时,经常采用逆向手段,寻找可能存在的溢出点。病毒分析师通过逆向,分析病毒的运行机制,提取特征。

    4、还原非开源项目

    当你想模仿某优秀软件实现某功能时,发现该软件并未开源,而又很难从其他渠道获取该软件的具体技术细节,那么逆向也许会帮你敲开思想的大门。

    Q:既然逆向这么神通广大,可以解决很多问题,那么它的原理机制是什么?

    “逆向”顾名思义,就是与将源码变为可执行程序的顺序相反,将编译链接好的程序反过来恢复成“代码级别”。这里之所以用到“代码级别”一词,是因源代码编译是“不可逆”过程,无法从编译后的程序逆推出源代码。

    “逆向”通常通过工具软件对程序进行反编译,将二进制程序反编译成汇编代码,甚至可以将一些程序恢复成更为高级的伪代码状态。C\C++程序在经过编译链接后,程序为机器码,直接可供CPU使用,对于这类程序我们使用IDA、OD等逆向程序,只能将其恢复成汇编代码状态,然后通过读汇编代码来解读程序的运行过程机制,显然这对于新手来说,直接阅读汇编代码门槛较高,所以一些逆向工具提供插件可以将一些函数恢复成伪代码级别。

    这里写图片描述

    相比C\C++这一类编译运行类程序,依靠java虚拟机、.NET等运行的程序,由于所生成的字节码(供虚拟机解释运行)仍然具有高度抽象性,所以对这类程序的逆向得到的伪代码可读性更强,有时甚至接近与源代码。但是在生成字节码的过程中,变量名、函数名是丢失的,所以逆向出的伪代码中这些名称也是随机命名的,从而给代码的阅读制造的一定障碍。而对于这类易反编译的程序,为了保护软件不被逆向,通常采用代码混淆技术,打乱其中的命名,加入干扰代码来设置各种障碍。

    至此,我们把程序恢复成了可读代码,如果你仅仅依靠阅读这些代码来梳理程序运行过程,这叫做“静态调试”。与此对应的“动态调试”则是让程序运行起来,更加直观的观察程序的运行过程。经常编写程序的同学在debug时常常用到“断点”,而在动态调试中,断点起着很大的作用,否则程序将不会暂停下来让你慢慢观察各寄存器状态。

    Q:“断点”是如何工作的?


    x86系列处理器从8086开始就提供了一条专门用来支持调试的指令,即INT 3。简单地说,这条指令的目的就是使CPU中断(break)到调试器,以供调试者对执行现场进行各种分析。我们可以在想要观察的指令处设置一个断点,则程序会运行到该处后自动停下来;“单步调试”则是每条语句后面都会有INT3指令来阻断程序的运行,而这些INT3是对用户透明的,逆向工具并未将这些指令显示出来。

    这里写图片描述

    四、反汇编的多样性

    现在大多数程序是利用高级语言如C,C++,Delphi等进行编写 ,然后再经过编译链接,生成可被计算机系统直接执行的文件。不同的操作系统,不同的编程语言,反汇编出的代码大相庭径。反汇编工具如何选择?汇编代码如何分析?如何调试修改代码?这些问题都会让刚入门的新童鞋困惑。

    下面我们简单对比c++和c#程序反汇编后得到的代码: 
    图1

    图2

    图1是c++程序反汇编结果,图2为.net程序反汇编结果,两者功能都只是打印一句话。C++以push指令将字符串压入栈中,而.net以ldstr指令将字符串压入栈中,调用打印函数结束后,.net反汇编代码直接以ret指令返回结束,而c++反汇编代码先平衡完栈,再执行retn指令返回结束。

    由此可见,在反汇编过程中,我们确认好程序的编写语言和运行环境,才可选择适当的工具来反汇编程序。在分析反汇编代码时,如果熟悉高级语言的开发、运行过程及其反汇编指令,那更是事半功倍。

    五、常用的软件分析工具

    对于软件逆向分析,分为静态分析和动态分析,常用的软件如下:

    静态分析工具

    IDA Pro(Interactive Disassembler Professional )

    IDA Pro是总部位于比利时列日市(Liège)的Hex-Rayd公司的一款产品。IDA 的主要目标之一,在于呈现尽可能接近源代码的代码,而且通过派生的变量和函数名称来尽其所能地注释生成的反汇编代码,适用于三大主流操作 系统:Microsoft Windows.Mac OS X 和 Linux。IDA Pro提供了许多强大功能,例如函数的交叉引用查看、函数执行流程图及伪代码等,并且也有一定的动态调试功能。同时,IDA pro可以在windows、linux、iOS下进行二进制程序的动态调试和动态附加,支持查看程序运行内存空间,设置内存断点和硬件断点。

    IDA Pro是许多软件安全专家和黑客所青睐的“神兵利器”。

    这里写图片描述

    c32asm

    c32asm 是款非常好用的反汇编程序,具有反汇编模式和十六进制编辑模式,能跟踪exe文件的断点,也可直接修改软件内部代码 ,提供输入表、输出表、参考字符、跳转、调用、PE文件分析结果等显示 ,提供汇编语句逐字节分析功能,有助于分析花指令等干扰代码。 
    这里写图片描述

    Win32Dasm

    Win32dasm可以将应用程序静态反编译为WIN 32汇编代码,利用Win32dasm我们可以对程序进行静态分析,帮助快速找到程序的破解突破口。笔者下载的 Win32Dasm还可以附加到正在运行的进程,对进程进行动态调试,但如果原程序经过了加密变换处理或着是被EXE压缩工具压缩过,那么用Win32dasm对程序进行反汇编就没有任何意义了。 
    这里写图片描述

    VB Decompiler pro

    VB Decompiler pro是一个用来反编译VB编写的程序的工具。VB Decompiler反编译成功后,能够修改VB窗体的属性,查看函数过程等 ,VB Decompiler Pro 能反编译Visual Basic 5.0/6.0的p-code形式的EXE, DLL 或 OCX文件。对native code形式的EXE, DLL或OCX文件,VB Decompiler Pro 也能给出反编译线索。

    这里写图片描述

    还有对.net程序和delphi程序的静态反汇编分析工具,在以后的章节中会使用到,到时再详细讲解。

    动态分析工具

    Ollydbg

    Ollydbg运行在windows平台上,是 Ring 3级调试器,可以对程序进行动态调试和附加调试,支持对线程的调试同时还支持插件扩展功能, 它会分析函数过程、循环语句、选择语句、表[tables]、常量、代码中的字符串、欺骗性指令、API调用、函数中参数的数目,import表等等 ;支持调试标准动态链接库(Dlls),目前已知 OllyDbg 可以识别 2300 多个 C 和 Windows API 中的常用函数及其使用的参数,是 Ring3级功能最强大的一款动态调试工具。 
    这里写图片描述

    Windbg

    Windbg是Microsoft公司免费调试器调试集合中的GUI的调试器,支持Source和Assembly两种模式的调试。Windbg不仅可以调试应用程序,还可以 对内核进行调试。结合Microsoft的Symbol Server,可以获取系统符号文件,便于应用程序和内核的调试。Windbg支持的平台包括X86、IA64、AMD64。Windbg 安装空间小,具有图形操作界面,但其最强大的地方是有丰富的调试指令。 
    这里写图片描述

    其它对.net,delphi等程序的动态调试工具在以后的章节中介绍。

    辅助工具

    系统监视工具: 
    Wireshark (免费软件,网络监视和包分析类软件) 
    Outpost Firewall (共享软件,使用hook技术的Windows防火墙) 
    ProcExp (免费软件,强大的进程分析软件) 
    FileMon (免费软件,强大的文件读写监视软件) 
    RegMon (免费软件,强大的注册表读写监视软件)


    反保护工具:

    LordPE (Win32 PE文件修改,转存工具) 
    ImportREC (Win32 PE文件结构修复软件) 
    AIl versions ASPack unpacker (免费软件,ASPack压缩壳脱壳工具) 
    UnPECompact(免费软件,PECompact压缩壳脱壳工具) 
    UPX(自由软件,UPX压缩壳加壳和脱壳工具)


    其它:

    Hedit (共享软件,16进制编辑器) 
    PEiD (免费较件,软件信息和编写语言分析工具) 
    以上只是常用的一些程序分析工具,还有很多工具这里没有提到,有兴趣的读者可以根据自身需求查找下载。

    六、从hello world说起

    为了让大家直观地了解逆向的过程,我们就从大家最初学习编程时的hello world程序开始讲解:

    #include <stdio.h>
    
    void main() 
    
    {
    
        printf("hello world!\n");
    
    }
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这是我们编写的打印hello world程序,是不是看起来很亲切,接下来将编译好的hello

    world程序用IDA反汇编,生成的代码如下图:

    这里写图片描述

    第一行main函数名前面的__cdecl,是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除 。还有__fastcall与__stdcall,三者都是调用约定(Calling convention),它决定以下内容:

    1、函数参数的压栈顺序 
    2、由调用者还是被调用者把参数弹出栈 
    3、产生函数修饰名的方法

    push offset Format是将参数压入栈,在这里就是讲要打印的“hello world!\n”压入栈,供printf函数使用,在反汇编程序代码中,如果调用的函数有参数,都是先将函数的参数先用push指令压入栈中,例如:add(int a,int b),调用add函数前,先将参数a和b压入栈,根据 __cdecl调用规则,先push b,再push a,最后再调用add函数。 
    call ds:printf就是调用printf函数打印“hello world“字符。 
    add esp, 4是平衡栈,平衡掉刚才压入的函数参数。 
    xor eax, eax将eax寄存器清零。 
    retn 返回,程序执行结束。

    这就是hello world程序的逆向代码分析,只是举一个简单的例子,真正要逆向分析一个较大较复杂的程序还是有一定难度,需要更多的知识与经验。

    七、Crackme

    crackme(通常简称CM)是用来测试程序设计人员的逆向工程技能的小程序。 
    KeygenMe、ReverseMe、UnpackMe,KeygenMe是要求别人做出程序对应的 keygen (序号产生器)。 
    ReverseMe 要求别人把它的算法做出逆向分析。 
    UnpackMe 是则是要求别人把它成功脱壳 。

    分析这些程序都能提高个人的程序分析能力,这些程序都有各自侧重的知识点。 
    下面就以一个验证序列号的crackme小程序作为例子进行破解,得到正确的序列号。 
    直接运行程序是这样的 
    这里写图片描述

    开始破解程序,首先用IDA打开文件 
    这里写图片描述

    在函数(Function name)窗口中看见CWinApp,CCmdTarget更类,熟悉的同学已经知道该程序使用MFC编写,结合自己的开发经验,就能猜到获取编辑框中的内容用的函数是GetDlgItemText(),定位到调用该函数的位置0×00401557。在之前有三个指令, 
    这里写图片描述

    在调用GetDlgItemText()之前有三个push指令,

    .text:00401549 push 0Ah ; int //字符串最大长度 
    .text:0040154B lea edx, [ebp+String] 
    .text:0040154E push edx ; char * //字符串缓存区 
    .text:0040154F push 3E8h ; int //指向输入框控件 
    .text:00401554 mov ecx, [ebp+var_20] ;

    注意到刚才弹框的提示内容“Incorrect try again!!”,可以在IDA字符串窗口中找到,定位到使用该字符串的位置 
    这里写图片描述

    细心的同学已经发现了,在上面loc_401585代码段处有字符串比较(lstrcmpA),比较完成后有两个分支,一个提示输入正确“Correct way to Go!!”,另一个提示输入错误 “Incorrect try again!!”,结合上面获取文本输入框内容的代码段信息可以判断,lpString2和lpString1中有一个存储正确的验证码,另一个存储输入的内容,接下来我们用两种方法让我们的验证码通过验证。

    获取正确的验证码

    在0040158D call ds:lstrcmpA处设置断点,点击Debugger->Start Process或按F9开始动态调试,在程序输入框中随便输入一串字符,实验中输入的是‘1qaz2wsx’,然后点击“Check”控件,程序停在我们设置的断点处,然后查看寄存器ecx和edx中的值,所示如下: 
    这里写图片描述

    这里写图片描述

    如图所示,ecx寄存器存放的是lpString2:‘’,edx寄存器存放的是lpString1:‘1qaz2wsx’,获得正确验证码”’”’,接下来在程序中试验一下: 
    这里写图片描述

    结果正确!

    修改二进制代码

    修改PE文件,使输入的内容显示正确。在上一小节,程序比较完lpString1和lpString2有两个分支,一个是正确输入的提示框,另一个是错误输入提示框。修改代码跳转,只要跳转到弹出“Correct way to go!!”代码段就可以了,结合代码,当两个字符串不同时会执行jnz short loc_4015AD指令,跳转到loc_4015AD代码段,将jnz指令改为jz,可在两个字符串不同时跳转到“Correct way to go!!”代码段。jnz的十六进制码为75,jz的十六进制码为74,只需将可执行程序中的75改为74就可以。

    通过IDA Pro查看十六进制文件窗口找到该跳转指令 
    这里写图片描述

    用Hedit打开程序,找到该跳转指令

    这里写图片描述

    在二进制的文件中该跳转指令在0×00001595处,而不是IDA显示的0×00401595,发生了什么?这涉及到PE文件内存映射方面的基础知识,童鞋们可查阅相关资料。

    将跳转指令75修改为74,保存修改后运行,随意输入一段字符串看运行结果: 
    这里写图片描述 
    结果正确!那么,如果输入原来程序的验证码‘’,结果会是什么?为什么会是这样?


    感谢FREEBUF网站 

    REFERENCE 
    1.  逆向工厂(一):从hello world开始 
    2.  逆向工厂(二):静态分析技术

    展开全文
  • python逆向入门教程

    2021-01-01 13:22:55
    我们在Windows 10上开始python逆向之旅,首先开始搭建开发环境,python解释器使用最新的3.6.1,IDE使用PyCharm社区版2017.1.3,下载地址如下所示,下载完成后直接双击安装包安装即可,随后设置PyCharm的Project ...
  • SO逆向入门实战教程一:OASIS

    千次阅读 多人点赞 2021-05-31 15:55:17
    这是SO逆向入门实战教程的第一篇,总共会有十三篇,十三个实战。有以下几个注意点: 主打入门级的实战,适合有一定基础但缺少实战的朋友(了解JNI,也上过一些Native层逆向的课,但感觉实战匮乏,想要壮壮胆,入...

    一、前言

    这是SO逆向入门实战教程的第一篇,总共会有十三篇,十三个实战。有以下几个注意点:

    • 主打入门级的实战,适合有一定基础但缺少实战的朋友(了解JNI,也上过一些Native层逆向的课,但感觉实战匮乏,想要壮壮胆,入入门)。
    • 侧重新工具、新思路、新方法的使用,算法分析的常见路子是Frida Hook + IDA ,在本系列中,会淡化Frida 的作用,采用Unidbg Hook + IDA 的路线。
    • 主打入门,但并不限于入门,你会在样本里看到有浅有深的魔改加密算法、以及OLLVM、SO对抗等内容。
    • 细,非常的细,奶妈级教学。
    • 一共十三篇,1-2天更新一篇。每篇的资料放在文末的百度网盘中。另外,我创建了一个Unidbg学习交流群,欢迎私聊我入群玩耍。

    二、准备

    在这里插入图片描述
    s方法就是我们的分析目标,它接收两个参数。参数1是字节数组,参数二是布尔值,为false。伪代码如下:

    import java.nio.charset.StandardCharsets;
    
    public class oasis {
    
        public static void main(String[] args) {
            String input1 = "aid=01A-khBWIm48A079Pz_DMW6PyZR8" +
                    "uyTumcCNm4e8awxyC2ANU.&cfrom=28B529501" +
                    "0&cuid=5999578300&noncestr=46274W9279Hr1" +
                    "X49A5X058z7ZVz024&platform=ANDROID&timestamp" +
                    "=1621437643609&ua=Xiaomi-MIX2S__oasis__3.5.8_" +
                    "_Android__Android10&version=3.5.8&vid=10190135" +
                    "94003&wm=20004_90024";
    
            Boolean input2 = false;
    
            oasis test = new oasis();
            String sign = test.s(input1.getBytes(StandardCharsets.UTF_8), input2);
            System.out.println(sign);
    
        }
    
        public String s(byte[] barr, boolean z){
            return "Sign";
        };
    }
    

    返回值是32位的Sign,比如伪代码中的输入,返回3882b522d0c62171d51094914032d5ea ,且输入不变的情况下,输出也固定不变。

    三、Unidbg模拟执行

    目标函数的实现在liboasiscore.so中,在IDA中看一下
    在这里插入图片描述
    似乎并不是静态绑定,接下来去JNI OnLoad查看一下动态绑定的位置。

    在这里插入图片描述

    很不幸的是,这个样本经过了OLLVM混淆,很难直接找到动态绑定的地址。

    在以Frida为主的路子里,我们可以使用hook_RegisterNatives脚本得到动态绑定的地址,但这里我们采用Unidbg的方案。

    下载Unidbg最新版,在IDEA中打开,跑通如图例子,和图示一致即可。

    在这里插入图片描述

    首先搭一个架子,新建包和类,并把APK和SO资源放在目录下

    在这里插入图片描述

    首先导入通用且标准的类库,然后一步步往下,如下写了注释

    package com.lession1;
    
    // 导入通用且标准的类库
    import com.github.unidbg.linux.android.dvm.AbstractJni;
    import com.github.unidbg.AndroidEmulator;
    import com.github.unidbg.Module;
    import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
    import com.github.unidbg.linux.android.AndroidResolver;
    import com.github.unidbg.linux.android.dvm.*;
    import com.github.unidbg.linux.android.dvm.array.ByteArray;
    import com.github.unidbg.memory.Memory;
    
    import java.io.File;
    
    // 继承AbstractJni类
    public class oasis extends AbstractJni{
        private final AndroidEmulator emulator;
        private final VM vm;
        private final Module module;
    
        oasis() {
            // 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
            emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.oasis").build();
            // 获取模拟器的内存操作接口
            final Memory memory = emulator.getMemory();
            // 设置系统类库解析
            memory.setLibraryResolver(new AndroidResolver(23));
            // 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
            vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession1\\lvzhou.apk"));
            // 加载目标SO
            DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession1\\liboasiscore.so"), true); // 加载so到虚拟内存
            //获取本SO模块的句柄,后续需要用它
            module = dm.getModule();
            vm.setJni(this); // 设置JNI
            vm.setVerbose(true); // 打印日志
    
            dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad
        };
    
        public static void main(String[] args) {
            oasis test = new oasis();
        }
    }
    

    样本的init相关函数和JNI OnLoad函数已经运行过了,接下来Run
    在这里插入图片描述
    日志中可以发现,JNI OnLoad中主要做了两件事

    • 签名校验
    • 动态绑定

    值得一提的是,如果创建Android虚拟机时,选择不传入APK,填入null,那么样本在JNI OnLoad中所做的签名校验,就需要我们手动补环境校验了。

    在这里插入图片描述
    接下来就是如何执行我们目标函数的问题了,这并不是一个小问题。Unidbg封装了相关方法执行JNI函数以及有符号函数等,但需要区分类方法和实例方法,我觉得有些别扭,在Frida的使用过程中,通过地址直接Hook和Call是一种非常美妙的体验,所以这里我们只介绍通过地址模拟执行这个更一般和通用的法子,如果对前一种方法感兴趣,可以看Unidbg提供的相关测试代码。

    字节数组需要裹上unidbg的包装类,并加到本地变量里,两件事缺一不可。

    package com.lession1;
    
    // 导入通用且标准的类库
    import com.github.unidbg.linux.android.dvm.AbstractJni;
    import com.github.unidbg.AndroidEmulator;
    import com.github.unidbg.Module;
    import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
    import com.github.unidbg.linux.android.AndroidResolver;
    import com.github.unidbg.linux.android.dvm.*;
    import com.github.unidbg.linux.android.dvm.array.ByteArray;
    import com.github.unidbg.memory.Memory;
    
    import java.io.File;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.List;
    
    // 继承AbstractJni类
    public class oasis extends AbstractJni{
        private final AndroidEmulator emulator;
        private final VM vm;
        private final Module module;
    
        oasis() {
            // 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
            emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.oasis").build();
            // 获取模拟器的内存操作接口
            final Memory memory = emulator.getMemory();
            // 设置系统类库解析
            memory.setLibraryResolver(new AndroidResolver(23));
            // 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
            vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession1\\lvzhou.apk"));
            //
    //        vm = emulator.createDalvikVM(null);
    
            // 加载目标SO
            DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession1\\liboasiscore.so"), true); // 加载so到虚拟内存
            //获取本SO模块的句柄,后续需要用它
            module = dm.getModule();
            vm.setJni(this); // 设置JNI
            vm.setVerbose(true); // 打印日志
    
            dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad
        };
    
        public static void main(String[] args) {
            oasis test = new oasis();
            System.out.println(test.getS());
        }
    
        public String getS(){
            // args list
            List<Object> list = new ArrayList<>(10);
            // arg1 env
            list.add(vm.getJNIEnv());
            // arg2 jobject/jclazz 一般用不到,直接填0
            list.add(0);
            // arg3 bytes
            String input = "aid=01A-khBWIm48A079Pz_DMW6PyZR8" +
                    "uyTumcCNm4e8awxyC2ANU.&cfrom=28B529501" +
                    "0&cuid=5999578300&noncestr=46274W9279Hr1" +
                    "X49A5X058z7ZVz024&platform=ANDROID&timestamp" +
                    "=1621437643609&ua=Xiaomi-MIX2S__oasis__3.5.8_" +
                    "_Android__Android10&version=3.5.8&vid=10190135" +
                    "94003&wm=20004_90024";
            byte[] inputByte = input.getBytes(StandardCharsets.UTF_8);
            ByteArray inputByteArray = new ByteArray(vm,inputByte);
            list.add(vm.addLocalObject(inputByteArray));
            // arg4 ,boolean false 填入0
            list.add(0);
            // 参数准备完成
            // call function
            Number number = module.callFunction(emulator, 0xC365, list.toArray())[0];
            String result = vm.getObject(number.intValue()).getValue().toString();
            return result;
        }
    }
    
    

    可以发现,通过地址方式调用,似乎有一点点麻烦,但这个麻烦其实并不大,我们常常需要对未导出函数进行分析,与其一会儿用符号名调用,一会儿用地址,不如统一用地址嘛。

    运行测试
    在这里插入图片描述
    结果与Hook得到的一致,即模拟执行顺利完成。

    四、ExAndroidNativeEmu 模拟执行

    这个样本非常简单,我们用ExAndroidNativeEmu来梅开二度。如果说Unidbg是小号游轮,那么ExAndroidNativeEmu就是皮划艇。在此处皮划艇可有可无,我们只是做个演示,但有时非皮划艇不可,遇到了我们再说。

    import posixpath
    from androidemu.emulator import Emulator, logger
    from androidemu.java.classes.string import String
    
    # Initialize emulator
    emulator = Emulator(
        vfp_inst_set=True,
        vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
    )
    
    # 加载SO
    lib_module = emulator.load_library("tests/bin/liboasiscore.so")
    
    # find My module
    for module in emulator.modules:
        if "liboasiscore" in module.filename:
            base_address = module.base
            logger.info("base_address=> 0x%08x - %s" % (module.base, module.filename))
            break
    
    # run jni onload
    emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
    # 准备参数
    a1 = "aid=01A-khBWIm48A079Pz_DMW6PyZR8uyTumcCNm4e8awxyC2ANU.&cfrom=28B5295010&cuid=5999578300&noncestr=46274W9279Hr1X49A5X058z7ZVz024&platform=ANDROID&timestamp=1621437643609&ua=Xiaomi-MIX2S__oasis__3.5.8__Android__Android10&version=3.5.8&vid=1019013594003&wm=20004_90024"
    # 通过地址直接调用
    result = emulator.call_native(module.base + 0xC364 + 1, emulator.java_vm.jni_env.address_ptr, 0x00, String(a1).getBytes(emulator, String("utf-8")), 0)
    # 打印结果
    print("result:"+ result._String__str)
    
    

    ExAndroidNativeEmu同样提供了对JNI函数的调用封装,但我们这边依然用地址方式调用,就是不用,就是玩儿。

    在这里插入图片描述

    可以发现结果也很顺利,在这个样本上,Unidbg和ExAndroidNativeEmu 都能很轻松的处理,其中ExAndroidNativeEmu的代码量甚至更少一些,这得益于样本中和JAVA层的交互极少,一旦涉及到JNI交互,皮划艇就让人难受了,在Python中补JAVA的逻辑,简直不是人该受的委屈。

    但ExAndroidNativeEmu 也有它的用武之地

    • 代码量较少,适合学习和分析,可以方便的结合自己的知识和业务增删功能。
    • 在样本比较简单的情况下(即与JAVA交互少,系统调用少,一切都少,只是纯粹的Native运算)甚至比Unidbg更好用。
    • ExAndroidNativeemu的code trace做的比Unidbg好很多,在指令的trace上做了非常多的优化。

    五、算法分析

    因为这是个简单的样本,输出又是32位,很容易就让人联想到哈希算法,掏出FindHash跑一下。

    在这里插入图片描述
    运行FindHash提示的脚本,根据输出找到对应的函数并分析,很快就定位到0x8AB2这个函数,并且它是MD5_Update函数。如果对各类算法的原理缺少了解,可以看一下R0ysue的SO基础课哟,手算MD5/SHA1/DES/AES +工程实践+逆向分析,就等你来。

    function hook_md5_update(){
        var targetSo = Module.findBaseAddress("liboasiscore.so");
        let relativePtr = 0x8AB2 + 1;
    
        console.log("Enter");
        let funcPtr = targetSo.add(relativePtr);
        Interceptor.attach(funcPtr,{
            onEnter:function (args) {
                console.log(args[2]);
                console.log(hexdump(args[1],{length:args[2].toInt32()}));
    
            },onLeave:function (retval){
            }
        })
    }
    

    Hook结果

     0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
    b2fa5800  59 50 31 56 74 79 26 24 58 6d 2a 6b 4a 6b 6f 52  YP1Vty&$Xm*kJkoR
    b2fa5810  2c 4f 70 6b 26 61 69 64 3d 30 31 41 2d 6b 68 42  ,Opk&aid=01A-khB
    b2fa5820  57 49 6d 34 38 41 30 37 39 50 7a 5f 44 4d 57 36  WIm48A079Pz_DMW6
    b2fa5830  50 79 5a 52 38 75 79 54 75 6d 63 43 4e 6d 34 65  PyZR8uyTumcCNm4e
    b2fa5840  38 61 77 78 79 43 32 41 4e 55 2e 26 63 66 72 6f  8awxyC2ANU.&cfro
    b2fa5850  6d 3d 32 38 42 35 32 39 35 30 31 30 26 63 75 69  m=28B5295010&cui
    b2fa5860  64 3d 35 39 39 39 35 37 38 33 30 30 26 6e 6f 6e  d=5999578300&non
    b2fa5870  63 65 73 74 72 3d 4a 32 33 33 39 67 41 43 79 30  cestr=J2339gACy0
    b2fa5880  44 35 6b 33 32 39 35 33 71 30 31 67 74 66 36 78  D5k32953q01gtf6x
    b2fa5890  30 38 31 39 26 70 6c 61 74 66 6f 72 6d 3d 41 4e  0819&platform=AN
    b2fa58a0  44 52 4f 49 44 26 74 69 6d 65 73 74 61 6d 70 3d  DROID&timestamp=
    b2fa58b0  31 36 32 31 35 32 36 32 39 38 31 32 37 26 75 61  1621526298127&ua
    b2fa58c0  3d 58 69 61 6f 6d 69 2d 4d 49 58 32 53 5f 5f 6f  =Xiaomi-MIX2S__o
    b2fa58d0  61 73 69 73 5f 5f 33 2e 35 2e 38 5f 5f 41 6e 64  asis__3.5.8__And
    b2fa58e0  72 6f 69 64 5f 5f 41 6e 64 72 6f 69 64 31 30 26  roid__Android10&
    b2fa58f0  76 65 72 73 69 6f 6e 3d 33 2e 35 2e 38 26 76 69  version=3.5.8&vi
    b2fa5900  64 3d 31 30 31 39 30 31 33 35 39 34 30 30 33 26  d=1019013594003&
    b2fa5910  77 6d 3d 32 30 30 30 34 5f 39 30 30 32 34        wm=20004_90024
    0x1a
               0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
    bbe3c322  80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    bbe3c332  00 00 00 00 00 00 00 00 00 00                    ..........
    0x8
               0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
    bae15ed8  f0 08 00 00 00 00 00 00
    

    MD5 Update一共被调用了三次,需要注意的是,MD5的Update的后两次调用,都是数据的填充,属于算法内部细节,所以我们只用关注第一次的输出。

    我们的明文是从aid开始的,前面多了一块,这一块每次运行都不变,所以猜测它是盐,使用逆向之友Cyberchef 测试一下:

    在这里插入图片描述

    大功告成!

    六、尾声

    这是一个非常简单的样本,用于熟悉Unidbg的简单操作。下一讲会复杂一点点的,熟悉Unidbg的更多基础操作。

    样本百度网盘:https://pan.baidu.com/s/1eg7FRtbKkD2ZEh6nARBK7w 提取码:yh5h

    展开全文
  • CTF—逆向入门题目(超详细)

    万次阅读 多人点赞 2018-09-13 18:56:50
    以下为一些简单的Windows逆向入门题目,帮助一些刚接触逆向又无法下手的朋友,如果对安卓逆向感兴趣的朋友可以看一下我的这一篇安卓逆向入门题目哦:https://blog.csdn.net/CharlesGodX/article/details/86602958 ...

    0x00:介绍

    以下为一些简单的Windows逆向入门题目,帮助一些刚接触逆向又无法下手的朋友,如果对安卓逆向感兴趣的朋友可以看一下我的这一篇安卓逆向入门题目哦:https://blog.csdn.net/CharlesGodX/article/details/86602958

    0x01:题目

    1.Bugkuctf平台中的逆向题easy_vb:

    打开文件发现需要输入注册码获取flag

    话不多说先放入PEID看看,养成这个好习惯,发现是用VB6写的

    我们载入IDA进行分析,用alt + t搜索字符串CTF,然后crtl + t搜索下一个字符串,直到看到flag

    2.Bugkuctf平台中的逆向题Easy_Re:

    先把文件下载下来载入PEID

    运行文件发现有字符串flag,于是考虑用IDA打开文件用alt+F12查找字符串flag

    来到这里发现xmmword后面有两串奇怪的字符串,我们将其选中按R键将其变成字符串发现flag

    3.南邮CTF逆向题Hello,RE!

    下载文件用PEID载入,无壳,运行一下发现让输入flag,老办法用IDA打开查找字符串flag

    查找到之后用f5查看伪代码

    看到如下结果,同样将v5~v11的结果用R改为字符串得到flag

    4.实验吧 Just Click

    下载文件用exeinfo这款软件查看发现程序用C#撰写

    打开软件发现需要点击相应的数字才能发现flag

    因为是用C#写的所以我们考虑用Reflector软件将其打开

    找到MainWindow发现类似主函数的东西,分析发现需要按顺序点击8次就能出现flag

    按这个顺序点击即出现flag。

    5.南邮CTF py交易

    链接:https://pan.baidu.com/s/1o8fVxkI密码:kd37

    下载文件发现是pyc格式,我们直接在网上找在线反编译python的网站:https://tool.lu/pyc/

    反编译后发现是这样的

    分析算法:首先输入一段字符串,进入encode函数之后与字符串correct进行比较

    encode函数就是将输入的字符串中每个字符ascii都与32进行异或运算,然后每个在加上16得到新的字符串,最后再将这个字符

    串进行base64加密。

    所以我们只需将"XlNkVmtUI1MgXWBZXCFeKY+AaXNt"进行base64解密,再将每个字符ascii码都减16,接着与32异或即可得

    到flag

    python代码如下:

    import base64
     
    correct ='XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
     
    s = base64.b64decode(correct)
     
    flag =''
     
    for i in s:
     
    i = chr((ord(i)-16)^32)
     
    flag += i
     
    print flag

     

    运行即可得到flag:nctf{d3c0mpil1n9_PyC}

    6.Jarvis OJ :FindKey

    下载文件发现是一个名字比较长的东西(大多数题目后缀名都比较长)

    用一款叫做斯托夫文件格式分析器分析一下这个软件的类型

    发现是python写的,将其后缀名改为.pyc然后放入在线反编译网站里得到如下

    import sys
    
    lookup = [
    196,153, 149,206, 17,221, 10, 217, 167, 18, 36, 135, 103, 61, 111, 31, 92, 152, 21, 228, 105, 191, 173, 41, 2, 245, 23, 144, 1, 246, 89, 178, 182, 119, 38, 85, 48, 226, 165, 241, 166, 214, 71, 90, 151, 3, 109, 169, 150, 224, 69, 156, 158, 57, 181, 29, 200, 37, 51, 252, 227, 93, 65, 82, 66, 80, 170, 77, 49, 177, 81, 94, 202, 107, 25, 73, 148, 98, 129, 231, 212, 14, 84, 121, 174, 171, 64, 180, 233, 74, 140, 242, 75, 104, 253, 44, 39, 87, 86, 27, 68, 22, 55, 76, 35, 248, 96, 5, 56, 20, 161, 213, 238, 220, 72, 100, 247, 8, 63, 249, 145, 243, 155, 222, 122, 32, 43, 186, 0, 102, 216, 126, 15, 42, 115, 138, 240, 147, 229, 204, 117, 223, 141, 159, 131, 232, 124, 254, 60, 116, 46, 113, 79, 16, 128, 6, 251, 40, 205, 137, 199, 83, 54, 188, 19, 184, 201, 110, 255, 26, 91, 211, 132, 160, 168, 154, 185, 183, 244, 78, 33, 123, 28, 59, 12, 210, 218, 47, 163, 215, 209, 108, 235, 237, 118, 101, 24, 234, 106, 143, 88, 9, 136, 95, 30, 193, 176, 225, 198, 197, 194, 239, 134, 162, 192, 11, 70, 58, 187, 50, 67, 236, 230, 13, 99, 190, 208, 207, 7, 53, 219, 203, 62, 114, 127, 125, 164, 179, 175, 112, 172, 250, 133, 130, 52, 189, 97, 146, 34, 157, 120, 195, 45, 4, 142, 139]
    
    pwda = [188, 155, 11, 58, 251, 208, 204, 202, 150, 120, 206, 237, 114, 92, 126, 6, 42]
    
    pwdb = [53, 222, 230, 35, 67, 248, 226, 216, 17, 209, 32, 2, 181, 200, 171, 60, 108]
    
    flag = raw_input('Input your Key:').strip()
    
    if len(flag) != 17:
        print 'Wrong Key!!'
        sys.exit(1)
    
    flag = flag[::-1]
    
    for i in range(0, len(flag)):
        if ord(flag[i]) + pwda[i] & 255 != lookup[i + pwdb[i]]:
            print 'Wrong Key!!'
            sys.exit(1)
    
    print 'Congratulations!!'

    下面写个脚本满足输出flag的条件就ok了

    import sys
    
    lookup = [
    196,153, 149,206, 17,221, 10, 217, 167, 18, 36, 135, 103, 61, 111, 31, 92, 152, 21, 228, 105, 191, 173, 41, 2, 245, 23, 144, 1, 246, 89, 178, 182, 119, 38, 85, 48, 226, 165, 241, 166, 214, 71, 90, 151, 3, 109, 169, 150, 224, 69, 156, 158, 57, 181, 29, 200, 37, 51, 252, 227, 93, 65, 82, 66, 80, 170, 77, 49, 177, 81, 94, 202, 107, 25, 73, 148, 98, 129, 231, 212, 14, 84, 121, 174, 171, 64, 180, 233, 74, 140, 242, 75, 104, 253, 44, 39, 87, 86, 27, 68, 22, 55, 76, 35, 248, 96, 5, 56, 20, 161, 213, 238, 220, 72, 100, 247, 8, 63, 249, 145, 243, 155, 222, 122, 32, 43, 186, 0, 102, 216, 126, 15, 42, 115, 138, 240, 147, 229, 204, 117, 223, 141, 159, 131, 232, 124, 254, 60, 116, 46, 113, 79, 16, 128, 6, 251, 40, 205, 137, 199, 83, 54, 188, 19, 184, 201, 110, 255, 26, 91, 211, 132, 160, 168, 154, 185, 183, 244, 78, 33, 123, 28, 59, 12, 210, 218, 47, 163, 215, 209, 108, 235, 237, 118, 101, 24, 234, 106, 143, 88, 9, 136, 95, 30, 193, 176, 225, 198, 197, 194, 239, 134, 162, 192, 11, 70, 58, 187, 50, 67, 236, 230, 13, 99, 190, 208, 207, 7, 53, 219, 203, 62, 114, 127, 125, 164, 179, 175, 112, 172, 250, 133, 130, 52, 189, 97, 146, 34, 157, 120, 195, 45, 4, 142, 139]
    
    pwda = [188, 155, 11, 58, 251, 208, 204, 202, 150, 120, 206, 237, 114, 92, 126, 6, 42]
    
    pwdb = [53, 222, 230, 35, 67, 248, 226, 216, 17, 209, 32, 2, 181, 200, 171, 60, 108]
    
    flag = ''
    
    for i in range(0,17):  //这里就是要满足wrong key的条件才能得到正确的flag
    
      flag+=chr(lookup[i + pwdb[i]]-pwda[i] & 255)  
    
    flag=flag[::-1]
    
    print flag

    运行一下就得到flag了

    7.Jarvis OJ :stheasy

    拿到题目下载了一个很复杂的文件,我们先放入斯托夫文件格式分析器分析,发现是ELF文件:

    我们用IDA将其打开,很容易找到关键函数位置:

    按下F5编译一下,观察到如下函数:

    有一个sub_8048630函数决定了Flag的对错,所以我们只需要研究一下它:

    这里我为了便于观察重新命名了a,b函数,我们双击a和b查找一下他们具体的值,将a这两排选中用shift + E快捷键选择第四个选项,用数组表示a如下,b同理:

    研究完算法之后就可以写脚本了:

    a = [
      0x48, 0x5D, 0x8D, 0x24, 0x84, 0x27, 0x99, 0x9F, 0x54, 0x18, 
      0x1E, 0x69, 0x7E, 0x33, 0x15, 0x72, 0x8D, 0x33, 0x24, 0x63, 
      0x21, 0x54, 0x0C, 0x78, 0x78, 0x78, 0x78, 0x78, 0x1B
         ]
    
    b = [
      0x6C, 0x6B, 0x32, 0x6A, 0x39, 0x47, 0x68, 0x7D, 0x41, 0x67, 
      0x66, 0x59, 0x34, 0x64, 0x73, 0x2D, 0x61, 0x36, 0x51, 0x57, 
      0x31, 0x23, 0x6B, 0x35, 0x45, 0x52, 0x5F, 0x54, 0x5B, 0x63, 
      0x76, 0x4C, 0x62, 0x56, 0x37, 0x6E, 0x4F, 0x6D, 0x33, 0x5A, 
      0x65, 0x58, 0x7B, 0x43, 0x4D, 0x74, 0x38, 0x53, 0x5A, 0x6F, 
      0x5D, 0x55, 0x00
         ]
    
    flag = ''
    
    c = []
    
    for i in range(0,len(a)):
        c.append(a[i]/3-2)    //append() 方法用于在列表末尾添加新的对象
        c[i] = int(c[i])      //将数据转换为整形,不转换会出错
    for j in range(0,len(a)):
        flag += chr(b[c[j]])
    print(flag)
        
    

     

    最后运行得到Flag:

    0x02:总结

    上面仅仅是一些入门的题目,如果是新手的话先把这些题目弄懂,弄透。熟悉各种工具的使用,不断的总结,逆向最重要的是分析,要自己多去分析。

    展开全文
  • bugku的ctf逆向入门练习(入门逆向)

    万次阅读 2018-10-29 22:00:31
    bugku的逆向第一题:入门逆向 下载压缩包解压后发现不能打开,先查壳: 发现无壳,载入OD后 发现一段赋值操作的代码,载入IDA,在主函数中找到这段代码: 选中每个语句按“R”转字符串,得到flag:flag{Re...

    bugku的逆向第一题:入门逆向

    下载压缩包解压后发现不能打开,先查壳:

     发现无壳,载入OD后

    发现一段赋值操作的代码,载入IDA,在主函数中找到这段代码:

    选中每个语句按“R”转字符串,得到flag:flag{Re_1s_S0_C0oL}


     

     

     

    展开全文
  • 安卓逆向入门笔记.pdf

    2019-10-29 10:29:46
    一份简单的安卓逆向入门笔记,介绍安卓文件组成等文件格式
  • bugku的ctf逆向入门练习(逆向入门

    千次阅读 2018-11-07 20:30:39
    bugku逆向第五题:逆向入门 下载文件后发现是一个exe的可执行文件,双击出现如下界面: 还是先查壳: 发现不是有效的pe文件,用notpad打开试试: 发现是“image/png;base64”,猜测是经过base64加密的...
  • 本课程可以带领你游戏逆向入门 可以学会 CE,OD,找游戏基址,功能CALL等实现变态功能 可以更深入的理解从高级语言到汇编语言的一个对应过程 可以学会 逆向软件  外挂与反外挂 破解防破解  游戏安全的基础...
  • Python爬虫进阶之JS逆向入门

    千次阅读 2019-05-29 09:29:43
    最近有朋友推荐了一个很简单的需要 js 逆向的网站 中国土地市场网 主要是需要获取下面的信息 分析 首先当然是抓包分析返回的数据 或者直接将链接放到代码里面,将响应内容打印出来。 如果一样的话说明直接就能获取...
  • Frida框架逆向入门-第一篇

    千次阅读 2021-04-09 16:59:34
    Frida框架逆向入门-第一篇1、安装Frida2、下载安装Frida-server3.使用frida4.mumu模拟器使用adb连接 !!!!!本文章仅供学习使用!!!!! 1、安装Frida pip install frida pip install frida-tools 测试是否...
  • 逆向入门之apk

    2019-12-28 02:19:45
    关于apk apk,是Android应用程序包(Android application package),是一种压缩包,通过将应用程序所需的代码、程序配置文件和各种图片资源等打包,得到的一个文件,通过手机安装,所以是可以通过解压缩软件(如...
  • Android逆向-Android基础逆向(1) Android 逆向 -Android 基础逆向(2) Android逆向-Android基础逆向(3) Android逆向-Android基础逆向(4) Android 逆向 -Android 基础逆向(5) Android 逆向 -Android 基础...
  • ios逆向入门教程(一)

    千次阅读 2019-03-01 13:04:18
    scp -P 2222 /Users/fujin/Desktop/逆向/Crack-file-master/crack\ file/dumpdecrypted.dylib root@127.0.0.1:/var/mobile/Containers/Data/Application/3F928AD9-8FE7-4BA0-BDD9-568F101B542F/Documents ...
  • 安卓APP逆向入门破解

    千次阅读 2019-03-21 09:13:25
    前言: 前段时间做爬虫遇到一个app,里面的数据需要登录之后才能拿到,而且... 这不是明摆着欺负人么,按赵四哥那句话来说就是: 生死看淡,不服就干! ...从而实现从网络抓包的密文到明文的转换。...jd_gui(源码查看...
  • 吾爱破解安卓逆向入门教程

    万次阅读 多人点赞 2016-02-19 09:50:13
    吾爱破解安卓逆向入门教程(1-5)笔记 作者: 加菲猫 一、环境配置 安装 java jdk,并设置好环境变量。 测试: java -version 二、初识 APK、Dalvik字节码以及Smali 1. apk是什么? ...
  • X64位游戏软件安全逆向入门 在职教师,从事it行业多年。兼职培训10余年,...
  • ios逆向入门笔记(详细到哭)

    千次阅读 2020-02-29 23:01:18
    ios逆向入门笔记 手机越狱 版本iphone5 ios8.4.1 遇到问题 1.Cydia跳出Failed to fetch http://repo666.ultrasn0w.com 的錯誤訊息 done! 解决方法 :删除这个源就行了 woc 买来的手机越狱过了然后被卖家...
  • bugkuCTF之逆向入门解题思路

    千次阅读 2019-08-09 17:33:49
    逆向入门 我们把文件下载得到的exe文件拖入ida。看到有个main主函数。 printf就是输出 然后每一条按下“R”得出结果 R,后的字符即可。flag{Re_1s_S0_C0oL} ...
  • Reverse | 逆向入门学习(一)

    千次阅读 2019-07-18 17:01:37
    Reverse(逆向)是CTF竞赛中的一种常见题目类型,主要考察参赛选手逆向工程相关的知识,考查形式为通过对一个二进制程序(exe、dll或者是elf等)进行逆向分析,了解程序内部的实现机制,最终目的可能是得到一个密码...
  • Android逆向入门3——怎样才能找到sign算法-附件资源
  • 建议收藏 | 最全的 JS 逆向入门教程合集

    万次阅读 多人点赞 2019-11-20 20:28:00
    基础入门 实战案例浅析 JS 加密 - DES 与 Base64 实战案例浅析 JS 加密 - RSA 与 XXTEA 实战案例浅析 JS 加密 - 基础总结篇 Chrome调试工具常用功能讲解 (点击文章标题跳转详情 ) 通用加密算法案例解析 Python...
  • 来自:Python编程与实战(微信号:pthon1024) 前言: ... 这不是明摆着欺负人么,按赵四哥那句话来说就是: 生死看淡,不服就干! ...从而实现从网络抓包的密文到明文的转换。 环境配置: ...dex2jar...
  • CTF-安卓逆向入门题目

    万次阅读 多人点赞 2019-01-22 22:52:03
    以下题目都是比较简单的安卓逆向题目,主要训练目的是熟悉安卓逆向的一些基础题目,如果是第一次接触安卓逆向,建议先去学一点安卓开发的相关知识,这样做题目就更快一些,当然题目做多了自然也就熟悉了,题目我都...
  • 逆向入门

    2021-03-30 19:26:13
    逆向基础入门 记入一下入门逆向的过程 先从buuctf上题目开始希望可以一直坚持下去 目录逆向基础入门easyrereverse1reverse2内涵的软件新年快乐helloword easyre 最简单题:改后缀为txt ctrl+f 寻找flag字眼得到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,320
精华内容 6,128
关键字:

逆向入门