通过增加MD5 验证防止Crack 爆破判断的一种思路

文 号

t72841

7 回复 / 1727 浏览


LCatro2 年前 -2015-07-24 21:23t72841 0阶

  爆破程序验证判断的原理


  首先大家来看一段代码:

3:#include
<stdio.h>


4:void main(void) {
5:        int input=0;
00401028   mov        
dword ptr [ebp-4],0

6:        scanf("%d",&input);
0040102F   lea        
eax,[ebp-4]

00401032   push      
eax

00401033   push      
offset string "%d" (00425028)

00401038   call      
scanf (00401110)

0040103D   add        
esp,8

7:        if (input==100)
00401040   cmp        
dword ptr [ebp-4],64h    
把变量input 对比0x64(十进制100)
00401044   jne        
main+45h (00401055)    
如果不等于100 的话条转到地址0x401055
8:            printf("OK!");
00401046   push      
offset string "OK!" (00425024)

0040104B   call      
printf (00401090)

00401050   add        
esp,4

9:        else
00401053   jmp        
main+52h (00401062)    
直接跳到地址0x401062
10:           printf("ERR!");
00401055   push      
offset string "ERR!" (0042501c)

0040105A   call      
printf (00401090)

0040105F   add        
esp,4

11:   }


  标记为红色的代码就是if 判断的主要结构代码.执行的流程如下:
247868



  转换成汇编代码版就是这样
247869




  现在我们知道了if 的结构,那么人家破解程序的原理是怎么样的呢?其实道理很简单,只需要把代码:
00401044  jne         main+45h (00401055)


  改写成
00401044  nop
00401044  nop


  就可以随意输入任何数字都能够成功输出字符串”OK!”

PS:什么是nop ?

  nop 指令是空指令,它本身没有任何意义,也就是说当处理器执行到这条指令时不会做任何的事情.


  那么我们破解成功的原理是什么呢?还是用图片来表达吧.
247870



  可能大家会感到奇怪,为什么这个地方即使输入错误都不会跳转呢?聪明的你可能明白,当程序执行完cmp 对比指令之后,接下来就是要根据对比的结果来进行跳转,问题是现在没有跳转指令,空指令把原来的跳转指令覆盖了,所以处理器只执行两条空指令,什么都不做,然后就输出字符串”OK!” ,最后跳出if 判断语句块.这个就是爆破的原理.


  如何使用验证实现


  上面已经演示出如何把判断结构通过修改指令的形式来使它按照我们的意愿来执行代码流.在一些需要验证密码的程序中,一旦被攻破掉验证这重防御将会使索要所有的数据都变得不安全,于是提出一种防止爆破的思路:通过密码解密数据,然后验证数据签名是否为加密之前的签名,如果相同的话则解密成功

  加密过程图解:
247892


  解密过程图解:
247893


  大家也许会有疑问,假如把判断数据签名的判断结构给爆破掉了是不是就可以达到破解验证的效果呢?其实是不会的,因为程序使用输入的密码来使整一块数据进行运算,即使是让程序跳过了数据验证,运算出来的数据还是错误的,只要密码不正确,那么解密数据的运算得出的数据就永远不会正确.


  在文件加解密程序的应用


  程序代码如下:


Encrypt:


#include <malloc.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
  
#include "encoder_md5.h"
#include "encoder_xor.h"
  
void main(int argc,char** argv) {
    
if (3==argc) {
        
FILE* file=fopen(argv[1],"r");
        
if (NULL!=file) {
            
fseek(file,0,SEEK_END);
            
unsigned long file_size=ftell(file);
            
fseek(file,0,SEEK_SET);
  
            
char* buffer=(char*)malloc(file_size+LENGTH_MD5);
            
if (NULL!=buffer) {
                
fread(buffer+LENGTH_MD5,1,file_size,file);
                fclose(file);
  
                char*
md5=calcu_md5(&buffer[LENGTH_MD5],file_size);
                memcpy(buffer,md5,LENGTH_MD5);
                encoder_xor((unsigned
char*)buffer,file_size+LENGTH_MD5,(unsigned char*)argv[2],strlen(argv[2]));
  
            
    file=fopen(argv[1],"w+");
                
fwrite(buffer,1,file_size+LENGTH_MD5,file);
                fclose(file);
                free(buffer);
            
} else
                printf("Alloc Memory ERROR!");
        
} else
            
printf("Open ERROR!");
    }
}



Decrypt:

#include <malloc.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
  
#include "encoder_md5.h"
#include "encoder_xor.h"
  
void main(int argc,char** argv) {
    
if (3==argc) {
        
FILE* file=fopen(argv[1],"r");
        
if (NULL!=file) {
            
fseek(file,0,SEEK_END);
            
unsigned long file_size=ftell(file);
            
fseek(file,0,SEEK_SET);
  
            
char* buffer=(char*)malloc(file_size);
            
if (NULL!=buffer) {
                fread(buffer,1,file_size,file);
                fclose(file);
  
                encoder_xor((unsigned
char*)buffer,file_size,(unsigned char*)argv[2],strlen(argv[2]));
                char*
md5=calcu_md5(&buffer[LENGTH_MD5],file_size-LENGTH_MD5);
                char
sorc_md5[LENGTH_MD5+1]={0};
                char
dest_md5[LENGTH_MD5+1]={0};
                
memcpy(sorc_md5,buffer,LENGTH_MD5);
                
memcpy(dest_md5,md5,LENGTH_MD5);
                if (!strcmp(sorc_md5,dest_md5))
{
                    file=fopen(argv[1],"w+");
                    
fwrite(&buffer[LENGTH_MD5],1,file_size-LENGTH_MD5,file);
                    fclose(file);
                } else
                    printf("ERROR
Password!");
  
                free(buffer);
            } else
                printf("Alloc Memory
ERROR!");
        
} else
            
printf("Open ERROR!");
    }
}


  测试:

  Encrypt 下建立test.txt ,随意输入一些数据

247876


  在控制台中加密文件

247872


  查看test.txt 文件的数据

247873


  解密时输入错误密码

247874


  输入正确密码

247875


  数据正常恢复


247876


  输入密码错误时堆的数据情况

247894



坚定的潜水党2 年前 -2015-07-24 23:54781104 1阶
= =
同时爆破验证和签名

LCatro2 年前 -2015-07-25 01:37781116 2阶
签名是MD5 的,不可能做到爆破,密码长度也可以随意设置,就像RSA P/S Key 一样选择512 或者1024 位密码来加密..

smith2 年前 -2015-07-28 17:21781917 3阶
破解程序和破解数据加密的场景搞混了吧。
破解程序如破解正版软件,改跳转指令确实是常用的方法。这个时候攻击的目标是程序自身
但楼主的程序是数据加密的场景,如解压一个winrar压缩包,要提供密钥才能解压。这个时候攻击的是加密的算法和密钥,如果这个时候去攻击winrar这个软件是徒劳无功的。因为winrar自己也不知道密钥。

那抹余辉2 年前 -2015-07-28 20:05781963 4阶
#include "encoder_md5.h"

#include "encoder_xor.h"
我是新手、缺少这两个头文件,怎么办

LCatro2 年前 -2015-07-29 14:54782128 5阶
引用 smith:
破解程序和破解数据加密的场景搞混了吧。
破解程序如破解正版软件,改跳转指令确实是常用的方法。这个时候攻击的目标是程序自身
但楼主的程序是数据加密的场景,如解压一个winrar压缩包,要提供密钥才能解压。这个时候攻击的是加密的算法和密钥,如果...
是这样的,在数据加密验证的时候Crack 了那个特定的判断就可以输入任意密码绕过验证,之前写过一个类Winrar 的程序,过了很久自己把关键的判断给破解就可以绕过密码的输入验证..

phpskycn2 年前 -2015-08-04 01:06783286 6阶
同样没看明白LZ想实现什么。
如果从数据加密/解密的角度,增加的验证环节基本没有价值,因为它发生在数据解密之后,其加密的强度依旧取决于加密算法本身,没有任何改变。
从程序破解/反破解的角度,这个方法也没用什么作用

radio2 年前 -2015-08-04 02:17783289 7阶
引用 LCatro:
是这样的,在数据加密验证的时候Crack 了那个特定的判断就可以输入任意密码绕过验证,之前写过一个类Winrar 的程序,过了很久自己把关键的判断给破解就可以绕过密码的输入验证..
对于数据加密来说破解掉输入验证没有价值吧。去掉判断以后用来解密的密码仍然是错误的密码,解密出来的仍然是错误的数据(假设此时仍能够成功解密,比如解密之后没有校验明文之类的)。另外貌似一般都是解密完之后校验明文是否正确而不是解密之前先判断密码的吧。

返回 软件综合
返回 本页顶部

想参与大家的讨论?现在就 登陆 或者 注册


nkc Development Server https://github.com/lovetheory/nkc2

科创研究院 (c)2005-2016

蜀ICP备11004945号-2 川公网安备51010802000058号