【逆向解密】WannaRen加密文件的解密方法
2021-01-04 18:58:00 Author: mp.weixin.qq.com(查看原文) 阅读量:112 收藏

本文为看雪论优秀文章

看雪论坛作者ID:Cc28256

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

- End -

看雪ID:Cc28256

https://bbs.pediy.com/user-home-845934.htm

  *本文由看雪论坛 Cc28256 原创,转载请注明来自看雪社区。

# 往期推荐

公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458378868&idx=1&sn=495643d2a8b0167a290737a90236475f&chksm=b180d2fe86f75be8f3cb0be95e76731b13b9052fd06ac6e08250f14b9a30114da95710e1826f#rd
如有侵权请联系:admin#unsafe.sh