用户
搜索
  • TA的每日心情

    7 小时前
  • 签到天数: 139 天

    连续签到: 2 天

    [LV.7]常住居民III

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    14

    主题

    63

    帖子

    1358

    魔法币
    收听
    0
    粉丝
    23
    注册时间
    2017-12-31

    i春秋签约作者

    发表于 2018-7-29 22:08:51 1847902
    本帖最后由 Crazyman_Army 于 2018-12-15 23:33 编辑
    1. 本篇文章属于从零开始的程序逆向之路系列
    2. 未经允许禁止转载
    3. 作者:Crazyman_Army
    4. Date: 2018.7.29 初稿
      2018.12.14 重新修订

    0x00 知识回顾

    由于笔者省事,没开XP虚拟机,而且没关闭ASLR,所以每次重载的内存地址会不一样
    所以里面的所有内存地址都是一个例子,各位同学在实践的时候切勿就单单只看内存地址
    不清楚ASLR的同学又想了解这个的请看链接 ASLR是什么

    在第一章的内容中,笔者已经讲了OllyDbg(简称OD)的界面介绍以及基础的操作,普及了常用的汇编指令
    上次课在底下的附件中,我留下了一个演示样例,大家载入OD后搜索到字符串点击跟踪到反汇编窗口的时候会发现与第一章所讲的程序的框架不太一样.如下图:
    演示程序框架
    尤其可见多出了很多的call指令,call指令在汇编中就是调用子程序,下面放出代码,各位同学可以比对一下与上篇文件所贴出代码的不同。
    代码如下(C):(由于¤t被html解析为实体所以会出现显示不了的情况,各位同学凑合着看)

    #include <windows.h>
    #include <tlhelp32.h>   
    #include <stdio.h>
    BOOL getProcess(const char *procressName);
    BOOL getProcess(const char *procressName)
    {
            char pName[MAX_PATH];                                
            strcpy(pName, procressName);                            
            CharLowerBuff(pName, MAX_PATH);                       
            PROCESSENTRY32 currentProcess;                       
            currentProcess.dwSize = sizeof(currentProcess);        
            HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
            if (hProcess == INVALID_HANDLE_VALUE)
            {
                    printf("CreateToolhelp32Snapshot()调用失败!\n");
                    return FALSE;
            }
            _asm NOP;
            BOOL bMore = Process32First(hProcess, ¤tProcess);        
            while (bMore)
            {
                    CharLowerBuff(currentProcess.szExeFile, MAX_PATH);        
                    if (strcmp(currentProcess.szExeFile, pName) == 0)            
                    {
                            CloseHandle(hProcess);                                
                            return TRUE;
                    }
                    bMore = Process32Next(hProcess, ¤tProcess);           
            }
            CloseHandle(hProcess);    
            return FALSE;
    }
    int main()
    {
            char* process = "explorer.exe";
            if (getProcess(process))
            {
                    printf("发现explorer.exe\n");
                    system("pause");
                    exit(0);
            }
            else
            {
                    printf("没有发现explorer.exe\n");
                    printf("Cracke Success!");
            }
            getchar();
            return 0;
    }

    从代码角度来看,很明显,这次笔者讲判断进程封装成一个返回布尔值(BOOL)的函数,所以才会在核心判断部分出现如此多的调用,那这当然不能像上次一样改一步简单的跳转就解决问题,如果改得不对就会造成程序的崩溃。
    下面笔者通过三种方法来绕过这个检验explorer.exe进程的小程序

    方法一:  修改判断进程的变量名称

    1. 在智能搜索中,我们看到了explorer.exe这个进程的字符串,我们用鼠标选中后,双击,在 反汇编窗口 中跟随,如图所示:
      跟随
      反汇编窗口 如图所示:
      反汇编
    2. 用鼠标选中这条代码 右键 -> 数据窗口中跟随 –> 内存地址
      操作1
    3. 数据窗口 随之发生了变化,在数据窗口上选中一部分数据右键
      数据窗口
    4. 右键 –> 复制到可执行文件
      复制到可执行文件
    5. 点击 放大按钮 放大该窗口
      将窗口放大
    6. 然后找到explorer.exe 这个字符串,胡乱修改即可
      找到"explorer.exe"字符串
    7. 笔者这里就随意修改了
      随意修改字符串1
      随意修改字符串2
    8. 窗口上右键,保存文件
      保存文件
    9. 文件保存成功
      保存成功
    10. 点击运行
      运行
      第一种方法破解成功

      方法二: 确认过眼神,你不是我们的人

      回到判断的核心代码,这里面是jnz来进行进程的判断
      jnz的全拼写是jump if not zero那其的反义词即为jump if Zero,即为 jz或者 je (jump equal),那这样原来的判断explorer.exe就改为了不判断explorer.exe,这样就绕过了检测进程. 关键代码如下图:

      跳转的核心代码
      下面我们来实际操作:

    11. 用鼠标选中那段关键跳转代码后,双击 (如图中的jnz short 00291158如何去查找这段代码的方法已经在上篇文章讲解过了 如果还不懂的话在 动态调试 中还需会细说)
      选中关键跳转代码
      双击编辑
    12. jnz short 00291158 改为  jz short 00291158 或者  je short 00291158 ,将 使用nop填充 勾掉
      修改跳转
    13. 点击 汇编
      点击汇编
    14. 然后再 保存文件 ,保存后运行
      保存运行
      第二种破解方法成功

      方法三: 条件改强制

      如下图,这个jnz short 00101158是如果zf(Z位)=0时就会跳转
      jnz所在代码的地址为00101137,我们尝试让其无条件跳转到00101137下面一个地址,也就是00101139(下图红框锁标注的区域)

      方法三解释
      下面让我们实际操作一下:

    15. 双击地址为 00101137 的那条代码,如下图所示 将 jnz short 00101158 改为 jmp short 00101139 ,点击汇编
      强制跳转修改
    16. 点击运行按钮或者按F9
      运行
    17. 程序运行截图:
      程序运行截图
      很显然破解成功了,那具体是怎么个原理呢,我们这里留个悬念,等用od能分析一个简单程序后,并且讲解完动态调试后我们回过头来解释这个问题

      00x01 用OllyDbg 分析一个简单的程序

      下面,笔者将带领同学去用OllyDbg来分析一个简单的程序
      笔者为了方便写了一个简单的判断输入数字的程序,代码如下(C):

      #include <stdio.h>
      #include <stdlib.h>
      int main()
      {
          int flag = 0;
          printf("Please Input Flag Number:");
          scanf("%d", &flag);
          if (flag==12121)
          {
                  printf("\nRight!\n");
          }
          else
          {
                  printf("\nError!\n");
          }
          system("pause");
          return 0;
      }

      首先第一步:载入OD
      载入OD
      反汇编窗口上右键中文搜索引擎 -> 智能搜索
      智能搜索
      跟踪后的代码可以看到:
      跟踪
      PS:在有程序保护的情况下,你可能无法直接搜索到字符串,这里先不讨论.
      笔者将带领同学一步一步分析这个简单的程序(注意看图去理解):

      首先

    push ebp
    mov ebp,esp
    push ecx

    这里面涉及到寄存器知识这个详细的讲解在我的一个系列 从零开始撸汇编中所以在这里不做讨论,在这里可以简单理解为源码中的int main()后的{
    pic1
    push Text.013E3E34,push的意思是压栈
    这里就不得不要说说C语言中的printf
    下面是printf的函数声明

    int printf(const char *format, ...)

    那在默认的情况下,printf函数应该有一个const char*型的参数,而这一行就是把“Please Input Flag Number:”这个字符串指针型常量当成参数1,压入栈中
    pic2

    mov [local.1],0x0

    那究竟这个[local.1]是何方神圣呢?
    我们选中这段代码:
    pic3
    右键 -> 分析 –> 从模块中删除分析
    pic4
    此时local.1]露出了他真面目[ebp-4]
    第一章的时候我们讲到了,mov(move)数据传送指令

    mov a,b 把b的值传给a

    当然mov也可以对内存中的数据进行赋值
    这里面 mov [ebp-4],0x0就是把0赋值给内存中所设置的变量,其中[]操作符可以引用内存中的数据

    mov [ebp-4],0x0

    类似于C语言源码中

    int flag=0;

    pic5
    call指令就是调用,那就是调用printf函数输出”Please Input Flag Number”这个字符串指针型常量
    pic6

    lea eax,dword ptr ss:[ebp-0x4]

    lea指令是一个汇编指令,可以将有效地址传送到指定的的寄存器。
    简单来说就把变量1的地址传递给了eax寄存器
    pic7

    push eax 
    push Text.00EE3E50

    现在的eax其实被传递了变量1的地址
    往下面一瞥会发现scanf函数
    下面是scanf函数的定义

    int scanf(const char * restrict format,...);

    一般代码中都是scanf(“格式说明符”,&变量地址)
    这里面格式说明符是函数的参数1,变量地址是函数的参数2
    而这个变量地址就类似于图中的push eax -> 参数2
    格式说明符就类似于图中的push Text.00EE3E50 ->参数1
    那为何是先把参数2推入,再把参数1推入呢?
    比如 int Text(a,b,c)这个函数Text参数为a,b,c
    在Windows调用规定中
    这个函数在汇编中会如下体现:

    Push c
    Push b
    Push a
    Call Text函数的地址

    在此你应该能发现参数a,b,c却被反向c,b,a的顺序压入栈中,然后再call Text函数的地址
    pic8
    Call指令当然就是调用scanf函数读取输入flag的值
    pic9

    Add esp,0xc

    由于_cdcel调用约定,故需要进行堆栈平衡

    cmp dword ptr ss:[ebp-0x4],0x2F59

    cmp比较指令, 而这里面比较的两个对象是变量1与0x2F9(12121)
    cmp指令可以影响标志寄存器
    pic10

    Jnz short Text.009E1036

    Jnz跳转指令,由于前面的cmp指令
    若变量1-12121=0         ZF/Z位=1           jnz不跳转
    若变量1-12121!=0        ZF/Z位=0    jnz跳转到009E1036
    pic11
    那这个程序的大致流程就是输入变量1的数以后与12121作比较,如果两个数相等,Z位=1,jnz不跳转,输入Right!;如果两个数不相等,Z位=0,jnz跳转到009E1036上,输出Error!
    然后剩下的代码分析和前面所讲的差不多,大家可以自己写一下注释
    pic12

    00x02 总结

    本篇我们介绍如何用OD来分析一个简单的程序,同学可以在事后自己用OD跑一下,这样会理解的更加深刻
    附件中就有本次文章所用到的几个程序的例子
    下一章 如何用OD(OllyDbg)动态调试程序

    00x03 课后练习

    自己独立分析分析附件中的程序,按照文章的步骤一步一步分析


    游客,如果您要查看本帖隐藏内容请回复


    评分

    参与人数 1魔法币 +5 收起 理由
    cl058015 + 5

    查看全部评分

    本帖被以下淘专辑推荐:

    • · 8086|主题: 5, 订阅: 0
    666666666666666666666666666666666666666666666
    使用道具 举报 回复
    6666666666666666666666666666
    使用道具 举报 回复
    发表于 2018-7-30 08:26:04
    正准备入坑逆向,感谢大牛分享!
    苟非吾之所有,虽一毫而莫取
    使用道具 举报 回复
    新手入门,楼主棒棒的
    使用道具 举报 回复
    币已砸(手动滑稽)
    使用道具 举报 回复
    好文 马上入坑逆向
    使用道具 举报 回复
    66666666666666
    使用道具 举报 回复
    谢谢大佬分享
    使用道具 举报 回复
    感谢分享
    使用道具 举报 回复
    发表于 2018-7-31 15:34:49
    感谢分享
    使用道具 举报 回复
    感谢分享
    使用道具 举报 回复
    发表于 2018-8-6 19:16:30
    感谢分享
    使用道具 举报 回复
    发表于 2018-8-8 10:45:47
    牛逼 大表哥
    使用道具 举报 回复
    发表于 2018-9-10 19:06:13
    不错不错
    使用道具 举报 回复
    学习了。
    使用道具 举报 回复
    12下一页
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册