Profil de 克莱克莱沃曼PhotosBlogListesPlus Outils Aide

Blog


24 mai

《Windows Internals》学习心得(2)- Windows Architecture

Windows

  • kernel32.dll, advapi32.dll: to provide Windows APIs
  • ntdll.dll: to switch from user mode to kernel mode
  • ntoskrnl.exe and drivers: major part of Windows OS
  • hal.dll: hardware abstraction layer

Ntoskrnl.exe and drivers

  • executive
  • kernel: KeXXX

Executive

  • system services: NTXXX, ZWXXX
  • executive support routines: ExXXX
  • executive components and relevant services

Executive components and relevant services

  • Object manager: ObXXX
  • Configuration manager: CmXXX
  • Process and thread manager: PsXXX
  • Memory manager: MmXXX
  • IO manager: IoXXX
  • P&P manager: PpXXX
  • Power manager: PoXXX
  • Cache manager: CcXXX
  • Security reference monitor: SeXXX

Drivers

  • ntfs.sys
  • volmgr.sys
  • ndis.sys

xikug谈黑盒,灰盒,白盒,逆向


早就想写点什么,自己都不知道一天在瞎忙什么,一直到最近才开始动手。。。我想通过这个乱谈系列跟大家分享一些心得。我打算在这个系列文章中讲点方法与思路,当然,很多方法并不是我的原创,只是我用这些方法和思路解决了我的实际问题。由于本人水平有限,很多说法只是我个人的理解,然后用我自己的语言表达出来,可能并不专业,所以在这里不负责任的乱谈一下,欢迎大家拍砖。
什么是代码逆向
代码逆向即是在没有源代码的情况下,对目标程序的行为、数据流、及编译器生成的代码进行分析,通过分析我们可以了解、发现程序的功能、流程、规则、及技术实现细节等,通过分析我们能对其进行优化、功能增强、漏洞填补、甚至还原成源代码等。这个分析过程我们可以称作逆向分析或逆向工程,简称逆向。
对我们个人而言或许我们能够从逆向分析的过程得到的最大好处就是学习到优秀程序的设计思想、及技术实现细节。
当今,逆向分析技术在很多地方都得到了应用,典型应用包括恶意软件分析、漏洞挖掘、BUG定位、技术探秘等。
有人可能会说逆向太无耻了,自己不会写就偷别人的代码。。。我就不相信说这话的人什么都会,我就不信他没有分析过别人的东西,学习过别人的东西,我只能说他是无知的。。。殊不知逆向是一个探索未知的方法,是一种学习态度,是代表不屈服于困难的精神。如果没有逆向当前的很多科学进步不了这么快,也可能不能取得进步,科学研究就是探索未知,把我们未知的东西进行分析研究变成已知,不光是软件领域有逆向工程的应用,其他领域如:基因重组、化工、制药、电子、建筑、航空、军事等领域也存在着他们各自的逆向工程应用,逆向工程帮助科研人员把未知的东西进行分解、研究、组合、改进等,甚至创造新的东西出来等,科学就是这样一点点进步的。
通过逆向得到的好处是显而易见的,然而任何技术都是把双刃剑,逆向分析技术也不例外。可能被人用于学习、解决技术问题或做有益于软件安全的事,也可能被人用于搞破坏。
逆向方法
白盒分析
白盒分析就是从代码级别上(可能是反汇编代码、反编译的伪代码或源代码),通过动态调试或静态反汇编分析和理解程序的功能、逻辑,找到程序的安全问题等。
黑盒分析
黑盒分析是指从程序的外部,通过观察程序运行时候的行为,规则等来猜测和断定程序可能的实现方法,是否有存在脆弱点等。
灰盒分析
灰盒分析通常需要借助一些专有工具(可能需要自己编写),如api监视工具,陷阱工具,内存比较工具,文件监视工具等对目标程序进行监控,看它发生了什么操作,调用了哪些api,产生了哪些结果,在系统哪些地方安插了HOOK或过滤等,以此来猜测和断定程序可能的实现细节。
目前越来越多的程序加了VM或进行了代码扭曲,用白盒分析此类程序可能会花很大力气也找不到突破口,而黑盒和灰盒分析往往对这类程序可能有意想不到的效果。
逆向手段
动态调试
通过调试器对目标程序进行追踪分析,能够清楚的了解到程序运行起来后内部的状态,运算结果等信息。
静态反汇编/反编译分析
使用反汇编器或反编译器把目标程序变为可读的汇编代码或伪代码,然后分析程序的结构,流程,逻辑等。
如何学习
逆向并不是想像中那么难,但也不是想像中的那么简单,真正困难的是如何有效的运行这些方法和手段来更快、更好的达到我们的目的,这需要累积大量的程序设计经验和逆向经验。
通常我们进行逆向的时候希望达到的目的大致可分为以下几种:
技术探秘/代码还原
软件漏洞挖掘
软件Bug定位
软件行为/规则分析
解除软件的使用限制
进行辅助程序的开发
我们要达到的目的不同,逆向时采用的方法和手段的“细腻”程度也不会相同,譬如我们对一个win32平台下的内核驱动进行“技术探秘/代码还原”的时候可能会把白盒、黑盒和灰盒所有的方法和手段都用上,每段汇编代码我们都必须看懂,代码实现了什么功能,跟另一段代码有什么关系,理解整个代码的架构和思想等;而我们在进行“软件行为/规则分析”的时候可能只需用上黑盒或灰盒分析法就够了,知道按下这个按钮后读写了哪些文件,哪些注册表项,调用了哪些api等等。分析过程中有时候我们只需静态反汇编看一下就可以了,有时候我们可能还需要动态调试一下,总之没有固定的套路,一切视情况而定。
扎实的编程基础是学好逆向的关键,基础打好了学什么都快。程序的基础就是算法和数据结构,语言只是一个实现的工具,绝大多数语言都是相通的,我们只要掌握一门,以后如果需要学习其他语言的话上手就会很快了,基础知识我们重点需要掌握以下内容:
1.至少一门高级程序设计语言,推荐C语言或Pascal
2.x86汇编语言
3.常用算法和数据结构
软件一般都是在特定平台下运行的,如Windows平台,Linux平台,WinCE平台,Java平台,Symbian平台,Plam平台等等。。。针对特定平台下的软件逆向,需要掌握特定平台下程序设计的相关知识,包括其SDK,进程管理,内存管理,文件系统等。这些东西不必全部精通,但要有个大致的了解,常用Api要知道,有特定平台下的程序设计经验最佳,遇到问题知道在哪里能找到自己想要的资料就够了。逆向的过程本身就是一个学习的过程,因此我们可以在逆向的过程中补充自己相关的知识,这样学习的效果是最佳的。(由于本人所接触的面比较窄,接触得最多的就是x86 Windows平台下的原生程序逆向,因此本系列文章中的内容除非特别说明都是指x86的Windows平台下原生程序和代码)
学习逆向的最好方式就是动手实践,在实践中有针对性的学习。通常来说我们逆向时所面临的东西对我们来说是未知的或者是可能知道但不确定的,如果是已知的就没必要再去逆向了。针对性的学习就是在自己逆向的时候缺什么知识就补什么知识,日积月累过后我们的收获是相当可观的,不光是经验值的增长,还有知识面的增长和知识深度的增长。
编程的经验对于我们实践逆向时也很重要,例如进行“漏洞挖掘”的时候我们可能会以程序设计者的角色进行思考,程序在哪些地方需要进行防范,哪些地方可能会出现漏洞等等,如果我们有足够的经验的话,可以很快定位到相关的代码部分对其进行分析,看是否存在可能的漏洞。又如在进行“技术探秘/代码还原”的时候,由于现在的程序规模越来越大,我们不可能每条指令,每段代码都去看,都去逆向,假如一个1M的程序需要这样做的话,光时间成本上来说成本都是相当高的,因此我们需要快速定位关键代码段,而丰富的编程经验有助于我们做到这一点。拓宽自己的编程知识面、积累编程经验跟积累逆向经验同样重要。丰富的编程经验能让我们事半功倍。
另外在进行代码还原时我们最好能用原始程序的实现语言进行还原,这是为了避免麻烦,因为现代的编程语言通常都有自己的Framework,提供各种各样的类库,他们功能各异,互不兼容,如一个VCL的程序我们硬要用MFC对其还原,VCL的某一非常复杂的机制或功能在MFC中可能没有,而自己如果在MFC中实现的话工作量是相当庞大的,这时的结果可能就会是事倍功半了。因此我建议在代码还原时最好使用原语言进行,原来是C的就用C,原来是Delphi的就用Delphi,原来是Python的就用Python。。。
逆向工程时常具有“四两拔千斤”的功效,我也不太会表述,等你有足够的实践之后大概才能体会到,这个就只能意会不能言传了
学习资源
上面说了那么多,是时候介绍一些学习资源的时候了,这些资源都是比较基本的,可说是学习阶段必备的,希望在大家学习和实践的过程中能帮到大家。

《Windows程序设计(第5版)》 – Windows平台下程序设计的经验教程。URL - http://www.china-pub.com/2382
《Windows核心编程》 – 又是一本经验的书,可以帮助你把Windows下的编程技术提升一个层次。 URL - http://www.china-pub.com/131
《深入解析Windows操作系统(第4版)》 – 这本书是关于Windows内部机理核心的权威之作。这本书对提高你的逆向水平也是大大有帮助的,当然,前提是在你看懂之后。 URL - http://www.china-pub.com/32775
《加密与解密》 – 不错的入门书籍,快出第三版了,http://www.china-pub.com/12210
网站
www.rootkit.com – 很多关于系统安全,系统内核方面的资料和代码
www.codeproject.com – 很多程序设计的代码和文章
msdn.microsoft.com – 包含最新的微软平台下的开发资料
论坛
bbs.pediy.com - 看雪论坛,国内最大的加解密论坛,已经向软件安全转型,上面汇集了国内大批高手。
www.unpack.cn - 一蓑烟雨,国内最专业的脱壳论坛,关注面也很广,除了脱壳外还有汉化、软件安全、木马病毒、编程、游戏、文学、音乐、艺术等,上面的高手也不少。
bbs.driverdevelop.com - 驱网论坛,驱动开发的论坛,高手不少,但是发言的比较少,可以去逛逛。
forum.sysinternals.com – Sysinternals,很多系统方面的资料,不少高手在上面发言。
www.debugman.com - 第8个男人,在下创建的论坛,旨在为志趣相投的朋友提供一个交流平台,目前关注方向为程序设计、逆向、代码安全和系统底层等。
工具
这里列出的工具只是很少一部分,对工具的选用,我的观点是哪个随手就用哪个。
OllyDbg – 调试器,ring3下的调试器,上手快,功能强大,有很多插件。
SoftIce – 调试器,ring0级调试器,当然ring3程序也是可以调的,功能强大,但已经不更新了,不支持vista等较新的操作系统。
WinDbg – 调试器,MS自家的调试器,就不多介绍了,两个字 - 推荐。
IDA – 反汇编器,最强大的静态反汇编分析工具。
LordPE – PE工具,可编辑PE文件等。
PEiD – PE工具,可识别PE文件的格式信息,如用什么编译器编译的,是不是被什么壳处理过了等。
FileMon – 文件监视工具,可监视系统或程序对哪些文件做了什么操作。
RegMon – 注册表监视工具,可监视系统或程序对哪些注册表项做了什么操作。
SSM – HIPS,这个工具有时候可以给逆向带来很多方便,如抓取某个文件,禁止访问某个注册表项,盒灰分析等。

23 mai

《Windows Internals》学习心得(1)

以前做开发的时候用VC, MFC, 对于Win32和Driver是一点也不感兴趣。后来乱七八糟混了好几年,IT业大变,自己碰巧就做上了测试。测试又做了快5年了,大部分时间是自我摸索,到了现在这情况变成了一定要学习Windows internals了。
这本书从开始看也两年多了,我很难说我看懂了多少,只是最近才开始有点立体的感觉,当然这跟工作上接触到一些相关内容还是有关系的,否则只是硬看应该是看不懂才对。我想目前由于不精通Windows internals对于我的测试工作有以下几个障碍:
  1. 跟开发人员的沟通存在Gap。
  2. 不能更好地去设计测试用例,因此不能抓到更深入,复杂的bug。
  3. 对于一些复杂点的问题很难去debug。
  4. 对于code review, code coverage, security test等等都受到了很大的限制。
  5. 职业发展也有了bottleneck。
因此,我觉得在今后的几年应该在Windows internals上下下功夫,争取能够达到精通。今天我就想谈谈自己以前比较混乱的一些概念,windows internals, windows kernel, SDK, DDK, Win32, driver。
  • Windows internals应该是讲Windows OS实现的一些细节,它并不局限在kernel mode的模块和知识,还涉及到了user mode的一些系统进程。因此,我说学习windows internals就应该包括了所有Windows OS的知识。
  • Windows kernel,我主要是按照在kernel mode运行的模块来理解的,因为Kernel mode里还有个kernel,容易造成概念的混淆。
  • Win32 API就是windows提供的在user mode的应用程序编程接口,它的工具包叫做SDK。但是涉及到系统服务,也就是内核上的服务的时候,他们是通过ntdll.dll转到kernel mode去实现的。也就是说,实现的细节还是在kernel mode。
  • Kernel里提供了一套接口叫DDK,是给driver的开发人员使用的。如果你想在Kernel mode运行你的程序,你应该只能通过编写driver来实现,正常来说。
由于windows internals的大部分知识都是在kernel里,因此我也想主要集中在kernel mode里的模块和知识。
  1. 对user mode开放的服务都有哪些,实现细节如何?
  2. DDK里的函数都有哪些,实现细节如何?
  3. Executive层的模块都有哪些,实现细节如何?比如,memory manager, IO manager, cache manager等等。
  4. Windows里的driver都有哪些,实现细节如何?比如我认为最重要核心的ntfs。
学习资料:
《Windows Internals》
《Programming the Microsoft Windows driver model》

最后,HAL不想学,也不知道有没有用,Hardware不想学,WDF暂时不想学。现在是新手,能想到的也就这么多了。

Debugging Tools for Windows 学习使用心得

其实我的主要工作一直是UI test, UI automation, manual test 等等。曾经跟领导提过想做些深入点的测试,领导则反问 “你Kernel debugging 怎样?”。这是一个非常有趣的问题,你因为不具备良好的kernel debugging的能力,所以不给你做深入的测试工作,而因为你没有做深入的测试使你也不可能具备kernel debugging的能力。很多时候事情就是如此矛盾的,而我也曾经说过,一切最终还得靠自己。
前几天发现一个奇怪的bug,分给这里的一个seniro dev他也没太多的办法。我在一个高手的指导下自己debug and figure out了root cause。在和这个高手的交流中,也基本明确了一下步的学习目标。以前的我主要是靠自己摸索,我想多跟高手接触,交流还是非常非常有帮助的,当然机会也不是很多。那么我的下一步的目标就是“code review, debugging, windows internal and WDM”。Debugging可以说贯穿了其他的三个,因此我今天先谈谈我对debugging tools的感受。
我们基本都是用微软提供的几个debuggers,ntsd, cdb, kd, windbg。ntsd和cbd是user mode debugger, kd是kernel mode debugger, 他们都是command line的,而windbg是UI的debugger, 可以调试both user and kernel mode。大多数情况,我都是用windbg,但是在一些特殊的情况下,我还是需要其他的tools。今天我主要是谈windbg,我想学习windbg大概分这么几个阶段:
  1. windbg的命令,共三种命令,普通命令,加'.'的命令和加'!'的命令。Debugging tools自带的那个教程学习一下就应该差不多了,以后就靠经验积累了。
  2. 如何开始debug, 这要根据你所debug的目标和要debug的问题来决定了。你应该决定是使用user debugger 还是 kernel debugger, 是 local debugging 还是 remote debugging, 是用windbg还是ntsd, 等等。通常都有几种方式去debug,你要能够准确选择一个最合适的。
  3. 怎样进行debug。对于一个需要debug的问题,你需要有你的策略。比如,在哪里设置断点,如何设置断点,等等技巧。
  4. 利用一些tools。有些情况下只有debugger还是很难debug,你需要使用其他的tools去辅助。比如,有的时候在问题显露的时候已经太晚了,你很难追踪问题发生时的情况。这里,你可以用一些tool使得在事情发生的时候跳到debugger上去。
  5. Code review非常重要。当你发现一个bug的时候,如果你代码熟悉,你可能直接就能想到问题出在哪里,或者你稍微读读代码就能发现问题所在,根本就不需要debugger。而在你debugging的时候,常常你也需要review相关的代码来理解这个问题。
  6. 汇编语言。当你在调试optimized code的时候,你常常不能通过dv, dt等命令正确地看参数,变量等等,或者变量根本就没得显示。这就需要你具备汇编语言的阅读能力,了解calling convention, 不同cpu的汇编,以及在disassembly模式去调试。
  7. Kernel debugging。如果你具备了以上的技能以后,user mode debugging应该不成什么问题了。但是对于调试kenrel mode还是需要一些附加的知识与技能。首先,kernel debugger的命令很多跟user mode的就不一样了,你需要学习。其次,你需要了解Windows internal的知识和数据结构,以及Windows driver model。这也是为什么我把Windows internal 和 WDM作为两个学习要点。
最后引用高手的两句话,“debug一两年,你就没什么问题debug不了了”,“只要能repro,你总能找到办法去debug”。
15 octobre

Kernel stack not resident (Using .pagein)

You might find yourself debugging an issue and a thread you are interested in is paged out.  Here's the steps to use to page in the stack for the kernel side and user side...   Be careful when doing this on a live machine that you want to release after debugging as paging in certain section of memory can cause it to bugcheck... 

2: kd> !thread fffffa8004415460
THREAD fffffa8004415460  Cid 087c.0acc  Teb: 000007fffffd5000 Win32Thread: 0000000000000000 WAIT: (WrLpcReply) UserMode Non-Alertable
    fffffa80044157f0  Semaphore Limit 0x1
Waiting for reply to ALPC Message fffff88018c943f0
Impersonation token:  fffff8801d302060 (Level Impersonation)
Owning Process            fffffa80046e5610       Image:         snmp.exe
Wait Start TickCount      367059906      Ticks: 15906005 (2:20:55:35.268) //Been waiting a while.
Context Switch Count      13819416
UserTime                  00:00:38.173
KernelTime                00:02:33.972
Win32 Start Address 0x000007fefa7724bc
Stack Init fffffa600440ddb0 Current fffffa600440d6e0
Base fffffa600440e000 Limit fffffa6004408000 Call 0
Priority 11 BasePriority 8 PriorityDecrement 1 IoPriority 2 PagePriority 5
Kernel stack not resident. // We can't see what the stack looks like as it been waiting so long its been paged out.

2: kd> .pagein fffffa600440d6e0  //Grab Current from above...  This will get us the kernel side...
You need to continue execution (press 'g' <enter>) for the pagein to be brought in.  When the debugger breaks in again, the page will be present.
2: kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff800`0163e1d0 cc              int     3
1: kd> !thread fffffa8004415460
THREAD fffffa8004415460  Cid 087c.0acc  Teb: 000007fffffd5000 Win32Thread: 0000000000000000 WAIT: (WrLpcReply) UserMode Non-Alertable
    fffffa80044157f0  Semaphore Limit 0x1
Waiting for reply to ALPC Message fffff88018c943f0
Impersonation token:  fffff8801d302060 (Level Impersonation)
Owning Process            fffffa80046e5610       Image:         snmp.exe
Wait Start TickCount      367059906      Ticks: 15906070 (2:20:55:36.282)
Context Switch Count      13819416
UserTime                  00:00:38.173
KernelTime                00:02:33.972
Win32 Start Address 0x000007fefa7724bc
Stack Init fffffa600440ddb0 Current fffffa600440d6e0
Base fffffa600440e000 Limit fffffa6004408000 Call 0
Priority 11 BasePriority 8 PriorityDecrement 1 IoPriority 2 PagePriority 5
Kernel stack not resident.
Child-SP          RetAddr           : Args to Child                                                           : Call Site
fffffa60`0440d720 fffff800`01647abe : fffffa60`0440da88 fffff880`18c943f0 fffffa60`0440da88 fffff880`18c943f0 : nt!KiSwapContext+0x7f
fffffa60`0440d860 fffff800`016484c5 : 00000000`00303cb0 fffffa60`0440da88 00000000`00000009 00000000`00000001 : nt!KiSwapThread+0x12e
fffffa60`0440d8c0 fffff800`01681067 : 00000000`00000000 00000000`00000011 00000000`00000001 00000000`00000000 : nt!KeWaitForSingleObject+0x5f5
fffffa60`0440d940 fffff800`018be424 : fffffa60`0440da88 00000000`00303cb0 fffffa80`04415460 00000000`00000000 : nt!AlpcpSignalAndWait+0x97
fffffa60`0440d980 fffff800`018be868 : 00000000`00000000 00000000`00000000 00000000`00303cb0 00000000`00300318 : nt!AlpcpReceiveSynchronousReply+0x44
fffffa60`0440d9e0 fffff800`018a834f : fffffa80`04352e60 fffffa80`00020000 00000000`00303cb0 00000000`00300318 : nt!AlpcpProcessSynchronousRequest+0x251
fffffa60`0440db00 fffff800`016437b3 : fffffa80`04415460 fffffa60`0440dca0 00000000`00000280 fffff800`0189c654 : nt!NtAlpcSendWaitReceivePort+0x19f
fffffa60`0440dbb0 00000000`77af4dca : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffffa60`0440dc20)
00000000`016aebc8 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77af4dca

1: kd> .pagein /p fffffa80046e5610 00000000`016aebc8 //We take the process ID of the thread and the usermode address at the bottom of the stack.
You need to continue execution (press 'g' <enter>) for the pagein to be brought in.  When the debugger breaks in again, the page will be present.
1: kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff800`0163e1d0 cc              int     3
1: kd> !thread fffffa8004415460 //Viola!  Now we have the whole stack, you might need to do a .reload for symbols.
THREAD fffffa8004415460  Cid 087c.0acc  Teb: 000007fffffd5000 Win32Thread: 0000000000000000 WAIT: (WrLpcReply) UserMode Non-Alertable
    fffffa80044157f0  Semaphore Limit 0x1
Waiting for reply to ALPC Message fffff88018c943f0
Impersonation token:  fffff8801d302060 (Level Impersonation)
Owning Process            fffffa80046e5610       Image:         snmp.exe
Wait Start TickCount      367059906      Ticks: 15906135 (2:20:55:37.296)
Context Switch Count      13819416
UserTime                  00:00:38.173
KernelTime                00:02:33.972
Win32 Start Address 0x000007fefa7724bc
Stack Init fffffa600440ddb0 Current fffffa600440d6e0
Base fffffa600440e000 Limit fffffa6004408000 Call 0
Priority 11 BasePriority 8 PriorityDecrement 1 IoPriority 2 PagePriority 5
Kernel stack not resident.
Child-SP          RetAddr           : Args to Child                                                           : Call Site
fffffa60`0440d720 fffff800`01647abe : fffffa60`0440da88 fffff880`18c943f0 fffffa60`0440da88 fffff880`18c943f0 : nt!KiSwapContext+0x7f
fffffa60`0440d860 fffff800`016484c5 : 00000000`00303cb0 fffffa60`0440da88 00000000`00000009 00000000`00000001 : nt!KiSwapThread+0x12e
fffffa60`0440d8c0 fffff800`01681067 : 00000000`00000000 00000000`00000011 00000000`00000001 00000000`00000000 : nt!KeWaitForSingleObject+0x5f5
fffffa60`0440d940 fffff800`018be424 : fffffa60`0440da88 00000000`00303cb0 fffffa80`04415460 00000000`00000000 : nt!AlpcpSignalAndWait+0x97
fffffa60`0440d980 fffff800`018be868 : 00000000`00000000 00000000`00000000 00000000`00303cb0 00000000`00300318 : nt!AlpcpReceiveSynchronousReply+0x44
fffffa60`0440d9e0 fffff800`018a834f : fffffa80`04352e60 fffffa80`00020000 00000000`00303cb0 00000000`00300318 : nt!AlpcpProcessSynchronousRequest+0x251
fffffa60`0440db00 fffff800`016437b3 : fffffa80`04415460 fffffa60`0440dca0 00000000`00000280 fffff800`0189c654 : nt!NtAlpcSendWaitReceivePort+0x19f
fffffa60`0440dbb0 00000000`77af4dca : 000007fe`fea5c72b 00000000`00001000 00000000`016aee90 00000000`01460058 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffffa60`0440dc20)
00000000`016aebc8 000007fe`fea5c72b : 00000000`00001000 00000000`016aee90 00000000`01460058 00000000`0030ed80 : ntdll!NtAlpcSendWaitReceivePort+0xa
00000000`016aebd0 000007fe`fea6c592 : 00000000`00302b50 00000000`016aef30 000007fe`fe95c8b8 00000000`00001000 : RPCRT4!LRPC_CCALL::SendReceive+0xbb
00000000`016aec50 000007fe`fea6c5e2 : 00000000`016aed00 00000000`00000000 00000000`00000000 00000000`01460058 : RPCRT4!I_RpcSendReceive+0x42
00000000`016aec80 000007fe`feafad2c : 00000000`016aef30 00000000`00000000 00000000`00000000 00000000`0030ed80 : RPCRT4!NdrSendReceive+0x32
00000000`016aecb0 000007fe`feafaef0 : 00000000`00000000 000007fe`fe95d090 00000000`00000011 00000000`016aece0 : RPCRT4!NdrpClientCall3+0x11c
00000000`016aef00 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : RPCRT4!NdrClientCall3+0x7c

1: kd>

From http://blogs.technet.com/brad_rutkowski/archive/2007/08/30/kernel-stack-not-resident-using-pagein.aspx

8 août

驳斥《C++/CLI 的十宗罪》

  由于公司有大批程序员死在 C/C++ 的内存管理上(泄露,越界),作为有先见之明的,伪程序员的老板征集了各方意见后,决定将现有的所有框架代码移植到 C++/CLI 下面来。由于本人在 C/C++ 上多花了几年功夫,自然,这个“简单”的问题就落在了我的肩膀上。用了一端时间 C++/CLI 后,发觉 C++/CLI 不是鼓吹中的那么好,特别是对于 C++ 程序员来说,简直是噩梦。依据 MS 的历史,我认为 MS 在 VC9.0 以后的版本中才能将 C++/CLI 实现的比较完美。(MS 的历史:要在第三个版本才会比较好用,例如:VC4、VC5 都不是很好,VC6 我用了 7 年;.NET 2002、2003 也一堆问题,.NET 2005 终于好用了)。

特在此总结了 C++/CLI 的十宗罪,罪状如下:

第一、没有全局变量

  好吧,我承认“现代”的高级语言都没有全局变量,不过,实现这些高级语言的运行库都有无数的全局变量(反汇编跟踪下就知道了)——哦,只许州官放火,不许百姓点灯。好吧,我承认没有全局变量,我还能生存,大不了晚上我不点灯不做事了。

 

[Comments] 既然你知道现代的高级语言都没有全局变量,怎么能把它作为C++/CLI的罪过呢?为什么现代的高级语言都没有全局变量呢?为什么面向对象的一大特点就是封装性呢?既然你研究了几年了C/C++,应该清楚和明白吧?简单的来说,在C语言时代是没有封装性的概念的,因此全局变量满天飞,使得大型程序维护起来十分的不方便。这也是为什么面向对象概念兴起的一个很重要的原因。作为一个程序员如果从整体的设计来考虑的话,是应该尽量少用或者不用全局变量的。这也是为什么现代的高级语言都取消了全局变量了,而且如果面向对象的设计能力比较强的话,是可以完全的抛弃全局变量的使用的。当然了,对于很多C程序员来说,不能使用全局变量有点要命的感觉。不过这不能怨语言,应该看看自己是否真正理解了面向对象的概念。

还有就是,为什么他们的运行库有无数全局变量?因为他们是用C来实现的,在C语言里是必须要用全局变量的。

 

第二、类的静态成员变量的初始化时机问题

  没有全局变量是吧,那么我用静态成员变量来模拟总可以了吧。很好,顺利编译通过!但是,但是,这个静态成员变量是什么时候初始化的呢?哦,用到这个类的任何变量/函数/实例的时候初始化的!这个到是很好,解决了全局变量的初始化顺序问题。问题是,我只不过希望静态成员变量将本类注册到类工厂去。我从来不会直接访问这个类的任何变量/函数/实例——我只访问他的基类。这样一来,我的类工厂里空空如野,我的系统再也工作不起来了。

 

[Comments]这个问题跟第一个问题是类似的。还是你要想方设法的去用全局变量,而没有想过为什么不让你用全局变量。静态成员变量本来就不是做这个用途的,它是类的实例的一个全局变量,注意只是这个类的实例,而不是整个应用程序。这只能说明你错用了静态成员,而不能归于C++/CLI的罪过。

还有就是静态成员完全可以初始化,只是你自己不会罢了。建议你好好的去学习一下.NET。

 

第三、弱智的、缺乏实际应用经验的人设计的的静态成员变量初始化地方的问题

  你能判断下面这句话是变量声明还是函数申明吗?
static int myfunc(mytype_or_myvar);
反观 C++ 的做法:
static int myfunc;
int myclass::myfunc(mytype_or_myvar);
你会有歧义吗?

 

[Comments]没看出这个有什么问题呀?你难道读程序是一句一句的读吗?我们怎么能够把程序分割理解呢?如果上下文一起来看,能有什么歧义呢?

 

第三、静态局部变量的问题

  你知道,仅仅一个“单件”,就多么的需要这个玩意!

 

[Comments]这个不太明白想表达什么。

 

第四、功能弱到极点的 array

  其实已经很强大了,比 C++ 的数组多了一个 Resize() 功能了。但是,跟 std::vector 比较起来,一个天上,一个地狱!好吧,我承认我对 CLI 不熟悉,那么,谁告诉我,C++/CLI 提供了什么样的东西,具备 std::vector 的功能?不要告诉我基于 CLI 的模板库,我严重怀疑其可工作否。

 

[Comments]看来你真的没好好学过C++/CLI,你不知道C++/CLI最大的优点在于通吃一切吗?你现在抱怨的是CLI,就算CLI没有你想要的功能。可是C++/CLI是C++和CLI的混合体,你没有必要一定用CLI,你当然还可以用回你所有的C++功能。std::vector算个什么呢?

 

第五、value struct 和 cpp struct 在模板函数上,对“引用”的符号匹配问题

  对于 cpp struct 应该匹配那个符号?'&' 还是 '%'?答案是 '%'。哦,我的天哪,居然还可以使用 pin_ptr<cpp struct> 来取地址!实在是太伟大了!那么,我的 cpp struct 变量究竟位于哪里?堆上?栈上?还是托管堆上?我迷糊了,编译器跟我一起迷糊了。

 

[Comments] 看来你还没分清楚cpp struct和CLI struct的区别呀。建议你还是好好学习一下再来讨论吧。

 

第六、模板函数的特例化问题

  对于下面这样的函数:
ref class myclass
{
         template<typename T>
         myclass % operator << (T % t){...}            // 函数一
         myclass % operator << (mytype % t){...} // 函数二
};

myclass c;
mytype t;
c << t;   // 式一

  你认为“式一”会调用那个函数?我知道你的答案,不过你的答案是错误的,因为你是一个资深的 C++ 程序员。当我意识到我的系统不能正常工作是源于此的时候,我很想联系恐怖份子,购置一枚或者更多的原子弹,然后在 google 上找到 MS 总部甚至分部的坐标,然后把原子弹的目标设置好……

 

[Comments] 建议你先学学template和generic的区别吧。

 

第七、C++ 和 C++/CLI 的代沟问题

  ref class,value class 里不能放置 cpp class 的实例,这个我还能接受。但是,cpp class 里不能放置 ref class,value class 的任何玩意——实例、引用、指针、interior_ptr,pin_ptr。我怎么实现回调式的功能?不是我非要设计出回调式的功能,而是,有那么多,那么多的Win32 API 是回调式的!好在我在知识的海洋中找到了 System::Runtime::InteropServices::GCHandle,不然,我定制的原子弹就会上升成为氢弹!

 

[Comments] 这不是C++和C++/CLI的代沟问题,是C++和CLI的代沟问题,C++/CLI提供了你一个桥梁把他们联系在一起,你应该感激才对。没有C++/CLI你更没法搞。这是C++/CLI一大优点,而不是罪过。

 

第八、.NET提供的类库太过于臃肿

  大家知道,C/C++ 的 CRT 也就几百K,C/C++ 仍然实现了你所能看到的软件的绝大部分——但是,你看到的,用 .NET 实现的软件有几个?好吧,我承认我偷梁换柱,不过,由于.NET库的臃肿,以致于稍微一个好听,合义的单词都被.NET用掉了。这让我不能放心用 Object,String,IStream...等等作为我自己的类型的命名,我只好绞尽脑汁想别的名字——正如您所猜测的一样,我的英文很差,必须要借助翻译工具才能看懂 C++/CLI 的白皮书。这让我想出新鲜的,上口的,其他跟我英文一样差的人能看明白的单词,其难度真不啻于用随机数拼出一个Windows啊!

  随机数拼出一个Windows:这个是我一个哥们的研究课题,把 N 台电脑关在一个小黑屋里,让他们随机生成二进制数据,并当作程序来运行。当速度足够快或者运气足够好的时候,生成的二进制数据刚好跟你从 DB 商手里买到的 Window XP 一样。(UMU:U墨的手法,说明几率极小极小!)

 

[Comments]首先.NEt是否臃肿,是否应该臃肿本身就是一个问题。就算.NET是臃肿的,也不是C++/CLI的罪过。那是.NET的问题。

 

第九、四饼!

  这个问题是由第八个问题引起的,因为有这么多,这么深的名字空间,各个名字空间下有这么多相同名字的类型名,让我不得不整天::...::...::...::...::...::...::...::...::...::...::...::...::......
真的,四饼占了我代码的十分之一的字数。如果按字数发工资,我是乐意的,问题是,不是按字数发工资!

 

[Comments]如果你用了using spacename, 是不用那么多::的,这个是.NET的常识问题,也是.NET的一大预言特点。优于C++的。

 

第十、GC不能解决内存泄露问题

  首先,我们要承认 GC 的一个伟大壮举:GC 能够在程序退出去的时候,释放所有的内存。但是,在 8 年前,我用 C 在 DOS 也能够办到这个功能。问题是,运行十天后,我 C/C++ 占用的不会释放的内存,GC 也不能释放,甚至 GC 占用的内存还更多。因为 GC 分不清楚没有置空的句柄是否还有机会被再次使用。

 

[Comments] 看来你一点都不懂GC呀。建议你先去好好学学。

综上所述,作者是一个对.NET没有太多概念的C/C++的程序员。因此,对C++/CLI的理解,就显得非常肤浅了。不知道作者搞了几年的C/C++为什么就这么轻率的发表这样不负责任,有很大误导作用的文章出来。从作者这样热衷全局变量的使用来看,好像面向对象的设计水平也很普通。希望大家不要被误导,看到作者的文章,使我对C++/CLI更加有信心了。

7 août

发现C++/CLI是发现新大陆了吗?

最近事情不多,想学点什么新东西了。最近几个月Facebook的F8炒得很火,吸引我看了看他们的Application是什么样子的,应该怎么开发。看完之后,自己不太感兴趣,因为自己要建Server,比较麻烦。然后,随便找找发现了C++/CLI。原来微软在2005年就推出了这门算是新的语言吧,以前只是知道有managed C++,评价不高,C++/CLI是它的改进版本。随便了解了一下,感觉这门语言还是很吸引我的。原因很简单,就是它的强大混合编程的能力。它可以调用.NET, MFC, Windows API。目前的测试开发语言主要是C#。一直以来自己都认为C#是最适合自己的测试开发工具了,因为用C++开发测试程序不太现实,至少不能作为主要的开发语言。VB,Java什么的是我一直都讨厌的,其他script的什么的,功能都太弱了。唯有C#是除了C++以外最强大的快速开发工具。可是,用C#开发里边就存在了一个不可逃避的问题,就是调API的艰难。很多时候,我们都会碰到.NET不能完成的功能,尤其是偏系统一类的测试。那么我们就需要用Windows API来实现,可是C#调API的方式使我选择了逃避,或者说放弃。心里想,必要的时候就用C++吧,可是还真没用过。(这也就是测试人员的灵活性了,呵呵)

大概一天的时间都是看别人对C++/CLI的评论,使用心得什么的。感觉大部分人都是批评的态度,少数人喜欢和支持。说真的,自己也能理解C++程序员对它的看法。首先,它新增加的关键字确实让人不习惯,看起来,用起来都觉得怪怪的。其次,大部分C++程序员开发的项目都不需要用到.NET吧?至少目前应该是这样,很多人还在VC6下边编程。既然用不到它,就不会体会它的好处,那么就真的只有不好了。少数人喜欢它就是因为它强大的能力。目前来看,还真的没有其他语言从功能上说能与它相提并论。另外一点就是,从资料上来看,目前使用这门语言的人很少。因此我也很犹豫到底应不应该学习和使用它。

在疑惑中对它进行了学习,感想如下。

1。想真正的掌握好这门语言既需要C++的知识,又需要.NET的知识。正如一些人所说,学习它的难度要高于C++和C#。当然我是说真正的掌握,你可以用它只做C++程序,或者只做.NET程序,那就跟单独学C++或者C#的难度一样了。因为,大部分C++程序员侧重于底层,C#程序员侧重于上层,使得纯C++/C#程序员转向C++/CLI都需要一定的时间,不会轻易上手。当然对C++程序员来说会更简单一些,毕竟.NET还是比较容易掌握的(前提是他们能有心,耐心学习)。另外,如果同时具备了C++和C#的知识和经验,上手C++/CLI还是会很快的。比如我学了一天就可以上手了。

2。功能的确强大,.NET, MFC, API通吃。我装的VS2008 express 版本不支持MFC, 不过.NET+API真的就足够对付任何工作了。

3。语法没有想象中的那么不可接受。首先,它增加的关键字其实并不多,大概10几个吧,很容易学习和掌握。其次,很多关键字你也可以完全不用的,比如ref struct, value struct可以完全不用。value class 我觉得也没什么太大用。ref class就足够了。这样的话,虽然是4个关键字,可是对你来说就只是一个。再次,这里边真的有一个习惯问题, */^, using/using spacename, ./->, new/gcnew 这些东西你很快就应该能够适应。

4。和C#的比较。C#的语法还是简洁许多,也更自然。如果纯做.NET程序,目前来看没有必要非用C++/CLI。可是如果需要调用API的话,C++/CLI给你带来的方便性还是要大于语法的繁琐。因为语法可以去适应,习惯。功能的强大可是可遇而不可求的东西呀。所以,个人认为做测试程序,C++/CLI还是一个不错的选择,能够更广阔的设计我们的test case。

5。关于C++/CLI的未来。未来真的很难说,一看市场的需求,二要看微软的支持。目前来看,C++多用于系统,驱动方面。C#用于应用方面。相信未来的一段时间都会维持在这种状态中。C++/CLI给我们带来的是C++,C#混合编程。可是市场上有多少产品的开发需要这种混合编程呢?目前来看很少,就算有,有些公司就单独用C++,C#来做,再集成起来也是解决的一种方法。另一方面,C++程序员和C#程序员分的很开,没有适合用这种语言的中间状态的程序员。如果没有产品,没有程序员的支持,很难说能有什么太好的前途。不过我想C#加上C++/CLI的解决方案还是要比C#加上纯C++的解决方案更合适,合理一些。如果不嫌弃C++/CLI的语法,单独用它也可以完全的解决。现在来看,C++/CLI的阻力最大来自于它的语法了,如果语法简洁了,在.NET领域会有不少人用,从而引起它的流行,而在系统领域也得到一定的应用。个人认为,using space可以就改为using, *,^合并为*,由编译器智能判断是传统指针还是引用指针,new和gcnew也合并,编译器智能判断是应该在普通堆还是在托管堆上建立。如果这样的话,我想就没什么人会拒绝接受它了。

个人来说, C++/CLI还是极大的提高了我写测试程序的能力,我还是抱着积极的态度去采用它。这也避免我长期用C#而疏远了C++。用好C++/CLI就等于用好了三门语言。

2 août

如何debug lsass.exe

这两天需要调试lsass.exe, 昨天baidu竟然都查不到什么信息。lsass有它的一些特点。首先它是运行在user mode, 因此一般来讲应该用user mode的调试工具来调试。可是,如果用debuuger直接跟它相连,计算机就会自动重启,使得调试无法进行下去。其次,如果我们想在机器启动的时候调试lsass也比较麻烦,因为user mode debugger那个时候还不能工作。再次,调试lsass的时候,网络的访问不能进行,因此从网络上访问symbols and source code 也成为了不可能。经过认真研究,发现有三种方法可以用来调试lsass,并且各有优缺点。下面就一一讲解一下。

1. ntsd piped through KD

a. 修改注册表

HKLM\Software\Microsoft\Windows NT\CurrenVersion\Image File Execution Options\lsass.exe debugger = REG_SZ c:\debuggers\ntsd.exe -d -g -G

b. 用另一台机器通过kernal debugger和测试机相连

c.重起测试机

这样的话,在启动阶段,当系统调起lsass的时候,测试机的ntsd就开始工作,并且将输入,输出传送到kernal debugger上。这个属于在kernal debugger里进行user mode的调试。

优点:可以在启动的时候调试lsass。

缺点:symbols and source files 必须要copy在测试机上。(不太方便)

 

2.Debugging LSA via dbgsrv.exe

a.Find the PID for LSA via tlist.exe

b. C:\Program Files\Debugging Tools for Windows>dbgsrv.exe -t tcp:port=1234,password=spat

c.Run this command to attach to LSA on the remote machine.

I:\debugger>windbg.exe -premote tcp:server=192.168.1.102,port=1234,password=spat -p 596  -- where 596 = PID of LSASS

优点:symbols and source files 可以在调试机上

缺点:不能在启动的时候进行调试

 

3.Debugging LSA from Kernel

a. Get the process address for LSASS

0: kd> !process 0 0 lsass.exe

PROCESS 815196c0 SessionId: 0 Cid: 010c Peb: 7ffdf000 ParentCid: 00e4

DirBase: 042d2000 ObjectTable: 81519aa8 TableSize: 859.

Image: LSASS.EXE

b. Switch to the process context:

Either

.process /p /r 815196c0

Or

.process –i 815196c0 ;g;.reload /user

优点:symbols and source files 可以在调试机上,可以进行log out/ log on 过程的调试

缺点:不能在启动的时候进行调试

因此,如果想在启动的时候调试,就必然要选方法1。如果想在log on的时候调试,选择方法3。其他情况,可以选择方法2,或者方法3。

30 mars

我大学是如何学习coding的

记得大一学PASCAL的时候,觉得特别的难。刚开始接触,很难理解计算机的语言。虽然最后考了95分,可是根本没有真正的学会。后来又学了C语言,也没有什么coding的感觉。一直到了大三的第一学期,还是对计算机一窍不通,甚至不知道应该如何开,关计算机。看到非计算机专业的朋友熟练的操作着计算机,心里真不是一个滋味。也有大专的朋友show给我他的C,C++的程序,给我海阔天空的讲解了一番,心里真是佩服。大三第一学期过后的春节,说通父母给买了第一台计算机,从这个时候就真正开始了我的coding学习。

首先是要把C语言补回来,把C语言教程看了一遍,把语法理解,练习了几个小程序,找了找感觉。这个阶段是很快的,可能就是几天的时间吧,毕竟以前还是学过C的,然后开始学C++。当初问过老师,学C++需要多长时间。老师的回答是三个月。买了本C++教程,一天看完。心里很不以然老师说的三个月,感觉跟C差不多,就是多个class。后来就买了两本编游戏的书,开始模仿和练习编游戏,这样在当时我们的机房DOS系统下,我是专业里唯一一个能用GUI编程序的人。所有的assignment,我都编两个版本Text mode and UI mode, Text mode送给好朋友,UI mode自己用。这样半年之后,我才突然发觉,原来这才是C++。自己用了半年的时间才真正的学会C++。(C与C++的最大区别在于设计的思维:面向过程和面向对象,C到C++的转变是需要一个过程的)。这个时候再看当时show给我程序的朋友的C++的程序时,才发现他根本没有学会C++,他只是用C++在做C程序而已。

C,C++学会了,下一步怎么办?我的性格是愿意学习最难的东西,因此就选择了VC。当初去北京书店门口看到了广告“VC程序员试用期月薪4000”,觉得是天文数字了,也更坚定了要学习VC的决心。当初的学习条件还是非常艰苦的,市面上就两本书,我还很清楚的记得,一本是四川大学出版的,一本是龙门书局出版的。四川大学那本是入门的傻瓜教程,主要是教你怎样使用Visual Studio而已,没有深入的学习指导。龙门书局是翻译的微软的,讲的对我来说太深了,没法理解。因此主要学习川大那本,可是那本书还有问题,我按照书上所讲的去做,行不通。VC学习陷入困境,没有书可以查询,没有人可以问,自己也没有理解VC的精神。只能放弃VC,转而VB。VB当然很容易了,基本上不用怎么学就上手了。半年之后,有朋友想让我帮助编一个截取VCD片断的小程序。我用VB实现之后,准备给他用。然而,我却发现缺少dll, 这样我就把这个dll铐过去,又发现缺少其他的dll。搞了几次还不行,我也恼火了,就想能不能用VC试试。这次又拿起VC感觉就好多了,不但实现了这个小程序,并且第一次感觉VC入门了。从此之后,就走向了VC这条路。基本上来说,我是学校唯一一个会VC的人,而且几年以后也不过只有两个人会VC,另一个是跟了我一年,我带起来的。后来龙门书局的书也能理解了,再后来VC的书就慢慢多了,学习起来就越来越方便了。以我从小到大的学习经历来说,学习托福那段时间是最苦的,学习VC的这段时间是最难的。后来也没有再碰到这么大的困难学一个东西了。

Basically, 大学学习coding的时间是一年,半年从C到C++,半年从VB到VC。一年之后去单位做实习了。这一年发生了三件时间,比较值得回忆。

1。女朋友是通信系的,我帮助做了一次C语言的作业,没想到老师拿做范例在全系进行讲解。

2。MIS课程,我设计了一套足球俱乐部的管理系统,包括球员状态的纪录以及出场阵容的预测。老师对我的设计打分很一般,第一次上机我去找老师。因为家里有电脑,而且也喜欢编程序,因此我所有的课程都是第一次交作业,让老师验收。这个做法遭到了不少老师的抵抗,一直到我在国外读研究生的时候,才有老师欣赏。去找老师验收作业,他以为我是要讨论给我设计的分低的问题,就说“你设计的很好,可是不可能实现,因此我没有给你高分”。我说“我已经做完了,是来找你验收的”。他的脸色很不好,说“我一会儿去”。我走了,可是左等也不来,右等也不来,又看到别的同学叫他,他都出来,我也急了,硬把他给揪过来了。他看完之后没有说什么,问我是一个人做的,两个人做的。因为,我要帮一个哥们,就说是我们两个一起做的。他就说,“如果是你自己做的,我给你10分都嫌少(10分满分),如果是你们两个做的,我顶多给你8分”。后来的分数是7分。最后这门课程是我大学以来分数最低的(除了编译),不到70分。后来我读研的那个老师,也是说我的设计实现起来很困难,让我修改,我后来让他surprised的了,他却给了我全班最高分,唯一的90分。不过大学这个老师,后来对我还是很好的,在我困难的时候,帮了我很多,后来也含蓄的跟我表示过,不喜欢别人什么都没做,我帮他们。

3。编译原理的课程,因为不喜欢老师的教学风格,因此上课从来不带课本,都是自己拿本C++的书座在最后一排,自学。被老师看到过一次,问我为什么不看课本,我说没带,他再问我,我就一话不说了。应该是记住我了,而且很生气。这门课程上课没有听,下课也没有学。最后到了期末的上机实习,作业是Basic的解释程序。听说上一届的没有一个人能做出来的,我就回家了3天,一边自学编译原理,一边设计,编程。3天之后回到学校,大家都用怀疑的口气问我怎么样,没想到我的回答是“做出来了”。我们这届最后还有另外一个人做出来了,因为我是用C++,他是用PASCAL,而要求也是PASCAL,因此其他的人基本都是抄的他的了。第一次上机,我跟那个老师说,可不可以用C++,他说可以。我就说我做完了,你验收吧。他看了之后说了一句“挺好”。后来这门课,我不及格,上机10分给了我5分,理由是我上机时间太少。这事情在我们学校闹得很大,因为我平时的成绩基本都是80多分,90多分的也不少。后来进行试卷的review,其他学校的老师都不敢过来,只能自己学校review,最后的评语是“***同学不是无稽之谈,老师也有犯错误的时候”。可是,为了系里在学校的面子,还是牺牲了我。他们保证我补考肯定没有问题,可是我已经不敢相信他们了,因为补考不过,我毕业都有问题了。因此就在大四的时候多选了两门课来代替这门课的学分,也造成了自己断绝考验的准备,毕业就工作的现实。

学校是挺打压人才的,当然自己那个时候也很不懂事,在注重技术的时候,忽略了做人的一些要素。这里我想说的是,编程序一定要靠实践的积累,只有多动手,才能更深的理解问题,另外技术牛了之后也要更注意做人的方式,在中国很多时候会做人比会工作可能更重要。两个方面一起抓,以后才更容易有更好的发展。希望我的经历能给大家一点借鉴作用。