CrackMe[简单]-练习用CM-1

selph
selph
发布于 2021-04-24 / 722 阅读
0
0

CrackMe[简单]-练习用CM-1

学习环境:Windows 10 20H2 + Visual Studio 2019 + IDA

参考书籍:《C++反汇编与逆向分析技术揭秘》Chapter 5.1--5.3

看这种书籍上的介绍吧,总是想去动手练练,书上也没提供多少练习,这里啊,就开始自己写Crackme给自己练吧,当天学啥了,就写啥来逆

源程序

#include <stdio.h>
#include <string.h>
// 输入参数:ch5_carck1.exe username password
int main(int argc, char* argv[], char* envp) {
	char szPass[0x20] = { 0 };
	char szPassAdd[0x20] = { 0 };
	if (strlen(argv[1]) < 5) {
		printf("用户名过短");
		return 0;
	}
	strcpy(szPass, argv[1]);
	// 判断首字母是否是数字
	if ('0' < argv[1][0] && argv[1][0] < '9') {
		strcat(szPass, "_2021_");
		sprintf(szPassAdd,"%d",argv[1][0] + argv[1][2] + 1);
		strcat(szPass, szPassAdd);
	}
	else if (argv[1][0] = 'A') {
		strcat(szPass, "_2021_");
		sprintf(szPassAdd, "%d", argv[1][1] * argv[1][3]);
		strcat(szPass, szPassAdd);
	}
	else {
		strcat(szPass, "_2021_");
		sprintf(szPassAdd, "%d", argv[1][4] / argv[1][1]);
		strcat(szPass, szPassAdd);
	}
	//printf("正确密码:%s", szPass);
	if (!strcmp(szPass, argv[2])) {
		printf("密码正确");
	}
	else {
		printf("密码错误");
	}
	return 0;
}

由于代码写错了,else if那里条件少写了个等号,所以编译出来这个条件分支就没了。。尴尬,将错就错吧,下次争取不犯错

逆向分析

这里就IDA静态分析,用VS2019编译好release版本,保留pdb文件,然后就开始吧

F5C代码和反汇编一起看,分析效率比较高

IDA还原C代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *input; // esi
  char *input_1; // ecx
  char input_nextchar; // al
  char v6; // dl
  char *szPass_t; // ecx
  int nNum; // ecx
  unsigned int szPassAdd_Length; // edx
  char *szPass_t1; // edi
  int v14; // ecx
  const char *conclusion; // eax
  char szPassAdd[32]; // [esp+4h] [ebp-44h] BYREF
  char szPass[32]; // [esp+24h] [ebp-24h] BYREF

  *(_OWORD *)szPass = 0i64;
  input = (char *)argv[1];
  *(_OWORD *)&szPass[16] = 0i64;
  *(_OWORD *)szPassAdd = 0i64;
  *(_OWORD *)&szPassAdd[16] = 0i64;

  if ( strlen(input) >= 5 )
  {
    // strcpy
    input_1 = input;
    do
    {
      input_nextchar = *input_1++;
      input_1[szPass - input - 1] = input_nextchar;
    }
    while ( input_nextchar );

    v6 = *input;
    szPass_t = &szPassAdd[31];
    if ( (unsigned __int8)(*input - '1') > 7u )
    {
      *input = 'A';
      while ( *++szPass_t )                     // 将指针指向字符串最后
        ;
      *(_DWORD *)szPass_t = *(_DWORD *)a202;    // strcat(szPass,"_202")
      strcpy(szPass_t + 4, "1_");               // strcat(szPass, "1_");
      nNum = argv[1][1] * argv[1][3];           // nNum = argv[1][1] * argv[1][3]
    }
    else
    {
      while ( *++szPass_t )
        ;
      *(_DWORD *)szPass_t = *(_DWORD *)a202;
      strcpy(szPass_t + 4, "1_");
      nNum = v6 + input[2] + 1;                 // nNum = input[0] + input[2] + 1
    }
    sprintf(szPassAdd, "%d", nNum);             // szPassAdd = nNum
    // strcat(szPass,szPassAdd)
    szPassAdd_Length = strlen(szPassAdd) + 1;
    szPass_t1 = &szPassAdd[31];
    while ( *++szPass_t1 )
      ;
    qmemcpy(szPass_t1, szPassAdd, szPassAdd_Length);

    v14 = strcmp(szPass, argv[2]);
    if ( v14 )
      v14 = v14 < 0 ? -1 : 1;
    conclusion = "密码错误";
    if ( !v14 )
      conclusion = "密码正确";
    printf(conclusion);
  }
  else
  {
    printf("用户名过短");
  }
  return 0;
}

乱七八糟的,第一次用IDA F5功能看代码,我发现这里有些函数被优化到识别不出来

strlen识别出来了,strcat、strcpy反而识别不出来,不知道是啥问题

strcpy 函数

代码进入if分支之后的第一段:

    input_1 = input;
    do
    {
      input_nextchar = *input_1++;
      input_1[szPass - input - 1] = input_nextchar;
    }
    while ( input_nextchar );

看他的汇编:

.text:004010D0 strcpy:                                 ; CODE XREF: _main+5B↓j
.text:004010D0                 mov     al, [ecx]       ; ecx里是argv[1], 这里是取第一个字母
.text:004010D2                 lea     ecx, [ecx+1]    ; 取下一个字母的地址到ecx
.text:004010D5                 mov     [edx+ecx-1], al ; 将取出的字母放入szPass的下一位
.text:004010D9                 test    al, al          ; 判断是否把字母全部取出
.text:004010DB                 jnz     short strcpy    ; ecx里是argv

看C看不明白,看汇编秒懂哈哈哈哈

strcat 函数

里面这一段代码出现了两遍

      while ( *++szPass_t )  // 这里while循环将字符串指针指向字符串末尾
        ;	
      *(_DWORD *)szPass_t = *(_DWORD *)a202;	// 将'_202'这4个字节的字符赋值给字符串末尾
      strcpy(szPass_t + 4, "1_");				// 然后使用strcpy函数将'1_'给添加到字符串末尾+4个字节之后

这里看C勉强能看

代码还原

然后就是些零零碎碎的计算了,可以直接做代码还原了:

#include<stdio.h>
#include<string.h>
int main(int argc, char* argv[], char* envp) {
	char szPass[32] = { 0 };
	char szPassAdd[32] = { 0 };
	int nNum = 0;

	if (strlen(argv[1]) >= 5) {
		strcpy_s(szPass,argv[1]);
		if (argv[1][0] - '1' > 7) {
			argv[1][0] = 'A';
			strcat_s(szPass,"_2021_");
			nNum = argv[1][1] * argv[1][3];
		}
		else {
			strcat_s(szPass, "_2021_");
			nNum = argv[1][0] + argv[1][2] + 1;
		}
		sprintf_s(szPassAdd,"%d",nNum);
		strcat_s(szPass,szPassAdd);
		if (!strcmp(szPass, argv[2])) {
			printf("密码正确");
		}
		else {
			printf("密码错误");
		}
	}
	else {
		printf("用户名过短");
	}
}

不知道为什么,这个判断条件被优化成了这个怪怪的东西,但结果是对的

截图演示

image-20210424161215530


评论