安全程序设计

安全程序设计 概述 在当前的软件行业里,太多的程序有安全问题,代码在被发布前只是经过很少的测试,即使 一些有专业测试人员的软件公司也很少进行安全编程方面的测试,原因在于缺少对安全编程 技术的了解。本文将尝试给程序员一个比较清晰的概念,安全漏洞的来源,和避免安全漏洞 的技巧,使写安全程序的过程变得轻松起来。 运用好的编程技巧是非常重要的,甚至你的代码只是将运行在限制的时期和限制的条件下。 许多程
摘要由CSDN通过智能技术生成

安全程序设计

概述
在当前的软件行业里,太多的程序有安全问题,代码在被发布前只是经过很少的测试,即使
一些有专业测试人员的软件公司也很少进行安全编程方面的测试,原因在于缺少对安全编程
技术的了解。本文将尝试给程序员一个比较清晰的概念,安全漏洞的来源,和避免安全漏洞
的技巧,使写安全程序的过程变得轻松起来。
运用好的编程技巧是非常重要的,甚至你的代码只是将运行在限制的时期和限制的条件下。
许多程序员的程序常超越其最初的设计范围,大部分的安全漏洞出现的环境是当初程序员不
知道或没有想到的。典型的是,程序员假设当前的系统调用永不会失败,或者程序参数永远
不可能超过某个长度。因而,程序员能做到最好的事情就是对问题进行假定编程,仔细分析
它们是否正确,和想象可以使其失败的条件。

Internet发展
?主机数 4300万 46%
?网民数 1.54亿 55%
?2001网民数 4.5亿
?美国网民数 8300万 26%
?2001美国网民数 1.3亿
?美国人在线上税 2500万 38%
?AOL用户 1700万 42%
?WEB服务器 500万 128%
?YAHOO每天页面浏览 2.35亿次 147%
?网上新闻发布 213万 89%
?在线股市交易 33.6万 125%
?电子商务营业额 211亿美元 154%


导致安全漏洞的二个最根本原因
溢出
什么是溢出:
数据存储过程中超过数据结构所能容纳的实际长度都可成为溢出。

产生溢出的理论基础:

1. 平面内存结构,4GB或更大逻辑地址空间
程序运行时可以被装载到相对固定的地址空间,使得确定攻击代码地址更为方便

2. 数据与代码同处于一个地址空间,堆栈可执行
代码数据共同存储这一现代计算机模型使得溢出攻击真正可行,攻击者可以精心编制输入数
据,得到运行权

3. CPU call调用利用栈保存返回地址
Call调用使用堆栈保存返回地址,使得跟改程序返回地址成为可能

4. C函数在栈中保存局部变量
看一下现代几乎所有的编译器产生的代码,就会发现在所有调用子程序的地方都有类似代码
push ebp
mov ebp, esp
sub esp, ??
编译器为了支持函数嵌套调用都使用堆栈来保存局部变量

5. C语言无自动边界检查功能
C语言不进行数据边界检察,当数据被覆盖时也不能被发现

6. 栈从高地址往低地址生长
数据存放是从低到高存放的,而堆栈却从高到低生长,当call调用子程序时的返回地址将被
压入堆栈,这就是说当发生call调用时,程序返回地址将位于子程序数据区的高处,使恶意
覆盖返回地址成为可能,只要精心安排输入数据就可以使执行类似ret的指令时,跳转到所需
要的地址

 

 

一个溢出的例子
#include <stdio.h>
#include <string.h>

void SayHello(char* str)
{
char buffer[8];
strcpy(tmpName, name);
printf("Hello %s/n", tmpName);
}

int main(int argc, char** argv)
{
SayHello(argv[1]);
return 0;
}


运行:

$ ./example sunx
Hello sunx

似乎一切正常,不会有什么问题
。。。。再试一下。。。

$./example sunxsunxsunx
Hello sunxsunxsunx?????
Segmentation fault (core dumped)

当程序打印完输入数据后崩溃了,这是为什么呢?
这个程序的函数含有一个典型的内存缓冲区编码错误. 该函数没有进行边界检查就复
制提供的字符串, 错误地使用了strcpy()而没有使用strncpy(). 如果你运行这个程序就会产
生段错误. 原因是在命令行输入的数据 “sunxsunxsunx” 长度超过了在SayHello函数中的
局部变量长度, 于是覆盖了在堆栈上方的返回地址,在print之后就崩溃了
让我们看看在调用函数时堆栈的模样:

分析:
程序的内存布局



数据段
代码段
0xFFFFFFFF

栈方向
0x00000000

第一次运行进入SayHello后的栈

第二次运行进入SayHello后的栈

sununxsunx

这里发生了什么事? 答案很简单: strcpy()将*str的内容(larger_string[])复制到buffer
[]里, 直到在字符串中碰到一个空字符. 显然,buffer[]比*str小很多. buffer[]只有16个字
节长, 而我们却试图向里面填入12个字节的内容. 这意味着在buffer结构之后, 堆栈中4个字
节被覆盖. 包括RET地址,我们已经把Buffer指向内存的12个字节全都填成了“sunxsunxsunx
”, 这意味着现在的返回地址是0x786e7573. 当函数返回时, 程序试图读取返回地址的下
一个指令, 此时我们就得到一个段错误.
因此缓冲区溢出允许我们更改函数的返回地址. 这样我们就可以改变程序的执行流程.

如果攻击者精心准备数据
jmp label2
label1: pop esi
mov [esi+8], esi
xor eax, eax
mov [esi+7], al
mov [esi+12], eax
mov al, 0bh
mov ebx, esi
lea ecx, [esi+8]
lea edx, [esi+12]
int 80h
xor ebx, ebx
mov eax, ebx
inc eax
int 80h
label2: call label1
cmd: db “/bin/sh”, 0

 


上面代码的机器码
char shell_code[] =
"/xeb/x1f/x5e/x89/x76/x08/x31/xc0”
“/x88/x46/x07/x89/x46/x0c/xb0/x0b"
"/x89/xf3/x8d/x4e/x08/x8d/x56/x0c”
“/xcd/x80/x31/xdb/x89/xd8/x40/xcd"
"/x80/xe8/xdc/xff/xff/xff/bin/sh";

如果用程序输入这些数据就可以得到一个命令行shell

溢出漏洞的实际利用方法
Remote root exploit
远程,不经认证而获得执行权,
主要针对程序:各种Daemon
HTTP 、FTP 、POP 、Sendmail …
Local root exploit
本地,利用程序的漏洞获得执行权,主要被用来提升用户权限
主要针对程序:所有的特权程序


那些程序具有特权:
Daemon
HTTP 、FTP 、POP 、Sendmail …
系统服务
一些系统相关的服务
如:Syslog …
suid/sgid程序
Unix一项特殊技术,使普通用户也能做部分只有超级用户才能执行的任务lpasswd、at、cro
ntab、ping
普通rwx之上加上s位,kernel在载入进程映象时自动将进程有效用户/组标识置为映象文件文
件属主/组
例:
ls -l /bin/eject
-r-s--x--x 1 root /usr/bin/passwd

OS本身

解决方法
更为小心的程序设计
将安全相关的功能隔离到仔细检查的代码内
非可执行栈
会导致若干技术难题
基于编译器的方法
在代码内自动增加边界检查(very slow)
运行过程中进行栈完整性检查(slight slowdown)
重新排列栈变量(no slowdown)


输入过滤
关于Perl

Perl作为CGI编程的主要语言之一,其安全性也受到很大的关注。在 W3C组织的 "WWW Secur
ity FAQ" 之 "CGI Scripts"一章中,Perl安全编程就整整占了一节。由此可见 Perl CGI 安
全编程的重要性。
  
---------------------
1、NULL字符
---------------------

  开发人员已经习惯了C语言的工作模式
如果说 strcmp("root","root/x00")==0,相信没有什么人反对。但是在Perl中 "root"!="r
oot/0"
对于每一个希望发现CGI漏洞的安全专家或黑客来说,最常用的方法之一是通过传递特殊字符
(串),绕过CGI限制以执行系统级调用或程序。
  阅读以下例子:

# parse $user_input
$database="$user_input.db";
open(FILE "<$database");

这个例子用于打开客户端指定的数据库文件。例如客户端输入"haha",则系统将打开"hah
a.db"文件考只读方式)。这种处理方式在Web应用中是很常见的。
  现在,让我们在客户端输入"haha%00",在该PERL程序中$database="haha/0.db",然后
调用open函数打开该文件。但结果是什么呢?系统会试图打开"haha"文件
  出现这种情况的原因是由于PERL允许在字符串变量中使用NULL空字符,因此,也就有了

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
{************************************************************** 浅谈软件安全设计(一) code by 黑夜彩虹 & vxin with almost pure delphi 网站:http://soft.eastrise.net 2007-03-07 --- 转载时请保留作者信息。 **************************************************************} 此CM的设计模式: 1、插入一些花指令 2、写了一些代码迷惑Cracker 3、有简单的Anti_DEDE 和检测调试器 话不多说,请看以下代码: unit main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,strutils; Const C1= 17856; C2= 23589; type TForm1 = class(TForm) Image1: TImage; Edit1: TEdit; Label1: TLabel; Label2: TLabel; Edit2: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} Procedure Anti_DeDe();//检测DEDE反编译器 var DeDeHandle:THandle; i:integer; begin DeDeHandle:=FindWindow(nil,chr($64)+chr($65)+chr($64)+chr($65)); if DeDeHandle0 then begin For i:=1 to 4500 do SendMessage(DeDeHandle,WM_CLOSE,0,0); end; end; Function ABC42():Boolean; //检测调试器; var YInt,NInt:Integer; begin asm mov eax,fs:[30h] movzx eax,byte ptr[eax+2h] or al,al jz @No jnz @Yes @No: mov NInt,1 @Yes: Mov YInt,1 end; if YInt=1 then Result:=True; if NInt=1 then Result:=False; end; function EncryptModule(SourceStr:String;Key:Word;N:Integer):String; var //加密函数 I:Integer; begin SetLength(Result,Length(SourceStr));//利用SetLength函数指定密文长度 //对每一个索引元素进行变换 for I:=1 to Length(SourceStr) do begin Result[I]:=Char(byte(SourceStr[I]) xor (Key Shr N)); Key:= (byte(Result[I]) + Key)*C1+C2; end; end; //==========以下是549的函数,据说没有暴破点,顺便试一试 //========函数作用:动态改变程序运行罗辑 function GetEIP: Integer;//自动生成address的方法 asm mov eax, [esp]; sub eax, 5; //call GetEIP占用5字节 end; function PatchOneItem(PatchItem: String): Boolean; var PatchAddress: Integer; PatchLength: DWord; PatchData: Pointer; PatchDataStr: String; i: Integer; PatchByte: Byte; PID, PHandle: THandle; WriteCount: DWord; begin Result := False; if Length(PatchItem) < 11 then Exit; PatchAddress := StrToInt(\'0x\' + LeftStr(PatchItem, 8)); for i := 1 to Length(PatchItem) do begin if PatchItem[i] \' \' then PatchDataStr := PatchDataStr + PatchItem[i]; end; PatchLength := (Length(PatchDataStr) - 9) div 2; GetMem(PatchData, PatchLength); try for i := 0 to PatchLength - 1 do begin PatchByte := StrToInt(\'0x\'+PatchDataStr[10 + i * 2] + PatchDataStr[10 + i * 2 + 1]); Byte(Pointer(Integer(PatchData) + i)^) := PatchByte; end; GetWindowThreadProcessId(Application.Handle, PID); PHandle := OpenProcess(PROCESS_ALL_ACCESS, False, PID); WriteProcessMemory(PHandle, Pointer(PatchAddress), PatchData, PatchLength, WriteCount); CloseHandle(PHandle); finally FreeMem(PatchData, PatchLength); end; Result := PatchLength = WriteCount; end; procedure Patch(PatchFile: String); var PatchItems: TStrings; PatchIndex: Integer; begin if not FileExists(PatchFile) then Exit; PatchItems := TStringList.Create; try PatchItems.LoadFromFile(PatchFile); for PatchIndex := 0 to PatchItems.Count - 1 do begin PatchOneItem(PatchItems[PatchIndex]); end; finally PatchItems.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin Anti_DeDe; //检测DEDE,检测到关闭它。 if ABC42 then ExitProcess(0); //检测调试器,终止。 end; procedure TForm1.Button1Click(Sender: TObject); //注册按纽,开始检测 begin //========在这里插入一些花指令 asm jz @Start jnz @Start db 0E8h, 24h, 0, 0 ; db 0, 8Bh, 44h, 24h db 4, 8Bh, 0, 3Dh db 4, 0, 0, 80h db 75h, 8, 8Bh, 64h db 24h, 8, 0EBh, 4 db 58h, 0EBh, 0Ch, 0E9h db 64h, 8Fh, 5, 0 db 0, 0, 0, 74h db 0F3h, 75h, 0F1h, 0EBh db 24h, 64h, 0FFh, 35h db 0, 0, 0, 0 db 0EBh, 12h, 0FFh, 9Ch db 74h, 3, 75h, 1 db 0E9h, 81h, 0Ch, 24h db 0, 1, 0, 0 db 9Dh, 90h, 0EBh, 0F4h db 64h, 89h, 25h, 0 db 0, 0, 0, 0EBh db 0E6h db 0EBh, 1, 0Fh, 31h ; db 0F0h, 0EBh, 0Ch, 33h db 0C8h, 0EBh, 3, 0EBh db 9, 0Fh, 59h, 74h db 5, 75h, 0F8h, 51h db 0EBh, 0F1h db 0B9h, 4, 0, 0 ; @Start: end; if length(edit2.Text)>3 then //比较大于3位 begin //============再写一些骗Cracker if edit2.Text=EncryptModule(Edit1.Text,12345,10) then begin showmessage(\'请重启本软件。\'); //=======还可以再写一些记号,这里我就不写了 end; PatchOneItem(edit2.Text); //真正的比较 showmessage(\'ok\'); //弹出OK对话框 end; end; end.  //====附件为CM,会破解的兄弟可以试试其强度....

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值