快乐小胖兔的一窟

用自己的方式过一生 --- zyq654321

VC知识库BLOG 首页 新随笔 联系 聚合 登录
  23 Posts :: 0 Stories :: 95 Comments :: 2 Trackbacks

公告

Welcome you

留言簿(4)

随笔分类

随笔档案

文章档案

相册

网站联接

BLOG友情链接

搜索

最新评论

阅读排行榜

评论排行榜

2006年5月16日 #

       很多程序员在用了VC2005后都发现,微软对字符串的安全操作做了不少努力,比如使用了新的安全的字符串操作函数,对字符串的超界问题做出检测。在这里并不对这种机制做出评价(这要广大程序员自己体会),也不是详细得说如何使用安全函数的问题。在这里,以实际使用中碰到的一个小BUG为例,让大家养成一个好的习惯---看文档。

       VC2005中的一段程序

       TCHAR strFrom[MAX_PATH];

    ZeryMemory(strFrom, sizeof(strFrom));

_tcscpy_s(strFrom,MAX_PATH, _T("D:\\TEST\\"));

    SHFILEOPSTRUCT fileStruct;

fileStruct.wFunc = FO_DELETE;

    fileStruct.pFrom = strFrom;

   

    SHFileOperation(&fileStruct);

    这是一个调用SHELL的文件操作对TEST目录进行删除的操作,看上去应该是工作正常的。实际上,如果是release版本,也不会有什么问题。然而,一般程序员写完后,先编译的都是Debug版,这样问题就来了,这段程序怎么运行都不正常,如果是初学者或是不爱看文档的人估计就要花时间去查了。

    开始我也觉得奇怪,写法应该是没错了。于是去看SHFILEOPSTRUCT结构的说明,有一点引起我的注意:An additional NULL character must be appended to the end of the final name to indicate the end of pFrom。意思是说缓冲区中应该有附加的NULL,但上面的程序应该不会是这个问题啊,因为ZeryMemory已经为缓冲区全部置0了。实际上,这是_tcscpy_s的问题。(又是文档)_tcscpy_s会在Debug版本中在缓冲区中填上0xFD。呵呵,这样就查出为什么会出问题的了。

      题外话,即然说到0xFD被填进了缓冲区,就多说一些。大家都知道编译选项/GZ(非优化编译版本才可使用)被使用后,变量和内存区在初始化时会被填入一些值,比如用0xCC初始化自动变量,用0xFD初始化被保护的内存,其作用中便于进行错误的查找。

注:在VC2005中,/GZ选项已经不再使用(确切说是不建议使用),取而代之的是/RTC(运行期错误检查)。
发表于 2006-05-16 18:06 快乐小胖兔的一窟 阅读(2063) | 评论 (1)编辑 收藏

2006年5月12日 #

        昨天晚上被夫人命令到一个朋友家去给看电脑,说是上网非常慢,还乱跳网页。
        想想自己很少给人干这种IT的活,想推掉,不过这个念头是不能有的。结了婚的男人要以夫人为大啊,哈哈!吃完饭就出门了。
        到了朋友家,开机,上网,咚,自动跳出些网页,奇虎社区什么的好几个。虽然偶不是搞安全和病毒的,但一看就知道,应该是流氓软件在做怪。
        来的匆忙,没带什么东西(哈哈,其实也真没什么东西,我用的电脑是全是公司给装的安全软件,从没被这些问题困扰)。打开进程管理器,没有发现什么名称不正常的进程。在看注册表RUN子键下,发现有个YOK_SuperSearch.dll的启动项。呵呵,看名子就不是好东西。于是Delete干掉它。然后在硬盘上查相应文件,Delete它,倒。不行!
        于是再回注册表,按下F5刷新,发现这个项又被自动加上了。不   说,有守护进程。怎么查它呢,电脑上什么都没有;就上网找个工具吧,regmonitor常用,还是不错的。下下来后,果然可以定位到explore.exe会对注册表的一些项进行查询,如果没有就会添加。
        想想,网上应该可以下到清它的软件。不过,应该不用这么麻烦。关键的是,做软件工程师不是一年两年了,一般是不会都网上下些程序来跑的(刚才的那个工具也是在自已的网上硬盘中下的)。
        最简单的方法也是最快的,在那种情况应该是。我又下了个自己写的小小debugger,把它挂上explore.exe进程,然后挂起它。这样再去delete注册表。嘿嘿,然后用力按住电脑的POWER键,至少五秒(强行关机)。机器重起后,再delete掉相应文件就行了。
        唉,你说现在的软件,真是与时俱进啊,都出流氓了!
        今天早晨,我上网看了看,发现YOK还被冠以“助手”等美名。真是有点那个了。
        这些日子,偶也在看知识产权方面的书,现在就以相关知识对它评论一下。
        先说个例子,老的程序员都应该知道江民公司的“逻辑锁”这件事。大致内容就是江民公司为了防盗版,采用了对使用了盗版江民公司杀毒软件的计算机实施“锁住硬盘”的操作。这明显是一种“防卫过当”。它符合我国《中华人民共和国计算机住处系统安全保护条例》中第23条中“有害数据”和“影响计算机系统正常运行”的行为,因此是违法的。
        而现在的YOK这些流氓软件,显然是在打法规的擦边球。虽然它们有的是不请自到,或是赖着不走,但好象与上面两条还有点“差距”,不算违法(事实上YOK已经影响了IE的运行效率,并干扰了用户!)。不过,流氓就是流氓,我想它们与社会上的那些小痞子一样,要不改邪归正,走下去必会等到法律的惩罚!
发表于 2006-05-12 16:27 快乐小胖兔的一窟 阅读(4585) | 评论 (6)编辑 收藏

2006年5月10日 #

    今天在写工作文档时,有网上好友问我关于CPUID被禁止取序列号后该怎么办?于是立即由single-core变为duo-core,呵呵,大脑不用升级就可自动完成,且无新成本发生。

网上其实也说到了禁止取序列号的代码:应该是下面这几句:

MOV ECX, 119H
RDMSR
OR EAX, 0020 0000H
WRMSR

我简单说明这几句,第二和第四分别是读写MSR寄存器的指令,第一句则表明了要访问的MSR的地址。119H对应该的MSR寄存器叫做BBL_CR_CTL MSR寄存器;第三句则是对第21位置1。在BBL_CR_CTLbit21被置以后,processor number这一功能会被禁止,直至系统reset。要说明的是此bit21write-once属性的,即仅可以在reset后被写入一次,这是在硬件上就做好的,所以一旦此位被修改,再写就不可能了。

对一些软件为什么要去置此位就不说了,大多也是安全或加密什么的。但是至少有一点,用户也许要使用processor number,这样就无法去读取了。

最简单的方法是不让修改此位的软件运行,更简单说就是检查一下当前正在运行的程序,看其执行文件中是否包含了相应代码的机器码,当然了,如果必要还要检查其加载的模块。下面是相应的机器码(注意,此代码不一定与“干这事的那个人”的代码完全相符。):

B9 19 01 00 00   mov         ecx,119h

0F 32            rdmsr           

0D 00 00 20 00   or          eax,200000h

0F 30            wrmsr  

发表于 2006-05-10 12:50 Hercules Space 阅读(1789) | 评论 (1)编辑 收藏

2006年3月20日 #

        呵呵,最近都很忙,BLOG也没空写,今天在VCKBASE转了一下,手又有点痒,就少写一点吧:
问题的链接见:http://blog.vckbase.com/bruceteen/archive/2006/03/20/18606.html
        其实解释这个问题很简单,VC中显示的没错,星星的想法看上去也没错,但是,请大家注意这个问题,继承或多重继承中常用到的虚函数问题。
        我想说的是,不少人可能都很精通虚函数,也用过不少,但是可能有人会不太注意virtual table,也就是虚表这个问题。如果精通COM的话,这应该可以比较容易理解,因为COM主要就利用这一类虚表。
        星星的代码中的类C从类B和类A中派生出来,同时在类C中重用了类B中的虚函数fb。这里有一个重要概念,此fb函数不权是类C的成员函数,它更是在类C是类B虚表的函数。
        这样就可以演示一下实际情况了,当从C类产生一个新的对象c时,系统同时为c对象生成相应的类B和类C的“实例”(这里的实例指它们的成员和虚表等)。而this的含义并不是完全指c这个对象,它是一个“相对”的意思,这里在多重继承时就让不少人感到了岐义。
        一个小技巧可以理解它,就是改代码为class C: public B, C,这样,类B的虚表就排在了第一个,如此地址就一样了。呵呵,语言表达不是太好,不过基本意思应该到了。
发表于 2006-03-20 15:04 Hercules Space 阅读(2122) | 评论 (4)编辑 收藏

2005年8月24日 #

MSBuild2.0对这两种状态是无法区别的(MSBuild都认为这个Property存在且值为空),这是偶与MSBuild的开发人员之一Dan Moseley交流后得到的。据称在后面的版本中将会对此作出改进。

但是目前MS使用的是下面的方法(Dan Moseley提出的):为每个须判断的Property各加一个新的Property作其相应的状态变量。听上去感觉不错,这确实算一种通用的方法。但是想想,如果此Property数量是几十或都上百,那DOUBLE一下可不是闹着玩的。一个Property用二个Property来控制,代码的复杂度不得不增加不少,而这些并不是必须的。

我的解决方法是,仍用一个变量,但如何区别呢?只须加一个初始文件,为每个Property先设上UNDEINFED或类似的值,这样当后面用时即可简单区分开来。例子:

Init.Targets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">      

       <PropertyGroup>         

              <MY1>UNDEFINE</ MY1>

<MY2>UNDEFINE</ MY2>

<MY3>UNDEFINE</ MY3>

<MY4>UNDEFINE</ MY4>

       </PropertyGroup>

</Project>

 

在所须的项目文件一开始加入<Import Project="Init.Targets"/>即可。

这样Condtion=”$(MY1)==’UNDEFINED’”Condtion=”$(MY1)==’’”就可以区别Property的两种状态了。

发表于 2005-08-24 13:17 快乐小胖兔 阅读(2145) | 评论 (6)编辑 收藏

MSBuild提供了很方便的方法来读取环境变量。它的策略是当执行MSBuild任务时,将当前的环境变量直接转化MSBuild中的属性(Property),这样在MSBuild文件脚本中使用$()来对环境变量直接引用。但这无法解决这个问题:自定义的Target中的一些程序要读取环境变量。下面用两种方法两解决这个问题:

解决方法一般是,写一个新的Task用来设定环境变量。程序用C#来实现,代码异常简单。如下:

using System;
using
System.Collections.Generic;
using System.Text;

using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;

namespace MYTools
{
   
public class
SetEnvVar : Task
    {
       
private string
_strEnVarName;
       
private string _strEnVarValue;

        public string EnvVarName
        {
            get {
return
_strEnVarName; }
            set { _strEnVarName = value; }
        }

        public string EnvVarValue
        {
            get {
return
_strEnVarValue; }
            set { _strEnVarValue = value; }
        }
 
       
public override bool
Execute()
        {
            Environment.SetEnvironmentVariable(_strEnVarName, _strEnVarValue);
           
return true
;
        }
    }
}
在开始加上:

<UsingTask TaskName="SetEnvVar"  AssemblyFile="XXX\MyTools.dll"/>
Target里就可以这样用了:
< SetEnvVar EnvVarName="PATH" EnvVarValue="$(PATH)"/>


    方法二来的更为简单一些,它不须写个
Task。但是它的范围限于控制台程序,它的出发点是,先设好环境变量,再调用相应的Target,脚本如下:

    <PropertyGroup>   

       <EnvSet>$(EnvSet)PATH=$(PATH);C:\Windows\System32\;&amp;&amp;</EnvSet>

       <EnvSet>$(EnvSet)SET VAR1=C:&amp;&amp;</EnvSet>

       <EnvSet>$(EnvSet)SET VAR2=D:&amp;&amp;</EnvSet>     

    </PropertyGroup>

    <Target Name="PreSetEnvVar">

       <Exec Command="$(EnvSet)&amp;&amp;MSBuild MyProject.Proj /t:TargetNeedEnvVar" />          

</Target>


    比较而言,方法
2简单,但应该场合受限制;方法1则是通用方法,但要加一个新的Task

 

发表于 2005-08-24 12:53 HERCULES BLOG 阅读(1800) | 评论 (1)编辑 收藏

2005年8月8日 #

        又过生日了!鬼混至今,什么也没做成,汗!偶的生日好记,大家想着当偶过生日时,天气就会转凉了,就记住了!今天下大雨,天气果然很凉。不过声明一下,台风可不是偶带来滴!
        上午7:00就起床了,一直看书到11:00点,感觉挺爽!啊,你问看的什么书?NO,NO,当然不是小说了。《####》,酷吧!我也觉得是,也许我当老师就挺好的,偶感觉偶知道的真多!哦,你说小心腰!还好,偶经常活动,说大话不怕闪了腰^_^。而且偶较爱看拳击,特别是现在看的韩巧声解说的,才不怕闪了腰呢。
        一整天都在下雨天,下午陪LP逛了逛超市,提着30多斤的东西回家了.LP奖励偶,在肯得基买了偶最爱吃的蛋塔...立马回家放进冰箱冻着吃。前几天就向LP声明,我绝对不过生日,于是这几个蛋塔就权当生日蛋糕吧!
        17:00了,双休日是要锻炼的。来几组俯卧撑,再举几组哑铃,然后掰几组二头肌...,偶只有一个哑铃,但是挺重,足足有52磅。用它偶最爱的动作是单手侧举,今天也来了它6组。心头一个感觉,太轻,不爽!下周去举杠铃去。
        练完擦干汗,与LP合影数张,张张显出肥肉。于是对LP说,“如果有人说偶这是‘五花肉’,偶就请它吃饭!”。
        当晚上网,遇一LP同事,于是发了张请他评价,回日:“下网后一定去擦哑铃!”,接着又回“肩上的肌肉叫什么”。偶答:“三角肌”,后又答:“下次有空吃饭!”。然后满意下网。
         看了看时间,才8:00,又看了一会书。然后陪LP看电视,不知不觉就调到了偶最爱看的类型---史泰龙的《特警判官》,下决心,看完它!于是看完它,时间已是11:30了,LP已经进入梦乡,不过偶还是看会书再睡吧...
发表于 2005-08-08 10:51 HERCULES BLOG 阅读(3049) | 评论 (11)编辑 收藏

2005年7月2日 #

写个自己的调试器

       对于写了一段时间的程序员来说,了解一些debugger的实质无疑对于技术的提高是有很大帮助的。而debugging自身也是一门非常细节化,比较复杂的技术。好的Debug工具如SoftICE,也是技术稍深一些的程序员必备的技术之一。这篇随笔并不会去讨论Debug技术的实质,而只是利用Platform SDK和最新的DbgHelp.dll提供的API作为引擎写一个自己的debugger,也即是写一个实用的debuggerHost端。呵呵,本人不擅于写文章,所以下面就指一个例子来说,即如何给一个进程设下断点并捕获。其实看过IA32的人都知道,CPUDebug提供了强力的支持,比如设单步跟踪的标志;再比如BIOS代码的调试,对于ROMBIOS代码是无法进行插CCint 3)指令进行断点设置的,这时CPU提供了地址中断,执行至指定地址时,CPU自行中断。在这里并不会提及这些内容,当然也许有时间,或大家感兴趣,我可以写点或讨论一二。下面单就如何给进行设断点来说说,即然是随笔,所以可能会有错别字,欢迎大家来找别字 ^_^

       调试目标程序时,你得有相应的权限。用下面这个函数得到它:

//--------------------------------------------------------------------------

// EnableDebugAccessCtl

// Description: Enable or disable debug access control

// Return value: 0 means succeed

DWORD EnableDebugAccessCtl(BOOL bEnable)

{

    HANDLE hTokenHandle = NULL;

    if( !OpenProcessToken(GetCurrentProcess(),

                            TOKEN_ADJUST_PRIVILEGES,

                            &hTokenHandle ) )

    {

        return GetLastError();

    }

    // Lookup the privilege value

    TOKEN_PRIVILEGES tp;

    tp.PrivilegeCount = 1;

    if( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid ) )

    {

        CloseHandle(hTokenHandle);

        return -1;

    }

    // Enable/disable the privilege

    tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;

    if( !AdjustTokenPrivileges( hTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL ) )

    {

        CloseHandle(hTokenHandle);

        return -1;

    }

 

    CloseHandle(hTokenHandle);

    return 0;

}

       我们可以attach一个正在运行的进行或重新启动一个进程进行调试。DebugActiveProcess可以实现第一种情况;第二种情况可以用CreateProcess,下面是代码,注意创建标志。

// Create Process

    STARTUPINFO si;

    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    ZeroMemory( &pi, sizeof(pi) );

    if( !CreateProcess(NULL, csCmdParms.GetBuffer(),

                        NULL, NULL, FALSE,

                           CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS,

                        NULL, NULL,

                        &si, &pi ) )

    {

        return GetLastError();     

    }

 

这下初调试的程序就跑起来了,接下来一般来用一个循环来获取目标进行的Debug事件。主循环看起来应该是这样的,嘿,MSDN给了你一个模板,那就用吧:

DEBUG_EVENT DebugEv;                   // debugging event information 
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation 
for(;;) 
{  
// Wait for a debugging event to occur. The second parameter indicates 
// that the function does not return until a debugging event occurs.  
    WaitForDebugEvent(&DebugEv, INFINITE);  
// Process the debugging event code.  
    switch (DebugEv.dwDebugEventCode) 
{ 
        case EXCEPTION_DEBUG_EVENT: 
        // Process the exception code. When handling 
        // exceptions, remember to set the continuation 
        // status parameter (dwContinueStatus). This value 
        // is used by the ContinueDebugEvent function.  
            switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) 
            { 
                case EXCEPTION_ACCESS_VIOLATION: 
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                    break; 
                case EXCEPTION_BREAKPOINT: 
                // First chance: Display the current 
                // instruction and register values. 
                    break; 
                case EXCEPTION_DATATYPE_MISALIGNMENT: 
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                    break; 
                case EXCEPTION_SINGLE_STEP: 
                // First chance: Update the display of the 
                // current instruction and register values. 
                    break; 
                case DBG_CONTROL_C: 
                // First chance: Pass this on to the system. 
                // Last chance: Display an appropriate error. 
                    break; 
                default;
                // Handle other exceptions. 
                    break;
            }  
        case CREATE_THREAD_DEBUG_EVENT: 
        // As needed, examine or change the thread's registers 
        // with the GetThreadContext and SetThreadContext functions; 
        // and suspend and resume thread execution with the 
        // SuspendThread and ResumeThread functions. 
            break;
        case CREATE_PROCESS_DEBUG_EVENT: 
        // As needed, examine or change the registers of the 
        // process's initial thread with the GetThreadContext and 
        // SetThreadContext functions; read from and write to the 
        // process's virtual memory with the ReadProcessMemory and 
        // WriteProcessMemory functions; and suspend and resume 
        // thread execution with the SuspendThread and ResumeThread 
        // functions. Be sure to close the handle to the process image 
        // file with CloseHandle.
            break; 
        case EXIT_THREAD_DEBUG_EVENT: 
        // Display the thread's exit code. 
            break; 
        case EXIT_PROCESS_DEBUG_EVENT: 
        // Display the process's exit code. 
            break; 
        case LOAD_DLL_DEBUG_EVENT: 
        // Read the debugging information included in the newly 
        // loaded DLL. Be sure to close the handle to the loaded DLL 
        // with CloseHandle.
            break; 
        case UNLOAD_DLL_DEBUG_EVENT: 
        // Display a message that the DLL has been unloaded. 
            break; 
        case OUTPUT_DEBUG_STRING_EVENT: 
        // Display the output debugging string. 
            break; 
    } 
 
// Resume executing the thread that reported the debugging event.  
ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus); 
 }

可以从CASE语句中看到可以获取什么样的事件。注意CREATE_PROCESS_DEBUG_EVENT事件,我们设置断点是在这里,也就是进行被创建的时候。

说到这里就要