首页
社区
课程
招聘
[原创]【逆向解密】WannaRen加密文件的解密方法
2020-4-11 19:50 21353

[原创]【逆向解密】WannaRen加密文件的解密方法

2020-4-11 19:50
21353

WannaRen简介

这是最近在各大论坛十分火热的勒索病毒WannaRen,包括微博、卡饭等等,虽然名字很像某家族病毒,但实际上并不是,并且下图这个程序也并非勒索程序,而是作者提供密码后的解密程序。 

庆幸的是,该作者公布了RSA的私钥,使得经过该勒索病毒加密的文件得以还原,各个厂商也都纷纷推出了工具,所以我也利用周末的时间研究了一下该样本,学习一下如何调用库来解密这些文件。
真正勒索程序是下面这张图片的dll模块,WINWORD.EXE是白文件,wwlib.dll是恶意代码的主要模块,you为加密的病毒数据文件

该勒索病毒wannaren使用的是RSA+RC4组合加密文件,没有密钥的情况下很难解密,但是因为RC4强度并不高也可以通过爆破来解密,因为作者公开了其密钥,所以使得这个勒索病毒可以解密,这篇文章将不再详述逆向分析病毒的行为,因为已经有安全厂商的报告说明的很详细了,主要目的是如何解密被wannaren加密的文件。

获取加密文件

首先,要构造勒索病毒环境,制造勒索现场,将WINWORD.EXE和wwlib.dll放置C:\ProgramData目录下,you放在C:\Users\Public目录下

执行WINWORD.EXE 目录下会生成一个ym文件,使用记事本打开将时间调整到系统当前时间的几天前(我改的4天)


然后准备几个文件,尝试让wannaren加密,再次执行WINWORD.EXE病毒就会执行起来(或者重新启动,因为病毒会将自身注册服务开机启动)



这是被勒索文件的结构: 

编程解密文件

CR4加密数据解密后文件的头与尾部有标记存在,所以在内存中将标记清除才能还原文件 。
编译环境是win10+Visual Studio 2017
我使用的是OpenSSL进行解密的下载地址:https://slproweb.com/products/Win32OpenSSL.html
使用下载已编译好的安装包极为方便,具体的步骤大家可以自行搜索即可安装,这里就不详细赘述了。(值得注意的是下载要根据自身项目选择,项目是32位就下载32位,64就选64)
引用目录、动态库等等将环境配置好后就可以开始写代码了

#include <iostream>
#include "fstream" 
#include <Windows.h>
#include <openssl/rsa.h>
#include <openssl/rc4.h>
#include "openssl/pem.h"
#include "openssl/err.h"
#include "string.h"
#include "resource.h"
extern "C" {
#include "openssl/applink.c"
};
using namespace std;
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")


#define OPENSSLKEY "test.key"
#define RELESE(P) if (P){delete P;P = NULL;}
#define RELESE_ARRAY(P) if (P){delete[] P;P = NULL;}


FILE * pFile;
long lSize;
char * buffer;
size_t result;


// 释放资源文件
bool FreeResFile(DWORD dwResName, LPCSTR lpResType, LPCSTR lpFilePathName)
{
	HMODULE hInstance = ::GetModuleHandle(NULL);//得到自身实例句柄  
	
	HRSRC hResID = ::FindResource(hInstance, MAKEINTRESOURCE(dwResName), lpResType);//查找资源  
	HGLOBAL hRes = ::LoadResource(hInstance, hResID);//加载资源  
	LPVOID pRes = ::LockResource(hRes);//锁定资源  

	if (pRes == NULL)//锁定失败  
	{
		return FALSE;
	}
	int dwResSize = ::SizeofResource(hInstance, hResID);//得到待释放资源文件大小  
	HANDLE hResFile = CreateFile(lpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建文件  

	if (INVALID_HANDLE_VALUE == hResFile)
	{
		//创建文件失败  
		return FALSE;
	}

	int dwWritten = 0;//写入文件的大小     
	WriteFile(hResFile, pRes, dwResSize, (LPDWORD)&dwWritten, NULL);//写入文件  
	CloseHandle(hResFile);//关闭文件句柄  

	return (dwResSize == dwWritten);//若写入大小等于文件大小,返回成功,否则失败  
}

//RSA解密CR4密钥
char *my_decrypt(char *str, char *path_key) {
	char *p_de;
	RSA *p_rsa;
	FILE *file;
	int rsa_len;

	if ((file = fopen(path_key, "r")) == NULL) {

		perror("open key file error");

		return NULL;
	}

	if ((p_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL) {


		ERR_print_errors_fp(stdout);

		return NULL;
	}

	rsa_len = RSA_size(p_rsa);

	p_de = (char *)malloc(rsa_len + 1);

	memset(p_de, 0, rsa_len + 1);

	if (RSA_private_decrypt(rsa_len, (unsigned char *)str, (unsigned
		char*)p_de, p_rsa, RSA_PKCS1_PADDING) < 0) {

		return NULL;
	}

	RSA_free(p_rsa);

	fclose(file);
	return p_de;
}

// 使用rc4解密到内存  
char *  TestRc4DecryptFile(char* file_data, int data_size,
	const char *rc4_dencrypt_key, int encrypt_chunk_size = 16)
{
	char * out_data_all = NULL;
	if ((out_data_all = (char*)malloc(data_size)) == NULL)
	{
		printf("no enough memory!\n");
		return 0;
	}
	memset(out_data_all, 0, data_size);
	
	//用指定密钥对一段内存进行加密,结果放在outbuffer中  
	char code[64] = { 0 };
	int codelen = sizeof(code);
	RC4_KEY rc4_key;
	RC4_set_key(&rc4_key, strlen(rc4_dencrypt_key), (unsigned char *)rc4_dencrypt_key);

	char *in_data = new char[encrypt_chunk_size + 1];
	char *out_data = new char[encrypt_chunk_size + 1];

	int i = 0;
	//循环解密
	while (i < data_size)
	{
		encrypt_chunk_size = (data_size - i) / 16 > 0 ? 16 : data_size % 16;
		memcpy(in_data, (file_data + i), encrypt_chunk_size);
		RC4(&rc4_key, encrypt_chunk_size, (unsigned char *)in_data, (unsigned char *)out_data);
		memcpy(out_data_all+i, out_data, encrypt_chunk_size);
		i += encrypt_chunk_size;
	};

	//解密后的数据文件仍然有标记wannaren,所以需要再处理一下
	char* restore_file_data = NULL;
	if ((restore_file_data = (char*)malloc(data_size - 0x12)) == NULL)
	{
		printf("no enough memory!\n");
		return 0;
	}
	memset(restore_file_data, 0, data_size - 0x12);
	memcpy(restore_file_data, out_data_all + 0x9, data_size - 0x12);

	RELESE_ARRAY(in_data);
	RELESE_ARRAY(out_data);
	RELESE_ARRAY(out_data_all);
	return restore_file_data;
}

void out_help()
{
	printf("	\nThis is a WannaRen decryption program\n\n");
	printf("	/h		--See how to use it\n\n");
	printf("	/u [filename]	--encryption a file name is original file name\n\n");
	printf("	--Files are only allowed to be WannaRen encryption file--\n");
	printf("	");

}


bool loadfile(char *loadFileName)
{
	// 一个不漏地读入整个文件,只能采用二进制方式打开
	pFile = fopen(loadFileName, "rb");
	if (pFile == NULL)
	{
		fputs("File error", stderr);
		printf("open file fail");
		return false;
	}

	// 获取文件大小 
	fseek(pFile, 0, SEEK_END);
	lSize = ftell(pFile);
	rewind(pFile);

	// 分配内存存储整个文件
	buffer = (char*)malloc(sizeof(char)*lSize);
	if (buffer == NULL)
	{
		fputs("Memory error", stderr);
		printf("Memory alloc falil");
		return false;
	}

	// 将文件拷贝到buffer中 
	result = fread(buffer, 1, lSize, pFile);
	if (result != lSize)
	{
		fputs("Reading error", stderr);
		printf("Load file to memory falil");
		return false;
	}
	return true;
}


char* GetRC4key()
{
	char file_key[256] = { 0 };
	memcpy(file_key, (BYTE *)(buffer + 0xB), 0x100);
	FreeResFile(IDR_KEY3, "KEY", OPENSSLKEY);
	return my_decrypt(file_key, OPENSSLKEY);
}

int DecryptData(char *loadFileName)
{
	char* rc4Key = GetRC4key();
	char* file_data = NULL;//RC4加密的指针
	
	//计算数据位置,来分割获取数据
	if ((file_data = (char*)malloc(lSize - 0x11F)) == NULL)
	{
		printf("no enough memory!\n");
		return -1;
	}
	memset(file_data, 0, lSize - 0x11F );
	memcpy(file_data, (BYTE *)(buffer + 0x116), lSize - 0x11F);

	//调用RC4解密数据块,返回解密数据
	file_data = TestRc4DecryptFile(file_data, lSize - 0x11F, rc4Key);
	
	
	//保存到文件
	FILE *p2file;
	char outFile[MAX_PATH];
	memset(outFile, 0, sizeof(outFile));
	memcpy(outFile, loadFileName, strlen(loadFileName));
	//截断后缀
	char * set = strrchr(outFile, (char)0x2E);
	*set = 0x00;

	p2file = fopen(outFile, "wb");
	int a = fwrite(file_data, lSize - 0x11F-0x12, 1, p2file);
	fclose(p2file);
	/* 打印结果,并释放内存 */
	
	printf("succeed");
	RELESE_ARRAY(file_data);
	return 0;
}


int main(int argc, char * argv[]) {

	switch (argc)
	{
	case 1:
		out_help();
		break;
	case 2:
		if (!strcmp(argv[1], "/h"))
		{
			out_help();
		}
		else
		{
			printf("	\n**Error parameter**\n");
			out_help();
		}
		break;

	case 3:
		if (!strcmp(argv[1], "/u"))
		{
			printf("uncompress data ");
			if (loadfile(argv[2]));
				DecryptData(argv[2]);
		}
		else
		{
			out_help();
		}
		break;
	default:
		printf("	\n**Error parameter**\n");
		out_help();
		break;
	}
	return 0;
}

我将作者公开的私钥放到了资源中,释放后读取进行解密。
上面的代码中还是有很多瑕疵的,比如我没有判断文件到底是不是WannaRen加密的文件等等,有兴趣的话你可以完善一下。

测试一下刚才的加密后图片进行解密测试一下看下效果。


看下文件是不是可以正常浏览

解密成功
主要代码已经贴出来了,工程就不放了,毕竟还是要自己动手才能更好的进步。

上传了编译好的程序,需要cmd参数执行
病毒样本由于超出附件大小,所以大家可以在卡饭上下载:https://bbs.kafan.cn/thread-2177942-1-1.html

sha1
534a7ea9c67bab3e8f2d41977bf43d41dfe951cf
0ee594c8d687596f68a7b259097fe3296a86e6c4
17ff3d8995e28d15ebf081d02b8532a6813897cf

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-4-18 11:35 被Cc28256编辑 ,原因:
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (4)
雪    币: 17421
活跃值: (5004)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
jishuzhain 7 2020-4-11 22:39
2
0
之前本地实验一直无法加密,原来是需要修改fm文件里的时间,楼主,你好棒!
雪    币: 2011
活跃值: (8355)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2DCoXrq 2020-4-12 08:59
3
0
还要修改文件里面的时间?难怪我一直跑不出行为。。。
雪    币: 10845
活跃值: (1049)
能力值: (RANK:190 )
在线值:
发帖
回帖
粉丝
看场雪 3 2020-12-23 11:53
4
0
感谢LZ分享
雪    币: 2604
活跃值: (231)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
星雪鸢尾 2021-2-4 02:48
5
0
感谢,另外环世界瞩目
游客
登录 | 注册 方可回帖
返回