工作中需要完成一些加密狗的简单功能,现稍作记录。具体详见深思API帮助工具及帮助文档。
1.加密狗登录与登出
1)登录
slm_login函数详细说明:
1.会自动查找跨锁查找许可句柄。
2.在runtime库里面分配管理内存与进程线程信息。
3.对与调用者需要定期监控会话进程,如果进程死锁或者崩溃,自己释放对应的内存和其它资源。
4.LM库属于客户定制自动编译,包含RSA 公钥、认证设备ID、开发商编号等一切认证手段。
5.LM后续操作必须都要login之后才能有权限操作 比如读写、加解密等操作。
bool login_dog()
{
SS_UINT32 status = 0;
SS_BYTE tmp_buf[1024] = { 0 };
SS_UINT32 buf_len = 0;
ST_INIT_PARAM init_param = { 0 };//初始化参数
ST_LOGIN_PARAM login_param = { 0 };//登录相关参数
// psd是必传参数,每个开发者私有,不可泄露,请从深思云开发者中心获取
SS_BYTE psd[16] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
init_param.version = SLM_CALLBACK_VERSION02;
init_param.pfn = NULL;
init_param.flag = 0;
memcpy(init_param.password, psd, sizeof(psd));
/*slm_init函数是深思Runtime API的初始化函数,在使用其他深思Runtime API之前,
必须要先调用此函数, 初始化函数主要是对Runtime环境的初始化,并且启动反调试机制。*/
status = slm_init(&init_param);
if (status != SS_OK)
{
return false;
}
login_param.license_id = LICENSEID;
login_param.size = sizeof(ST_LOGIN_PARAM);
login_param.login_mode = SLM_LOGIN_MODE_LOCAL;
login_param.timeout = 600;
status = slm_login(&login_param, STRUCT, &g_slm_handle, NULL);
if (status != SS_OK)
{
return false;
}
return true;
}
2)登出
不再使用许可时,需要调用slm_logout退出登录许可,以免其占用Runtime库中的内存和资源。
例如,由于Runtime 库只支持最多256个登录句柄,若只登录许可而不登出许可,一旦超出256个登录点将占满所有Runtime库中的资源,导致后续的登录失败
bool logout_dog()
{
if (g_slm_handle == 0)
{
return true;
}
SS_UINT32 status = 0;
status = slm_logout(g_slm_handle); //currenthandle当前许可句柄值,通过login的第三个参数得到
if (status != SS_OK)
{
return false;
}
return true;
}
2.加密狗信息读取与写入
1)读取
这里一开始用了slm_mem_read和slm_mem_write,但出现很诡异的状况,参照参考文档去编写后仍无法正常读写,只有在写完后马上读才可以读到。后查阅文档,上述接口读写的是SenseShield服务内存托管内存(SS的托管内存)
文档详述:
SS的托管内存原理是 APP利用有效的许可作为凭证,在SS模块内数据加密且数据校验,其内存二进制数据没有明文, 并且无法非法修改。黑客极难查看与篡改使用。
用户可以把自己APP的一些敏感数据保存到SS的托管内存,比如帐号口令,SQL数据库的帐号与密码,涉及到操作权限的临时数据放到SS内存托管里面。 另外一方面APP跟SS耦合度极大的提高,防止黑客脱离SS调试与运行。
内存托管的好处:
1.敏感数据内存不泄密、无法篡改。
2.可以跨线程安全交互数据。
3.APP软件、许可、SS三者强耦合,软件防止被破解能力极高。(黑客需要手工剥离和重建才能使软件)
所以读写出错貌似是一些权限之类的问题,需要在其他区域进行读写。
后改用slm_user_data_getsize,slm_user_data_read,slm_user_data_write,可正常读取写入二进制信息。
在读写前需要通过slm_user_data_getsize先获取许可的用户数据区大小。
/** 许可数据区类型枚举 */
typedef enum _LIC_USER_DATA_TYPE {
/** 只读区 */
ROM = 0,
/** 读写区 */
RAW = 1,
/** 公开区 */
PUB = 2,
} LIC_USER_DATA_TYPE;
//存储读取信息的结构体
struct TimeLock
{
int version;///< 版本号,便于以后识别使用
int64_t dogId;///< 加密狗Id,最多支持32位
bool isUnLock;///< 是否完全解锁 =true:完全解锁 =false:时钟锁定
int time[6];///< 年月日时分秒
char reserve[512];///< 备用
};
bool decodePaFile(QString & outTime, QString & outShellNum)
{
TimeLock m_TimeLock;//存储读取信息的结构体
memset(&m_TimeLock, 0, sizeof(TimeLock));
//读锁
login_dog();
SS_UINT32 status = 0;
SS_UINT32 offerset = 0;
SS_UINT32 inputsize = 0;
SS_BYTE tmp_buf[18432] = { 0 };//1024*18
SS_UINT32 buf_len = 18432;
SS_UINT32 length = 0;
status = slm_user_data_getsize(g_slm_handle, RAW, &length); //currenthandle当前许可句柄值,通过login的第三个参数得到
status = slm_user_data_read(g_slm_handle, RAW, tmp_buf, offerset, 560);//读取
if (status != SS_OK)
{
return false;
}
memcpy(&m_TimeLock, tmp_buf, 560);
logout_dog();
return true;
}
2)写入
写入的流程和读取类似,下面例子是读取加密狗授权文件(.bin),并将其写入加密狗中。
bool LoadBinFile(QString strPath)
{
if (strPath == "")
{
return false;
}
//写入锁
QFile file(strPath);
if (!file.open(QIODevice::ReadOnly))
{
return false;
}
QByteArray text = file.readAll();
if (text.size() != sizeof(TimeLock))
{
return false;
}
else
{
login_dog();
SS_UINT32 status = 0;
SS_UINT32 offerset = 0;
SS_UINT32 inputsize = 0;
SS_UINT32 length = 0;
status = slm_user_data_getsize(g_slm_handle, RAW, &length); //currenthandle当前许可句柄值,通过login的第三个参数得到
if (status == SS_OK && length != 0)
{
std::unique_ptr<SS_BYTE[]> temp_buf(new SS_BYTE[560]);
memcpy(temp_buf.get(), text.data(), 560);
status = slm_user_data_write(g_slm_handle, temp_buf.get(), offerset, 560);
if (status != SS_OK)
{
return false;
}
}
logout_dog();
}
return true;
}
3.获取设备信息
slm_enum_device,此接口可以枚举到本地锁的设备信息,包括锁的锁号,锁的时间等。
下面例子为获取设备锁锁号和锁内时间,这里的锁内时间是指锁内时钟的时间,要与锁设定的到期时间区分开来。
bool getDeviceInfo(QString &outRetTime, QString &outRetShellNum)
{
SS_UINT32 status = 0;
char* result = nullptr;
status = slm_enum_device(&result);
if (status == SS_OK && result != 0)
{
//这里拆解信息
QString strInfo(result);
slm_free(result);
//获取时间,时间存储的形式为秒(s)数
int nTimeIndex = strInfo.indexOf("clock");
if (-1 != nTimeIndex)
{
//这里简单进行字符串处理,获取时间
int nEndIndex = strInfo.indexOf(",", nTimeIndex);
outRetTime = strInfo.mid(nTimeIndex + 7, nEndIndex - nTimeIndex - 7);
outRetTime = outRetTime.trimmed();
}
//获取锁号
int nShellIndex = strInfo.indexOf("shell_num");
if (-1 != nShellIndex)
{
int nEndIndex = strInfo.indexOf(",", nShellIndex);
//shell_num":
outRetShellNum = strInfo.mid(nShellIndex + 11, nEndIndex - nShellIndex - 11);
outRetShellNum = outRetShellNum.replace("\"", "");
outRetShellNum = outRetShellNum.trimmed();
}
return true;
}
return false;
}
全部设备信息,根据这个可以截获需要的信息。