有段时间没有用windows了,刚一开机又是系统补丁更新。匆匆瞥了一眼看到了“内核缓冲区溢出漏洞补丁”几个字眼。靠,又是内核补丁。打完这个补丁后MD的内核符号文件又得更新了。于是抱怨了几句,一旁的兄弟问什么是缓冲区溢出。这个…三两句话还真说不清楚。解释这个问题用C语言比较方便,但是单从C代码是看不出来什么的,具体原理要分析机器级代码才能说清楚。既然是浅谈原理,那就从最基本的开始吧。

本文的定位是对此方面一无所知的读者,所以大牛们可以直接飘过…

缓冲区溢出这个名词想必大家并不陌生吧,在微软的系统漏洞补丁里经常可以看到这个词(微软这算是普及计算机知识么? – -)。从C语言来分析的话,最简单的一种溢出就是向数组中写入数据时超出了预定义的大小,比如定义了长度为10的数组,偏偏写入了10+个数据。C标准告诉我们这种做会产生不可预料的结果,而在信息安全领域看来,缓冲区溢出的艺术就是要让这种“不可预料的结果”变成攻击者想达成的结果。比如远程攻击服务器上的程序,使其返回一个具有管理员权限的shell什么的。千万别觉得这是天方夜谭,印象中微软历史上爆出过不少这样的漏洞,前段时间不就有覆盖微软全版本的MS12-020么(新的也有,但是我没关注 – -)。虽然网上广为流传的只是一个远程让服务器死机的shellcode,但是让远程服务器执行任意代码理论上是可行的。关于漏洞利用这块的东西我不怎么擅长,所以就不敢再多说了。

一般来说关于缓冲区溢出漏洞,官方的描述都是诸如“攻击者通过提交一个精心构造的字符串使得缓冲区溢出从而执行任意代码”之类的。这里的重点词是两个,“精心构造”和“字符串”。精心构造可以理解,那“字符串”呢?我们都知道,一段二进制代码是什么东西取决于机器对其的解释,如果把这段代码当作变量,当作整型是一个值,当作浮点型又是一个值,如果把它当成可执行代码的话,又会是另外一种解释。所以这里的字符串实际上就是一段可执行代码的字符串表现形式。接下来我们的重点就是如何“精心构造”这个“字符串”和如何让机器把我们构造的字符串(也就是数据)当作可执行代码来执行。

必须说明的是,真正意义上的shellcode要解决诸如函数地址重定位,汇编级系统调用,以及应对编译器抵抗此类缓冲区溢出攻击的“栈随机化”等技术,这些东西对于我们这篇“科普性质”的文章来说显然过于艰深,加之作者本人也是一个水货,故不会提及。我们只研究最浅显的原理。

我们先来看一段代码:

Read More

无聊中,于是写了一个冒泡排序的泛型算法。算法很简单,但是个人觉得从C标准库中学到的这种泛型的思想很有益处。

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
71
72
/*
* 冒泡排序的泛型实现
*/
#include <stdio.h>
#include <string.h>
static void Swap(char *vp1, char *vp2, int width)
{
char tmp;
if ( vp1 != vp2 ) {
while ( width-- ) {
tmp = *vp1;
*vp1++ = *vp2;
*vp2++ = tmp;
}
}
}
void BubbleSort(void *base, int n, int elem_size,
int (*compare)( void *, void * ))
{
int i, last, end = n - 1;
char *elem_addr1, *elem_addr2;
while (end > 0) {
last = 0;
for (i = 0; i < end; i++) {
elem_addr1 = (char *)base + i * elem_size;
elem_addr2 = (char *)base + (i + 1) * elem_size;
if (compare( elem_addr1, elem_addr2 ) > 0) {
Swap(elem_addr1, elem_addr2, elem_size);
last = i;
}
}
end = last;
}
}
int compare_int(void *elem1, void *elem2)
{
return (*(int *)elem1 - *(int *)elem2);
}
int compare_double(void *elem1, void *elem2)
{
return (*(double *)elem1 > *(double *)elem2) ? 1 : 0;
}
int main(int argc, char *argv[])
{
int num_int[8] = {8,7,6,5,4,3,2,1};
double num_double[8] = {8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1};
int i;
BubbleSort(num_int, 8, sizeof(int), compare_int);
for (i = 0; i < 8; i++) {
printf("%d ", num_int[i]);
}
printf("\n");
BubbleSort(num_double, 8, sizeof(double), compare_double);
for (i = 0; i < 8; i++) {
printf("%.1f ", num_double[i]);
}
return 0;
}

最近频繁需要实现在windows控制台下输入星号密码的功能,Unix/Linux那种没有任何屏显的实现总感觉对用户不太友好。今天在自己的Linux代码库中发现了自己去年写图书馆管理系统的时候写的一个密码输入函数。索性拿来修改了接口并且重新优化了处理逻辑后移植到了windows下(其实也就是加上几句条件编译罢了)。代码如下:

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#ifndef _WIN32 // 如果不是WIN32环境,则要自定义getch()函数
#include <termio.h>
int getch(void)
{
struct termios tm, tm_old;
int fd = 0, ch;
if (tcgetattr(fd, &tm) < 0) {
return -1;
}
tm_old = tm;
cfmakeraw(&tm);
if (tcsetattr(fd, TCSANOW, &tm) < 0) {
return -1;
}
ch = fgetc(stdin);
if (tcsetattr(fd, TCSANOW, &tm_old) < 0) {
return -1;
}
return ch;
}
#else
#include <conio.h>
#endif // _WIN32
/*
* 密码输入函数,参数 passwd 为密码缓冲区,buff_len 为缓冲区长度
*/
char *passwd_input(char *passwd, int buff_len)
{
char str;
int i = 0;
int enter_num = 13;
int backspace_num;
#ifndef _WIN32
backspace_num = 127;
#else
backspace_num = 8;
#endif
if (passwd == NULL || buff_len <= 0) {
return passwd;
}
while (1)
{
// 如果没有按下退格键
if ((str = getch()) != (char)backspace_num) {
if (i < buff_len - 1) {
passwd[i++] = str;
printf("*");
}
} else {
if (i != 0) {
i--;
printf("\b \b");
}
}
// 如果按下了回车键
if (str == (char)enter_num) {
passwd[--i] = '\0';
if (i != buff_len - 1) {
printf("\b \b");
}
break;
} else if (str == -1) {
fprintf(stderr, "Error to set termio noecho.n");
}
}
return passwd;
}
/*
// 测试示例(请自行添加头文件)
int main(void)
{
char pass[7];
printf("亲,试试输入密码(长度限制 6):");
passwd_input(pass, 7);
printf("\n%s\n", pass);
return 0;
}
*/

周一在2号实验楼323嵌入式实验室用U盘时发现有病毒,重启后发现刚启动时电脑就有病毒,看来还原卡没起作用。一时兴起打包了病毒文件回来研究下。

病毒文件名:SysAnti.exe
文件大小:52.5KB
MD5:4B160901566108C6F89F21444CE503E7

PEID查壳信息:

PEID显示是ASPack壳,这一款兼容性良好的老牌壳。不过估计用工具脱不了,OD载入时出错,看来经过特意加密防止反编译。我也懒得深入研究壳了,直接丢入虚拟机的XP,创建快照。打开Procmon和Total Uninstall监视,然后运行。

先分析Total Uninstall下直观的文件和注册表修改情况:

文件修改:

1.在C:\windows\Fonts目录下创建cjavv.fon和fprij.fon文件,从文件名上分析可能是随机文件名。

2.与实验室看到的不同,病毒在C:\windows\system32目录下创建了SysAnti.exe文件,而不是实验室的C:\Program Files\Common File目录。

3.修改了C:\windows\system32\drivers\etc目录下的hosts文件,将主流杀软网站指向本地127.0.0.1地址,以阻止用户访问。

Read More