• 【逆向笔记】OD工具使用-逆向TraceMe.exe


    阅览目录


    名词注释

    一、OD运行环境设置

    1、System breakpoint:系统断点,OllyDbg用CreateProcessA加载DEBUG_ONLY_THIS_PROCESS参数执行,程序运行之后会触发一个INT13,在系统空间里。

    2、Entry point of main module:主模块的入口点,即文件的入口点。

    3、WinMain:程序的WinMain()函数入口点

    OD的设置中-选项-调试设置-事件  中设置,我们选择第三种环境设置。如下:



    二、OD 常用快捷键

    1、系统环境切换:

          Ctrl+G  进入系统领空,左边地址以7开头。Ctrl+G 跳转到API、地址的位置。查看电脑操作系统的交互情况。
         ALT+F9 进入用户领空,左边地址以0开头。ALT+F9跳转到用户程序如Traceme.exe。查看程序进程情况。

    2、无断点时按F2 在所在位置下断点,再按F2取消所在位置已经设置的断点。
          Alt+b 打开断点编辑器,可编辑和删除所有下过的断点。

    3、空格键 可快速切换断点状态。   空格:汇编光标所在的命令。

    4、F9 运行程序。Ctrl+F9.当位于某个CALL中,返回到调用这个CALL的来路地址。这样OD就会停在遇到的第一个返回命令(如RET、RETF或IRET)。


    逆向之猜

    逆向与开发的知识是成正比关系,只有对开发特别熟悉,逆向一个程序才能猜测到该用哪个关键的API才能快速定位到程序的数据处理。

    通过PEID查看程序特征,根据程序语言用IDE生成特征或者熟悉开发的API函数。就可以更方便地让我们定位到获取edit值的函数。

    【GetDlgItemText 函数】 用于获取对话框中指定控件的标题或文本。

    ‘使用OD的快捷键【Ctrl+G】跳转到API的位置下断点。

    F2设置断点,当【GetDlgItemTextA】这个函数被调用OD就会中断

    【ALT+B】快捷键可以打开断点窗口查看,在断点位置按【空格键】可以激活与禁用断点。

    按快捷键【Ctrl+F9】可以回到调用函数的尾部ret处。

    算法逆向

    F7跟进004011E5地址内的函数,进入子程序call 00401340,特别值得注意的是00401359是跳转到4013680的,相关汇编代码注释如下:

    加密函数汇编注释如下:

    00401340  push ebp                                 ;  ebp入栈
    00401341  mov ebp,dword ptr ss:[esp+0xC]           ;  将用户名移动到ebp中
    00401345  push esi                                 ;  esi入栈
    00401346  push edi                                 ;  edi入栈
    00401347  mov edi,dword ptr ss:[esp+0x18]          ;  将参数从堆栈中传给edi(用户名长度值) edi = 5
    0040134B  mov ecx,0x3                              ;  ecx = 3
    00401350  xor esi,esi                              ;  esi 清 0
    00401352  xor eax,eax                              ;  eax 清0
    00401354  cmp edi,ecx
    00401356  jle XTraceMe.00401379                    ;  edi<ecx条件成立时跳转,i<len
    00401358  push ebx
    00401359  /cmp eax,0x7                             ;  比较eax与7的值
    0040135C  jle XTraceMe.00401360                    ;  当等于7,ZF=1短跳转
    0040135E  |xor eax,eax
    00401360  |xor edx,edx                             ;  edx清0
    00401362  |xor ebx,ebx
    00401364  |mov dl,byte ptr ds:[ecx+ebp]            ;  地址低8位的一个字节,dl是存储一个字节的寄存器,ecx = 3,  [ecx+ebp] = d
    00401367  |mov bl,byte ptr ds:[eax+0x405030]       ;  00405030  0C 0A 13 09 0C 0B 0A 08
    0040136D  |imul edx,ebx                            ;  edx * ebx 赋值给 edx, dl为高8位,存储1字节数
    00401370  |add esi,edx                             ;  edx+esi,把值赋予给esi ,esi = 4B0 + 3F2
    00401372  |inc ecx                                 ;  递增指令,ecx 由 3 -> 4 -> 5
    00401373  |inc eax                                 ;  eax = 1,eax++
    00401374  |cmp ecx,edi
    00401376  jl XTraceMe.00401359                    ;  ecx 是否大于等于5(用户名长度), jl指令大于不等于满足时跳转
    00401378  pop ebx
    00401379  push esi                                 ; /<%ld>
    0040137A  push TraceMe.00405078                    ; |Format = "%ld"
    0040137F  push ebp                                 ; |s
    00401380  call dword ptr ds:[<&USER32.wsprintfA>]  ; wsprintfA
    00401386  mov eax,dword ptr ss:[esp+0x1C]
    0040138A  add esp,0xC
    0040138D  push ebp                                 ; /String2
    0040138E  push eax                                 ; |String1
    0040138F  call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; lstrcmpA
    

    程序原先输入的用户名:abcde、序列号:123456。在堆栈窗口看到的是d、e,也就是

       name[3]=‘d’,
       name[4]=‘e’
    

    根据00401367 地址处判断,密码数组索引第3位之后的值逐位取出与固定地址的值比对。然后在0040137A 会输出密钥的值2201。

    仔细逆推一遍:

       edx =  64 * 0C = 4B0
    
       edx =  65 * 0A = 3F2
    
       4B0 + 3F2  =  8A2 
       
    8A2对应的十进制为2210
    

    逆向结论:

    abcde转换为十六进制从索引值第3位开始逐位取值,64:d、65:e ,然后与00405030 0C 0A 13 09 0C 0B 0A 08对应的值进行相乘然后累加。得出的8A2转换成十进制2210就是密码的值。

    把加密函数取出来就成了算号注册机,下面是加密函数反汇编转换来的C代码:

    #include "stdafx.h"
    #include <string.h>
    
    
    
    //char name[65] = "abcdexxxx";
    char name[65];
    char table[8] = { 0xC ,0xA ,0x13 ,0x09 ,0x0C ,0x0B ,0x0A ,0x08 };
    int main()
    {
    	printf(" 输入key:
     ");
    	scanf_s("%s",name,65);
    
    
    	//会用到一个固定地址的值
    	//会用到姓名里的后两位
    	//eax = i
    	int user_len = strlen(name);
    	int key_code= 0; //esi
    	
    
    
    	int count_ecx = 3;  //esi
    	int eax = 0;      //eax
        for (; count_ecx<user_len;)
    	{
    		if (eax>7)
    			eax = 0;
    
    		int ebx = 0;
    		int edx = 0;
    
    		edx = name[count_ecx];
    		ebx = table[eax];
    		ebx = edx * ebx;
    
    		key_code += ebx;
    		count_ecx++;
    		eax++;
    
    	}
    	printf("key_code: %d", key_code);
        return 0;
    }
    
    

    暴力破解

    在定位到GetDlgItemText这个API处,F8单步步过向下跟随到有test判断的地方,注意观察数据堆栈区的位置。

    汇编指令注释如下:

    0040119C  mov esi,dword ptr ss:[esp+0x100]         ;  Case 3F5 of switch 0040115E
    004011A3  mov edi,dword ptr ds:[<&USER32.GetDlgIte>;  user32.GetDlgItemTextA
    004011A9  push ebx
    004011AA  lea eax,dword ptr ss:[esp+0x4C]
    004011AE  push 0x51                                ; /Count = 51 (81.)
    004011B0  push eax                                 ; |Buffer
    004011B1  push 0x6E                                ; |ControlID = 6E (110.)
    004011B3  push esi                                 ; |hWnd
    004011B4  call edi                                 ; GetDlgItemTextA
    004011B6  lea ecx,dword ptr ss:[esp+0x9C]
    004011BD  push 0x65                                ; /最大字符数
    004011BF  push ecx                                 ; |文本缓冲区指针
    004011C0  push 0x3E8                               ; |控件标识
    004011C5  push esi                                 ; |对话框句柄
    004011C6  mov ebx,eax                              ; |将用户名的长度转到ebx中
    004011C8  call edi                                 ; GetDlgItemTextA
    004011CA  mov al,byte ptr ss:[esp+0x4C]            ;  将用户名的第一个字节给al
    004011CE  test al,al                               ;  检查有没有输入用户名
    004011D0  je XTraceMe.00401248                     ;  如果没有输入用户名跳走,告知输入的字符太少,zf=0跳转
    004011D2  cmp ebx,0x5
    004011D5  jl XTraceMe.00401248                     ;  如果用户名不大于5那么就跳转到错误提示处
    004011D7  lea edx,dword ptr ss:[esp+0x4C]          ;  用户名地址放到edx中
    004011DB  push ebx                                 ;  用户名长度
    004011DC  lea eax,dword ptr ss:[esp+0xA0]          ;  密码地址放到eax
    004011E3  push edx                                 ;  用户名地址入栈
    004011E4  push eax                                 ;  密码地址入栈
    004011E5  call TraceMe.00401340                    ;  调用函数,相当于a("123456",abcde,5)
    004011EA  mov edi,dword ptr ds:[<&USER32.GetDlgIte>;  user32.GetDlgItem
    004011F0  add esp,0xC                              ;  平衡堆栈
    004011F3  test eax,eax                             ;  函数返回值都是在eax里面的,eax=0注册失败,eax=1注册成功
    004011F5  nop                                      ;  zf标志位为0,满足条件时跳转
    
    

    第一个test指令首先对比用户名是否大于5,不大于5就跳转到弹出错误提示的地方。否则继续执行。F8单步步过进入【GetDlgItemTextA】后面的调用查看相关的代码。004011E5地址处call调用一个函数,并且在之前push了三个参数。

    第二处test指令下面那条je跳转指令用nop填充掉。

    然后右键【复制到可执行文件】-【所有修改】

    选择全部复制

    选择【保存文件】,暴力破解就完成了

    成功后输入任何密码都提示成功弹框,截图如下:

    样本引用

    《加密与解密》这本书里的附带小程序TraceMe.exe。

    http://pan.baidu.com/s/1miRZZg0 bdq1

  • 相关阅读:
    从联想昭阳到MacBook Pro,致我的那些败家玩意——电脑
    讲真,这两款idea插件,能治愈你英语不好的病
    大专学历以后就职会不会有瓶颈
    恕我直言,我怀疑你并不会生成随机数
    一文教会你如何在 Spring 中进行集成测试,太赞了
    30岁开始学编程晚吗?
    delphi下TList的用法
    在Delphi下基于MapWinGIS添加和删除图层标注的方法
    SMS模型格网转换为MIKE21的格网源代码
    样条曲线的Fortran程序
  • 原文地址:https://www.cnblogs.com/yuanscn/p/12577607.html
Copyright © 2020-2023  润新知