
所有高级语言写成的程序无一例外的被编译链接成机器所能识别的的机器码,而每条汇编语句对应着每条二进制的机器指令。。。这个没什么好说的,说一下C语言向汇编语言的转化,对于每个专业的黑客来说,汇编语言比C语言更优雅,来得更猛烈些,但也无可否认,C语言的高效性学过的都知道,几句代码便可很爽快地完成一件令人满意的事,但今天的主角不是C语言的高效性,也不是C语言,而是汇编语言,看一段C语言编译后的汇编程序:
C源代码:(VC)
/***开始处***/
void add(int,int,int)
main()
{
int a=1;
int b=2;
int c=0;
add(a,b,c)
c++;
}
void add(int a,int b,int c)
{
c=a+b;
}
/***结束处***/
执行结果是:a=1,b=2,c=1。奇怪?c不是4吗?看了其对应的编译器编译后的汇编指令便十分清楚:
;编译后的汇编指令如下:
mov bp,sp
sub sp,6
mov word ptr [bp-6],001 ;int a
mov word ptr [bp-4],002 ;int b
mov word ptr [bp-2],000 ;int c
push [bp-2]
push [bp-4]
push [bp-6]
call ADDR
add sp,6
inc word ptr [bp-2]
ADDR: push bp
mov bp,sp
mov ax,[bp+4]
add ax,[bp+6]
mov [bp+8],ax
mov sp,bp
pop bp
ret
;代码结束看完这段是不是恍然大悟了,,
深入探讨汇编语言:
说一下闲言粹语,学习机器语言的最好方法是从硬件厂家那里拿到一份基于其生产的CPU的机器指令学习手册,接着便进入汇编语言,,看一下通过编译器编译过的那段泥汇编代码,局部变量a,b,c在栈中生存,在调用add()函数时,add()函数从主函数时中取得a,b,c的值,三个Push语句,在add()函数Ret前的Pop bp收回了原来为调用add()函数使用的栈空间,使得a,b,c的值仍然没有被改变,事情还没完,
在main()函数之前执行的神秘函数:
受过C语言的洗礼,无论你怎么说,他们都会说:“main()函数是程序的入口函数。”(初学者都是这么认为的,其实是错的。)没错,我们看到的的确是指令从main()函数开始执行的,可是在main()函数执行之前,总是有一个函数先执行了,(真正的入口函数),然后一个call指令就把执行指令交给了main()函数,于是main()函数的生命周期开始了,先于main()函数执行的这个函数叫做start()函数,普通的C语言程序是以C标准让参数入栈(从右到左的顺序),而且不管你有没有写main()函数的参数,main()函数总是会从系统处获得两个参数:argc和argv,学过C语言的都知道,更多关于argc和argv的知识见谭浩强教授的C程序设计的第三版,其实还有一个参数,指向环境变量的字符串指针数组(并不是所有的C程序都有这个参数的)
start()函数从何而来:
我清楚的记得,我在编写程序换时候并没有写这个所谓的start()函数啊,怎么会凭空冒出一个start()函数来呢?
那你是怎么得到可执行程序的呢?编译-->链接。没错,就在编译成功的那一刻,编译器便自作多情地将start()函数写入我们编写的可爱程序里面(不管你愿不愿意,解释语言除外),并在执行时优先于main()函数从系统处获取执行权限。
start()函数的作用:
在可执行文件被内存加载后,start()函数便获得优先执行权,进而初始化各全局变量及静态成员,如构件编号,系统版本号,次版本号以及main()函数的3个参数(一般是两个,用VC,BC等编译的话就是前面所说的3个了,详见CRTO.C和C0W.asm文件)。初始化结束后便是3次很明显的Push操作,以作为调用main()函数撤销3个参数,于是一个call指令之后,main()函数的生命周期开始了,main()函数的入口标志:main proc near,当main()函数执行到Pop ebp时,main()函数的生存空间意味着要结束了(内存将要被回收),然后retn --> main endp,main()函数生命周期结束了。返回到start()函数,start()函数接着就调用Call _exit,退出程序,把执行权交给操作系统,操作系统接着就销毁该程序的进程。
基于交互式的反汇编环境:
交互与不交互的区别,总的来说就是智能与非智能的区别,交互模式是整个指令执行的过程中要结合程序上下文做出智能的判断,如果不交互的话,程序执行到某条自修改指令时(如程序编码,软件加壳等)不会提示出错信息,而是死板的翻译出来(一般是带有一定的错误性);而交互模式下,汇编环境便给出警告,把掌控树立交给程序员,让程序员来决定显示的内容,在研究蠕虫病毒时(蠕虫头通常都经过编码,以便过IDS),就经常会遇到这样的情况,于是经常无法获取正确的蠕虫代码。
补丁及注册机技术:
很多时候,我们都很讨厌收费软件,进而去搜索破解版软件和注册机,结果轻轻一点,软件免费了,。。。真是爽,关于补丁,可分为文件补丁和内存补丁,一般下载一个补丁文件替换源文件便可,那么补丁文件是怎么做出来的呢?举个简单的例子,用含有if()语句写出来程序,在汇编中总是对应着jne或je。。。等,如果刚好这条指令就是决定你是否可以执行我们想要的结果(这条指令称为关键性跳转指令),那么如果你把je改为jne或nop掉,那么程序撤销流程就刚好相反了。。。。基于这种情况,你只要把修改后的程序保存一下就可以了,而这个文件就是所谓的补丁。注册机则难一些,要充分了解编程者所写的解密算法,然后用你最熟悉的语言实现这种算法,写完之后的程序就叫做注册机。。。。黑客们总是不厌其烦的与软件厂商作对,,,,呵呵;
用C语言和汇编语言打造通用型蠕虫头:
蠕虫的天然栖息地是有系统漏洞的一切主机,主要是windows和linux/unix系统,自然这两种蠕虫头相差比较大,linux型的蠕虫主要是入注linux的某个模块获得管理员权限,然后随机运行。蠕虫头的生命周期,从你不幸从好友那里接到一从份带蠕虫头撤销示受或者什么什么换邮件,并且你很兴奋的打开它的时候,它开始了其生命周期,蠕虫头生命存在的意义,在于两个方面,一个是获取主机的管理员权限,来执行自己,另一个是把它的尾从黑客指定的服务器下载到感染的主机上,蠕虫头的任务结束了,调用自销毁函数结束自己的生命,接着下来的就是蠕虫尾的事了,蠕虫尾做什么事呢?中过熊猫烧香的小朋友就不用我多说了,,,,要说撤销是蠕虫的实现,像爱莎,梅莉莎等,处在win2k时代左右的人都是谈之色变的,其中大部分代码就是用汇编与C语言写的,,,,蠕虫头灵活的运用了hash算法进行压缩和采用了编码技术,因为大部分的程序漏洞和系统漏洞都是基于缓冲区溢所引起的,所以能容纳蠕虫头的地方非常小,,选取一种较佳的hash算法能让代码缩小近十倍的长度,在黑客眼中,缓冲区的空间十分宝贵,多一个字节的长度也是浪费。在通用徃方面,不能采用call一个绝对地址,因为随着操作系统的版本不同,API函数的地址也不尽相同,加之动态链接库加载的移位,等问题也会导致蠕虫头的通用性大大降低(详见我的百度空间)。编码则是利用一个解码器把经过C语言编码过的二进制机器码还原为我们原来的代码,多一个解码器难道不占用空间吗?呵呵,如果过不了防火墙或IDS,你的蠕虫再小也没用,对吧,编码的作用就是过这些东西的,原理类似加壳。。。
软件远程更新升级:
更新的意义在于软件的完善和新功能的体验,除非你重新安装该软件,并且为最新版的软件,若不然更新升级将经过下面的步骤:首先是调用远程程序,远程程序会把原来的文件(要更新的文件)改名,然后把新文件代替掉要更新撤销文件,最后把那个旧文件删了,或用一个含有特殊名字的文件把它保存在系统的另一个位置里,windows的更新就属于后面那种,把旧文件放到包含有S....S的文件夹里面(由于打不了美元符号,用两个SS表示)。
代码挖掘:
如果是开源的话谈不上什么代码挖掘,代码都以现成品放到你的面前,还挖掘什么?这里当然指的是非开源产品,代码挖掘的最好的也是最终极的工具是汇编语言,在windows下,位于system32的动态链接库包含了大量的API函数,而这些API函数掩盖了很多系统底层的东西,如堆算法等,还有如,一个CDC便把你从学生的角色变为老师的角色:只需要下命令叫这些函数去与显卡打交道,我记得在汇编下要自己写指令寻址并与显存打交道(段地址为:B800h撤销内存地址,详见王爽教授的汇编语言第2版),而windows的API却帮我们做了打交道的事,我们只要发出命令叫CDC去做就街上,,,,在MSDN下有很多这样函数的声明原型,但却没有源代码,即使是这样,MSDN依然是一个开发者喜欢的场所,因为可以或得很多宝贵的资料(英文版的更佳,排版得好)
漏洞程序的编写:
大部分都是利用汇编语言编写,微软补丁日(每月的第2个周2)是黑客们最喜欢的日子,跟过生日一样开心,因为很多人都是迟迟才补,或中了招才补,所以这段时间就是黑客大显身手的时候,资深的黑管或安全专家(像卡巴斯基这样的怪才)不到一天就能完成这样的工作、、、