2014年11月30日 星期日

让VC编译出的静态库lib同时支持MT、MD、ML、MTd、MDd、MLd

这是前一阵子用Clang折腾API库的时候获得的意外收获。

还是遵循惯例先给出方案,然后再给分析啥的:
方案很简单,用俺编写的这个小工具处理一下静态库就行啦。首先用16进制编辑工具打开编译好的lib库,查找DEFAULTLIB:LIBC字样,应该能找到类似于/DEFAULTLIB:LIBCMT这样的文字,用空格覆盖掉就好啦。

上述方案对应的库名根据静态库的编译选项不同会不太一样,ML对应libcMT对应libcmtMD对应libcmdDEBUG版的库名大家举一反三一下好了。

这么做还是有一定的限制的:
1. 需要确认lib库源代码中没有使用这6种编译方式中其中一种特有的函数,尤其是debug版,很容易带上一些合法性检查之类的东西,所以推荐使用该方案时基于Release版的静态库进行修改。
2. 如果静态库内需要调用多线程版本的C语言函数,而使用该库的工程使用的是ML编译选项,那么静态库实际调用的则是单线程版的函数,很有可能会在多线程情况下产生问题(这应该是设计初衷吧)。

现在讲讲其中的道理吧,这些东西在我实验成功后我根据之前一些不完整的认知已经有了一些猜测,而真正搞明白,还是在看了微软的Microsoft PE and COFF Specification之后。这里我尝试尽可能少的摘录PECOFF文档的内容来把事情说明白。
大家应该对#pragma comment(lib,xxx.lib)或者#pragma comment(linker,/XX)应该不陌生,这种做法相当于在源代码中指定了链接器参数。这些代码先被编译器发现,然后翻译成链接选项以字符串的形式存储在obj中的directive段中,当链接器生成可执行文件的时候,会分析directive段存储选项进行链接。
而编译选项的MTMD也是用同样的原理将需要链接的CRT库版本存储于obj中的,当链接器从两个不通过的obj分别解析到/DEFAULTLIB:LIBCMT.lib/DEFAULTLIB:LIBCMD.lib,又发现有个obj文件引用到的函数,在两个库中都有,链接器就不知道该用谁的了,冲突就产生了。
这种面临MTMD冲突的静态库,我们可以将它视为一个存着多个obj文件的特殊压缩包,和通常认知的压缩包相比,这个压缩包附带了一些其它信息同时也没有对数据进行压缩。(这里对应的英文是Archive,没想出合理的中文名词,就用压缩包来描述了)
而我们的解决方案中真正修改的是这个压缩包中存储的obj文件的directive段,抹去了编译器由MTMD选项产生的链接选项。在这种情况下,我们的lib库中的obj文件所引用到的C语言库完全取决于工程中其它obj文件的链接选项。相当于自认为不论是MT版还是MD版还是ML版的C语言库,只要是函数名叫这个,我调用就没问题(可能微软认为这种情况至少也该警告一下)。于是冲突就这样‘解决’了,当然,是否真的没有问题,还要取决于静态库的代码是如何实现的。

2014年10月14日 星期二

如何从lib中提取obj

最近有这方面需求,就查了一下,网上除了某培训班的代码可以提取之外,似乎并没有多少关于从lib中提取obj方法的记载。
其实完全不需要写代码嘛……
VS自带的工具就可以帮忙嘞!
相关命令有两个:
lib /list和lib /extract

使用方法如
lib /list xx.lib
lib /extract:.\Release\abc.obj xx.lib

于是可以通过如下方法批量提取lib中的obj
lib /list xx.lib>a.txt
编辑a.txt按照lib /extract的命令格式查找替换一下另存为批处理
然后就可以批量提取啦

2014年9月8日 星期一

使用phantomjs+casperjs遇到的坑

这两周帮同学搞一个网站上的在线书籍接触到了phantomjscasperjscasperjs大法确实不错啊,可以编排模拟网页访问操作步骤,并截取页面中的指定区域或元素的内容。不过这东西坑和bug也不少,现在把一些还有印象的东西记录一下。

1.循环
Casperjs的执行方式类似于感觉就像在写汇报,而且不支持向后跳转到之前的step,不过也不是完全没有办法循环了,网上的办法是通过递归调用来实现的,就像这样(别忘了在外面初始化一下i):

casper.someloop = function() {
    this.then(function () {
        if(this.i == 10){
this.echo(loop end);
this.exit();
        }
this.i++
});
this.run(this.someloop);
}

同时如果处理的是一个数组内的所有元素的话,还可以用each或者eachThen,官网的例子如下:
var links = [
    'http://google.com/',
    'http://yahoo.com/',
    'http://bing.com/'
];

casper.start().each(links, function(self, link) {
    self.thenOpen(link, function() {
        this.echo(this.getTitle());
    });
});

casper.run();

不过这个函数有个限制,第一个参数传入的数组不可以是在之前某个step中动态获取到的数组,它是不会理会动态获取到的结果的。

2.等待
首先,所有等待函数放在各类then中执行会出问题,根本就不等直接就过去了,也不知道这算不算是casperjsbug。然后是waitFor系列函数,waitFor系列函数是自带超时的,并不是你不传入超时时间它就一直等下去了。还有startopen系列函数打开网页好像没法在网页所有资源加载完毕之前就走到后面的等待判断中。

3.Selector
并不是使用document.querySelector能对象就可以使用casperjs的带selector系列函数,具体什么样的元素可以什么样的元素不可以我还么有找到规律,好像是动态创建的元素会有这个问题。

4.PDF
使用casperjscapture函数截取pdf的时候会出很多问题,同样的问题在截取图片或者使用phantomjs原生的capture函数时并不会发生,同时会引发下面一个问题

5.casper.options.page
想要解决上面的那个问题又不想截取成图片,翻翻文档貌似就这个东西可用了,不过这应该是一个未完成的功能,设置了这个page对象之后,我的then就没触发过,一直在等待。

6.页面大小
打开一个网页,然后使用evaluate函数执行js修改网页大小,这是再使用capture函数截取网页内容会发现之前调整大小一点效果都没有,后来俺从github上搞了份没有tag的最新版问题才得以改善。

7.evaluate
这其实也不算是坑,也就算是个注意事项吧,使用evaluate执行脚本的时候如果想要引用function外面的变量需要传参进去,否则是不能用的。

8.其他
其实casperjs这东西坑肯定不止这点,有些我忘记了有些我还没遇到,被恶心的时候可以尝试从github上搞个新版本下来。

2014年8月26日 星期二

SEH和VEH







先说结论吧:
VEH可以在修复问题后继续执行产生异常的代码 ,SEH不行
SEH比起VEH更方便简洁直观因为VEH一旦注册handler,任何位置的任何异常都会走到handler里。
VEH比SEH触发更早,VEH如果将问题处理完毕,可以阻止SEH的触发,如果VEH认为问题没法处理好也可以继续触发SEH。
VEH可以拿来做很多事,比如说内存修改器之类的

下面是两组实验代码:

int *pb = 0;

LONG CALLBACK VectoredHandler(
    _In_  PEXCEPTION_POINTERS ExceptionInfo
    )
{
    *pb = 1;//b = 1
   
    printf("veh\n");
    return EXCEPTION_CONTINUE_EXECUTION;
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    AddVectoredExceptionHandler(1, VectoredHandler);
    __try
    {
        int b = 0;
        pb = &b;
        printf("before\n");
        int a = 1 / b;//exception
        printf("after\n");
    }
    __except (1)
    {
        printf("seh");
    }
    getchar();
    return 0;
}

-----------------------------------------------------------------------------------

DWORD offset_of_b = 0;

LONG CALLBACK VectoredHandler(
    _In_  PEXCEPTION_POINTERS ExceptionInfo
    )
{
    *(int*)(ExceptionInfo->ContextRecord->Ebp + offset_of_b) = 1;//b = 1
   
    printf("veh\n");
    return EXCEPTION_CONTINUE_EXECUTION;
    return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain(int argc, _TCHAR* argv[])
{
    AddVectoredExceptionHandler(1, VectoredHandler);
    __try
    {
        int b = 0;
        offset_of_b = (DWORD)&b;
        __asm{
            sub offset_of_b,ebp
        }
        printf("before\n");
        int a = 1 / b;
        printf("after\n");
    }
    __except (1)
    {
        printf("seh");
    }
    getchar();
    return 0;
}

2014年8月20日 星期三

.net保护思路

本文是我从零开始学习.net保护一周多得出的结论,中间断续穿插了一些别的任务。
公司的.net壳已经做到了按方法加密的程度,如果想再提升,可以从两个方向着手:

1. 加强代码乱序、混淆
2. 按照DNGuard的思路搞

其实还有一点,就是修复反射漏洞,不过我不确定公司的壳有没有这个漏洞而且这也用不上啥大动作姑且不列。两个方向中第一个方向俺没有研究,第二个方向由于DNGuard的作者很大方说的挺清楚,所以比较明朗,这里简单说一下。
简单来讲,现在稍微好点.net壳和脱壳都走到了JIT层,普通的壳无非就是hook的更深然后检查一些其它hook点是不是被脱壳机搞过了,脱壳机也就是选择更深层的hook点截获JIT层编译的il代码并逃避或破坏壳的检测(反射漏洞又是另一条路,操作简单也比较好防,先放放),总的来讲还是脱壳方占优,公司的壳就在这个层次的初级(目测)。
还有一种路线是整体保护,类似飞信的vm,优点是自己做了.net framework该完成的事情,运行程序不再需要.net framework,所有JIT层的手段都失效了,结合成熟的PE保护方案可以保护的很好。同时缺点很多:

1. 程序的发行拖家带口带上了一整套运行环境必然会大幅度增加发行体积。
2. 被保护方可以使用的功能是有限的,兼容性还不好(就连mono都没把功能搞全)
3. 以我们公司现在的水平,就算抄monollvm,性能估计也达不到.net framework的水平。

其中12点对于公司的产品来讲是硬伤,所以我比较倾向于结合两条保护路线的DNGurad思路。
DNGuard的思路就是.net framework做的事情我包揽一部分。比如说我替换的伪il代码我来JIT编译,正常的il代码交给.net frameworkJIT编译。DNG的另一个独特功能也是这种部分包揽的思路。利用这样的思路DNG对两种保护取长补短,达到了强度、性能与兼容性的平衡,而且实现起来可参考的东西还很多比较可行,是我比较倾向的公司.net壳升级方向。

2014年8月6日 星期三

最近boss给俺安排的任务越来越杂了

前一阵子俺还在折腾Hook,然后穿插着给一些C#代码找错,后来让俺学.net程序加壳,然后是看看某个C#程序调用C++dll为啥出错,今天俺又在修改一个COM组件使之可以被PHP调用,我还顺便搭了个PHP开发环境,用上了久违的eclipse……
好在上述内容除了壳之外都还算常规开发的范畴内(hook在常规开发也不难看到),俺也不在乎多掌握一些编程语言和扩展组件开发方式,我比较赞成程序员不应在一个语言或者平台中固步自封的观点,接触这些可以拓宽我在程序设计与编写时的视野,这有助于俺能力的提升。
不过俺还是比较抵触壳这东西的,我确实希望有朝一日可以成为比优秀更好的安全方面的开发人员,同时,如果可能我希望我的能力并不局限于安全方面。壳这东西对我来说,一方面兴趣不大,另一方面太过偏门。如果只是短暂的接触,我会有机会思考如何解决这样一个问题,如何去设计代码,同时更加了解某些语言的底层机制,这会给我带来收获。但是如果是长期身陷其中,我不希望是这个领域。
这对于我之前所说的杂乱的‘常规开发’也是有效,短暂的接触可以拓宽视野,深入学习就要选择好方向了。俺希望在一方面深入钻研同时在其他方面拓宽视野,毕竟我不可能精通所有方面。
或许我应该趁年轻再更多的接触一些其他领域,或许当初刚毕业时我因为工作方向的不确定性拒绝了那份薪水不错的offer并不是非常正确。不过,我并没有那么多时间,在我某一个人生目标达成之前我并不打算结婚,而且我也不打算太晚结婚,所以时间就有些紧了。我已经在某事业单位浪费了毕业后的第一年,我不想继续浪费下去了……
等等,写到这里我发现我已经跑离最初写这篇博文的想法很远了,最初只是想发几句牢骚来着……
越说越乱,我捋一下,这是我真实的想法:我希望能掌握一个技能达到在某环境找工作不愁的程度,然后去到这个环境普通的工作,普通的结婚,然后度过我普通的一生。这也是我真实的想法:我希望有朝一日可以成为比优秀更好的(安全方面的研究人员||编程人员)。我的两个人生目标正在互相影响着,并产生了一定的冲突,这让我略有急躁,我不知道如何才能很好的解决这个冲突,以至于我正在通过写博客的方式来发泄这份情绪……
好吧,我明白自己是怎么回事了,写博文这办法有点效果,效果不太大,今天就到这吧,先洗洗睡了……