Security-Enhanced CRT,边界与潜规则

Windows Security-Enhanced CRT 相关函数(即_s后辍函数)并不是对老函数的直接替代,其中不仅有更严格的参数检查,还会有buffer的重置操作等,使用的时候要格外小心,一定要仔细阅读MSDN描述或者VC runtime源码 (以Visual Studio 2008为例: C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src)。

下面不妨测试3个代码片段:

测试代码【C1】:

void main() {
    char dst1[8];
    memcpy(dst1, NULL, 0);
    memcpy(NULL, NULL, 0);
}

测试代码【C2】:

void main() {

    char dst1[8];

    memcpy_s(dst1, 8, NULL, 0);
    memcpy_s(NULL, 0, NULL, 0);
    char *dst2 = (char *)malloc(0);
    if (dst2) {
        memcpy_s(dst2, 0, NULL, 0);
    free(dst2);
    }
}

测试代码【C3】:

void main() {

    char dst1[8];

    memcpy_s(NULL, 0, dst1, 8);

    char *dst2 = (char *)malloc(0);
    if (dst2) {
        memcpy_s(dst2, 0, dst1, 8);
        free(dst2);
    }
}

这几段代码实际上什么也没有做,只是参数有变化而已,但测试中会发现【C3】会导致程序崩溃。具体原因就是因为memcpy_s进行了更严格的参数检测,可对参考memcpy_s的源码,引用自Visual Studio 2008 runtime source memcpy_s.c:

/***
*  memcpy_s.c - contains memcpy_s routine
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*  Purpose:
*       memcpy_s() copies a source memory buffer to a destination buffer.
*       Overlapping buffers are not treated specially, so propagation may occur.
*
*******************************************************************/

#include <cruntime.h>
#include <string.h>
#include <internal.h>

/***
*  memcpy_s - Copy source buffer to destination buffer
*
*  Purpose:
*       memcpy_s() copies a source memory buffer to a destination memory buffer.
*       This routine does NOT recognize overlapping buffers, and thus can lead
*       to propagation.
*
*       For cases where propagation must be avoided, memmove_s() must be used.
*
*  Entry:
*       void *dst = pointer to destination buffer
*       size_t sizeInBytes = size in bytes of the destination buffer
*       const void *src = pointer to source buffer
*       size_t count = number of bytes to copy
*
*  Exit:
*       Returns 0 if everything is ok, else return the error code.
*
*  Exceptions:
*       Input parameters are validated. Refer to the validation section of the function.
*       On error, the error code is returned and the destination buffer is zeroed.
*
*******************************************************************/

errno_t __cdecl memcpy_s(
    void * dst,
    size_t sizeInBytes,
    const void * src,
    size_t count
)
{
    if (count == 0)
    {
        /* nothing to do */
        return 0;
    }

    /* validation section */
    _VALIDATE_RETURN_ERRCODE(dst != NULL, EINVAL);
    if (src == NULL || sizeInBytes < count)
    {
        /* zeroes the destination buffer */
        memset(dst, 0, sizeInBytes);

        _VALIDATE_RETURN_ERRCODE(src != NULL, EINVAL);
        _VALIDATE_RETURN_ERRCODE(sizeInBytes >= count, ERANGE);
        /* useless, but prefast is confused */
        return EINVAL;
    }

    memcpy(dst, src, count);
    return 0;
}

参考阅读:

1, _snwprintf_s的潜规则https://blog.dynox.cn/?p=791
2,   Security Features in the CRT:
      https://msdn.microsoft.com/zh-cn/library/vstudio/8ef0s5kh(v=vs.110).aspx

驱动的PE文件头校验和

众周所知,应用层PE程序的校验和一般为0,进程加载时系统并不检验,但驱动程序是要强制验证的,并且WinDDK在生成.sys文件时已经设置的校验和。

但是在写上篇blog时(https://blog.dynox.cn/?p=1391)发现系统并不必须对驱动程序文件进行CheckSum的验证。实验中发现系统只是针对 SERVICE_BOOT_START (0) 的驱动文件进行验证,而此时其实是Windows启动程序WinLoad.exe做的加载及验证。

签名文件的修改

已经签名的PE文件原则来说不再修改的,但总有需求要添加些数据在PE文件中,为了更文件分发等。经研究发现,签名的PE文件也是可以修改或追加数据的,当然追加的数据只能是纯数据,比如相关配置信息,或安装包的附加数据等。

我们先来看一下签名文件的格式及数据校验范围,此图引用自Microsoft的关于PE文件签名的文档[REF1]:

image

其中灰色部分是不进行签名校验的数据,但这三部分只有第三个区域才有追加数据的可能,因为第一部分是4个字节,是PE文件自有的校验数据,有兴趣的同学可以参阅 REF2;第二部分是PE文件头的数据目录,其结构是固定的8个字节(前4字节为偏移,后4字节为长度)。第三部分是签名数据部分,经有限观察,长度在3K字节左右。

实际实用中,最后“Remaining content”这部分并存在,签名数据就已经在文件尾了,所以这就给我们在签名数据中(亦是文件尾)加入我们自定义数据的可能。

下面不妨做个实验,以Intel显卡驱动igdkmd64.sys为例:

pic2.0

签名信息如下:

pic1

然后我们添加16个字符至文件尾: pic3

检查下文件长度,从3,828,152变为3,828,168:

image

此时右键打开文件属性就会发现签名信息认证没有了:

pic2

之后,我们还要修改数据目录,以增大签名数据区的大小。用PE Tool 1.0.0.5打开igdkmd64.sys,查看数据目录部分:image

所以我们只需要将文件偏移0x000001d0处的目录项中的长度从0x29B8改成029C8即可,即增加了16字节。修改好再来看文件属性,此时签名项的tab又出现了,即说明通过了Windows的签名检测:

image

不妨再用Windows签名工具检验一次,以确保签名数据的有效:

image

自此足以说明签过名的文件是可以修改的,当然只能做有限修改。对应用层PE程序,以上的修改便已足够,但对驱动程序来说,还要做一件事情,就是更新PE文件头的校验和。相关工具可以参见[REF2]中的工具,也可以通过我写了小程序[REF3]做校验和计算。

image

将新计算出的校验和 0x003A7A63更新至文件偏移0x180处即可,然后就可以用新文件替换掉老的,重启系统即可以加载新修改后的驱动了。

参考资料: REF1: Windows Authenticode Portable Executable Signature Format, Version 1.0  — March 21, 2008 http://www.microsoft.com/whdc/winlogo/drvsign/Authenticode_PE.mspx REF2:   An Analysis of the Windows PE Checksum Algorithm by Jeffrey Walton http://www.codeproject.com/Articles/19326/An-Analysis-of-the-Windows-PE-Checksum-Algorithm REF3:   PECheckSum: http://dynox.cn/soft/PECheckSum.zip

金华50KM越野赛

金华山越野赛1金华山越野赛3

金华50KM越野赛完赛,海拔累计上升2900米,用时9小时25分,破天荒拿到了名次,还有200软妹的奖金。

组织方首次规划此类赛事,经验明显不足,很多地方都没有做到位,比如路标不但不准确而且很多路段缺失,CP1竟然没有补给等等。不过,金华的志愿者非常热情,特别是下午天这么热的情况下,依然兢兢业业,绝对要表扬一下。

雨中风古线-还能怎么虐

风古线

风古线统计

湿身、湿脚、雨大、雾大(白天能见度不足20米)、低温、夜路和走不尽的杭州特色台阶路 ……

和杭州朋友过客、马良继2012年2月走7尖以来,再次同行共走虐线。

南京山地马拉松

NanjingMM

311012015nanjing

距离: 42公里
累计海拔爬升: 1,200米
完赛用时:5小时20分

此次是马拉松处女赛,争取下次进5,不过下次参加公路马拉松可能性更大,目标是进4。