初期防御设施在5000分后可能被轻易地摧毁。 积分破5000后,有能力出动大量舰队的玩家比比皆是,初期的防御设施将不足以提供保护。 另外防御设施也可能被星际导弹摧毁,抵制星际导弹的方法为使用拦截导弹。详细情形可以用战斗仿真器计算,记得考虑科技。

    基本的防御可以参考如下:

 

每 4 座轻型雷射炮建造 1 座大型雷射炮

每 12 座轻型雷射炮建造 1 座中子炮

每 30 座轻型雷射炮建造 1 座高斯炮

每 100 座轻型雷射炮建造 1 座等离子武器

超过 20 座轻型雷射炮建造小型防御圆顶

超过 100 座轻型雷射炮建造大型防御圆顶

    但是防御只能防轻微的攻击,如果本身只是资源星,而且又经常的运输资源回母星,这种星球不需要太多的防守,但是仍然需要些微的防守防小狼,大概看你一天的生产量,不要让对方觉得摧毁你的防御是划的来的就好了,防御太多也只是大狼无聊发泄的对象,顺便回收战场废墟;究级的防守方式是舰队保护 (FleetSave, FS)。

    一般来说,防御工事在系统状况接近崩溃时会自动关闭,以便有修复的机会。在战斗结束后,被摧毁的防御设施有70%的机会被修好。

 

防御设施的优缺点:

 

       同等资源防御可以摧毁同等资源舰队

       舰队造价一般是同杀伤力防御的两倍

       防御有 70% 修复

       缺乏机动性

       当防御被催毁时,不会产生宇宙废墟,好处是攻击者无法回收,坏处是防御者无法回收

 

飞弹发射器(Missile Launcher)

    飞弹发射器是一种造价低廉,构造简单的防御系统。源自简单的弹道武器,它不需要特别的研究就可以建造。低廉的造价让它适于对抗小型舰队,但是随着时间的消逝它也慢慢失去了作用。在后期它只能扮演为更强大的防御系统拦截火力的角色。

 

巡洋舰 对这种船舰的快速射击: 10

导弹舰 对这种船舰的快速射击: 20

死星 对这种船舰的快速射击: 200

 

    初期量产型炮灰I

    最弱的防御。只有当你有很多金属或是要造炮灰的时候才盖它们。对抗轻型战斗机的表现普普。

 

轻型雷射炮(Small Laser)

    为了对抗在造船技术领域的巨大进步,科学家必须发展出一种新的防御装置,以便对抗装备完善的船舰和舰队。

    很快的,轻型雷射炮诞生了,通过光子集束射击能造成比弹道武器更大的伤害。另一方面,它的防护盾能力也被提升以对抗现代船舰的火力。

    为了不让生产成本大幅上升,它的基本结构和飞弹发射器相比没什么太大的不同。

    因为它的性能价格比极高,从刚起步的帝国到强大的星际帝国,到处都能见到它的身影。

 

导弹舰 对这种船舰的快速射击: 20

毁灭者 对这种船舰的快速射击: 10

死星 对这种船舰的快速射击: 200

    初期量产型炮灰II 不过C/P值挺好的 相当适合用来当高斯炮及离子炮的肉盾炮灰。当你没有足够金属或是你的敌人有太多巡洋舰的时候才建造他们。

 

重型雷射炮(Heavy Laser)

    重型雷射炮是轻型雷射炮必然的改进,它的结构被加强了,并且加入了新的材料。它的外壳被改造得有了真正的抵抗力。同时它的能量系统和射控计算机也被改进,能在一个目标上释放更多的能量,造成更大的伤害

    导弹舰 对这种船舰的快速射击: 10

    死星 对这种船舰的快速射击: 100 

    中期无用炮灰I ,笔者我没盖过,也不打算盖他。

    对抗轻型战斗机很有用。对付其它船舰可以说几乎无用。

 

高斯炮(Gauss Cannon)

    和先进的核融合技术,新型能量来源,超空间技术和进步的合金治炼技术相比,投射武器简直就是快报废的老古董。 但也因为能量技术的进步,让它找回自己在新世纪中的定位: 它的原理早在20和21世纪就被熟知:粒子加速。

    高斯炮是一座大型的粒子加速器,成吨重的炮弹被巨大的电磁场加速,这些炮弹的出膛速度是如此之高,它燃尽了周遭空气中的尘埃,后座力撼动大地。

    它的破坏力足以击穿任何现代的装甲,防护盾被击穿也不是少数例子。有时甚至能把目标直接打个对穿。

    死星 对这种船舰的快速射击: 50

    听说很适合跟轻雷海搭档,详细比率跟实战情况再观察。

    你可以造的第一管大炮。对抗巡洋舰和重型战斗机很有效,如果要对付战列和毁灭者你应该使用离子炮。 对付炮灰部队几乎是无用的。当你有炮灰支持的时候才使用高斯炮。

 

中子炮(Ion Cannon)

    这种技术在21世纪时就已存在,当时称为EMP,也就是电磁脉冲。它有一个特性,脉冲能量会造成瞬间的突波,对电子设备造成严重的干扰,并把那些灵敏的电子设备摧毁。往日这类武器被搭载在炸弹或是火箭飞弹上,但随着EMP领域研究发展的结果,现在这类武器可以被很简单地装设成火炮。中子炮是这类武器中的佼佼者。密集的中子束可以破坏目标上任何未加防护的电子系统,并使得船舰上的防护盾产生器不稳定。并不会造成实质上的伤害。

    各式舰艇中只有巡洋舰搭载了中子炮,因为中子炮的能量消耗非常的巨大,在战斗中通常都是直接毁灭目标船舰,而不是瘫痪它的行动能力。

 

    导弹舰 对这种船舰的快速射击: 10

    死星 对这种船舰的快速射击: 100

    好像对防护盾高的船有奇效,其实用性有待确认。

    通常大部分的玩家都会忽略这项设施,因为它的火力不够强。但是它的护盾是它的最大优势。对于轻型战斗机、重型战斗机的攻击几乎是免疫,可以吸收许多伤害,让你的其它更大管的炮有更多时间对付敌人。

 

等离子武器(Plasma Cannon)

    激光技术目前已经趋向完美,离子技术也将到达它的终点站,这些现存的武器系统似乎?A也没有改进的余地。但是人们想到了一个主意,也就是把两种系统合而为一。雷射被用来把重氢加热到百万度的高温,再利用研发离子技术所获得的电磁场知识,来包裹这团可怕的等离子团。

    这青蓝色的等离子球在飞往目标的途中看起来是如此的赏心悦目,但是从宇宙飞船上的船员的观点来看,这看来温和的等离子团代表的是毁灭与死亡。等离子武器被看作是最可怕的武器,当然这项技术的代价也是高昂的。

    相对于死星的终极防御兵器,不过需要大量的轻雷掩护。

    是的,这就是天杀的大肥枪。对付所有大船都很有用。巡洋舰?一发就可以炸成碎片。战列舰?一发让它就蒸发成怡人的气体。毁灭者?好吧,它可以活过一发因为毁灭者看起来令人愉快。当你要对抗大船的时候就使用离子炮,对付小船可以说是没用的。如果离子炮没有躲在一些导弹或是轻雷后面,那只要几台轻型战斗机就可以搞定他。

 

小型防护圆顶(Small Shield Dome)

    在防护盾发生器足够小到能装备到飞船上以前,早就存在一种在星球表面装备的大型的防护盾发生器。它把整个星球都罩了起来,并产生出一种力场,能在能量爆发以前把它们大量的吸收。小型的舰队常常对这种小型防护圆顶束手无策。随着技术上的不断发展,防护盾也被不断的强化。在后期人们还能将造大型的防护园顶,它更加的强大。

    每种防护圆顶,在星球上只能各设置一个。

 

大型防护圆顶(Large Shield Dome)

    小型防护圆顶的改进。它们使用相同的技术,但大型防护圆顶却可以抵挡多得多的进攻能量。大型防护圆顶的力场产生器在运转时也比较安静。

    星球级防护罩,初期对小型运输机、大型运输机和轻型战斗机有一定的防卫能力,但是之后就没什么用处了。

 

拦截导弹(Anti-Ballistic Missile)

    拦截导弹能拦截摧毁星际导弹。每一枚拦截导弹可以摧毁一枚星际导弹。

    用来拦截敌人的星际导弹,自动会发射,它会是唯一防御星际导弹的东西,以1:1击落星际导弹。

 

星际导弹(Interplanetary Missile)

    星际导弹摧毁敌方的防御设施,无关舰队。被星际导弹摧毁的防御设施不会被重建。

    射程:(脉冲引擎等级)*2-1(每太阳系)

    攻击力:12000+(武器技术等级*1200)

    时间:对同一太阳系发射需要30秒,之后每跨一个太阳系,飞行时间增加60秒

    不能发射飞弹到射程之外。星际导弹可以催毁目标星球的炮台,而不会有70%的修复。

 游戏中左方选单的「银河系」可用来观看银河系概况,看一页要花费十重氢。

 

    首先说明星球编号的意义:

 

    每个人的星球都有一组编号[A:BBB:CC], A代表银河系编号,从19,BBB代表太阳系编号,从1499,CC代表星球编号,从1~15。

 

    实力强的玩家(s)、实力弱的玩家(n): 相信很多人都认为这代表上线率,但其实并不是。实力强的玩家(s)、实力弱的玩家(n)都代表基于新手保护无法进攻或间谍的对象。如果你在新手保护(积分5000)内,积分超过你分数五倍的玩家,显示(s) ,是实力强的玩家。积分低于分数1/5的玩家,显示(n),是实力弱的玩家。 闲置过久的账号(iI)不受新手保护限制。

 

    假期模式(u): 开一次至少两天,这段期间可以登入,但是不能做任何事,别人也不能攻击或探查你。两天过后可以终止假期模式,若置之不理则继续维持假期模式。 假期模式中资源产量只有基本值金属20晶体10。假期模式至少48小时,也就是48小时后你才能取消,而假期内你也不能动作,没有产量。请注意,虽然你开了假期后,对方不能打你,但是已经在路上的舰队还是打的到的,而且你还会因为假期而不能FS。

    就算有舰队在打你的路上,你也可以开假期。 假期的玩家会显示为(u)。

    星球编号的旁边的字母:

    T:战场废墟,舰队的残骸,光标移到上面几秒可以看到资源数量,要用回收船才能取得。

    M:月球,战斗产生的大量残骸有机率行成月球,10万资源有1%机率,最高到20%。鼠标移到月球上,可以看到 S 代表大小,T 代表温度。

    A:被摧毁的星球,刚被放弃的殖民星,两天内会消失。

    R:空间跳跃门,两个有空间跳跃门的月球可让舰队瞬间移动,请参考月球相关建筑说明。

    *:代表有舰队动作,被间谍或是被攻击都算。

    在玩家名字旁的字母:

 

    i 或 i I:闲置星球,i 代表此人一个礼拜没动作了(21次更新分数没动作),i I代表此人四个礼拜没动作了。

    g:被处罚的星球

    s:实力强的玩家

    n:实力弱的玩家

    u:假期模式

    被摧毁的星球:代表这个殖民星球的主人把这个星球放弃了,两天左右将变成无人的空白位置。

    行动:探测器发射:发射间谍卫星,预设的数量可以在「设置」调整。

    书写新闻:传送短讯,事实上一次只能书写250中文字(500字符),多余会被截断。

    询问好友:可以请对方加你为好友,互设好友的人可以看到对方的在线状况,名单上限20人。

    30min? 有时候星球的后面会显示时间,那代表该星球的玩家上次上线距离现在的时间。如果有非该玩家派遣的舰队到该星球活动,也会显示时间。除了好友或同盟,你是无法知道对方是否在线的。

 

    搜集情报:对想了解的星球发射间谍卫星可以获得情报。能够搜集到的情报多寡取决于双方的间谍技术差距与间谍数量。防御间谍探测的机率意味着间谍因为被发现而被迫战斗的机率。当对方的星球有舰队停留时,间谍被发现的机率将随之提高。请勿任意间谍别人的星球,除非你要攻击他。间谍会被大多数陌生的玩家视为有敌意的行为。

 

    间谍探测公式:

K = 间谍数量 + 双方间谍技术等级差 ^ 2 (等级差可能为负数)

K >= 1 可见资源

K >= 2 可见舰队

K >= 3 可见防御

K >= 5 可见建筑

K >= 7 可见研究

 &>7599;&>0010;&>2826;&&8451;&&1995;&&7117;&>6377;15&&9063;&&4892;&>6143;&O5292;&>6080;&>0154;&&0340;&>6143;&>9699;&>4847;&>1619;&&0528;&>1487;&>0316;&>0026;&>7542;&>7665;&&0340;&>3545;&&5937;&<2290;&>7542;&>7665;&&0340;&>4517;&&5201;&>6465;&>0214;&>6159;&&5201;&>5317;&>6377;&>7542;&>7665;&&9134;&&3337;&O5292;&>3558;&>7542;&>7665;&&9134;&&3337;&>7966;&>4448;&&6873;&>3450;&&0340;&>2352;&>6631;&>5191;&&4892;&<2300;&>7542;&>7665;&<2301;&>0219;&>1153;&>1363;&>1487;&<2290;&>0999;&&5760;&>7542;&>7665;&&9134;&&3337;&<9978;&<9981;&&5201;&>5658;&>4102;&>0219;&>0309;&&6164;&>8304;&O5292;&>2240;&>0026;&>7542;&>7665;&>5104;&>1151;&>1518;&>7542;&>7665;&&9134;&&3337;&&6830;&>1516;&>5658;&>4102;&&0340;&&6164;&>8304;&&7117;&>0250;&>8040;&>2833;&<2290;&<9968;&>0010;&>9609;&>3478;&>6368;&>2823;&>1487;&>0197;&>5317;&>6377;&>0061;&&9063;&>6143;&O5292;&>1253;&>1547;&<9968;&&9063;&>7597;&>6143;&>1644;&>0843;&&9063;&>7542;&>7665;&>6143;&<2290;

    &>7542;&>7665;&>6143;&>5918;&>4323;/&>6356;&>1517;&>2312;&&5813;&>6143;&>9699;&&0340;&>7010;&>0917;&&9029;&>8857;&>0987;&>6143;&>9699;&>1517;&&1216;&>1487;&&6827;&&4892;&>6143;&>9699;&>5918;&>4323;&>5110;&>5913;&>1517;&<2290;&>5918;&>4323;&>6143;&>9699;&>6102;&O5292;&&5813;&>6143;&>9699;&<9981;&&3021;&>6377;&&3328;&&8431;&>7963;&>1160;&O5292;&>0063;&<9981;&&3021;&>7491;&>2312;&&0740;&&1350;&>5110;&>4314;&&1569;&<2290;&>6143;&>9699;&>5918;&>4323;&>1518;&>0250;&>0197;&&4987;&>5703;&>7585;&&0340;&>6143;&>9699;&>4418;&>4335;&>3384;&>2312;&>0004;&>2825;&O5292;&>1487;&>3545;&>0854;&&8388;&&5853;&>5110;&&6827;&>5915;&<2290;&&6825;&>7573;&>6399;&&8388;&&8450;&>4481;&&5774;&>6045;&>0381;&>8982;&>6377;&>0316;&>9992;&O5292;&&4987;&>5703;&>7585;&&0340;&>6143;&>9699;&&6164;&>8304;&<9981;&>0250;&&2487;&&2493;&>2686;&>1152;&<2290;&>7542;&>7665;&>6143;&>5918;&>4323;&>1518;&>1487;&&1435;&>1051;&>0877;&&6827;&&4892;&>7542;&>7665;&O5292;&>1516;&<9968;&>0301;&&2622;&&0340;&>7542;&>7665;&&9035;&&1561;&>1040;&&4987;&>5703;&>7585;&&0340;&>6143;&>9699;&>8040;&>2833;&<2290;

    &>6143;&>9699;&>3646;&>4615;&>0998;&>4067;&>7597;&>6143;&&0340;&>2823;&>3567;&<9968;&>9575;&>6159;163&>6041;&>2278;&O5292;&>7542;&>7665;&>6143;&>1017;&>1487;&&3021;&>6377;&<9981;&>1516;&&0340;&>1464;&>1270;&<2290; &&6890;&>4120;&&2534;&>1495;4~9&&0340;&>6143;&>9699;&>0026;&&6739;&>2823;&>6143;&>9699;&&0340;&>6426;&>9575;&&6739;&&9640;&O5292;&>1516;&>6102;&&2534;&>1495;&>4840;&>2823;&&0340;&>6143;&>9699;&>8201;&>4230;&>4840;&>0302;&<2290;&&9640;&>8201;&>0351;&>2826;&&8451;&&3021;&>1355;&>6143;&>0135;&>9983;&&0340;&&3021;&&7327;&>2686;&>1152;&<2289;&&7325;&>7682;&>0135;&&7327;&>1464;&>0302;&<2289;&>6680;&&4701;&>1512;&>1457;&&0005;&>1378;&>8040;&&2791;&&7325;&>7682;&&7327;&&8477;&>0302;&<2290;

 

    &>6143;&>9699;&>2823;&>3567;&>1015;&&4920;(&>7880;&>4847;&>1015;&>0986;&&0340;&>2823;&>3567;&>0998;&>4067;&>6159;60%&>6426;&>9575;)&O5306;

 

&>0301;&&2622; 1: &>4179;&>2343;&>6041;&>2278;:  64, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  48 &>1040;  80 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 2: &>4179;&>2343;&>6041;&>2278;:  68, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  53 &>1040;  83 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 3: &>4179;&>2343;&>6041;&>2278;:  73, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  54 &>1040;  82 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 4: &>4179;&>2343;&>6041;&>2278;: 173, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110; 108 &>1040; 238 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 5: &>4179;&>2343;&>6041;&>2278;: 167, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  95 &>1040; 239 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 6: &>4179;&>2343;&>6041;&>2278;: 155, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  82 &>1040; 228 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 7: &>4179;&>2343;&>6041;&>2278;: 144, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110; 116 &>1040; 173 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 8: &>4179;&>2343;&>6041;&>2278;: 150, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110; 123 &>1040; 177 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622; 9: &>4179;&>2343;&>6041;&>2278;: 159, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110; 129 &>1040; 188 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;10: &>4179;&>2343;&>6041;&>2278;: 101, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  79 &>1040; 122 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;11: &>4179;&>2343;&>6041;&>2278;:  98, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  81 &>1040; 116 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;12: &>4179;&>2343;&>6041;&>2278;: 105, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  85 &>1040; 129 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;13: &>4179;&>2343;&>6041;&>2278;: 110, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  60 &>1040; 160 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;14: &>4179;&>2343;&>6041;&>2278;:  84, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  42 &>1040; 126 &>6041;&>2278;&>0043;&&8388;

&>0301;&&2622;15: &>4179;&>2343;&>6041;&>2278;: 101, &>6377; 60% &&0340;&>6426;&>9575;&>0171;&>0110;  54 &>1040; 149 &>6041;&>2278;&>0043;&&8388;

 

    &>7542;&>7665;&>6143;&>2823;&>3567;&&5201;&>2810;&>3569;&>5165;&&6275;&>2815;&>1602;&O5311; &&6825;&&5201;&&0475;&>0320;&&0446;&>1069;&&0340;&&0446;&>6631;&<2290;

    &>2914;&>6524;&>0320;&>6159;&>1018;&>4320;&>2987;&>1457;&>3637;&O5292;&&7027;&<9968;&>1488;&>7542;&>7665;&&9134;&&3337;&>6159;&>4456;&>9645;&&6149;&&0340;&O5292;&>1487;&>0197;&>4930;&>4930;&>4314;&&5774;&>1040;&>0061;&&9063;&>6143;&>9699;&&7117;&>7542;&>8385;&>0102;&O5292;&>4182;&<9988;&&6164;&>8304;&>0063;&&6186;&>2238;&>6469;&>0102;&O5292;&>0877;&>0915;&>3450;&&5201;&<9981;&&5201;&>5703;&>7585;&>6143;&>9699;&<2290;

    &>0013;&>6399;&>0026;&>1457;&>3637;&>6399;&O5292;&>2823;&>2810;&>0250;&>6681;&>5454;&&6164;&>8304;&&8656;&>7714;&>6469;&&6873;&>5321;&>6143;&>9699;&>2823;&>3567;&O5292;&>0197;&>0840;&>6041;&>0301;&>1457;&>3637;&&0340;&>6143;&>9699;&>0250;&&6873;&>5321; 170 &>6041;&>2278;&>0197;&<9978;&&0340;&>0250;&>7604;&&6739;&&6275;&>2815;&O5292;&>0294;&>6159;&>2914;&>6524;&>6159;&>1482;&>1457;&>3637;&>1333;&<9968;&&9033;&&6164;&>8304;&O5292;&>0687;&>6159;&&7325;&>7682;&>2330;&O5292;&>1017;&<9981;&&8656;&&5201;&&7027;&>0040;&>2823;&<2290;&>2914;&>6524;&>6159;&&6317;&&1163;&&6828;&&0340;&>9420;&&1435;&>6143;&>9699;&O5292;&>0063;&>4314;&&5758;&>2312; 170 &>6041;&>2278;&>0197;&<9978;&>0250;&>7604;&&6739;&>2909;&<2290;

 

    &>1518;&>6399;&>1017;&>6159;&>0250;&>6681;&>5454;&>5112;&&0053;&&8656;&>7714;&&6873;&>5321;&>7542;&>7665;&>6143;&>9699;&>2823;&>3567;&O5292;&&0475;&&3258;&>4049;&&5268;&>1010;&&5201;&>4314;&&6896;&>2810;&>3569;&&5774;&>6045;&>6469;&&6873;&>5321;&>2823;&>3567;&<2290;

 

    &>7542;&>7665;&>6143;&&0340;&>1151;&&3021;&>6377;&>0154;&>4120;&&8382;&O5292;&>5105;&>7542;&>7665;&>6143;&>5918;&>1738;&>2909;&O5311;&&6825;&>0010;&&8382;&&9064;&>7809;&>6377;&<9968;&>3450;&&1572;&>6696;&O5292;&>7599;&>0010;&>0154;&&7117;&>1508;&>6377;&>1916;&>2909;&O5292;&>0063;&>1508;&>6377;&>0248;&&2570;&>8857;&<2290;&>6377;&>0154;&>1916;&>7426;&>9420;&>1344;&<9968;&>0010;&>2826;&&8451;&&1995;&O5292;&&6825;&>6679;&&6164;&>8304;&&6319;&&3328;&&8431;&&5843;&>1160;&>3481;&>6131;&O5292;&>1487;&>0197;&>0320;&>7542;&>7665;&>6143;&>3436;&>0840;&>6292;&&8706;&&2473;&>3545;&>5163;&&0693;&&6947;&O5307;&>0063;&>6377;&>0154;&>1916;&>7426;&>5955;&>3621;&>1508;&>2320;&O5292;&>0248;&&2570;&>8857;&>1018;&>2909;&>0309;&>1478;&<9968;&>0010;&&0456;&>1453;&O5307;&>5110;&>6159;&>0063;&>1487;&>0197;&>0004;&&2773;&&0340;&>5240;&&4935;&O5292;&>0063;&>6159;&<9981;&&8169;&<2290;

 

    &>3545;&>0405;&&0053;&>2411;&>9609;&>3478;&>6469;&&5828;&O5292;&&6824;&>6377;&>1478;&<9968;&>0010;&>0540;&>4471;&>7880;&>4847;&&0340;&>6041;&>7861;&O5292;&>3601;&>6159;&>7704;&&6828;&&0041;&<9968;&&9063;&>6143;&&0340;&&1354;&>0301;&O5292;&>9992;&>6469;&>0570;&>5915;&>0987;&&6339;&>6495;&O5292;&>5214;&>1040;&&9044;&>3450;&>0250;&&8271;&>6102;&&8388;&>5915;&>0987;&&0340;&&0446;&>6631;&O5292;&>2312;&&0446;&>6631;&&8468;&&6817;&>7542;&>7665;&O5292;&>8982;&>1518;&>5226;&&7027;&&9063;&>6143;&>4403;&>1069;&&2447;&>2522;&>2320;&O5292;&>5171;&>1040;&<9968;&>3450;&&1243;&>4230;&>3601;&&6305;&<2290;

 

    &&6164;&>8304;&>6143;&O5306;&&6164;&>8304;&>6143;&>1487;&>0197;&&6873;&>5321;&>2312;&>7597;&>6143;&&8468;&&6817;&<2290;

    &>2823;&>1518;&>6041;&O5306;&>2823;&>1518;&>6041;&>1017;&&6873;&>5321;&>2312;&>1518;&>6041;&&8134;&>7827;&&1995;&O5292;&>2823;&>2810;&&7117;&>6159;&&6328;&&8134;&>7827;&<2290;&>0294;&>6159;&>0063;&>6377;&>0154;&<9987;&>6432;&>2823;&>1518;&>6041;&<2290;

    &>5915;&>0987;&&6339;&>6495;&O5306;&&3328;&&8431;&>5915;&>0987;&&6339;&>6495;&>1017;&&6873;&>5321;&&0446;&>6631;&>6143;&>9699;&&8468;&&6817;&O5292;&>4314;&&1569;&>0027;&&5201;&>6159;&&7325;&>7682;&>0026;&>0027;&O5292;&>0063;&>1487;&&3021;&>3601;&>2320;&>4314;&&6896;&&3328;&&8431;&<2290;

    &&1532;&>0108;&>7597;&>6143;&O5306;&>7597;&>6143;&&0340;&>0301;&&2622;&<9981;&>2909;&O5292;&>5442;&>0010;&>2320;&>6041;&<2290;

 一般说的 RF,就是 RapidFire的缩写。叫做快速攻击。 譬如巡洋舰对轻型战斗机有 3的快速攻击,代表他可以一次攻击内攻击三台轻型战斗机。 而其它如战列没有快速攻击的舰队,一次只能攻击一台。

 

    攻击是每一艘打对方随机一艘,然后根据快速火力的机率来判断是不是再对随机一艘船开火一次.,攻击伤害则是要先能打穿盾(1%+),对盾造成伤害,然后才能打到装甲(结构分的 1/10),装甲低于70%时会有(全满装甲-剩余装甲/全满装甲)的机率爆炸,未爆炸的话则盾在该回合结束时自动补满。

    OG中在舰队的说明页有列出关于那个舰队RF的数据,那个数字(下面简称RF)就是平均某舰队能够对某舰队射击的次数(期望值)。

    而计算的方式是这样的:

    每次攻击方舰队发动一次成功的攻击时,会有一定的机率对防守方舰队再次发动攻击,那个RF机率下面就简写为RF%

    RF和RF%互推的公式:

    RF%=1-1/RF

    RF=1/(1-RF%)

    例如轻型战斗机(简称轻战)对间谍卫星的RF为5,则 RF% = 1-1/5 = 0.8 = 80%,所以每次轻型战斗机成功的攻击间谍卫星后就会有80%的机率再度发动一次攻击。目标爆了则攻击下一台。也就是说攻击1次、攻击5次、甚至攻击10次…都是有可能发生的。

    上面的例子轻型战斗机攻击间谍卫星,如果间谍卫星没爆(我是说假如XD),就有80%的机率可连续攻击直到间谍卫星爆掉。

    间谍卫星爆掉之后"则攻击下一台"不是再攻击下一台间谍卫星,假如原本对方舰队组成是1轻型战斗机2间谍卫星,间谍卫星被我方打爆一台之后,攻击下一台就是有1/2会攻击到间谍卫星、1/2会攻击到轻型战斗机。

 所有结果取’整数’

    飞行时间

    a) 前往战场废墟:

    (10 + (35.000 / Prozent * 开根(5000 / 速度))) / (24 * 60 * 60)

    b) 在小星系内:

    (10 + (35.000 / 负载百分数* 开根((1.000.000 + 星球距离 * 5000) / 速度))) / (24 * 60 * 60)

    c) 在银河系中:

    (10 + (35.000 / 负载百分数* 开根((27.000.000 + 小星系之间距离* 95.000) / 速度))) / (24 * 60 * 60)

    d) 银河外:

    (10 + (35.000 / 负载百分数* 开根(银河之间距离 * 20.000.000 / 速度))) / (24 * 60 * 60)

 

    单位:天

    重氢消耗

    a)小星系内:

    1 + 取整[飞船数 * 单位消耗 * ((1.000.000 + 5.000 * 行星之间距离) / 35.000.000) * (负载百分数+ 1)^2]

 

    举例:

    1 艘大运输舰从 3:22:1 满载开往 3:22:2

    - 飞船数= 1

    - 单位消耗 = 50

    - 距离 –> |1-2| =1

    - 负载百分数 = 100% = 1

    1 + 取整[ 1 * 50 * ((1.000.000 + 5.000 * 1) / 35.000.000) * (1 + 1) ^ 2;0]

    = 1 + 取整[ 50 * 0,0287142 * 4]

    = 1 + 取整[5,74284]

    = 1 + 6

    = 7

    b) 银河系内:

    1+ 取整[飞船数* 单位消耗* ((2.700.000 + 95.000 * 距离) / 35.000.000) * (负载百分数+ 1) ^ 2]

 

    c) 银河间:

    1+ 取整[飞船数 * 单位消耗 * ((20.000.000 * 距离) / 35.000.000) * (负载百分数 + 1) ^ 2]

首先说明,这个办法要离线升级包。把这个解压到一个目录,比如:d:\nod32


首先:在IIS里新建站点或虚拟目录;比如要通过 http://192.168.1.1 更新,那就新建站点,主机头名就设为 192.168.1.1 ,主目录指向“d:\nod32”(此处的目录为病毒库目录),然后在目录安全性那里设置匿名用户,不然就不是免ID的了。IIS的设置高手们都会,这里就不多说了。

第二,在HTTP头中找到MIME映射,单击“文件类型”,点击新类型,添加两组类型:


关联扩展名:.nup


内容类型(MIME):application



关联扩展名:.ver


内容类型(MIME):application


然后就像加NOD32其它网上服务器的办法一样,把192.168.1.1加入你的ESS里。

如果升级不成功你在D:NOD32目录里建一个index.htm的文件里写上几个字如:“NOD32升级”

然后在IE里打开http://192.168.1.1    如果能打开,并显示      NOD32升级   字样,这样才说明你的IIS建的正确,否则请到网上查IIS建立的相关问题。

C盘根目录下的boot.ini(隐藏文件)可以使用ANSI控制码来控制启动菜单显示出彩色。您可以根据以下短短的代码来为自己设计一个彩色的Windows启动菜单。这也有助于让您理解ANSI控制码及其转义,当然彩色的菜单也非常具有个性。

C盘根目录下的boot.ini(隐藏文件)可以使用ansi控制码来控制启动菜单显示出彩色,例如下面的boot.ini:

CODE:  ——————————————————————————–

       timeout=30

default=multi(0)disk(0)rdisk(0)partition(3)\WINDOWS

[operating systems]

multi(0)disk(0)rdisk(0)partition(2)\WINDOWS=” [1;32mMicrosoft [1;34mWindows [1;33mXP

Professional” /NOEXECUTE=OPTIN /FASTDETECT

c:\Avldrxp.bin=” [1;36mAvlgo - PELDR” /noguiboot

multi(0)disk(0)rdisk(0)partition(3)\WINDOWS=” [1;31mMicrosoft [1;33mWindmows [1;35mSermver 2003” /noexecute=optout /fastdetect

c:\1.bin=” [1;37mMS-DOS”

——————————————————————————–


关于ansi控制码:

箭头+“ [”声明了转义序列的开始

后面的 “1 ”定义了默认的字体宽度。对于文本属性来说,这些值是有意义的:0、1、22、4、24、5、25、7、27, 分别表示:默认值、粗体、非粗体、下划线、非下划线、闪烁、非闪烁、 反显、非反显。

30m —— 37m 设置前景色

40m —— 47m 设置背景色

可选颜色:红色、绿色、黄色、蓝色、洋红、青色和白色。

他们对应的颜色代码是:

30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋红)、36(青色)、37(白色)。

用同样色方法设置背景色,不过要把第一个数字“3”替换成“4”,

40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋红)、46(青色)、47(白色)。


比如可以 [1;32m;43m 粗体 绿色前景 黄色背景

注意:boot.ini 是系统重要文件,谨慎修改,只修改 如 “Microsoft Windows XP Professional” 双引号里面的内容,外面的不要修改! 

黑洞是密度超大的星球,吸纳一切,光也逃不了.(现在有科学家分析,宇宙中不存在黑洞,这需要进一步的证明,但是我们在学术上可以存在不同的意见)

补注:在空间体积为无限小(可认为是0)而注入质量接近无限大的状况下,磁场无限强化的情况下黑洞真的还有实体存在吗?

或物质的最终结局不是化为能量而是成为无限的场?

发生在黑洞周围的有趣现象

在你阅读以下关于黑洞的复杂科学知识以前,先知道两个发生在黑洞周围的两个有趣现象。根据广义相对论,引力越强,时间越慢。引力越小,时间越快。我们的地球因为质量较小,从一个地方到另一个地方,引力变化不大,所以时间差距也不大。比如说,喜马拉雅山的顶部和山底只差几千亿之一秒。黑洞因为质量巨大,从一个地方到另一个地方,引力变化非常巨大,所以时间差距也巨大。如果喜马拉亚山处在黑洞周围,当一群登山运动员从山底出发,比如说他们所处的时间是2005年。当他们登顶后,他们发现山顶的时间是2000年。

另外一个有趣的现象是根据广义相对论,引力越强,时间越慢,物体的长度也缩小。假如银河系被一个黑洞所吸引,在被吸收的过程中,银河系会变成一个米粒大小的东西。银河系里的一切东西包括地球都按相同比例缩小。所以在地球上的人看来,银河系依旧是浩瀚无边。地球上的人依旧照常上班学习,跟他们在正常情况下一样。因为在他们看来,周围的人和物体和他们的大小比例关系不变。他们浑然不知这一切都发生一个米粒大的世界里。

旦因为黑洞周围引力巨大,任何物体都不能长时间待留。假如银河系被一个黑洞所吸引,地球上的人只有几秒的时间去体验第一个现象。

首先,对黑洞进行一下形象的说明:

      

黑洞有巨大的引力,连光都被它吸引.黑洞中隐匿着巨大的引力场,这种引力大到任何东西,甚至连光,都难逃黑洞的手掌心。黑洞不让任何其边界以内的任何事物被外界看见,这就是这种物体被称为“黑洞”的缘故。我们无法通过光的反射来观察它,只能通过受其影响的周围物体来间接了解黑洞。据猜测,黑洞是死亡恒星或爆炸气团的剩余物,是在特殊的大质量超巨星坍塌收缩时产生的。另外,黑洞必须是一颗质量大于钱德拉塞卡极限的恒星演化而成的,质量小于钱德拉塞卡极限的恒星是无法形成黑洞的.(参考:《宇宙简史》——霍金·著)

再从物理学观点来解释一下:

黑洞其实也是个星球(类似星球),只不过它的密度非常非常大, 靠近它的物体都被它的引力所约束(就好像人在地球上没有飞走一样),不管用多大的速度都无法脱离。对于地球来说,以第二宇宙速度(11.2km/s)来飞行就可以逃离地球,但是对于黑洞来说,它的第二宇宙速度之大,竟然超越了光速,所以连光都跑不出来,于是射进去的光没有反射回来,我们的眼睛就看不到任何东西,只是黑色一片。

      1783年剑桥的学监约翰*米歇尔在《伦敦皇家学会哲学学报》上发表一篇文章。他指出,一个质量足够大并足够紧致的恒星会有如此强大的引力墙,以至于连光线都不能逃逸——任何丛恒星表面发出的光,还没到达远处即被恒星的吸引力吸引回来。米歇尔暗示,可能存在这样大量的恒星,虽然由于它们发出的光不会到达我们这里。所以,我们不能看到它们,但我们仍然可以感到它们引力的吸引作用。这正是我们现在称为黑洞的物体。因为黑洞是不可见的,所以有人一直置疑,黑洞是否真的存在。如果真的存在,它们到底在哪里?

黑洞的产生过程类似于中子星的产生过程;恒星的核心在自身重量的作用下迅速地收缩,发生强力爆炸。当核心中所有的物质都变成中子时收缩过程立即停止,被压缩成一个密实的星球。但在黑洞情况下,由于恒星核心的质量大到使收缩过程无休止地进行下去,中子本身在挤压引力自身的吸引下被碾为粉末,剩下来的是一个密度高到难以想象的物质。任何靠近它的物体都会被它吸进去,黑洞就变得像真空吸尘器一样

为了理解黑洞的动力学和理解它们是怎样使内部的所有事物逃不出边界,我们需要讨论广义相对论。广义相对论是爱因斯坦创建的引力学说,适用于行星、恒星,也适用于黑洞。爱因斯坦在1916年提出来的这一学说,说明空间和时间是怎样因大质量物体的存在而发生畸变。简言之,广义相对论说物质弯曲了空间,而空间的弯曲又反过来影响穿越空间的物体的运动。

让我们看一看爱因斯坦的模型是怎样工作的。首先,考虑时间(空间的三维是长、宽、高)是现实世界中的第四维(虽然难于在平常的三个方向之外再画出一个方向,但我们可以尽力去想象)。其次,考虑时空是一张巨大的绷紧了的体操表演用的弹簧床的床面。

爱因斯坦的学说认为质量使时空弯曲。我们不妨在弹簧床的床面上放一块大石头来说明这一情景:石头的重量使得绷紧了的床面稍微下沉了一些,虽然弹簧床面基本上仍旧是平整的,但其中央仍稍有下凹。如果在弹簧床中央放置更多的石块,则将产生更大的效果,使床面下沉得更多。事实上,石头越多,弹簧床面弯曲得越厉害。

同样的道理,宇宙中的大质量物体会使宇宙结构发生畸变。正如10块石头比1块石头使弹簧床面弯曲得更厉害一样,质量比太阳大得多的天体比等于或小于一个太阳质量的天体使空间弯曲得厉害地多。

如果一个网球在一张绷紧了的平坦的弹簧床上滚动,它将沿直线前进。反之,如果它经过一个下凹的地方 ,则它的路径呈弧形。同理,天体穿行时空的平坦区域时继续沿直线前进,而那些穿越弯曲区域的天体将沿弯曲的轨迹前进。

现在再来看看黑洞对于其周围的时空区域的影响。设想在弹簧床面上放置一块质量非常大的石头代表密度极大的黑洞。自然,石头将大大地影响床面,不仅会使其表面弯曲下陷,还可能使床面发生断裂。类似的情形同样可以宇宙出现,若宇宙中存在黑洞,则该处的宇宙结构将被撕裂。这种时空结构的破裂叫做时空的奇异性或奇点。

现在我们来看看为什么任何东西都不能从黑洞逃逸出去。正如一个滚过弹簧床面的网球,会掉进大石头形成的深洞一样,一个经过黑洞的物体也会被其引力陷阱所捕获。而且,若要挽救运气不佳的物体需要无穷大的能量。

我们已经说过,没有任何能进入黑洞而再逃离它的东西。但科学家认为黑洞会缓慢地释放其能量。著名的英国物理学家霍金在1974年证明黑洞有一个不为零的温度,有一个比其周围环境要高一些的温度。依照物理学原理,一切比其周围温度高的物体都要释放出热量,同样黑洞也不例外。一个黑洞会持续几百万万亿年散发能量,黑洞释放能量称为:霍金辐射。黑洞散尽所有能量就会消失。

处于时间与空间之间的黑洞,使时间放慢脚步,使空间变得有弹性,同时吞进所有经过它的一切。1969年,美国物理学家约翰 阿提 惠勒将这种贪得无厌的空间命名为“黑洞”。

我们都知道因为黑洞不能反射光,所以看不见。在我们的脑海中黑洞可能是遥远而又漆黑的。但英国著名物理学家霍金认为黑洞并不如大多数人想象中那样黑。通过科学家的观测,黑洞周围存在辐射,而且很可能来自于黑洞,也就是说,黑洞可能并没有想象中那样黑。霍金指出黑洞的放射性物质来源是一种实粒子,这些粒子在太空中成对产生,不遵从通常的物理定律。而且这些粒子发生碰撞后,有的就会消失在茫茫太空中。一般说来,可能直到这些粒子消失时,我们都未曾有机会看到它们。

霍金还指出,黑洞产生的同时,实粒子就会相应成对出现。其中一个实粒子会被吸进黑洞中,另一个则会逃逸,一束逃逸的实粒子看起来就像光子一样。对观察者而言,看到逃逸的实粒子就感觉是看到来自黑洞中的射线一样。

所以,引用霍金的话就是“黑洞并没有想象中的那样黑”,它实际上还发散出大量的光子。

根据爱因斯坦的能量与质量守恒定律。当物体失去能量时,同时也会失去质量。黑洞同样遵从能量与质量守恒定律,当黑洞失去能量时,黑洞也就不存在了。霍金预言,黑洞消失的一瞬间会产生剧烈的爆炸,释放出的能量相当于数百万颗氢弹的能量。

但你不要满怀期望地抬起头,以为会看到一场烟花表演。事实上,黑洞爆炸后,释放的能量非常大,很有可能对身体是有害的。而且,能量释放的时间也非常长,有的会超过100亿至200亿年,比我们宇宙的历史还长,而彻底散尽能量则需要数万亿年的时间


“黑洞”很容易让人望文生义地想象成一个“大黑窟窿”,其实不然。所谓“黑洞”,就是这样一种天体:它的引力场是如此之强,就连光也不能逃脱出来。

根据广义相对论,引力场将使时空弯曲。当恒星的体积很大时,它的引力场对时空几乎没什么影响,从恒星表面上某一点发的光可以朝任何方向沿直线射出。而恒星的半径越小,它对周围的时空弯曲作用就越大,朝某些角度发出的光就将沿弯曲空间返回恒星表面。

等恒星的半径小于一特定值(天文学上叫“施瓦西半径”)时,就连垂直表面发射的光都被捕获了。到这时,恒星就变成了黑洞。说它“黑”,是指任何物质一旦掉进去,就再不能逃出,包括光。实际上黑洞真正是“隐形”的,等一会儿我们会讲到。

黑洞的形成

跟白矮星和中子星一样,黑洞很可能也是由恒星演化而来的。

当一颗恒星衰老时,它的热核反应已经耗尽了中心的燃料(氢),由中心产生的能量已经不多了。这样,它再也没有足够的力量来承担起外壳巨大的重量。所以在外壳的重压之下,核心开始坍缩,直到最后形成体积小、密度大的星体,重新有能力与压力平衡。

质量小一些的恒星主要演化成白矮星,质量比较大的恒星则有可能形成中子星。而根据科学家的计算,中子星的总质量不能大于三倍太阳的质量。如果超过了这个值,那么将再没有什么力能与自身重力相抗衡了,从而引发另一次大坍缩。

这次,根据科学家的猜想,物质将不可阻挡地向着中心点进军,直至成为一个体积很小、密度趋向很大。而当它的半径一旦收缩到一定程度(一定小于史瓦西半径),正象我们上面介绍的那样,巨大的引力就使得即使光也无法向外射出,从而切断了恒星与外界的一切联系——“黑洞”诞生了。

除星体的终结可能产生黑洞外,还有一种特殊的黑洞——量子黑洞。这种黑洞很特殊,其史瓦西半径很小很小,能达到十的负二十几次方米,比一个原子还要小。与平常的黑洞不同,它并不是由很大质量的星体塌缩而形成的,而是原子塌缩而成的,因此只有一种条件下才会创造量子黑洞——大爆炸。在宇宙创生初期,巨大的温度和压力将单个原子或原子团压缩成为许多量子黑洞。而这种黑洞几乎是不可能观测到或找到的,它目前只存在于理论中。


特殊的黑洞

与别的天体相比,黑洞是显得太特殊了。例如,黑洞有“隐身术”,人们无法直接观察到它,连科学家都只能对它内部结构提出各种猜想。那么,黑洞是怎么把自己隐藏起来的呢?答案就是——弯曲的空间。我们都知道,光是沿直线传播的。这是一个最基本的常识。可是根据广义相对论,空间会在引力场作用下弯曲。这时候,光虽然仍然沿任意两点间的最短距离传播,但走的已经不是直线,而是曲线。形象地讲,好像光本来是要走直线的,只不过强大的引力把它拉得偏离了原来的方向。

在地球上,由于引力场作用很小,这种弯曲是微乎其微的。而在黑洞周围,空间的这种变形非常大。这样,即使是被黑洞挡着的恒星发出的光,虽然有一部分会落入黑洞中消失,可另一部分光线会通过弯曲的空间中绕过黑洞而到达地球。所以,我们可以毫不费力地观察到黑洞背面的星空,就像黑洞不存在一样,这就是黑洞的隐身术。

更有趣的是,有些恒星不仅是朝着地球发出的光能直接到达地球,它朝其它方向发射的光也可能被附近的黑洞的强引力折射而能到达地球。这样我们不仅能看见这颗恒星的“脸”,还同时看到它的侧面、甚至后背!

“黑洞”无疑是本世纪最具有挑战性、也最让人激动的天文学说之一。许多科学家正在为揭开它的神秘面纱而辛勤工作着,新的理论也不断地提出。不过,这些当代天体物理学的最新成果不是在这里三言两语能说清楚的。有兴趣的朋友可以去参考专门的论著。

按组成来划分,黑洞可以分为两大类。一是暗能量黑洞,二是物理黑洞。暗能量黑洞主要由高速旋转的巨大的暗能量组成,它内部没有巨大的质量。巨大的暗能量以接近光速的速度旋转,其内部产生巨大的负压以吞噬物体,从而形成黑洞,详情请看宇“宙黑洞论”。暗能量黑洞是星系形成的基础,也是星团、星系团形成的基础。物理黑洞由一颗或多颗天体坍缩形成,具有巨大的质量。当一个物理黑洞的质量等于或大于一个星系的质量时,我们称之为奇点黑洞。暗能量黑洞的体积很大,可以有太阳系那般大。但物理黑洞的体积却非常小,它可以缩小到一个奇点。

黑洞吸积

黑洞通常是因为它们聚拢周围的气体产生辐射而被发现的,这一过程被称为吸积。高温气体辐射热能的效率会严重影响吸积流的几何与动力学特性。目前观测到了辐射效率较高的薄盘以及辐射效率较低的厚盘。当吸积气体接近中央黑洞时,它们产生的辐射对黑洞的自转以及视界的存在极为敏感。对吸积黑洞光度和光谱的分析为旋转黑洞和视界的存在提供了强有力的证据。数值模拟也显示吸积黑洞经常出现相对论喷流也部分是由黑洞的自转所驱动的。

天体物理学家用“吸积”这个词来描述物质向中央引力体或者是中央延展物质系统的流动。吸积是天体物理中最普遍的过程之一,而且也正是因为吸积才形成了我们周围许多常见的结构。在宇宙早期,当气体朝由暗物质造成的引力势阱中心流动时形成了星系。即使到了今天,恒星依然是由气体云在其自身引力作用下坍缩碎裂,进而通过吸积周围气体而形成的。行星——包括地球——也是在新形成的恒星周围通过气体和岩石的聚集而形成的。但是当中央天体是一个黑洞时,吸积就会展现出它最为壮观的一面。 

然而黑洞并不是什么都吸收的,它也往外边散发质子.

爆炸的黑洞

黑洞会发出耀眼的光芒,体积会缩小,甚至会爆炸。当英国物理学家史迪芬·霍金于1974年做此语言时,整个科学界为之震动。黑洞曾被认为是宇宙最终的沉淀所:没有什么可以逃出黑洞,它们吞噬了气体和星体,质量增大,因而洞的体积只会增大,霍金的理论是受灵感支配的思维的飞跃,他结合了广义相对论和量子理论。他发现黑洞周围的引力场释放出能量,同时消耗黑洞的能量和质量,这种“霍金辐射”对大多数黑洞来说可以忽略不计,而小黑洞则以极高的速度辐射能量,直到黑洞的爆炸。

奇妙的萎缩的黑洞

当一个粒子从黑洞逃逸而没有偿还它借来的能量,黑洞就会从它的引力场中丧失同样数量的能量,而爱因斯坦的公式E=mc^2表明,能量的损失会导致质量的损失。因此,黑洞将变轻变小。

沸腾直至毁灭

所有的黑洞都会蒸发,只不过大的黑洞沸腾得较慢,它们的辐射非常微弱,因此另人难以觉察。但是随着黑洞逐渐变小,这个过程会加速,以至最终失控。黑洞委琐时,引力并也会变陡,产生更多的逃逸粒子,从黑洞中掠夺的能量和质量也就越多。黑洞委琐的越来越快,促使蒸发的速度变得越来越快,周围的光环变得更亮、更热,当温度达到10^15℃时,黑洞就会在爆炸中毁灭。

关于黑洞的文章:

自古以来,人类便一直梦想飞上蓝天,可没人知道在湛蓝的天幕之外还有一个硕大的黑色空间。在这个空间有光,有水,有生命。我们美丽的地球也是其中的一员。虽然宇宙是如此绚烂多彩,但在这里也同样是危机四伏的。小行星,红巨星,超新星大爆炸,黑洞……

 

黑洞,顾名思义就是看不见的具有超强吸引力的物质。自从爱因斯坦和霍金通过猜测并进行理论推导出有这样一种物质之后,科学家们就在不断的探寻,求索,以避免我们的星球被毁灭。

 

黑洞与地球毁灭的关系

 

黑洞,实际上是一团质量很大的物质,其引力极大(仡今为止还未发现有比它引力更大的物质),形成一个深井。它是由质量和密度极大的恒星不断坍缩而形成的,当恒星内部的物质核心发生极不稳定变化之后会形成一个称为“奇点”的孤立点(有关细节请查阅爱因斯坦的广义相对论)。他会将一切进入视界的物质吸入,任何东西不能从那里逃脱出来(包括光)。他没有具体形状,也无法看见它,只能根据周围行星的走向来判断它的存在。也许你会因为它的神秘莫测而吓的大叫起来,但实际上根本用不着过分担心,虽然它有强大的吸引力但与此同时这也是判断它位置的一个重要证据,就算它对距地球极近的物质产生影响时,我们也还有足够的时间挽救,因为那时它的“正式边界”还离我们很远。况且,恒星坍缩后大部分都会成为中子星或白矮星。但这并不意味着我们就可以放松警惕了(谁知道下一刻被吸入的会不会是我们呢?),这也是人类研究它的原因之一。

恒星,白矮星,中子星,夸克星,黑洞是依次的五个密度当量星体,密度最小的当然是恒星,黑洞是物质的终极形态,黑洞之后就会发生宇宙大爆炸,能量释放出去后,又进入一个新的循环.

另外黑洞在网络中指电子邮件消息丢失或Usenet公告消失的地方。

黑洞名称的提出

黑洞这一术语是不久以前才出现的。它是1969年美国科学家约翰·惠勒为形象描述至少可回溯到200年前的这个思想时所杜撰的名字。那时候,共有两种光理论:一种是牛顿赞成的光的微粒说;另一种是光的波动说。我们现在知道,实际上这两者都是正确的。由于量子力学的波粒二象性,光既可认为是波,也可认为是粒子。在光的波动说中,不清楚光对引力如何响应。但是如果光是由粒子组成的,人们可以预料,它们正如同炮弹、火箭和行星那样受引力的影响。起先人们以为,光粒子无限快地运动,所以引力不可能使之慢下来,但是罗麦关于光速度有限的发现表明引力对之可有重要效应。

1783年,剑桥的学监约翰·米歇尔在这个假定的基础上,在《伦敦皇家学会哲学学报》上发表了一篇文章。他指出,一个质量足够大并足够紧致的恒星会有如此强大的引力场,以致于连光线都不能逃逸——任何从恒星表面发出的光,还没到达远处即会被恒星的引力吸引回来。米歇尔暗示,可能存在大量这样的恒星,虽然会由于从它们那里发出的光不会到达我们这儿而使我们不能看到它们,但我们仍然可以感到它们的引力的吸引作用。这正是我们现在称为黑洞的物体。它是名符其实的——在空间中的黑的空洞。几年之后,法国科学家拉普拉斯侯爵显然独自提出和米歇尔类似的观念。非常有趣的是,拉普拉斯只将此观点纳入他的《世界系统》一书的第一版和第二版中,而在以后的版本中将其删去,可能他认为这是一个愚蠢的观念。(此外,光的微粒说在19世纪变得不时髦了;似乎一切都可以以波动理论来解释,而按照波动理论,不清楚光究竟是否受到引力的影响。)

事实上,因为光速是固定的,所以,在牛顿引力论中将光类似炮弹那样处理实在很不协调。(从地面发射上天的炮弹由于引力而减速,最后停止上升并折回地面;然而,一个光子必须以不变的速度继续向上,那么牛顿引力对于光如何发生影响呢?)直到1915年爱因斯坦提出广义相对论之前,一直没有关于引力如何影响光的协调的理论。甚至又过了很长时间,这个理论对大质量恒星的含意才被理解。

为了理解黑洞是如何形成的,我们首先需要理解一个恒星的生命周期。起初,大量的气体(大部分为氢)受自身的引力吸引,而开始向自身坍缩而形成恒星。当它收缩时,气体原子相互越来越频繁地以越来越大的速度碰撞——气体的温度上升。最后,气体变得如此之热,以至于当氢原子碰撞时,它们不再弹开而是聚合形成氦。如同一个受控氢弹爆炸,反应中释放出来的热使得恒星发光。这增添的热又使气体的压力升高,直到它足以平衡引力的吸引,这时气体停止收缩。这有一点像气球——内部气压试图使气球膨胀,橡皮的张力试图使气球缩小,它们之间存在一个平衡。从核反应发出的热和引力吸引的平衡,使恒星在很长时间内维持这种平衡。然而,最终恒星会耗尽了它的氢和其他核燃料。貌似大谬,其实不然的是,恒星初始的燃料越多,它则燃尽得越快。这是因为恒星的质量越大,它就必须越热才足以抵抗引力。而它越热,它的燃料就被用得越快。我们的太阳大概足够再燃烧50多亿年,但是质量更大的恒星可以在1亿年这么短的时间内用尽其燃料, 这个时间尺度比宇宙的年龄短得多了。当恒星耗尽了燃料,它开始变冷并开始收缩。随后发生的情况只有等到本世纪20年代末才初次被人们理解。

1928年,一位印度研究生——萨拉玛尼安·强德拉塞卡——乘船来英国剑桥跟英国天文学家阿瑟·爱丁顿爵士(一位广义相对论家)学习。(据记载,在本世纪20年代初有一位记者告诉爱丁顿,说他听说世界上只有三个人能理解广义相对论,爱丁顿停了一下,然后回答:“我正在想这第三个人是谁”。)在他从印度来英的旅途中,强德拉塞卡算出在耗尽所有燃料之后,多大的恒星可以继续对抗自己的引力而维持自己。这个思想是说:当恒星变小时,物质粒子靠得非常近,而按照泡利不相容原理,它们必须有非常不同的速度。这使得它们互相散开并企图使恒星膨胀。一颗恒星可因引力作用和不相容原理引起的排斥力达到平衡而保持其半径不变,正如在它的生命的早期引力被热所平衡一样。

然而,强德拉塞卡意识到,不相容原理所能提供的排斥力有一个极限。恒星中的粒子的最大速度差被相对论限制为光速。这意味着,恒星变得足够紧致之时,由不相容原理引起的排斥力就会比引力的作用小。强德拉塞卡计算出;一个大约为太阳质量一倍半的冷的恒星不能支持自身以抵抗自己的引力。(这质量现在称为强德拉塞卡极限。)苏联科学家列夫·达维多维奇·兰道几乎在同时也得到了类似的发现。

这对大质量恒星的最终归宿具有重大的意义。如果一颗恒星的质量比强德拉塞卡极限小,它最后会停止收缩并终于变成一颗半径为几千英哩和密度为每立方英寸几百吨的“白矮星”。白矮星是它物质中电子之间的不相容原理排斥力所支持的。我们观察到大量这样的白矮星。第一颗被观察到的是绕着夜空中最亮的恒星——天狼星转动的那一颗。

兰道指出,对于恒星还存在另一可能的终态。其极限质量大约也为太阳质量的一倍或二倍,但是其体积甚至比白矮星还小得多。这些恒星是由中子和质子之间,而不是电子之间的不相容原理排斥力所支持。所以它们被叫做中子星。它们的半径只有10英哩左右,密度为每立方英寸几亿吨。在中子星被第一次预言时,并没有任何方法去观察它。实际上,很久以后它们才被观察到。

另一方面,质量比强德拉塞卡极限还大的恒星在耗尽其燃料时,会出现一个很大的问题:在某种情形下,它们会爆炸或抛出足够的物质,使自己的质量减少到极限之下,以避免灾难性的引力坍缩。但是很难令人相信,不管恒星有多大,这总会发生。怎么知道它必须损失重量呢?即使每个恒星都设法失去足够多的重量以避免坍缩,如果你把更多的质量加在白矮星或中子星上,使之超过极限将会发生什么?它会坍缩到无限密度吗?爱丁顿为此感到震惊,他拒绝相信强德拉塞卡的结果。爱丁顿认为,一颗恒星不可能坍缩成一点。这是大多数科学家的观点:爱因斯坦自己写了一篇论文,宣布恒星的体积不会收缩为零。其他科学家,尤其是他以前的老师、恒星结构的主要权威——爱丁顿的敌意使强德拉塞卡抛弃了这方面的工作,转去研究诸如恒星团运动等其他天文学问题。然而,他获得1983年诺贝尔奖,至少部分原因在于他早年所做的关于冷恒星的质量极限的工作。

强德拉塞卡指出,不相容原理不能够阻止质量大于强德拉塞卡极限的恒星发生坍缩。但是,根据广义相对论,这样的恒星会发生什么情况呢?这个问题被一位年轻的美国人罗伯特·奥本海默于1939年首次解决。然而,他所获得的结果表明,用当时的望远镜去观察不会再有任何结果。以后,因第二次世界大战的干扰,奥本海默本人非常密切地卷入到原子弹计划中去。战后,由于大部分科学家被吸引到原子和原子核尺度的物理中去,因而引力坍缩的问题被大部分人忘记了。

现在,我们从奥本海默的工作中得到一幅这样的图象:恒星的引力场改变了光线的路径,使之和原先没有恒星情况下的路径不一样。光锥是表示光线从其顶端发出后在空间——时间里传播的轨道。光锥在恒星表面附近稍微向内偏折,在日食时观察远处恒星发出的光线,可以看到这种偏折现象。当该恒星收缩时,其表面的引力场变得很强,光线向内偏折得更多,从而使得光线从恒星逃逸变得更为困难。对于在远处的观察者而言,光线变得更黯淡更红。最后,当这恒星收缩到某一临界半径时,表面的引力场变得如此之强,使得光锥向内偏折得这么多,以至于光线再也逃逸不出去 。根据相对论,没有东西会走得比光还快。这样,如果光都逃逸不出来,其他东西更不可能逃逸,都会被引力拉回去。也就是说,存在一个事件的集合或空间——时间区域,光或任何东西都不可能从该区域逃逸而到达远处的观察者。现在我们将这区域称作黑洞,将其边界称作事件视界,它和刚好不能从黑洞逃逸的光线的轨迹相重合。

当你观察一个恒星坍缩并形成黑洞时,为了理解你所看到的情况,切记在相对论中没有绝对时间。每个观测者都有自己的时间测量。由于恒星的引力场,在恒星上某人的时间将和在远处某人的时间不同。假定在坍缩星表面有一无畏的航天员和恒星一起向内坍缩,按照他的表,每一秒钟发一信号到一个绕着该恒星转动的空间飞船上去。在他的表的某一时刻,譬如11点钟,恒星刚好收缩到它的临界半径,此时引力场强到没有任何东西可以逃逸出去,他的信号再也不能传到空间飞船了。当11点到达时,他在空间飞船中的伙伴发现,航天员发来的一串信号的时间间隔越变越长。但是这个效应在10点59分59秒之前是非常微小的。在收到10点59分58秒和10点59分59秒发出的两个信号之间,他们只需等待比一秒钟稍长一点的时间,然而他们必须为11点发出的信号等待无限长的时间。按照航天员的手表,光波是在10点59分59秒和11点之间由恒星表面发出;从空间飞船上看,那光波被散开到无限长的时间间隔里。在空间飞船上收到这一串光波的时间间隔变得越来越长,所以恒星来的光显得越来越红、越来越淡,最后,该恒星变得如此之朦胧,以至于从空间飞船上再也看不见它,所余下的只是空间中的一个黑洞。然而,此恒星继续以同样的引力作用到空间飞船上,使飞船继续绕着所形成的黑洞旋转。

但是由于以下的问题,使得上述情景不是完全现实的。你离开恒星越远则引力越弱,所以作用在这位无畏的航天员脚上的引力总比作用到他头上的大。在恒星还未收缩到临界半径而形成事件视界之前,这力的差就已经将我们的航天员拉成意大利面条那样,甚至将他撕裂!然而,我们相信,在宇宙中存在质量大得多的天体,譬如星系的中心区域,它们遭受到引力坍缩而产生黑洞;一位在这样的物体上面的航天员在黑洞形成之前不会被撕开。事实上,当他到达临界半径时,不会有任何异样的感觉,甚至在通过永不回返的那一点时,都没注意到。但是,随着这区域继续坍缩,只要在几个钟头之内,作用到他头上和脚上的引力之差会变得如此之大,以至于再将其撕裂。

罗杰·彭罗斯和我在1965年和1970年之间的研究指出,根据广义相对论,在黑洞中必然存在无限大密度和空间——时间曲率的奇点。这和时间开端时的大爆炸相当类似,只不过它是一个坍缩物体和航天员的时间终点而已。在此奇点,科学定律和我们预言将来的能力都失效了。然而,任何留在黑洞之外的观察者,将不会受到可预见性失效的影响,因为从奇点出发的不管是光还是任何其他信号都不能到达他那儿。这令人惊奇的事实导致罗杰·彭罗斯提出了宇宙监督猜测,它可以被意译为:“上帝憎恶裸奇点。”换言之,由引力坍缩所产生的奇点只能发生在像黑洞这样的地方,在那儿它被事件视界体面地遮住而不被外界看见。严格地讲,这是所谓弱的宇宙监督猜测:它使留在黑洞外面的观察者不致受到发生在奇点处的可预见性失效的影响,但它对那位不幸落到黑洞里的可怜的航天员却是爱莫能助。

广义相对论方程存在一些解,这些解使得我们的航天员可能看到裸奇点。他也许能避免撞到奇点上去,而穿过一个“虫洞”来到宇宙的另一区域。看来这给空间——时间内的旅行提供了巨大的可能性。但是不幸的是,所有这些解似乎都是非常不稳定的;最小的干扰,譬如一个航天员的存在就会使之改变,以至于他还没能看到此奇点,就撞上去而结束了他的时间。换言之,奇点总是发生在他的将来,而从不会在过去。强的宇宙监督猜测是说,在一个现实的解里,奇点总是或者整个存在于将来(如引力坍缩的奇点),或者整个存在于过去(如大爆炸)。因为在接近裸奇点处可能旅行到过去,所以宇宙监督猜测的某种形式的成立是大有希望的。这对科学幻想作家而言是不错的,它表明没有任何一个人的生命曾经平安无事:有人可以回到过去,在你投胎之前杀死你的父亲或母亲!

事件视界,也就是空间——时间中不可逃逸区域的边界,正如同围绕着黑洞的单向膜:物体,譬如不谨慎的航天员,能通过事件视界落到黑洞里去,但是没有任何东西可以通过事件视界而逃离黑洞。(记住事件视界是企图逃离黑洞的光的空间——时问轨道,没有任何东西可以比光运动得更快。)人们可以将诗人但丁针对地狱入口所说的话恰到好处地用于事件视界:“从这儿进去的人必须抛弃一切希望。”任何东西或任何人一旦进入事件视界,就会很快地到达无限致密的区域和时间的终点。

广义相对论预言,运动的重物会导致引力波的辐射,那是以光的速度传播的空间——时间曲率的涟漪。引力波和电磁场的涟漪光波相类似,但是要探测到它则困难得多。就像光一样,它带走了发射它们的物体的能量。因为任何运动中的能量都会被引力波的辐射所带走,所以可以预料,一个大质量物体的系统最终会趋向于一种不变的状态。(这和扔一块软木到水中的情况相当类似,起先翻上翻下折腾了好一阵,但是当涟漪将其能量带走,就使它最终平静下来。)例如,绕着太阳公转的地球即产生引力波。其能量损失的效应将改变地球的轨道,使之逐渐越来越接近太阳,最后撞到太阳上,以这种方式归于最终不变的状态。在地球和太阳的情形下能量损失率非常小——大约只能点燃一个小电热器, 这意味着要用大约1干亿亿亿年地球才会和太阳相撞,没有必要立即去为之担忧!地球轨道改变的过程极其缓慢,以至于根本观测不到。但几年以前,在称为PSR1913+16(PSR表示“脉冲星”,一种特别的发射出无线电波规则脉冲的中子星)的系统中观测到这一效应。此系统包含两个互相围绕着运动的中子星,由于引力波辐射,它们的能量损失,使之相互以螺旋线轨道靠近。

在恒星引力坍缩形成黑洞时,运动会更快得多,这样能量被带走的速率就高得多。所以不用太长的时间就会达到不变的状态。这最终的状态将会是怎样的呢?人们会以为它将依赖于形成黑洞的恒星的所有的复杂特征——不仅仅它的质量和转动速度,而且恒星不同部分的不同密度以及恒星内气体的复杂运动。如果黑洞就像坍缩形成它们的原先物体那样变化多端,一般来讲,对之作任何预言都将是非常困难的。

然而,加拿大科学家外奈·伊斯雷尔(他生于柏林,在南非长大,在爱尔兰得到博士)在1967年使黑洞研究发生了彻底的改变。他指出,根据广义相对论,非旋转的黑洞必须是非常简单、完美的球形;其大小只依赖于它们的质量,并且任何两个这样的同质量的黑洞必须是等同的。事实上,它们可以用爱因斯坦的特解来描述,这个解是在广义相对论发现后不久的1917年卡尔·施瓦兹席尔德找到的。一开始,许多人(其中包括伊斯雷尔自己)认为,既然黑洞必须是完美的球形,一个黑洞只能由一个完美球形物体坍缩而形成。所以,任何实际的恒星——从来都不是完美的球形——只会坍缩形成一个裸奇点。

然而,对于伊斯雷尔的结果,一些人,特别是罗杰·彭罗斯和约翰·惠勒提倡一种不同的解释。他们论证道,牵涉恒星坍缩的快速运动表明,其释放出来的引力波使之越来越近于球形,到它终于静态时,就变成准确的球形。按照这种观点,任何非旋转恒星,不管其形状和内部结构如何复杂,在引力坍缩之后都将终结于一个完美的球形黑洞,其大小只依赖于它的质量。这种观点得到进一步的计算支持,并且很快就为大家所接受。

伊斯雷尔的结果只处理了由非旋转物体形成的黑洞。1963年,新西兰人罗伊·克尔找到了广义相对论方程的描述旋转黑洞的一族解。这些“克尔”黑洞以恒常速度旋转,其大小与形状只依赖于它们的质量和旋转的速度。如果旋转为零,黑洞就是完美的球形,这解就和施瓦兹席尔德解一样。如果有旋转,黑洞的赤道附近就鼓出去(正如地球或太阳由于旋转而鼓出去一样),而旋转得越快则鼓得越多。由此人们猜测,如将伊斯雷尔的结果推广到包括旋转体的情形,则任何旋转物体坍缩形成黑洞后,将最后终结于由克尔解描述的一个静态。

黑洞是科学史上极为罕见的情形之一,在没有任何观测到的证据证明其理论是正确的情形下,作为数学的模型被发展到非常详尽的地步。的确,这经常是反对黑洞的主要论据:你怎么能相信一个其依据只是基于令人怀疑的广义相对论的计算的对象呢?然而,1963年,加利福尼亚的帕罗玛天文台的天文学家马丁·施密特测量了在称为3C273(即是剑桥射电源编目第三类的273号)射电源方向的一个黯淡的类星体的红移。他发现引力场不可能引起这么大的红移——如果它是引力红移,这类星体必须具有如此大的质量,并离我们如此之近,以至于会干扰太阳系中的行星轨道。这暗示此红移是由宇宙的膨胀引起的,进而表明此物体离我们非常远。由于在这么远的距离还能被观察到,它必须非常亮,也就是必须辐射出大量的能量。人们会想到,产生这么大量能量的唯一机制看来不仅仅是一个恒星,而是一个星系的整个中心区域的引力坍缩。人们还发现了许多其他类星体,它们都有很大的红移。但是它们都离开我们太远了,所以对之进行观察太困难,以至于不能给黑洞提供结论性的证据。

1967年,剑桥的一位研究生约瑟琳·贝尔发现了天空发射出无线电波的规则脉冲的物体,这对黑洞的存在的预言带来了进一步的鼓舞。起初贝尔和她的导师安东尼·赫维许以为,他们可能和我们星系中的外星文明进行了接触!我的确记得在宣布他们发现的讨论会上,他们将这四个最早发现的源称为LGM1-4,LGM表示“小绿人”(“Little Green Man”)的意思。然而,最终他们和所有其他人都得到了不太浪漫的结论,这些被称为脉冲星的物体,事实上是旋转的中子星,这些中子星由于它们的磁场和周围物质复杂的相互作用,而发出无线电波的脉冲。这对于写空间探险的作者而言是个坏消息,但对于我们这些当时相信黑洞的少数人来说,是非常大的希望——这是第一个中子星存在的证据。中子星的半径大约10英哩,只是恒星变成黑洞的临界半径的几倍。如果一颗恒星能坍缩到这么小的尺度,预料其他恒星会坍缩到更小的尺度而成为黑洞,就是理所当然的了。

按照黑洞定义,它不能发出光,我们何以希望能检测到它呢?这有点像在煤库里找黑猫。庆幸的是,有一种办法。正如约翰·米歇尔在他1783年的先驱性论文中指出的,黑洞仍然将它的引力作用到它周围的物体上。天文学家观测了许多系统,在这些系统中,两颗恒星由于相互之间的引力吸引而互相围绕着运动。他们还看到了,其中只有一颗可见的恒星绕着另一颗看不见的伴星运动的系统。人们当然不能立即得出结论说,这伴星即为黑洞——它可能仅仅是一颗太暗以至于看不见的恒星而已。

还有其他不用黑洞来解释天鹅X-1的模型,但所有这些都相当牵强附会。黑洞看来是对这一观测的仅有的真正自然的解释。尽管如此,我和加州理工学院的基帕·索恩打赌说,天鹅X-1不包含一个黑洞!这对我而言是一个保险的形式。我对黑洞作了许多研究,如果发现黑洞不存在,则这一切都成为徒劳。但在这种情形下,我将得到赢得打赌的安慰,他要给我4年的杂志《私人眼睛》。如果黑洞确实存在,基帕·索思将得到1年的《阁楼》 。我们在1975年打赌时,大家80%断定,天鹅座是一黑洞。迄今,我可以讲大约95%是肯定的,但输赢最终尚未见分晓。

现在,在我们的星系中和邻近两个名叫麦哲伦星云的星系中,还有几个类似天鹅X-1的黑洞的证据。然而,几乎可以肯定,黑洞的数量比这多得太多了!在宇宙的漫长历史中,很多恒星应该已经烧尽了它们的核燃料并坍缩了。黑洞的数目甚至比可见恒星的数目要大得相当多。 单就我们的星系中,大约总共有1千亿颗可见恒星。这样巨大数量的黑洞的额外引力就能解释为何目前我们星系具有如此的转动速率,单是可见恒星的质量是不足够的。我们还有某些证据说明,在我们星系的中心有大得多的黑洞,其质量大约是太阳的10万倍。星系中的恒星若十分靠近这个黑洞时,作用在它的近端和远端上的引力之差或潮汐力会将其撕开,它们的遗骸以及其他恒星所抛出的气体将落到黑洞上去。正如同在天鹅X-1情形那样,气体将以螺旋形轨道向里运动并被加热, 虽然不如天鹅X-1那种程度会热到发出X射线,但是它可以用来说明星系中心观测到的非常紧致的射电和红外线源。

人们认为,在类星体的中心是类似的、但质量更大的黑洞,其质量大约为太阳的1亿倍。 落入此超重的黑洞的物质能提供仅有的足够强大的能源,用以解释这些物体释放出的巨大能量。当物质旋入黑洞,它将使黑洞往同一方向旋转,使黑洞产生一类似地球上的一个磁场。落入的物质会在黑洞附近产生能量非常高的粒子。该磁场是如此之强,以至于将这些粒子聚焦成沿着黑洞旋转轴,也即它的北极和南极方向往外喷射的射流。在许多星系和类星体中确实观察到这类射流。

人们还可以考虑存在质量比太阳小很多的黑洞的可能性。因为它们的质量比强德拉塞卡极限低,所以不能由引力坍缩产生:这样小质量的恒星,甚至在耗尽了自己的核燃料之后,还能支持自己对抗引力。只有当物质由非常巨大的压力压缩成极端紧密的状态时,这小质量的黑洞才得以形成。一个巨大的氢弹可提供这样的条件:物理学家约翰·惠勒曾经算过,如果将世界海洋里所有的重水制成一个氢弹,则它可以将中心的物质压缩到产生一个黑洞。(当然,那时没有一个人可能留下来去对它进行观察!)更现实的可能性是,在极早期的宇宙的高温和高压条件下会产生这样小质量的黑洞。因为一个比平均值更紧密的小区域,才能以这样的方式被压缩形成一个黑洞。所以当早期宇宙不是完全光滑的和均匀的情形,这才有可能。但是我们知道,早期宇宙必须存在一些无规性,否则现在宇宙中的物质分布仍然会是完全均匀的,而不能结块形成恒星和星系。

很清楚,导致形成恒星和星系的无规性是否导致形成相当数目的“太初”黑洞,这要依赖于早期宇宙的条件的细节。所以如果我们能够确定现在有多少太初黑洞,我们就能对宇宙的极早期阶段了解很多。质量大于10亿吨(一座大山的质量)的太初黑洞,可由它对其他可见物质或宇宙膨胀的影响被探测到。然而,正如我们需要在下一章看到的,黑洞根本不是真正黑的,它们像一个热体一样发光,它们越小则发热发光得越厉害。所以看起来荒谬,而事实上却是,小的黑洞也许可以比大的黑洞更容易地被探测到。


黑洞是一个重力场,密度极高,能将光吞没.在黑洞的地方,时空是弯曲的.在这样的地方,我们所熟知的物理定律还适用么?有些天文学家认为不能,有些人甚至提出理论,认为黑洞可能是通往别的宇宙或别的量网的门户.认为它们是一些通道,在那里我们所熟悉的时空因果全部不复相同.相信的在想:人进入黑洞,会不会活者发现自己已突然进入另一个地方另一个时间?黑洞会是穿越宇宙的捷径吗?会不会那是一条不受速率限制的通道?

黑洞”很容易让人望文生义地想象成一个“大黑窟窿”,其实不然。所谓“黑洞”,就是这样一种天体:它的引力场是如此之强,就连光也不能逃脱出来。

根据广义相对论,引力场将使时空弯曲。当恒星的体积很大时,它的引力场对时空几乎没什么影响,从恒星表面上某一点发的光可以朝任何方向沿直线射出。而恒星的半径越小,它对周围的时空弯曲作用就越大,朝某些角度发出的光就将沿弯曲空间返回恒星表面。

等恒星的半径小到一特定值(天文学上叫“史瓦西半径”)时,就连垂直表面发射的光都被捕获了。到这时,恒星就变成了黑洞。说它“黑”,是指它就像宇宙中的无底洞,任何物质一旦掉进去,“似乎”就再不能逃出。实际上黑洞真正是“隐形”的,等一会儿我们会讲到。

那么,黑洞是怎样形成的呢?其实,跟白矮星和中子星一样,黑洞很可能也是由恒星演化而来的。

我们曾经比较详细地介绍了白矮星和中子星形成的过程。当一颗恒星衰老时,它的热核反应已经耗尽了中心的燃料(氢),由中心产生的能量已经不多了。这样,它再也没有足够的力量来承担起外壳巨大的重量。所以在外壳的重压之下,核心开始坍缩,直到最后形成体积小、密度大的星体,重新有能力与压力平衡。

质量小一些的恒星主要演化成白矮星,质量比较大的恒星则有可能形成中子星。而根据科学家的计算,中子星的总质量不能大于三倍太阳的质量。如果超过了这个值,那么将再没有什么力能与自身重力相抗衡了,从而引发另一次大坍缩。

根据科学家计算,一个物体要有每秒中七点九公里的速度,就可以不被地球的引力拉回到地面,而在空中饶着地球转圈子了.这个速度,叫第一宇宙速度.如果要想完全摆脱地球引力的束缚,到别的行星上去,至少要有11.2km/s的速度,这个速度,叫第二宇宙速度.也可以叫逃脱速度.这个结果是按照地球的质量和半径的大小算出来的.就是说,一个物体要从地面上逃脱出去,起码要有这么大的速度。可是对于别的天体来说,从它们的表面上逃脱出去所需要的速度就不一定也是这么大了。一个天体的质量越是大,半径越是小,要摆脱它的引力就越困难,从它上面逃脱所需要的速度也就越大.

按照这个道理,我们就可以这样来想:可能有这么一种天体,它的质量很大,而半径又很小,使得从它上面逃脱的速度达到了光的速度那么大。也就是说,这个天体的引力强极了,连每秒钟三十万公里的光都被它的引力拉住,跑不出来了。既然这个天体的光跑不出来,我们然谈就看不见它,所以它就是黑的了。光是宇宙中跑得最快的,任何物质运动的速度都不可能超过光速.既然光不能从这种天体上跑出来,当然任何别的物质也就休想跑出来.一切东西只要被吸了进去,就不能再出来,就象掉进了无底洞,这样一种天体,人们就把它叫做黑洞.

我们知道,太阳现在的半径是七十万公里。假如它变成一个黑洞,半径就的大大缩小.缩到多少?只能有三公里.地球就更可怜了,它现在半径是六千多公里.假如变成黑洞,半径就的缩小到只有几毫米.那里会有这么大的压缩机,能把太阳 地球缩小的这么!这简直象<天方夜谭>里的神话故事,黑洞这东西实在太离奇古怪了。但是,上面说的这些可不是凭空想象出来的,而是根据严格的科学理论的出来的.原来,黑洞也是由晚年的恒星变成的,象质量比较小的恒星,到了晚年,会变成白矮星;质量比较大的会形成中子星.现在我们再加一句,质量更大的恒星,到了晚年,最后就会变成黑洞.所以,总结起来说,白矮星 中子星和黑洞,就是晚年恒星的三种变化结果.

现在,白矮星已经找到了,中子星也找到了,黑洞找到没有?也应该找到的.主要因为黑洞是黑的,要找到它们实在是很困难。特别是那些单个的黑洞,我们现在简直毫无办法。有一种情况下的黑洞比较有希望找到,那就是双星里的黑洞.

双星就是两颗互相饶着转的恒星.虽然我们看不见黑洞,但却能从那颗看的见的恒星的运动路线分析出来.这是什么道理呢?因为,双星中的每一个星都是沿着椭圆形路线运动的,而单颗的恒星不是这样运动。如果我们看到天空中有颗恒星在沿椭圆形路线运动,却看不到它的’同伴’,那就值得仔细研究了。我们可以把那颗星走的椭圆的大小,走完一圈用的时间,都测量出来.有了这些,就可以算出来那个看不见的’同伴’的质量有多大。如果算出来质量很大,超过中子星能有的质量,那就可以进一步证明它是个黑洞了。

再天鹅星座,有一对双星,名叫天鹅座X-1.这对双星中,一颗是看的见的亮星,另一颗却看不见.根据那可亮星的运动路线.可以算出来它的’同伴’的质量很大,至少有太阳质量的五倍.这么大的质量是任何中子星都不可能有的.当然,除这些以外还有别的证据。所以,基本上可以肯定,天鹅座X-1中那个看不见的天体就是一个黑洞.这是人类找到的第一个黑洞。

另外,还发现有几对双星的特征也跟天鹅座X-1很相似,它们里面也有可能有黑洞。科学家正对它们作进一步的研究. “黑洞”很容易让人望文生义地想象成一个“大黑窟窿”,其实不然。所谓“黑洞”,就是这样一种天体:它的引力场是如此之强,就连光也不能逃脱出来。

  根据广义相对论,引力场将使时空弯曲。当恒星的体积很大时,它的引力场对时空几乎没什么影响,从恒星表面上某一点发的光可以朝任何方向沿直线射出。而恒星的半径越小,它对周围的时空弯曲作用就越大,朝某些角度发出的光就将沿弯曲空间返回恒星表面。

  等恒星的半径小到一特定值(天文学上叫“史瓦西半径”)时,就连垂直表面发射的光都被捕获了。到这时,恒星就变成了黑洞。说它“黑”,是指它就像宇宙中的无底洞,任何物质一旦掉进去,“似乎”就再不能逃出。实际上黑洞真正是“隐形”的,等一会儿我们会讲到。

黑洞是怎样形成  其实,跟白矮星和中子星一样,黑洞很可能也是由恒星演化而来的。

  我们曾经比较详细地介绍了白矮星和中子星形成的过程。当一颗恒星衰老时,它的热核反应已经耗尽了中心的燃料(氢),由中心产生的能量已经不多了。这样,它再也没有足够的力量来承担起外壳巨大的重量。所以在外壳的重压之下,核心开始坍缩,直到最后形成体积小、密度大的星体,重新有能力与压力平衡。

  这次,根据科学家的猜想,物质将不可阻挡地向着中心点进军,直至成为一个体积趋于零、密度趋向无限大的“点”。而当它的半径一旦收缩到一定程度(史瓦西半径),正象我们上面介绍的那样,巨大的引力就使得即使光也无法向外射出,从而切断了恒星与外界的一切联系——“黑洞”诞生了。

  与别的天体相比,黑洞是显得太特殊了。例如,黑洞有“隐身术”,人们无法直接观察到它,连科学家都只能对它内部结构提出各种猜想。那么,黑洞是怎么把自己隐藏起来的呢?答案就是——弯曲的空间。我们都知道,光是沿直线传播的。这是一个最基本的常识。可是根据广义相对论,空间会在引力场作用下弯曲。这时候,光虽然仍然沿任意两点间的最短距离传播,但走的已经不是直线,而是曲线。形象地讲,好像光本来是要走直线的,只不过强大的引力把它拉得偏离了原来的方向。

  在地球上,由于引力场作用很小,这种弯曲是微乎其微的。而在黑洞周围,空间的这种变形非常大。这样,即使是被黑洞挡着的恒星发出的光,虽然有一部分会落入黑洞中消失,可另一部分光线会通过弯曲的空间中绕过黑洞而到达地球。所以,我们可以毫不费力地观察到黑洞背面的星空,就像黑洞不存在一样,这就是黑洞的隐身术。

  更有趣的是,有些恒星不仅是朝着地球发出的光能直接到达地球,它朝其它方向发射的光也可能被附近的黑洞的强引力折射而能到达地球。这样我们不仅能看见这颗恒星的“脸”,还同时看到它的侧面、甚至后背!

  “黑洞”无疑是本世纪最具有挑战性、也最让人激动的天文学说之一。许多科学家正在为揭开它的神秘面纱而辛勤工作着,新的理论也不断地提出。不过,这些当代天体物理学的最新成果不是在这里三言两语能说清楚的。有兴趣的朋友可以去参考专门的论著。

    黑洞是20世纪60年代最重大的科学发现

据最新的研究声称,科学家认为黑洞可能是通往其他宇宙的虫洞。如果这一理论是正确的,将会有助于解释例如黑洞信息悖论等量子难题,不过批评家指出这也会产生新的问题,例如虫洞是怎么形成的等等。

黑洞是一种拥有强大引力的物体,任何物体——即便是光——在进入其事件边界之后都不能逃逸出来。根据爱因斯坦的广义相对论,黑洞可以由任何物质形成,只要能够坍缩到足够小的空间内。

  尽管黑洞不能被直接看到,天文学家还是通过观察周围物质的环绕情况,推断出一些黑洞的位置。

  不过来自巴黎Bures-sur-Yvette地区法国高等科学研究所(Institut des Hautes Etudes Scientifiques)的物理学家Thibault Damour和来自德国Bremen国际大学的Sergey Solodukhin提出一个新的观点,即这些所谓的黑洞其实就是虫洞。

  虫洞是连接时空架构中两个不同地方的弯曲通道。如果你把宇宙想象为一个二维的纸张,虫洞就是连接连接这张纸片和另一张纸片的小通道。实际上这一理论认为,虫洞链向的是一个拥有自己星星、星系等的另一个宇宙。

引起空间扭曲的小球在我们三维世界的例子就是黑洞。黑洞事实上是存在于四维空间的一种现象,或者说,黑洞是连接三维世界与四维空间的通道(当然在下绝不是说“如果谁要去四维空间,就请往黑洞走”,那样只会“死无全尸”而已^O^)。我们有可能通过对黑洞的深入研究,找到克服四维空间的办法,那样的话,瓦普跳跃飞行就不再是梦想了。

现在科学家已经证实,黑洞的存在确实会令周围的空间极度扭曲。根据广义相对论,光线在正常的空间里以直线传播,但当空间扭曲时,光线会随着空间扭曲的方向而扭曲。如果能给一束射进黑洞的光线拍照的话,我们就会发现,光线呈螺旋形指向黑洞中心,因为黑洞的巨大质量已使周围的空间扭曲得不成形了。

编辑词条

开放分类:

宇宙、科学、天文、自然、天体物理


参考资料:

 1.http://www.qiji.cn/drupal/node/217

 2.http://www.qiji.cn/eprint/97/index.html

 3.http://www.astron.sh.cn/

 4.黑洞吸积——Ramesh Narayan、Eliot Quartaer 文 Shea 译

 5.循宇天文网:http://karajan.lamost.org/

 6.谈天天文网:http://www.2-sky.com/

 7.空间天文网:http://space.lamost.org/

 8.天文网:http://oka.16789.net/

 9.哎呦网:http://www.iyouv.com

 10.http://zhidao.baidu.com/question/19589813.html?fr=idfn

 11.http://www.girler.com/

 12.科学万花筒

 13.http://www.kepu.gov.cn/zlg/yuzhou/b19.htm


贡献者:

吞云、liopleurod、月影狂风、月风是也、o恨生剑舞o、ianwest、z5kmkoo、Blueboy001、南斗北辰、wanqqbb、udd66、胡呵、5ainiyuer、栤若·夏幻雪、玉雪风君、黛琪精灵、然境、狭盗二世、晓玲望月、mafengmafeng、瞬弟弟、 水木秋寒、张靖蕊、l19900314、1OO86、萧邦11月、liuhanshen、倒塌……、onnh、 zhangxuxin8765、木文一刀、595845492www、 陕西愣娃聪明咧、蓝雁精灵、温暖的月光、银河系1号、 iamchenzetian、zicheng794、gary_cong、981059152、yhwen8、hero5054、偶是dudu、吾知其然、rjjlisa、tianshixiadao、yangzifei、univyang、漂泊星海、sbsni、567和个发、梦呓的鱼、南方巨兽龙之帝、bj_jiajiao、龙奇星、xdschangjiu、云随风拭、TonyJiajie、AUKO_SUN、lcgxdy、绿色子龙、寂静之涯_大海、appleduck、745943、zhao_ming_zhou

本词条在以下词条中被提及:

第五维空间、弯曲时空、时间旅行、空间折叠、时空穿梭、高维空间、宇宙三禁律、维空间、银河系、时间、百慕大三角、霍金辐射、陈道明、虫洞、超弦理论、霍金的宇宙、史瓦西半径、通古斯大爆炸、相对论、恒星演化、白洞、天鹅座、郑丰、斯蒂芬·威廉·霍金、百慕大群岛、撕裂地平线、施瓦氏半径、光子带、时空隧道、星暴星系

有兴在这次x'con交流会上认识白远方兄弟,这是他很早前写的文章,提到了很多东西,保存一下。

作者:白远方 (ID: baiyuanfan, baiyuanfan@163.com, baiyuanfan@hotmail.com)
June 18, 2007
关键字:rootkit,反主动防御,网络监控,ring0,mcafee8.5i,KIS6,ZoneAlarm Pro,实用级产品测试
目录:
反主动防御rootkit的产生背景及其必要性
反网络访问主动防御
反API钩子进程行为主动防御
反系统Notify进程行为主动防御
绕过监控进入ring0安装驱动
实用级反主动防御rootkit的通用性问题
反主动防御rootkit的产生背景及其必要性
        当前随着新型木马,病毒,间谍软件对网络安全的威胁日益加重,传统的特征查杀型的安全产品和简单的封包过滤型防火墙已不能有效保护用户,因此各大安全公司纷纷推出自己的主动防御型安全产品,例如卡巴斯基kis6,mcafee8.5i,ZoneAlarm Pro等,这些产品应对未知的病毒木马都有很好的效果,若非针对性的作过设计的木马和rootkit,根本无法穿越其高级别防御。因此,反主动防御技术,作为矛和盾的另一方,自然被渗透者们提上日程;由于主动防御安全产品的迅速普及,为了不使后门木马被弹框报警,具有反主动防御能力的rootkit成为了一种必然选择。
反网络访问主动防御
        几乎现在每个防火墙都具有应用程序访问网络限制功能。一个未知的程序反弹连接到外网,或者是在本地监听端口,基本上都会引起报警。而且对系统进程的行为也有了比较严格的审查,原先的注射代码到winlogon等系统进程,在向外反弹连接的方法,很多主动防御软件都会阻止了。
        很多防火墙的应用程序访问网络限制,都可以通过摘除tcpip.sys上面的过滤驱动,并还原tcpip.sys的Dispatch Routines来绕过。据称这是因为在ndis层次取得进程id不方便而导致的。但是如果在一个实用级的rootkit里应用此方法则是不智之举,因为存在部分防火墙,如ZoneAlarm,其ndis过滤层必须和tdi过滤层协同工作,才会放行网络连接。至于ndis层次的中间层驱动的摘除,和NDIS_OPEN_BLOCK的还原,则是一项不太可能完成的任务,因为无法从原始文件中读取的方法,获得NDIS_OPEN_BLOCK的原始值;即使能够成功恢复ndis钩子,也不能保证系统可以正常运行,很可能会出现各种不明症状。
        到现在为止,绕过应用程序访问网络限制最好的选择,还是那两个:简单的一个,注射代码到一个ie进程,用它反弹连接出来,访问外网;复杂的选择则是应用内核驱动,如ndis hook/添加新的ndis protocol,来实现端口复用,或者使用tdi client driver反弹连接。已经有很多木马和rootkit使用前者,因其简单易行,在实际开发中工程量小,出现问题的可能性也少得多,产品成熟的时间代价也小。但是目前很多的主动防御已经注意到这一点,并且在程序行为监控中严密防范了其他程序对ie的感染行为。
    如图,想要使用僵尸IE访问网络的木马被拦截
反API钩子进程行为主动防御
        接下来是主动防御系统的很重要的一部分:进程行为监控。该部分主动防御软件一般通过两种解决方案来执行,一是API钩子,二是windows支持的notify routine。
        大量的主动防御安全软件,如KIS6,ZoneAlarm Pro,使用API钩子来监控进程的危险行为。如注射远程线程,启动傀儡IE,加载驱动,注册服务,修改敏感系统注册表键值等。但是作为一个rootkit,完全绕过这些操作,基本上是不可能的;于是摆放在面前的任务,就是如何击败这种主动防御。
        对于特定种类的监控,总是有特定的方法可以绕过。比如注射远程线程,如果常用的CreateRemoteThread被监控了,可以尝试采用Debug API, SetThreadContext的方法绕过,也可以尝试采用hook其ntdll!ZwYieldExecution等频繁调用的函数来装载自己的DLL模块。 注册表监控,我的朋友xyzreg曾经写过系列文章,提出了很多种方法,包括RegSaveKey, Hive编辑等方法绕过卡巴斯基的注册表监控,其Hive编辑的方法目前仍未能有任何主动防御系统拦截。
        但是从一个通用型,为实战设计的实用型rootkit来说,采用这些特定的技术并不是一个非常好的选择;因为这些技术可以保证对付一个主动防御软件,却不能保证通用,甚至通用性很差。而且针对每一个可能被主动防御拦截的行为,都采用一套特定的绕过技术,从工程代价上来讲,太过巨大,开发耗时,等其成熟更是不知道要多少时间来测试和更改。因此我们需要的一个相对涵盖范围广,能够解决绝大多数主动防御技术的解决方案。
        针对API钩子实现的进程行为监控,一个较好的通用解决方案就是卸载所有安全软件所安装的API钩子。为兼容性和稳定起见,几乎所有的安全软件在安装API钩子时都会选择hook SSDT表,例如KIS6,ZoneAlarm Pro。我们如果能够进入ring0,就可以使用一个驱动程序,读取系统文件ntoskrnl.exe/ntkrnlpa.exe/ntkrpamp.exe,从中提出我们所希望的SSDT表的原始函数地址,替换被安全软件hook的地址,用此方法可以通用性很好的解决绝大多数的API钩子实现的进程行为监控。不过此方法有一个前提,就是事先必须绕过监控进入ring0。关于如何实现此前提,请阅读第五部分,“绕过监控进入ring0安装驱动”。
    如图,ZoneAlarm Pro更改了大量的SSDT函数地址来监控程序行为。
反系统Notify进程行为主动防御
        部分主动防御安全软件不仅仅是用API钩子,同时使用了微软提供的Notify Routine,来监视进程的行为。使用该技术的安全软件不是太多,但是也不至于少到一个实用级别rootkit可以忽略的程度。
        以下几个微软DDK函数,PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine,PsSetLoadImageNotifyRoutine,被用作支持主动防御软件监控新进程的建立,新线程的建立,和一个新的模块被加载。处理该种类型的防御不能简单的清空NotifyRoutine就完事,因为系统本身,还有一些第三方正常模块和驱动,可能添加和使用该链表。
        解决方案,一是可以先将使用了该技术的主动防御系统的驱动程序模块做一个列表出来,然后遍历这三条链表,找出地址指向这些驱动模块的项,再将这些项删除脱链。但是这需要对大量主动防御系统的研究和测试,并且通用型也不好。第二种方法,由于Notify Routine的监控力度要远弱于API钩子,因此在纯ring3将程序做一些小的改动,就可以越过这种类型的监控。
        另外还有几个SDK函数,可以提供对文件和注册表的更改的notify。不能排除也有部分主动防御软件使用了它们。例如国产的超级巡警(AST.exe),使用了RegNotifyChangeKeyValue,做了对注册表敏感键值修改的事后警告提示。如果仅仅使用了API钩子清除技术,那么在此时就会被AST报警。和以上介绍的三个内核notify类似的也是,有不少正常的notify在被使用,不分青红皂白的全部卸载,会导致系统异常。
        因此可见,Notify类监控虽然使用的不多,但是其对付的难度和需要的工程量,比API监控还要大。
    如图,已经处理了API钩子监控的rootkit仍然被notify方式的AST报警。
绕过监控进入ring0安装驱动
        这部分是重中之重。由于几乎每个主动防御系统都会监控未知驱动的加载和试图进入ring0的举动, 而我们在第一,第二和第三部分绕过主动防御要做的处理,都必须需要ring0权限。因此监控进入ring0,是一个独立的话题,也是我们实现前三个部分需要的条件。
        直接添加注册表项,ZwLoadDriver安装驱动,是几乎要被任何主动防御系统报警。必须要采用一些隐蔽的或者是为人不知的方法。总结目前已经公布出来的进入ring0的办法,
有以下几种:
        感染文件,例如win32k.sys,添加自己的代码到里面,启动的时候就会被执行。这种方法的优点是简单易行,稳定度和兼容性很好。但是最大的缺点就是必须重新启动以后,才能进入ring0,这是一个产品级别的后门所不能容忍的。而且微软自己的系统文件保护容易绕过,mcafee和卡巴斯基的文件监控可就不是那么容易了。
        利用物理内存对象,来写入自己的代码到内核,并添加调用门来执行。这个是最早被人提出的不用驱动进入ring0的办法。因为出来的时间太长了,所以有以下一些问题:更新的操作系统内核不支持,如2003SP1;很多的主动防御系统会拦截,例如KIS6。所以这个办法也不理想。
        利用ZwSystemDebugControl。这个代码在国外有人放出来过,利用它写内存,挂钩NtVdmControl,进入ring0。此法缺陷在于老的windows2000不被支持,最新的windows2003sp1上也取消了这个函数的此能力。不过好处在于,这个方法用的人少,基本上没有主动防御会注意到它,并进行拦截。
        利用ZwSetSystemInformation的SystemLoadAndCallImage功能号加载一个模块进入ring0。这个方法提出来比较久了,但是因为用的人少,仍未被主动防御软件所重视。用得少的原因是,它不好用。它只能加载一个普通的模块到内核并且调用,却不是加载一个驱动,因此没有一个DriverObject。这导致了非常多的麻烦。因为要想使用这个办法,必须先用这个办法安装一个简单的内核模块,再用这个模块添加调用门等方式,执行代码清除主动防御的监视驱动安装的钩子,安装一个正常的驱动,才能最终完成任务。而且这个方法似乎对windows2003sp1以上的系统也无效。
        因此,要想有一个相对完美的进入ring0解决方案,最好是寻找别人不知道或者使用很少的方法,或者将上面的有缺陷的方法做一个综合,用多种方法通过判断情况来选择使用。我在这里有一个新的思路提供给大家,微软新公布了一部分文档,关于HotPatch的使用。HotPatch可以在执行中修改系统中存在的用户态公用dll的内容,甚至是修改内核模块的内容。具体代码和细节,在这里我不能多说。
        要想开发一个好的反主动防御rootkit,绕过监控进入ring0是必不可少的,然而这部分也是使用不成熟技术最多的,最容易出现严重问题的部分。作为一个负责任的实用级产品,一定要对这个部分作做详细的测试,来保证自己的产品不会在某些特殊的环境,比如64位CPU运行32位系统,多核处理器,HyperThread处理器上面,出现故障或者蓝屏。
实用级反主动防御rootkit的通用性问题
        前文已述,本文的宗旨在于讨论一种实用级别rootkit开发的可行性。因此,工程量的大小,需要投入的人力,时间和金钱,也是我们需要考虑的内容。必须要考虑更好的兼容性通用性,和工程上的开发代价和稳定成熟周期不能无限大。因此,对于部分新技术,例如BiosRootkit,VirtualMachine-Rootkit,本文不做讨论,因为那些都属于如果要想做稳定通用,工程代价非常大,以至于他们只拥有技术上面的讨论价值,而不具备作为一个产品开发的可选解决方案的可能性。至少是目前来看是如此。
        每个主动防御软件的原理和构造都是不相同的,因此不可能指望有某一种方法,从工程上可以解决一个主动防御系统,就可以无需测试的,保证无误的解决其他系统。因为这个原因,开发一个成熟稳定的反主动防御rootkit,必然要在兼容各种主动防御的系统的通用性上面下大功夫。按照不同的主动防御系统,在程序里switch case,应该是非常必要的,尽管绝大多数反主动防御代码原理上可以通用。基本上,在测试程序通用型的时候,常用的主动防御软件,是每种都要安装一个并且仔细测试的。
        以下举例说明,几个常用主动防御系统各自需要注意的特点,这都是笔者在实际开发中遇到的比较典型的例子。
Mcafee8.5,该主动防御软件在最大化功能时会禁止在系统目录下创建可执行文件,光这一点就会让几乎全部rootkit安装失败,若非针对它做了设计。在这个系统下面,也不可能使用感染文件的方法来进入ring0。
KIS6,该系统会自动列举运行的隐藏进程,并且弹框警告。因此在这系统下,不太可能把自己的进程隐藏。而且它列举隐藏进程的手段很底层,很难绕过。
ZoneAlarm Pro,该系统下,如果一个其它的进程启动IE并且访问网络,安全报警仍然会以该进程本身访问网络为准执行,另外还会弹框警告,除非将自己的僵尸IE进程的父进程更改,或者不用IE来反弹连接。
国产的瑞星,总体来说这个系统的主动防御弱于国外产品,但是它特殊在于,会对IE作出非常严格的限制,默认不允许IE装载任何非系统的dll。因此在这个系统下基本不可能利用IE反弹。
        其他的特殊情况还有很多。作为一个成熟产品开发者,这些都是必须要考虑的。
感谢:VXK(郭宏硕), xyzreg(张翼)。

 

 

http://www.huigezi.net/infoopen.asp?id=15

不想让ime显示默认的窗口,只想用它的转换和选字功能,看过拿铁游戏论坛上的一个兄弟的一些代码,修正了一些我认为的bug,加入了一组控制函数,使得程序中可以显示一些button,玩家可以不必用热键就能切换输入法、全角/半角,中/英文标点。
//不知道这个能不能解决缩进的问题
#pragma comment ( lib, "imm32.lib" )
#include <windows.h>
#include <imm.h>
class CIme{
    bool g_bIme;                     //ime允许标志
char g_szCompStr[ MAX_PATH ];    //存储转换后的串
char g_szCompReadStr[ MAX_PATH ];//存储输入的串
char g_szCandList[ MAX_PATH ];   //存储整理成字符串选字表
int g_nImeCursor;                //存储转换后的串中的光标位置
CANDIDATELIST *g_lpCandList;     //存储标准的选字表
char g_szImeName[ 64 ];          //存储输入法的名字
bool g_bImeSharp;                //全角标志
bool g_bImeSymbol;               //中文标点标志
void ConvertCandList( CANDIDATELIST *pCandList, char *pszCandList ); //将选字表整理成串
public:
CIme() : g_lpCandList( NULL ){ DisableIme(); } //通过DisableIme初始化一些数据
~CIme()
{
DisableIme();
if( g_lpCandList )
{
GlobalFree( (HANDLE)g_lpCandList );
g_lpCandList = NULL;
}
}
//控制函数
void DisableIme();          //关闭并禁止输入法,如ime已经打开则关闭,此后玩家不能用热键呼出ime
void EnableIme();           //允许输入法,此后玩家可以用热键呼出ime
void NextIme();             //切换到下一种输入法,必须EnableIme后才有效
void SharpIme( HWND hWnd ); //切换全角/半角
void SymbolIme( HWND hWnd );//切换中/英文标点
//状态函数
char* GetImeName();         //得到输入法名字,如果当前是英文则返回NULL
bool IfImeSharp();          //是否全角
bool IfImeSymbol();         //是否中文标点
void GetImeInput( char **pszCompStr, char **pszCompReadStr, int *pnImeCursor, char **pszCandList );
//得到输入法状态,四个指针任意可为NULL则此状态不回返回
//在pszCompStr中返回转换后的串
//在pszCompReadStr中返回键盘直接输入的串
//在pnImeCursor中返回szCompStr的光标位置
//在pszCandList中返回选字表,每项之间以\t分隔
//必须在消息中调用的函数,如果返回是true,则窗口函数应直接返回0,否则应传递给DefWindowProc
bool OnWM_INPUTLANGCHANGEREQUEST();
bool OnWM_INPUTLANGCHANGE( HWND hWnd );
bool OnWM_IME_SETCONTEXT(){ return true; }
bool OnWM_IME_STARTCOMPOSITION(){ return true; }
bool OnWM_IME_ENDCOMPOSITION(){ return true; }
bool OnWM_IME_NOTIFY( HWND hWnd, WPARAM wParam );
bool OnWM_IME_COMPOSITION( HWND hWnd, LPARAM lParam );
};
void CIme::DisableIme()
{
while( ImmIsIME( GetKeyboardLayout( 0 )))
ActivateKeyboardLayout(( HKL )HKL_NEXT, 0 );//如果ime打开通过循环切换到下一个关闭
g_bIme = false;
g_szCompStr[ 0 ] = 0;
g_szCompReadStr[ 0 ] = 0;
g_nImeCursor = 0;
g_szImeName[ 0 ] = 0;
g_szCandList[ 0 ] = 0;
}
void CIme::EnableIme()
{
g_bIme = true;
}
void CIme::NextIme()
{
if( !g_bIme )
return;
ActivateKeyboardLayout(( HKL )HKL_NEXT, 0 );
}
void CIme::SharpIme( HWND hWnd )
{
ImmSimulateHotKey( hWnd, IME_CHOTKEY_SHAPE_TOGGLE );
}
void CIme::SymbolIme( HWND hWnd )
{
ImmSimulateHotKey( hWnd, IME_CHOTKEY_SYMBOL_TOGGLE );
}
void CIme::ConvertCandList( CANDIDATELIST *pCandList, char *pszCandList ) //转换CandidateList到一个串,\t分隔每一项
{
unsigned int i;
if( pCandList->dwCount < pCandList->dwSelection )
{
pszCandList[ 0 ] = 0;
return;
}
//待选字序号超出总数,微软拼音第二次到选字表最后一页后再按PageDown会出现这种情况,并且会退出选字状态,开始一个新的输入
//但微软拼音自己的ime窗口可以解决这个问题,估计微软拼音实现了更多的接口,所以使用了这种不太标准的数据
//我现在无法解决这个问题,而且实际使用中也很少遇到这种事,而且其它标准输入法不会引起这种bug
//非标准输入法估计实现的接口比较少,所以应该也不会引起这种bug
for( i = 0; ( i < pCandList->dwCount - pCandList->dwSelection )&&( i < pCandList->dwPageSize ); i++ )
{
*pszCandList++ = ( i % 10 != 9 )? i % 10 + '1' : '0';//每项对应的数字键
*pszCandList++ = '.';//用'.'分隔
strcpy( pszCandList, (char*)pCandList + pCandList->dwOffset[ pCandList->dwSelection + i ] );//每项实际的内容
pszCandList += strlen( pszCandList );
*pszCandList++ = '\t';//项之间以'\t'分隔
}
*( pszCandList - 1 )= 0;//串尾,并覆盖最后一个'\t'
}
bool CIme::OnWM_INPUTLANGCHANGEREQUEST()
{
return !g_bIme;//如果禁止ime则返回false,此时窗口函数应返回0,否则DefWindowProc会打开输入法
}
bool CIme::OnWM_INPUTLANGCHANGE( HWND hWnd ) //ime改变
{
HKL hKL = GetKeyboardLayout( 0 );
if( ImmIsIME( hKL ))
{
HIMC hIMC = ImmGetContext( hWnd );
ImmEscape( hKL, hIMC, IME_ESC_IME_NAME, g_szImeName );//取得新输入法名字
DWORD dwConversion, dwSentence;
ImmGetConversionStatus( hIMC, &dwConversion, &dwSentence );
g_bImeSharp = ( dwConversion & IME_CMODE_FULLSHAPE )? true : false;//取得全角标志
g_bImeSymbol = ( dwConversion & IME_CMODE_SYMBOL )? true : false;//取得中文标点标志
ImmReleaseContext( hWnd, hIMC );
}
else//英文输入
g_szImeName[ 0 ] = 0;
return false;//总是返回false,因为需要窗口函数调用DefWindowProc继续处理
}
bool CIme::OnWM_IME_NOTIFY( HWND hWnd, WPARAM wParam )
{
HIMC hIMC;
DWORD dwSize;
DWORD dwConversion, dwSentence;
switch( wParam )
{
case IMN_SETCONVERSIONMODE://全角/半角,中/英文标点改变
hIMC = ImmGetContext( hWnd );
ImmGetConversionStatus( hIMC, &dwConversion, &dwSentence );
g_bImeSharp = ( dwConversion & IME_CMODE_FULLSHAPE )? true : false;
g_bImeSymbol = ( dwConversion & IME_CMODE_SYMBOL )? true : false;
ImmReleaseContext( hWnd, hIMC );
break;
case IMN_OPENCANDIDATE://进入选字状态
case IMN_CHANGECANDIDATE://选字表翻页
hIMC = ImmGetContext( hWnd );
if( g_lpCandList )
{
GlobalFree( (HANDLE)g_lpCandList );
g_lpCandList = NULL;
} //释放以前的选字表
if( dwSize = ImmGetCandidateList( hIMC, 0, NULL, 0 ))
{
g_lpCandList = (LPCANDIDATELIST)GlobalAlloc( GPTR, dwSize );
if( g_lpCandList )
ImmGetCandidateList( hIMC, 0, g_lpCandList, dwSize );
} //得到新的选字表
ImmReleaseContext( hWnd, hIMC );
if( g_lpCandList )ConvertCandList( g_lpCandList, g_szCandList );//选字表整理成串
break;
case IMN_CLOSECANDIDATE://关闭选字表
if( g_lpCandList )
        {
GlobalFree( (HANDLE)g_lpCandList );
g_lpCandList = NULL;
}//释放
g_szCandList[ 0 ] = 0;
break;
}
return true;//总是返回true,防止ime窗口打开
}
bool CIme::OnWM_IME_COMPOSITION( HWND hWnd, LPARAM lParam ) //输入改变
{
HIMC hIMC;
DWORD dwSize;
hIMC = ImmGetContext( hWnd );
if( lParam & GCS_COMPSTR )
{
dwSize = ImmGetCompositionString( hIMC, GCS_COMPSTR, (void*)g_szCompStr, sizeof( g_szCompStr ));
g_szCompStr[ dwSize ] = 0;
}//取得szCompStr
if( lParam & GCS_COMPREADSTR )
{
dwSize = ImmGetCompositionString( hIMC, GCS_COMPREADSTR, (void*)g_szCompReadStr, sizeof( g_szCompReadStr ));
g_szCompReadStr[ dwSize ] = 0;
}//取得szCompReadStr
if( lParam & GCS_CURSORPOS )
{
g_nImeCursor = 0xffff & ImmGetCompositionString( hIMC, GCS_CURSORPOS, NULL, 0 );
}//取得nImeCursor
if( lParam & GCS_RESULTSTR )
{
unsigned char str[ MAX_PATH ];
dwSize = ImmGetCompositionString( hIMC, GCS_RESULTSTR, (void*)str, sizeof( str ));//取得汉字输入串
str[ dwSize ] = 0;
unsigned char *p = str;
while( *p )PostMessage( hWnd, WM_CHAR, (WPARAM)(*p++), 1 );//转成WM_CHAR消息
}
ImmReleaseContext( hWnd, hIMC );
return true;//总是返回true,防止ime窗口打开
}
char* CIme::GetImeName()
{
return g_szImeName[ 0 ]? g_szImeName : NULL;
}
bool CIme::IfImeSharp() //是否全角
{
return g_bImeSharp;
}
bool CIme::IfImeSymbol() //是否中文标点
{
return g_bImeSymbol;
}
void CIme::GetImeInput( char **pszCompStr, char **pszCompReadStr, int *pnImeCursor, char **pszCandList )
{
    if( pszCompStr )
    *pszCompStr = g_szCompStr;
if( pszCompReadStr )
    *pszCompReadStr = g_szCompReadStr;
if( pnImeCursor )
    *pnImeCursor = g_nImeCursor;
if( pszCandList )
*pszCandList = g_szCandList;
}
//由于微软拼音实现了很多自己的东西,CIme和它的兼容性有些问题
//1、在函数ConvertCandList中所说的选字表的问题
//2、函数GetImeInput返回的szCompReadStr显然经过了加工而不是最初的键盘输入
//   它的每个可组合的输入占以空格补足的8byte,且新的不可组合的输入存为0xa1
//   我们可以在输入法名字中有子串"微软拼音"时,只显示末尾的一组8byte,如果有0xa1就什么都不显示,也可以直接用TextOut显示所有的

//******************************************************************//
//
// 做这个东西的时候得到了论坛上网友的热心帮助,整理之后再送给大家
// 这是个在DX程序下使用系统输入法的解决方案,可能有little bug :-)
// 不过还没发现,如果发现了,告诉我啊 jerrywang@163.net
// 程序中使用的CHK()CHKB()是为了监测内存泄漏,可以去掉,CTTFFONT
// 为显示信息用,可以用其他方法替换如 TxtOut() 等
//
//******************************************************************//
//////////////////////////////////////////////////////////////////////
//
// IM.h: CIM class (使用系统)输入法类
// 2001/4/30 Write by Jerry Wang
// 感谢大大鱼的帮助
// Need Lib: imm32.lib
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_IM_H__6887B165_972D_4D17_8A75_FE07930CE59C__INCLUDED_)
#define AFX_IM_H__6887B165_972D_4D17_8A75_FE07930CE59C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define _CIM_MAXINPUTCHARNUMBER 24 //最多输入的字节数
#include "FindMe.h"
class CIM 
{
private:
    CTTFFont ttffont;                    //显示信息
    LPCANDIDATELIST m_lpCandList;        //输入法候选文字列表
    LPSTR m_lpszImeInput;                //指向IME输入的文字字符串指针
    LPSTR m_lpszCurInputLanguageDesc;    //指向当前输入语言描述的指针
    char m_cCandidateList[255];          //候选文字列表缓冲区
    char m_cInput[64];                   //输入的字母
    BOOL CandidateToString( LPCANDIDATELIST lpCandidateList ); //转换候选文字列表到字符串
public:
    CIM();
    virtual ~CIM();
    LPSTR GetResultString( void );                     //取得输入的字符串
    void UpdateShow( );                                //显示输入法信息
    LPSTR GetCurInputLanguageDesc( );                  //取得应用程序当前使用语言的描述
    void ClearCandidateList( void );                   //清除输入法文字候选列表
    BOOL GetCandidateList( HWND hWnd );                //取得输入法文字候选列表
    BOOL ImeIsOpen( void );                            //输入法是否打开
    void OnChar( TCHAR ch );                           //处理WM_IME_CHAR消息
    void OnImeNotify( HWND hWnd,WPARAM wParam );       //处理WM_IME_NOTIFY消息
    void OnImeComposition( HWND hWnd, LPARAM lParam ); //处理WM_IME_COMPOSITION消息
};
#endif // !defined(AFX_IM_H__6887B165_972D_4D17_8A75_FE07930CE59C__INCLUDED_)
//*********************************************************************************************//
//////////////////////////////////////////////////////////////////////
//
// IM.cpp: CIM class (使用系统)输入法类
// 2001/4/30 Write by Jerry Wang
// 感谢大大鱼的帮助
// Need Lib: imm32.lib
//
//////////////////////////////////////////////////////////////////////
#include "imm.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CIM::CIM()
{
    m_lpszImeInput = new char[_CIM_MAXINPUTCHARNUMBER];
    ZeroMemory( m_lpszImeInput,_CIM_MAXINPUTCHARNUMBER );
    *m_lpszImeInput = '\0';
    *m_cInput = '\0';
    ttffont.Create( "黑体",15,RGB(255,255,255) );
    GetCurInputLanguageDesc();
}
CIM::~CIM()
{
    ZeroMemory( m_lpszImeInput,_CIM_MAXINPUTCHARNUMBER );
    CHKB( delete m_lpszImeInput ); 
    if( m_lpszCurInputLanguageDesc != NULL )
        CHKB( delete m_lpszCurInputLanguageDesc ); 
}
void CIM::OnImeComposition(HWND hWnd, LPARAM lParam)
{
    if (lParam & GCS_RESULTSTR)
    {
        HIMC hIMC; //输入设备上下文
        DWORD dwLen;
        LPSTR lpResultStr;
        hIMC = ImmGetContext(hWnd); //取得输入上下文
        if (!hIMC)
            return;
        dwLen = ImmGetCompositionString(hIMC,GCS_RESULTSTR,NULL,0L);
        dwLen+=1;
        if(dwLen)
        {
            lpResultStr = new char[ dwLen ];
            //// 缓冲区已经满了
            if( strlen( m_lpszImeInput ) + dwLen > _CIM_MAXINPUTCHARNUMBER - 2 )
            {
                MessageBeep( 0 );
                return;
            }
            ZeroMemory( lpResultStr ,dwLen ); 
            if (lpResultStr)
            {
                ImmGetCompositionString(hIMC,GCS_RESULTSTR, lpResultStr,dwLen);
                strcat( m_lpszImeInput,lpResultStr );
            }
            delete lpResultStr;
        }
        ImmReleaseContext(hWnd,hIMC); //释放输入上下文
    }
}
void CIM::OnImeNotify(HWND hWnd, WPARAM wParam)
{
    DWORD dwCommand = (DWORD) wParam;
    switch( dwCommand )
    {
    case IMN_CHANGECANDIDATE:
        GetCandidateList( hWnd );
        break;
    case IMN_CLOSECANDIDATE:
        ClearCandidateList();
        break;
    case IMN_OPENCANDIDATE:
        GetCandidateList( hWnd );
        break;
    }
}
void CIM::OnChar( TCHAR ch )
{
    int len = strlen( m_lpszImeInput );
    if( ImeIsOpen() ) //输入法打开状态
    {
    }
    else //输入法关闭状态
    {
        if( ch >= 32 && ch < 128 && len < _CIM_MAXINPUTCHARNUMBER - 1 ) //输入的是英文字母
        {
            *( m_lpszImeInput + len ) = ch;
            *( m_lpszImeInput + len + 1) = '\0';
        }
    }
    if( ch == 8 ) //BackSpace字符
    {
        if( len == 0 ) //字符串长度为零
            return;
        if( len == 1 ) //只有一个字符
        {
            *m_lpszImeInput = '\0';
            return;
        }
        BYTE cc1,cc2;
        cc1 = *(m_lpszImeInput + len -1); //分离字节
        cc2 = *(m_lpszImeInput + len -2);
        if( cc1 > 0xA0 && cc2 > 0xA0 ) //中文双字节的每个字节都>0xA0
            *( m_lpszImeInput + len -2 ) = '\0';
        else //是英文字符(单字节)
            *( m_lpszImeInput + len -1 ) = '\0';
    }
}
BOOL CIM::GetCandidateList(HWND hWnd)
{
    DWORD dwSize; 
    HIMC hIMC;
    ZeroMemory( m_cCandidateList,sizeof(m_cCandidateList) );
    if( m_lpCandList )
    {
        delete m_lpCandList;
        m_lpCandList = NULL;
    }
    if( GetKeyboardLayout(0)==0 )
    {
        return FALSE;
    }
    hIMC = ImmGetContext(hWnd); //取得输入上下文
    if(hIMC == NULL)
    {
        return FALSE;
    }
    ZeroMemory( m_cCandidateList,sizeof(m_cCandidateList) );
    if(dwSize = ImmGetCandidateList(hIMC,0x0,NULL,0)) 
    {
        m_lpCandList = (LPCANDIDATELIST)new char[dwSize]; 
        if(m_lpCandList)
        {
            ImmGetCandidateList(hIMC,0x0,m_lpCandList,dwSize);
            CandidateToString(m_lpCandList);
        }
    } 
    ImmReleaseContext(hWnd,hIMC);
    return TRUE;
}
void CIM::ClearCandidateList()
{
    if(m_lpCandList)
    {
        delete m_lpCandList;
        m_lpCandList = NULL;
    }
    ZeroMemory( m_cCandidateList,sizeof( m_cCandidateList ) );
}
LPSTR CIM::GetCurInputLanguageDesc()
{
    HKL hKL = GetKeyboardLayout(0); 
    if( m_lpszCurInputLanguageDesc != NULL )
        CHKB( delete m_lpszCurInputLanguageDesc ); //删除先 ^o^
    int lengh = ImmGetDescription(hKL,NULL,0); //取得描述的长度
    CHK( m_lpszCurInputLanguageDesc = new char[ lengh ] );
    if( lengh )
    {
        ImmGetDescription(hKL,m_lpszCurInputLanguageDesc,lengh);
    }
    else
    {
        strcpy( m_lpszCurInputLanguageDesc,"输入法关闭" );
    }
    return m_lpszCurInputLanguageDesc;
}
void CIM::UpdateShow()
{
    POINT pt;
    pt.y = 450;
    pt.x = 400; 
    ttffont.SetSurface( DD_GetBackScreen() );
    ttffont.ShowText( m_lpszCurInputLanguageDesc,&pt,RGB( 255,255,0 )); //显示输入法描述
    pt.y = 420;
    pt.x = 20;
    ttffont.ShowText( m_cCandidateList,&pt );
    pt.y = 450;
    pt.x = 20;
    if( *m_lpszImeInput == '\0' )
        return;
    ttffont.ShowText( m_lpszImeInput,&pt,RGB( 255,255,0 )); //输入的文字
}
LPSTR CIM::GetResultString()
{
    return m_lpszImeInput;
}
BOOL CIM::CandidateToString(LPCANDIDATELIST lpCandidateList)
{
    if( !m_lpCandList )
        return FALSE;
    if( m_lpCandList->dwCount>0 )
    {
        LPDWORD lpdwOffset;
        lpdwOffset = &m_lpCandList->dwOffset[0];
        lpdwOffset += m_lpCandList->dwPageStart;
        ZeroMemory( m_lpCandList,sizeof( m_lpCandList ) );
        DWORD z=1;
        for (DWORD i = m_lpCandList->dwPageStart; (i < lpCandidateList->dwCount) && (i < m_lpCandList->dwPageStart + m_lpCandList->dwPageSize); i++) 
        { 
            LPSTR lpstr = (LPSTR)m_lpCandList + *lpdwOffset++;
            char buf[255];
            sprintf( buf,"%d.%s",z,lpstr );
            strcat( m_cCandidateList,buf );
            z++;
        }
        return TRUE;
    }
    return FALSE;
}
BOOL CIM::ImeIsOpen()
{
    return ImmIsIME( GetKeyboardLayout(0) );
}

张佗辉,佗字太生僻,不好记,而且现在看来自己也不会做医生了,就不要占华佗他老人家的光了。所以打算改名,改成什么呢,想了很长时间,找了几本改名起名的书,感觉张逸轩这个名字还不错,过年回家改吧。

大家有没有更好的名字,提供参考下。

先谢谢了。

#include <windows.h>
#include <tchar.h>
#include <TLHELP32.H>
#include <stddef.h>

/*
push dwTime
call Sleep

mov  eax, [esp + 4]
push eax
call DeleteFileA
ret  4
*/

#pragma pack(push, 1)
typedef struct _tagDeleteStruct {
    BYTE  byPush;
    DWORD dwTime;
    BYTE  wCall1;
    DWORD dwSleep;
    DWORD dwMov;
    BYTE  byPushEax;
    BYTE  wCall2;
    DWORD dwDeleteFileA;
    BYTE  byRet;
    WORD  w4;
    CHAR  szFile[1];
} DELETESTRUCT, *PDELETESTRUCT;
#pragma pack(pop)

void EnablePrivilege(void)
{
    HANDLE           hToken;
    TOKEN_PRIVILEGES tp = { 0 };

    HANDLE hProcess = GetCurrentProcess();

    if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
        &hToken))
        return;

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid))
    {
        CloseHandle(hToken);
        return;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
        NULL, NULL);
    CloseHandle(hToken);
}

DWORD FindTarget(LPCTSTR lpszProcess)
{
    DWORD  dwRet     = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof( PROCESSENTRY32 );
    Process32First(hSnapshot, &pe32);
    do
    {
        if (0 == lstrcmpi(pe32.szExeFile, lpszProcess))
        {
            dwRet = pe32.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapshot, &pe32));
    CloseHandle(hSnapshot);
    return dwRet;
}

DWORD WINAPI DelProc(LPVOID lpParam)
{
    Sleep(50);
    DeleteFileA((LPCSTR)lpParam);
    return 0;
}

BOOL RemoteDel(DWORD dwProcessID, LPCSTR lpszFileName, DWORD dwTime)
{
    // 打开目标进程
    HANDLE hProcess = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE,
        dwProcessID);
    if (NULL == hProcess)
        return FALSE;

    // 向目标进程地址空间写入删除信息
    DWORD         dwSize = sizeof(DELETESTRUCT) + lstrlenA(lpszFileName);
    PDELETESTRUCT pDel   = (PDELETESTRUCT)GlobalAlloc(GPTR, dwSize);

    HMODULE hKernel32 = GetModuleHandle(_T("kernel32.dll"));
    // push dwTime
    pDel->byPush = 0x68;
    pDel->dwTime = dwTime;
    // call Sleep
    pDel->wCall1  = 0xe8;
    pDel->dwSleep = (DWORD)GetProcAddress(hKernel32, "Sleep");
    // mov  eax, [esp + 4]
    pDel->dwMov = 0x0424448b;
    // push eax
    pDel->byPushEax = 0x50;
    // call DeleteFileA
    pDel->wCall2        = 0xe8;
    pDel->dwDeleteFileA = (DWORD)GetProcAddress(hKernel32, "DeleteFileA");
    // ret  4
    pDel->byRet = 0xc2;
    pDel->w4    = 0x0004;
    lstrcpyA(pDel->szFile, lpszFileName);

    LPVOID lpBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT,
        PAGE_READWRITE);
    if (NULL == lpBuf)
    {
        GlobalFree((HGLOBAL)pDel);
        CloseHandle(hProcess);
        return FALSE;
    }

    // 修正近调用
    pDel->dwSleep       -= (DWORD)lpBuf + offsetof(DELETESTRUCT, dwMov);
    pDel->dwDeleteFileA -= (DWORD)lpBuf + offsetof(DELETESTRUCT, byRet);
    DWORD dwWritten;
    WriteProcessMemory(hProcess, lpBuf, (LPVOID)pDel, dwSize, &dwWritten);

    // 创建线程,远程删除!
    DWORD dwID;
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
        (LPTHREAD_START_ROUTINE)lpBuf,
        (LPVOID)((DWORD)lpBuf + offsetof(DELETESTRUCT, szFile)), 0, &dwID);

    GlobalFree((HGLOBAL)pDel);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPTSTR lpCmdLine, int nShowCmd)
{
    EnablePrivilege();

    CHAR szMe[MAX_PATH];
    GetModuleFileNameA(NULL, szMe, MAX_PATH);

    DWORD dwId = FindTarget(_T("explorer.exe"));
    RemoteDel(dwId, szMe, 50);
    return 0;
}

点这里下载示例代码

实现文件自删除不是一个特别新的话题了,不过貌似一直没有特别完美的解决方式。从早先Gary Nebbett的堆栈溢出版本到后来的批处理、临时文件等方式,无不存在着各样瑕疵:如堆栈溢出不支持XP,临时文件(批处理)不够优雅等等。

当然,还有用驱动发IRP的方式,不过这只是一个自删除,杀鸡焉用牛刀?于是这个方案在我这儿亦不讨论。

李马讨论的,只是一个2005年的老调重提:远线程注入。2005年李马提到的DLL远程注入技术只是远线程的最简单应用,局限很多,能做的事情很少;下面的自删除示例,则是如何让远线程能够做更多的事,也可以说是一个补充材料,不必记入原创文档了吧就。

言归正传。首先,我们假定这个线程函数是这样的:

DWORD WINAPI DelProc(LPVOID lpParam)
{
    Sleep(50);
    DeleteFileA((LPCSTR)lpParam);
return 0;
}

解释一下,先用Sleep等待要删除的程序结束,之后调用DeleteFile删除目标文件。

现在,你可以在VC的Project Settings->C/C++->Category: Listing Files->Listing file type中,设置输出文件的类型为“Assembly, Machine Code, and Source”或“Assembly with Machine Code”,这样就会在编译完成后生成带有汇编代码和指令机器码的附属文件供你下一步对照。——当然,如果你极熟悉汇编,这一步可以跳过。

在查看附属文件后,我们可以提取出对我们有用的汇编代码:

push 50
call Sleep
mov  eax, [esp + 4]
push eax
call DeleteFileA
ret  4

之后,对照着对应的机器码,构造下面的结构:

#pragma pack(push, 1)
typedef struct _tagDeleteStruct {
    BYTE  byPush;
    DWORD dwTime;
    BYTE  wCall1;
    DWORD dwSleep;
    DWORD dwMov;
    BYTE  byPushEax;
    BYTE  wCall2;
    DWORD dwDeleteFileA;
    BYTE  byRet;
    WORD  w4;
    CHAR  szFile[1];
} DELETESTRUCT, *PDELETESTRUCT;
#pragma pack(pop)

最后的szFile域,就是用来放置文件名的。其余的就不解释了,因为下面就要填充它了。远线程函数还是很模式化的代码,改造自两年前我的RemoteLoadLibrary:

BOOL RemoteDel(DWORD dwProcessID, LPCSTR lpszFileName, DWORD dwTime)
{
// 打开目标进程
    HANDLE hProcess = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE,
        dwProcessID);
if (NULL == hProcess)
return FALSE;
// 向目标进程地址空间写入删除信息
    DWORD         dwSize = sizeof(DELETESTRUCT) + lstrlenA(lpszFileName);
    PDELETESTRUCT pDel   = (PDELETESTRUCT)GlobalAlloc(GPTR, dwSize);
    HMODULE hKernel32 = GetModuleHandle(_T("kernel32.dll"));
// push dwTime
    pDel->byPush = 0x68;
    pDel->dwTime = dwTime;
// call Sleep
    pDel->wCall1  = 0xe8;
    pDel->dwSleep = (DWORD)GetProcAddress(hKernel32, "Sleep");
// mov  eax, [esp + 4]
    pDel->dwMov = 0x0424448b;
// push eax
    pDel->byPushEax = 0x50;
// call DeleteFileA
    pDel->wCall2        = 0xe8;
    pDel->dwDeleteFileA = (DWORD)GetProcAddress(hKernel32, "DeleteFileA");
// ret  4
    pDel->byRet = 0xc2;
    pDel->w4    = 0x0004;
    lstrcpyA(pDel->szFile, lpszFileName);
    LPVOID lpBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT,
        PAGE_READWRITE);
if (NULL == lpBuf)
    {
        GlobalFree((HGLOBAL)pDel);
        CloseHandle(hProcess);
return FALSE;
    }
// 修正近调用
    pDel->dwSleep       -= (DWORD)lpBuf + offsetof(DELETESTRUCT, dwMov);
    pDel->dwDeleteFileA -= (DWORD)lpBuf + offsetof(DELETESTRUCT, byRet);
    DWORD dwWritten;
    WriteProcessMemory(hProcess, lpBuf, (LPVOID)pDel, dwSize, &dwWritten);
// 创建线程,远程删除!
    DWORD dwID;
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
        (LPTHREAD_START_ROUTINE)lpBuf,
        (LPVOID)((DWORD)lpBuf + offsetof(DELETESTRUCT, szFile)), 0, &dwID);
    GlobalFree((HGLOBAL)pDel);
    CloseHandle(hThread);
    CloseHandle(hProcess);
return TRUE;
}

至于为什么最后不用VirtualFreeEx释放资源,那是因为注入的远程代码在执行的时候目标exe就已经消失了,所以这里的寄主程序肯定存在着内存泄露,真是造孽啊。

最后说三点。第一,RemoteDel是要挑选一个寄主程序的,这个程序应该始终运行并存在于当前的系统中,我在示例中挑选的是explorer.exe;并且,打开这个进程是需要调试权限的,提权的代码也一并加入在示例代码中,算是弥补了2005年的缺失。第二,为了方便定位,我修改了远程代码中的调用,也就是call ds:xxx(FF 15 xxx)改为了call near xxx(E8 xxx)。第三,自己手写机器码的做法的确不如纯汇编代码重定位来的优雅,但是我认为这里填充并定位Sleep和DeleteFile的片断也是纯汇编的办法无法比拟的。

游戏要实现模型的突出显示,最好是边缘高亮的效果.刚听到这个东西时,第一个进入头脑中的就是:边缘检测.于是就写了一个:
<tbody>

    <tr>

        <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 426.1pt; padding-top: 0cm; border-bottom: windowtext 1pt solid" valign="top" width="568">

        <div>Pass 0:</div>

        <div>渲染模型到一个RenderTarget Model上,并把模型的形状写入Alpha通道.</div>

        <div>Pass 1:</div>

        <div>对上面得到的Alpha通道用拉普拉斯模板进行滤波,得到一个边缘,写入另一个RenderTarget Edge.</div>

        <div>Pass 2:</div>

        <div>把前面得到的两个RenderTarget进行合成,输出到屏幕.</div>

        </td>

    </tr>

</tbody>
 
虽说效果还不错,但是用掉了两个RenderTarget,显然不合算.而且,用拉普拉斯在PixelShader中进行逐像素的处理,效率并不高.
 
阿来在GameDev上求得另一种方法:把模型画两遍,其中一次对模型进行一次放大,关闭Z-Write就出来这种效果了.不过有两个问题:
一是模型直接放缩是以模型坐标系的原点来的,而这个原点并不一定是在模型的中心.就算在中心,对于一些非凸多面体并不能得到很好的效果,有一些边缘会被模型遮住.
二是,关闭了深度检测,那这个模型怎么跟别的模型来进行遮挡处理呢?
 
后来在群里有人提醒说在3D游戏程序设计入门这本书上有,我才想起来有一个卡通渲染的例子.卡通渲染?那里面不也有轮廓生成吗?看了一下书上的实现方法,用的是三角形退化,有点复杂.于是google之,发现了一个跟GameDev上那个方法差不多思想的:
<tbody>

    <tr>

        <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 426.1pt; padding-top: 0cm; border-bottom: windowtext 1pt solid" valign="top" width="568">

        <div>Pass0:</div>

        <div>把模型按法线方向进行放大,然后渲染成单色模型,不过只渲染背面,避免遮住正常的模型.(把D3DRS_CULLMODE设成D3DCULL_CW就可以了)</div>

        <div>Pass1:</div>

        <div>正常渲染模型,不用做什么改变.不过不要忘了把D3DRS_CULLMODE改回来.</div>

        </td>

    </tr>

</tbody>
 
Shader:
<tbody>

    <tr>

        <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 426.1pt; padding-top: 0cm; border-bottom: windowtext 1pt solid" valign="top" width="568">

        <div><span style="color: blue">float4x4</span> matViewProjection;</div>

        <div>&nbsp;</div>

        <div><span style="color: blue">struct</span> VS_INPUT </div>

        <div>{</div>

        <div><span>&nbsp;&nbsp; <span style="color: blue">float4</span> Position : <span style="color: fuchsia">POSITION0</span>;</span></div>

        <div style="margin-left: 21pt; text-indent: -21pt"><span>&nbsp;&nbsp; <span style="color: blue">float3</span> Normal&nbsp;&nbsp; : <span style="color: fuchsia">NORMAL0</span>;</span></div>

        <div>};</div>

        <div>&nbsp;</div>

        <div><span style="color: blue">struct</span> VS_OUTPUT </div>

        <div>{</div>

        <div><span>&nbsp;&nbsp; <span style="color: blue">float4</span> Position : POSITION0;</span></div>

        <div>};</div>

        <div>&nbsp;</div>

        <div>VS_OUTPUT vs_main( VS_INPUT Input )</div>

        <div>{</div>

        <div><span>&nbsp;&nbsp; VS_OUTPUT Output;</span></div>

        <div>&nbsp;</div>

        <div style="margin-left: 21pt; text-indent: -21pt"><span>&nbsp;&nbsp; Output.Position = <span style="color: blue">float4</span>(Input.Position.xyz + Input.Normal*0.4f,1);</span></div>

        <div><span>&nbsp;&nbsp; Output.Position = <span style="color: red">mul</span>( Output.Position, matViewProjection );</span></div>

        <div>&nbsp;</div>

        <div style="margin-left: 21pt; text-indent: -21pt"><span>&nbsp;&nbsp; <span style="color: blue">return</span>( Output );</span></div>

        <div>}</div>

        </td>

    </tr>

    <tr>

        <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 426.1pt; padding-top: 0cm; border-bottom: windowtext 1pt solid" valign="top" width="568">

        <div><span style="color: blue">sampler2D</span> BaseMap;</div>

        <div>&nbsp;</div>

        <div><span style="color: blue">float4</span> ps_main() : <span style="color: fuchsia">COLOR0</span></div>

        <div>{</div>

        <div><span>&nbsp;&nbsp; <span style="color: blue">return</span> <span style="color: blue">float4</span>(0, 1, 0, 1);</span></div>

        <div>}</div>

        </td>

    </tr>

</tbody>
 
下面是两种方法实现的效果对比:
差不多哈,区别就是用卡通渲染的那种方法出来的中间也有轮廓线.
 

a

original address: http://blog.csdn.net/starflash2003/archive/2004/10/12/132884.aspx


类型修饰符

在你的着色器中打算使用的HLSL中有几个可选的类型修饰符。通常把不想被着色器的代码修改的量设为const(常量)类型修饰符。在赋值符号左边使用常量(例如作为一个lval)会产生一个编译错误。

可以用row_major(行优先)类型修饰符与col_major(列优先)类型修饰符指定在存储常数硬件中的矩阵格式。row_major(行优先)类型修饰符表示矩阵中的每一行被存储在一个单个的常数寄存器中。同样地,使用col_major(列优先)表示矩阵中的每一列被存储在一个单个的常数寄存器中。默认为列优先。

存储类别修饰符

存储类别修饰符通知编译器给定变量的作用域和生存期。这些修饰符是可选的,可在变量类型前以任意次序出现。

像C 语言一样,一个变量可以被声明为static(静态变量)或extern(外部变量)。(这两个修饰符是互斥的)在全局范围,static(静态)类别修饰符表示变量只能由着色器访问,而不能由应用程序通过API访问。任何在全局范围声明的非静态变量可以由应用程序通过API修改。像C语言一样,在局部范围使用static(静态)修饰符表示变量所含数据将在所声明函数内始终存在(译者注:即生存期为全局,作用域为函数内)。

在全局范围使用extern(外部)修饰符表示可由外部着色器通过API修改。不过这属于多此一举,因为在全局范围声明的变量默认就是这样。

使用shared(共享)修饰符设定将由两种效果共享的全局变量。

前缀为uniform的变量先在外部被初始化,然后进入HLSL着色器。(例如,通过Set*ShaderConstant*() API)。把全局变量当作被uniform声明。不过由于值在着色器中可以被修改,所以不可能是常数。

例如,假定你在全局范围声明了下列变量:

 

extern float translucencyCoeff;

const  float gloss_bias;

static float gloss_scale;

float diffuse;


变量diffuse和translucencyCoeff可被SetShadercConstant() API置位,也可被着色器本身修改。常量gloss_bias可被SetShadeConstant() API置位,不过不能被着色器代码修改。最后,静态变量gloss_scale不能被SetShaderConstant()API置位,不过可以也只能在着色器中被修改。

初始化

如前面例子显示的,和C语言中的习惯一样可以在变量声明时进行初始化。例如:

float2x2 fMat = {3.0f5.0f,  // row 1

                 2.0f1.0f}; // row 2

float4   vPos = {3.0f5.0f2.0f1.0f};

float fFactor = 0.2f;

向量运算

在HLSL中,当执行关于向量的数学运算时需要留心一些程序陷阱(gotchas)。如果为3D图形编写着色器,绝大部分程序陷阱(gotchas)可以靠直觉发现。例如,定义标准的二元运算符以进行每一维的运算。

float4 vTone = vBrightness * vExposure;

假定vBrightness和vExposure都是float4类型,相当于:

 

float4 vTone;vTone.x = vBrightness.x * vExposure.x;

vTone.y = vBrightness.y * vExposure.y;

vTone.z = vBrightness.z * vExposure.z;

vTone.w = vBrightness.w * vExposure.w;


要注意在4D向量vBrightness和vExposure间不是点乘。此外,用这种方式乘以矩阵变量不会引起矩阵相乘。点乘法和矩阵相乘法是通过内部函数mul()实现的,这将在后边讨论。

构造函数

常能在HLSL着色器中见到的属于其他语言特色的是构造函数,和C++中的类似不过增加了一些处理复杂数据类型的内容。构造函数使用的例子:

 

float3   vPos     = float3(4.0f1.0f2.0f);

float    fDiffuse = dot(vNormal, float3(1.0f0.0f0.0f));

float4   vPack    = float4(vPos, fDiffuse);


构造函数通常用在:想要临时定义一个常量(如上边的dot(vNormal, float3(1.0f, 0.0f, 0.0f)))或想同时显式地压缩更小的数据类型。(如上边的float4(vPos, fDiffuse))。在这个例子中,构造函数float4接收一个float3类型和一个float类型同时返回一个数据被压缩的float4类型。

original address: http://blog.csdn.net/starflash2003/archive/2004/10/13/135493.aspx


强制类型转换

为了有助于着色器的编写和所产生代码的效率,最好熟悉一下HLSL的强制类型转换机制。强制类型转换常用于扩展或缩减选定的变量以匹配要赋值的变量。例如,在下列例子中,初始化vResult时把float型常量0.0f强制转换为 float4型{0.0f , 0.0f , 0.0f , 0.0f }。

float4   vResult = 0.0f;

当把一个高维数据类型如向量或矩阵类型赋值给一个低维数据类型时就会发生类似的强制转换。这些情况下,额外数据都被有效省略。例如,编写下列代码:


float3   vLight;
float    fFinal, fColor;fFinal = vLight * fColor;

这个例子中,只是把float类型的标量fColor与vLight中的第一个成员相乘,从而把vLight强制转换为float类型。fFinal等于vLight.x * fColor。

最好先熟悉一下表4,HLSL的强制类型转换规则:

表6. HLSL的强制类型转换规则

<tbody>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Scalar-to-scalar</code></td>

        <td class="data" width="69%"><strong>一直有效。</strong>当布尔型被强制转换为整数或浮点型,false变为0,true变为1。当整数或浮点型被强制转换为布尔型,0变为false,非0变为true。当浮点型被强制转换为整数类型,值被向0舍入,这与C语言的一样截断机制一样。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Scalar-to-vector</code></td>

        <td class="data" width="69%"><strong>一直有效</strong><strong>。</strong> 该强制转换操作通过复制标量并填充到向量。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Scalar-to-matrix</code></td>

        <td class="data" width="69%"><strong>一直有效</strong><strong>。</strong> 该强制转换操作通过复制标量并填充到矩阵。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Scalar-to-structure</code></td>

        <td class="data" width="69%">该强制转换操作通过复制标量并填充到结构体。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Vector-to-scalar</code></td>

        <td class="data" width="69%"><strong>一直有效</strong><strong>。</strong> 选择向量的第一部分。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Vector-to-vector</code></td>

        <td class="data" width="69%">目标向量必须不大于资源向量。该强制转换操作是通过保留最左边的值,去掉多余值。这样做的目的是可以把行矩阵,列矩阵和数字结构看成向量。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Vector-to-matrix</code></td>

        <td class="data" width="69%">向量大小必须与矩阵大小相等。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Vector-to-structure</code></td>

        <td class="data" width="69%">结构体不大于向量,且结构体各部分均为数字则有效。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Matrix-to-scalar</code></td>

        <td class="data" width="69%"><strong>一直有效</strong><strong>。</strong> 选择了矩阵的左上部分。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Matrix-to-vector</code></td>

        <td class="data" width="69%">矩阵大小必须与向量大小相等。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Matrix-to-matrix</code></td>

        <td class="data" width="69%">目标矩阵在任何一维都不大于源矩阵,该强制转换操作是通过保持左上值,去掉多余值。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Matrix-to-structure</code></td>

        <td class="data" width="69%">结构体的大小等于矩阵的大小,结构体的所有成员都是数字。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Structure-to-scalar</code></td>

        <td class="data" width="69%">结构体必须包含至少一个数字型成员</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Structure-to-vector</code></td>

        <td class="data" width="69%">结构体必须至少与向量的大小一样,第一个成员必须是数字,一直到向量的大小。(译者注:即成员数量与向量大小一样)</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Structure-to-matrix</code></td>

        <td class="data" width="69%">结构体必须至少与矩阵的大小一样。第一个成员必须是数字,一直到矩阵的大小。(译者注:即成员数量与矩阵大小一样)</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Structure-to-object</code></td>

        <td class="data" width="69%">结构体至少包含一个对象的成员。该成员的类型必须和对象类型完全相同。</td>

    </tr>

    <tr valign="top">

        <td class="data" width="31%"><code class="ce">Structure-to-structure</code></td>

        <td class="data" width="69%">目标结构必须不大于源结构。一个有效的强制转换必定存在于所有相应的源成员与目的成员之间。</td>

    </tr>

</tbody>

结构体

正如上边第一个着色器示例显示的,在HLSL着色器中定义一个结构体常常带来方便。例如,许多着色器编写者在他们的顶点着色器代码中会定义一个输出的结构体,使用该结构体作为他们的顶点着色器主函数的返回类型。(对于像素着色器很少这样做因为大多数像素着色器只有一个float4输出。)一个结构体的例子如下,来自于NPR Metallic着色器,我们将在后边讨论该着色器。(译者注:NPR(non-photo reality),是一种独特的二w维效果)

 

struct VS_OUTPUT

{  

 float4 Pos   : POSITION;

   float3 View  : TEXCOORD0;

   float3 Normal: TEXCOORD1;

   float3 Light1: TEXCOORD2;

   float3 Light2: TEXCOORD3;

   float3 Light3: TEXCOORD4;

};


在HLSL着色器中也可以声明结构体作为普通使用。同样遵循上边所概括的强制类型转换规则。

取样器

要是想在像素着色器中对于每一个不同的纹理贴图进行取样,必须声明一个取样器。再调用一下前边描述的着色器中的hlsl_rings():

 

float4 lightWood; // xyz == Light Wood

 Colorfloat4 darkWood;  // xyz == Dark Wood

 Colorfloat  ringFreq;  // ring 

frequencysampler PulseTrainSampler;

float4 hlsl_rings (float4 Pshade : TEXCOORD0) : COLOR

{    float scaledDistFromZAxis = sqrt(dot(Pshade.xy, Pshade.xy)) * ringFreq;

    float blendFactor = tex1D (PulseTrainSampler, scaledDistFromZAxis); 

    return lerp (darkWood, lightWood, blendFactor);

}


在这个着色器中,我们在全局范围声明了一个被称为PulseTrainSampler的取样器并把它作为第一个参数传递给内部函数tex1D()(将在下一部分讨论内部函数)。HLSL取样器有一个非常直接的映射——在基于取样器概念的API与实际硅(在负责寻址纹理和过滤纹理的3D图形处理器中)之间轮换。在一个着色器中必须为每一个你计划访问的纹理贴图定义一个取样器,不过你可以在一个着色器中多次使用给定的取样器。这种处理方法在图像处理程序中非常普遍(在ShaderX2 - Shader Tips & Tricks的”Advanced Image Processing with DirectX 9 Pixel Shaders”章节有讨论),为了给由着色器代码表示的一个内部过滤器提供数据,输入的图像被以不同的纹理坐标多次取样。例如,下面的着色器使用光栅化引擎(rasterizer)通过一对Sobel滤波器把一个高度贴图(height map)转换为一个法线贴图(normal map)。

 

sampler InputImage;

float4 main( float2 topLeft    : TEXCOORD0,

 float2 left        : TEXCOORD1,

             float2 bottomLeft : TEXCOORD2, float2 top         : TEXCOORD3,

             float2 bottom     : TEXCOORD4,

 float2 topRight    : TEXCOORD5,

             float2 right      : TEXCOORD6,

 float2 bottomRight : TEXCOORD7): COLOR

{   // Take all eight taps  

 float4 tl = tex2D (InputImage, topLeft);

   float4  l = tex2D (InputImage, left);

   float4 bl = tex2D (InputImage, bottomLeft); 

  float4  t = tex2D (InputImage, top);

   float4  b = tex2D (InputImage, bottom);

   float4 tr = tex2D (InputImage, topRight);

   float4  r = tex2D (InputImage, right);

   float4 br = tex2D (InputImage, bottomRight);   // Compute dx using Sobel operator:   //   //           -1 0 1    //           -2 0 2   //           -1 0 1   

float dX = -tl.a - 2.0fl.a - bl.a + tr.a + 2.0fr.a + br.a;   // Compute dy using Sobel operator:   //   //           -1 -2 -1    //            0  0  0   //            1  2  1   

float dY = -tl.a - 2.0ft.a - tr.a + bl.a + 2.0fb.a + br.a;   // Compute cross-product and renormalize   

float4 N = float4(normalize(float3(-dX, -dY, 1)), tl.a);   // Convert signed values from -1..1 to 0..1 range and return   

return N * 0.5f + 0.5f;

}

original address: http://blog.csdn.net/starflash2003/archive/2004/10/09/130020.aspx


语言基础

现在你已经对什么是HLSL顶点和像素着色器以及他们如何与低层汇编着色器相互作用有了了解,我们将讨论一些语言本身的细节。

关键字

关键字是HLSL语言保留的预定义标识符,不能在你的程序中作为标识符使用。标有'*'的关键字不区分大小写。

表2. HLSL语言所保留的关键字

<tbody>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">asm*</code></td>

        <td class="data" width="25%"><code class="ce">bool</code></td>

        <td class="data" width="25%"><code class="ce">compile</code></td>

        <td class="data" width="25%"><code class="ce">const</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">decl*</code></td>

        <td class="data" width="25%"><code class="ce">do</code></td>

        <td class="data" width="25%"><code class="ce">double</code></td>

        <td class="data" width="25%"><code class="ce">else</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">extern</code></td>

        <td class="data" width="25%"><code class="ce">false</code></td>

        <td class="data" width="25%"><code class="ce">float</code></td>

        <td class="data" width="25%"><code class="ce">for</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">half</code></td>

        <td class="data" width="25%"><code class="ce">if</code></td>

        <td class="data" width="25%"><code class="ce">in</code></td>

        <td class="data" width="25%"><code class="ce">inline</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">inout</code></td>

        <td class="data" width="25%"><code class="ce">int</code></td>

        <td class="data" width="25%"><code class="ce">matrix*</code></td>

        <td class="data" width="25%"><code class="ce">out</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">pass*</code></td>

        <td class="data" width="25%"><code class="ce">pixelshader*</code></td>

        <td class="data" width="25%"><code class="ce">return</code></td>

        <td class="data" width="25%"><code class="ce">sampler</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">shared</code></td>

        <td class="data" width="25%"><code class="ce">static</code></td>

        <td class="data" width="25%"><code class="ce">string*</code></td>

        <td class="data" width="25%"><code class="ce">struct</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">technique*</code></td>

        <td class="data" width="25%"><code class="ce">texture*</code></td>

        <td class="data" width="25%"><code class="ce">true</code></td>

        <td class="data" width="25%"><code class="ce">typedef</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">uniform</code></td>

        <td class="data" width="25%"><code class="ce">vector*</code></td>

        <td class="data" width="25%"><code class="ce">vertexshader*</code></td>

        <td class="data" width="25%"><code class="ce">void</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">volatile</code></td>

        <td class="data" width="25%"><code class="ce">while</code></td>

        <td class="data" width="25%">&nbsp;</td>

        <td class="data" width="25%">&nbsp;</td>

    </tr>

</tbody>

下列关键字当前没有使用,不过保留给将来使用:

表3. 当前并没使用的保留关键字

<tbody>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">auto</code></td>

        <td class="data" width="25%"><code class="ce">break</code></td>

        <td class="data" width="25%"><code class="ce">compile</code></td>

        <td class="data" width="25%"><code class="ce">const</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">char</code></td>

        <td class="data" width="25%"><code class="ce">class</code></td>

        <td class="data" width="25%"><code class="ce">case</code></td>

        <td class="data" width="25%"><code class="ce">catch</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">default</code></td>

        <td class="data" width="25%"><code class="ce">delete</code></td>

        <td class="data" width="25%"><code class="ce">const_cast</code></td>

        <td class="data" width="25%"><code class="ce">continue</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">explicit</code></td>

        <td class="data" width="25%"><code class="ce">friend</code></td>

        <td class="data" width="25%"><code class="ce">dynamic_cast</code></td>

        <td class="data" width="25%"><code class="ce">enum</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">mutable</code></td>

        <td class="data" width="25%"><code class="ce">namespace</code></td>

        <td class="data" width="25%"><code class="ce">goto</code></td>

        <td class="data" width="25%"><code class="ce">long</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">private</code></td>

        <td class="data" width="25%"><code class="ce">protected</code></td>

        <td class="data" width="25%"><code class="ce">new</code></td>

        <td class="data" width="25%"><code class="ce">operator</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">reinterpret_cast</code></td>

        <td class="data" width="25%"><code class="ce">short</code></td>

        <td class="data" width="25%"><code class="ce">public</code></td>

        <td class="data" width="25%"><code class="ce">register</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">static_cast</code></td>

        <td class="data" width="25%"><code class="ce">switch</code></td>

        <td class="data" width="25%"><code class="ce">signed</code></td>

        <td class="data" width="25%"><code class="ce">sizeof</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">throw</code></td>

        <td class="data" width="25%"><code class="ce">try</code></td>

        <td class="data" width="25%"><code class="ce">template</code></td>

        <td class="data" width="25%"><code class="ce">this</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">typename</code></td>

        <td class="data" width="25%"><code class="ce">unsigned</code></td>

        <td class="data" width="25%"><code class="ce">using</code></td>

        <td class="data" width="25%"><code class="ce">union</code></td>

    </tr>

    <tr valign="top">

        <td class="data" width="25%"><code class="ce">virtual</code></td>

        <td class="data" width="25%">&nbsp;</td>

        <td class="data" width="25%">&nbsp;</td>

        <td class="data" width="25%">&nbsp;</td>

    </tr>

</tbody>

数据类型

HLSL支持各种数据类型,从简单的标量到较复杂的类型如向量和矩阵。

标量类型

语言支持以下标量数据类型:

表4. 标量数据类型

<tbody>

    <tr valign="top">

        <td class="data" width="45%"><code class="ce">bool</code></td>

        <td class="data" width="55%">true or false</td>

    </tr>

    <tr valign="top">

        <td class="data" width="45%"><code class="ce">int</code></td>

        <td class="data" width="55%">32-bit signed integer</td>

    </tr>

    <tr valign="top">

        <td class="data" width="45%"><code class="ce">half</code></td>

        <td class="data" width="55%">16-bit floating point value</td>

    </tr>

    <tr valign="top">

        <td class="data" width="45%"><code class="ce">float</code></td>

        <td class="data" width="55%">32-bit floating point value</td>

    </tr>

    <tr valign="top">

        <td class="data" width="45%"><code class="ce">double</code></td>

        <td class="data" width="55%">64-bit floating point value</td>

    </tr>

</tbody>

如果你已经熟悉了汇编层编程模型,就会知道并非所有图形处理器天生就支持这些数据类型。因此,整数也许需要由浮点硬件来仿真实现。这意味着并不保证超出整数 (在这些平台上用浮点数表示的)范围的整数运算如期望那样运行。另外,并非所有对象平台支持半精度或双精度值。如果不支持,将使用单精度浮点数来仿真。

向量类型

常常会在HLSL着色器中声明向量变量。声明这些向量有许多方法,包括下列所示:

表5. 向量类型

<tbody>

    <tr valign="top">

        <td class="data" width="27%"><code class="ce">vector</code></td>

        <td class="data" width="73%">一个四维向量; 每一维是浮点类型</td>

    </tr>

    <tr valign="top">

        <td class="data" width="27%"><code class="ce">vector &lt; </code><em>type,&nbsp; size</em><code class="ce"> &gt;</code></td>

        <td class="data" width="73%">维数为<em>size</em>的向量; 每一维是<em>type</em>类型</td>

    </tr>

</tbody>

声明向量最通常的方法是在类型后跟一个2-4的整数作为名字。例如,要声明一个4元组单精度浮点型,可以如下声明:


float4 fVector0;
float  fVector1[4];
vector fVector2;
vector 
<float4> fVector3;

例如,要声明一个3元组布尔型,可以如下声明:

bool3 bVector0;bool  bVector1[3];vector <bool, 3> bVector2;

一旦定义了一个向量,就可通过使用类似访问数组的语法或使用一个swizzle来访问其单独的维数。在这个swizzle例子中,维数必须来自于{x. y, z, w}或{r, g, b, a}命名空间 (不过不是两者都)。例如:

 

float4 pos = {3.0f5.0f2.0f1.0f};

float  value0 = pos[0]; // value0 is 3.0f

float  value1 = pos.x;  // value1 is 3.0f

float  value2 = pos.g;  // value2 is 5.0f

float2 vec0   = pos.xy; // vec0 is {3.0f, 5.0f}

float2 vec1   = pos.ry; // INVALID because of bad swizzle


需要注意的是ps_2_0和更低的像素着色器模型并不支持arbitrary swizzles(译者注:Arbitrary swizzle实际上可以看作一个“修正器”(modifier),它用于修改指令和寄存器。其主要功能是减少在一个着色器中使用的指令数目,从而提高效率。)因此,当编译成这些对象时,原本简洁的高层代码(使用swizzle)可能变成相当难理解的二进制汇编代码。你应当熟知这些汇编模型中可以用到的 swizzle。

矩阵类型

HLSL着色器中还常常会用到的变量类型是矩阵,它是二维数组。与标量和向量一样,矩阵由其他一些基本数据类型组成:布尔型,整型,半精度,单精度或双精度浮点型。矩阵可以是任意大小,不过一般都使用4行4列的矩阵。你可以再调用本章开头顶点着色器的例子,在全局范围声明两个4 × 4单精度浮点型矩阵:


float4x4 view_proj_matrix;
float4x4 texture_matrix0;

自然,也可使用其他维数的矩阵。例如,我们用不同方式声明一个3行4列的单精度浮点型矩阵:


float3x4            mat0;
matrix
<float34> mat1;

和向量一样,可以使用存取数组或结构体/swizzle的语法访问矩阵中的单一元素。例如,要访问矩阵view_proj_matrix的左上角元素可以使用如下面例子中数组下标的方法:

float fValue = view_proj_matrix[0][0];

也有用结构体的语法,定义结构体是由于要访问和swizzling of矩阵元素。从0开始的行列位置,如下:

_m00, _m01, _m02, _m03_m10, _m11, _m12, _m13_m20, _m21, _m22, _m23_m30, _m31, _m32, _m33

从1开始的行列位置,如下:

_11, _12, _13, _14_21, _22, _23, _24_31, _32, _33, _34_41, _42, _43, _44

也可以使用数组符号访问矩阵:例如:

float2x2 fMat = {3.0f5.0f,  // row 1

                 2.0f1.0f}; // row 2

float  value0 = fMat[0];      // value0 is 3.0f

float  value1 = fMat._m00;    // value1 is 3.0f

float  value2 = fMat._12      // value2 is 5.0f

float  value3 = fMat[1][1]    // value3 is 1.0f

float2 vec0   = fMat._21_22;  // vec0 is {2.0f, 1.0f}

float2 vec1   = fMat[1];      // vec1 is {2.0f, 1.0f}

original address: http://blog.csdn.net/starflash2003/archive/2004/10/08/128688.aspx


引言

高层着色语言(HLSL)是DirectX® 9最为强力的新组件之一。使用这种标准的高级语言, 在进行着色时编写者可以专注于算法而不用再去理会诸如寄存器的分配,寄存器读端口限制, 并行处理指令等等硬件细节. 除了把开发者从硬件细节中解放出来之外,HLSL 也具有高级语言所有的全部优势,诸如:代码重用容易, 可读性增强以及存在一个优化过的编译器。本书和 ShaderX2 - Shader Tips & Tricks 这本书的许多章节就用到了HLSL编写的着色器. 阅读完本章引言后,你会很容易理解那些着色器并在工作中用到它们。

这一章, 我们概述语言本身的基本结构以及将HLSL集成到你的应用程序中的方法。

一个简单的示例

在彻底描述HLSL之前, 让我们先看一下程序中HLSL顶点着色和HLSL像素着色的实现,这个程序渲染简单的木纹。下边显示的第一个HLSL着色是一个简单的顶点着色:


float4x4 view_proj_matrix;
float4x4 texture_matrix0;
struct VS_OUTPUT
{   float4 Pos     : POSITION;
   float3 Pshade  : TEXCOORD0;
};
VS_OUTPUT main (float4 vPosition : POSITION)
{  
 VS_OUTPUT Out 
= (VS_OUTPUT) 0;    //Transform position to clip space   
Out.Pos = mul (view_proj_matrix, vPosition);   // Transform Pshade   Out.Pshade = mul (texture_matrix0, vPosition); 
  return Out;}

最开始两行声明了一对4×4的矩阵,分别命名为view_proj_matrix和texture_matrix0。在全局矩阵之后,声明了一个结构体。这个VS_OUTPUT有两个成员:Pos和Pshade。

该着色器的main函数接受一个单精度float4类型的输入参数并返回一个VS_OUTPUT结构体。float4类型的vPosition是着色器唯一的输入,返回的VS_OUTPUT结构体定义了该顶点着色器的输出。目前不必关心在参数和结构体之后的关键字POSITION和TEXCOORD0。它们被称为语义,他们的含义将在本章后边讨论。

看一下main函数的实际代码部分,你可以看到一个内部函数mul,它被用来把输入的向量vPosition和矩阵view_proj_matrix相乘。在顶点着色器中这个函数最常被用来执行顶点-矩阵的乘法。就这样,vPosition被作为列向量,因为它是mul的第二个参数。如果向量 vPosition是mul的第一个参数,它将被作为行向量。内部函数mul以及其他内部函数将在本章后边更详细的讨论。在把输入的vPosition的位置转换换到裁减空间之后,vPosition与另一个矩阵texture_matrix0相乘以产生3D纹理坐标。两次转换的结果写入返回的结构体 VS_OUTPUT。一个顶点着色器必须总是以最小值输出到一个裁减空间位置。任何从顶点着色器输出的额外值都是通过贯穿光栅化多边型插值得到的,也可用来输入到像素着色器。就这样,通过一个内插器, 三维的Pshade从顶点着色器被传递到像素着色器。

下边,我们看到一个简单的HLSL木纹像素着色器。这个像素着色器和刚才我们描述的顶点着色器一起工作,它将被编译成模型ps_2_0。

 

float4 lightWood; // xyz == Light Wood 
Colorfloat4 darkWood;  // xyz == Dark Wood 
Colorfloat  ringFreq;  // ring
 frequencysampler PulseTrainSampler;
float4 hlsl_rings (float4 Pshade : TEXCOORD0) : COLOR
{   
 
float scaledDistFromZAxis = sqrt(dot(Pshade.xy, Pshade.xy)) * ringFreq; 
   
float blendFactor = tex1D (PulseTrainSampler, scaledDistFromZAxis); 
    
return lerp (darkWood, lightWood, blendFactor);
}
最开始几行在全局范围内声明了一对浮点类型的四元数组和一个浮点变量。在这些变量之后,声明了一个被称为PulseTrainSampler的取样器。取样器将在章节后边讨论,目前你可以把它看成一个在显存中的窗口,它与过滤状态和纹理坐标寻址模式发生关联。在变量和取样器声明后边是着色器代码的主体部分。你可以看到有一个输入参数Pshade,它是贯穿多边形插值得到的。它的值是由顶点着色器计算每一个顶点得出的。在像素着色器中,把着色空间Z轴上的笛卡尔距离作为一维纹理坐标来计算,衡量,使用,以存取绑定于PulseTrainSampler的纹理。tex1D()取样函数返回的颜色标量被用作混合因子,以混合在着色器全局范围内声明的两种相反颜色。像素着色器最终输出一个混合的四元向量结果。所有的像素着色器至少必须返回一个四元 RGBA 颜色。我们将在稍后章节中讨论像素着色器的附加选项。

汇编语言和编译对象

既然我们已经了解了一些HLSL着色器,这里简要讨论一下如何在代码中涉及Direct3D,D3DX,汇编着色器模型和你的程序。DirectX 8中第一次把着色器引入了Direct3D。在那个时候,这些虚拟着色器是这样定义的——每一个大致相当于一个特殊的3D硬件商生产的图形处理器。每个虚拟着色器都设计有汇编语言。在DirectX 8.0和DirectX 8.1中,编写这些着色器模型的程序(被命名为vs_1_1以及ps_1_1直到ps_1_4)相对短小并且一般由开发者直接用合适的汇编语言编写。如图 1左所示,凭借D3DXAssembleShader()程序把人们可读的汇编语言代码传递给D3DX库并返回着色器的二进制表示,该二进制表示由 CreatePixelShader()或CreateVertexShader()依次传递给Direct3D。更多传统汇编着色模型的细节,请参考在线和离线资源,包括Shader X 和DirectX SDK。

 

图1. Use of D3DX for Assembly and Compilation in DirectX 8 and DirectX 9

如图1右所示,在DirectX 9中的情形非常相似,凭借D3DXCompileShader() API,程序把HLSL着色器传递给D3DX并返回编译后着色器的二进制表示,该二进制表示由CreatePixelShader()或 CreateVertexShader()轮流传递给Direct3D。生成的二进制汇编代码是一个函数,它只取决于选择的编译对象,而不是什么用户或开发者系统上的特殊图形设备。就是说,生成的二进制汇编程序与平台无关,即可在任何地方编译或运行。事实上,Direct3D运行时本身并不知道HLSL的任何内容,除了二进制汇编着色器模型。这样做很有好处因为这就意味着 HLSL编译器的更新不必依赖于Direct3D运行时。事实上,在2003年夏季末本书截稿与首印期之间,Microsoft开始计划发布含有更新过的 HLSL编译器的DirectX SDK更新。

除了D3DX中HLSL编译器的开发之外,DirectX 9.0也提出了另外的汇编层着色器模型以展示最新的3D图形硬件的功能。直接使用汇编语言为新的模型(vs_2_0,vs_3_0,ps_2_0和ps_3_0) 做开发,程序开发人员会感到自由,不过我们希望绝大多数开发人员都转移到HLSL从而专注于着色器的开发。

实际的硬件

当然,仅仅因为你可以写一个HLSL程序来表达一个特殊的着色算法不等于它能够在硬件上运行。前面已经讨论过,应用程序通过调用D3DX中的 D3DXCompileShader() API把HLSL着色器编译成二进制汇编程序。这个API的入口参数之一是这样一个参数:它定义了HLSL编译器使用哪一个汇编语言模型(或编译对象)来表示最终着色器代码。如果一个程序在运行时执行HLSL着色器编译,程序会检测Direct3D设备的性能并选择匹配的编译对象。如果HLSL着色器中的算法太复杂以至于不能在选择的编译对象上执行,编译将会失败。这意味着尽管HLSL大大有利于着色器的开发,却不会把开发人员从这么一个现实中解放出来:把游戏封装后给拥有各种性能图形设备的用户。作为一个游戏开发人员,你仍然得为你的图像处理好一系列步骤,为更好的显示卡编写更好的着色器,为较老的卡编写更基本的。不过,有了编写完善的HLSL,负担可以大大减轻。

编译失败

如上所述, 给定的HLSL着色器编译特殊对象的失败说明对于编译对象来说着色器太过复杂。这就意味着着色器需要大量的资源或是需要一些诸如动态分支(不被所选编译对象所支持)的功能。例如,某个HLSL着色器可能被编写用于在一个着色器内存取所给定的六重纹理贴图。如果这个着色器被编译成ps_1_1, 编译将会失败,因为ps_1_1模型只支持四重纹理。其他编译失败的通常原因是超过了所选编译对象的最大指令计数器。某个HLSL中表示的算法也许仅仅需要大量指令而使得给定的编译对象不能被执行。

要重点注意的是所选编译对象不会限定编写人员所使用的HLSL语法。例如,着色器编写人员会使用'for'循环,子程序,'if-else'等等语句,编译本身不支持循换,分支或'if-else'语句的对象。这种情况下,编译器将展开循环,内联函数调用并同时执行'if-else'语句的两个分支(译者注:即if与else后的语句全都执行),根据'if-else'语句中所使用的原始值选择合适的结果。当然,如果最后所得到的着色器(程序)太长或相反超出了编译对象的资源,编译将失败。

命令行编译器: FXC

许多开发人员选择在着色器被封装之前把它从HLSL编译成二进制汇编语言,而不是在正在使用D3DX的客户机器上当程序载入时或首次运行时编译HLSL着色器。这保证了HLSL源代码不被窥视,同时也确保所有其程序能够永久运行的着色器已经通过其内部质量确认流程。在DirectX 9.0 SDK中提供了一个方便的命令行编译程序fxc允许开发人员脱机编译着色器。该程序有许多方便的选项,你不但可以以命令行方式编译你的着色器,也能产生指定编译对象的反汇编代码。如果你想优化你的着色器或只是想更详细的了解虚拟着色器的性能,在开发期间研究输出的反汇编代码是非常有用的。表1列出了这些命令行选项。

表1. FXC 命令行选项

<tbody>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-T </code><em>target</em></td>

        <td class="data" width="66%">编译对象 (默认: vs_2_0)</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-E </code><em>name</em></td>

        <td class="data" width="66%">入口点 <em>name </em>(默认: main)</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Od</code></td>

        <td class="data" width="66%">禁止优化</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Vd</code></td>

        <td class="data" width="66%">禁止确认</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Zi</code></td>

        <td class="data" width="66%">允许调试信息</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Zpr</code></td>

        <td class="data" width="66%">按照行顺序挑选矩阵</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Zpc</code></td>

        <td class="data" width="66%">按照列顺序挑选矩阵</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Fo </code><em>file</em></td>

        <td class="data" width="66%">输出目标文件</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Fc </code><em>file</em></td>

        <td class="data" width="66%">输出所生成代码的列表</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-Fh </code><em>file</em></td>

        <td class="data" width="66%">输出含有生成代码的头部</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-D </code><em>id=text</em></td>

        <td class="data" width="66%">定义宏</td>

    </tr>

    <tr valign="top">

        <td class="data" width="34%"><code class="ce">-nologo</code></td>

        <td class="data" width="66%">没有版权信息</td>

    </tr>

</tbody>

 

既然你了解了用于着色器开发的HLSL编译器的内容,我们就可以讨论实际的语言结构了。 在我们继续下面内容的时候,头脑里要一直保留着编译对象的概念以及潜在的汇编着色器模型的不同性能,这很重要。

NTSTATUS 

  KeWaitForSingleObject(

    IN PVOID  Object,

    IN KWAIT_REASON  WaitReason,

    IN KPROCESSOR_MODE  WaitMode,

    IN BOOLEAN  Alertable,

    IN PLARGE_INTEGER  Timeout  OPTIONAL   // Pointer to a time-out value that specifies the absolute or relative time, in 100 nanosecond units, 

    );



VOID 

  KeStallExecutionProcessor(

    IN ULONG  MicroSeconds                    // Specifies the number of microseconds to stall.

    );



BOOLEAN 

  KeSetTimer(

    IN PKTIMER  <font color=”#00ce00” ?>Timer,

    IN LARGE_INTEGER  <font color=”#00ce00” ?>DueTime, //

Specifies the absolute or relative time at which the timer is to expire. If the value of the DueTime parameter is negative, the expiration time is relative to the current system time. Otherwise, the expiration time is absolute. The expiration time is expressed in system time units (100-nanosecond intervals). Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

    IN PKDPC  <font color=”#00ce00” ?>Dpc  OPTIONAL

    );



为了重复地调用CustomTimerDpc例程,使用KeSetTimerEx来设置定时器,并在Period参数中规定一个再次发生的间隔。除了这个附加的参数,KeSetTimerEx就象KeSetTimer一样。

卡巴主动防御中检测隐藏进程的方法:

    Hook掉系统的SwapContext,这种方法是在2003年被提出的;这个函数被KiSwapThread调用,负责线程调度;下面这部分代码就是卡巴Detour SwapContext的汇编代码的分析(后面附上对应的c代码):


(PS:突破这种方式来隐藏进程的方法也在随后就出来了,自己实现线程调度,详见:http://hi-tech.nsys.by/33/)


一:KlifSetSwapContextHook


.text:0002DE60 KlifSetSwapContextHook proc near      

.text:0002DE60

.text:0002DE60 var_5C          = dword ptr -5Ch

.text:0002DE60 var_58          = dword ptr -58h

.text:0002DE60 var_54          = dword ptr -54h

.text:0002DE60 var_50          = dword ptr -50h

.text:0002DE60 var_48          = dword ptr -48h

.text:0002DE60 var_38          = dword ptr -38h

.text:0002DE60 var_34          = dword ptr -34h

.text:0002DE60 var_30          = word ptr -30h

.text:0002DE60 var_24          = dword ptr -24h

.text:0002DE60 var_20          = dword ptr -20h

.text:0002DE60 var_1C          = dword ptr -1Ch

.text:0002DE60 var_18          = dword ptr -18h

.text:0002DE60 var_10          = dword ptr -10h

.text:0002DE60 var_4           = dword ptr -4

.text:0002DE60

.text:0002DE60                 push    ebp

.text:0002DE61                 mov     ebp, esp

.text:0002DE63                 push    0FFFFFFFFh

.text:0002DE65                 push    offset dword_13158

.text:0002DE6A                 push    offset sub_2E46C

.text:0002DE6F                 mov     eax, large fs:0

.text:0002DE75                 push    eax

.text:0002DE76                 mov     large fs:0, esp

.text:0002DE7D                 sub     esp, 4Ch

.text:0002DE80                 push    ebx

.text:0002DE81                 push    esi

.text:0002DE82                 push    edi

.text:0002DE83                 mov     [ebp+var_18], esp

.text:0002DE86                 mov     ecx, g_NtoskrnlAddr

.text:0002DE8C                 cmp     word ptr [ecx], 5A4Dh ; Ms dos stub - MZ

.text:0002DE91                 jnz     Routine_Error

.text:0002DE97                 mov     eax, [ecx+3Ch]  ; e_lfanew

.text:0002DE9A                 add     eax, ecx

.text:0002DE9C                 cmp     dword ptr [eax], 4550h ; PE

.text:0002DEA2                 jnz     Routine_Error

.text:0002DEA8                 xor     edx, edx

.text:0002DEAA                 mov     dx, [eax+14h]      ; SizeOfOptionalHeader

.text:0002DEAE                 lea     eax, [edx+eax+18h] ; 指向第一Section(.text)

.text:0002DEB2                 mov     esi, [eax+0Ch]

.text:0002DEB5                 add     esi, ecx           ;.text区的内存地址

.text:0002DEB7                 mov     eax, [eax+10h]     ;.text区的内存大小

.text:0002DEBA                 xor     ebx, ebx

.text:0002DEBC                 mov     [ebp+var_20], ebx

.text:0002DEBF                 mov     [ebp+var_4], ebx

.text:0002DEC2                 mov     ecx, ds:NtBuildNumber

.text:0002DEC8                 mov     cx, [ecx]

.text:0002DECB                 cmp     cx, 2600        ; 1575 - 2195 Win2K

.text:0002DECB                                         ; 2202 - 2600 WinXP

.text:0002DECB                                         ; 3501 - 3790 Win2003

.text:0002DED0                 jle     Is2kORxp

.text:0002DED6                 mov     [ebp+var_1C], 5

.text:0002DEDD                 cmp     cx, 3790

.text:0002DEE2                 jl      short Is2003Release

.text:0002DEE4                 mov     ecx, offset g_2003CharCode ; 2003的特征码

.text:0002DEE9                 mov     [ebp+var_58], ecx

.text:0002DEEC                 mov     edx, 9

.text:0002DEF1                 mov     [ebp+var_54], edx

.text:0002DEF4                 xor     edi, edi

.text:0002DEF6                 jmp     short Is2003

.text:0002DEF8

.text:0002DEF8 Is2003Release:

.text:0002DEF8                 cmp     cx, 3604

.text:0002DEFD                 jle     short Is2003Beta3

.text:0002DEFF                 mov     ecx, offset g_ReleaseCharCode ; 2003Beat3-Release的特征码

.text:0002DF04                 mov     [ebp+var_58], ecx

.text:0002DF07                 mov     edx, 8

.text:0002DF0C                 mov     [ebp+var_54], edx

.text:0002DF0F                 mov     edi, 0Ah

.text:0002DF14                 jmp     short Is2003

.text:0002DF16

.text:0002DF16 Is2003Beta3:

.text:0002DF16                 mov     ecx, offset g_Bete3CharCode ; 2003的Beta-Beta3特征码

.text:0002DF1B                 mov     [ebp+var_58], ecx

.text:0002DF1E                 mov     edx, 7

.text:0002DF23                 mov     [ebp+var_54], edx

.text:0002DF26                 mov     edi, 9

.text:0002DF2B

.text:0002DF2B Is2003:

.text:0002DF2B                 mov     [ebp+var_50], edi

.text:0002DF2E                 push    edx

.text:0002DF2F                 push    ecx

.text:0002DF30                 push    eax

.text:0002DF31                 push    esi

.text:0002DF32                 call    KilfGetAddrByCharCode ; arg_c 特征码的长度

.text:0002DF32                                               ; arg_8 特征码的地址

.text:0002DF32                                               ; arg_4 搜索的最大范围

.text:0002DF32                                               ; arg_0 搜索的起始地址

.text:0002DF37                 mov     [ebp+var_24], eax

.text:0002DF3A                 cmp     eax, 0FFFFFFFFh

.text:0002DF3D                 jz      Search_Failed

.text:0002DF43                 add     eax, edi

.text:0002DF45                 add     esi, eax

.text:0002DF47                 cmp     dword ptr [esi], 0FF1043FFh

.text:0002DF4D                 jnz     short Search_Failed

.text:0002DF4F                 mov     ebx, esi

.text:0002DF51                 mov     [ebp+var_20], ebx

.text:0002DF54                 mov     [ebp+var_4], 0FFFFFFFFh

.text:0002DF5B                 jmp     SetSwapContextHook

.text:0002DF60

.text:0002DF60 Is2kORxp:

.text:0002DF60                 push    5

.text:0002DF62                 push    offset g_2kCharCode ; 2k的特征码

.text:0002DF67                 push    eax

.text:0002DF68                 push    esi

.text:0002DF69                 call    KilfGetAddrByCharCode ; arg_c 特征码的长度

.text:0002DF69                                               ; arg_8 特征码的地址

.text:0002DF69                                               ; arg_4 搜索的最大范围

.text:0002DF69                                               ; arg_0 搜索的起始地址

.text:0002DF6E                 mov     [ebp+var_24], eax

.text:0002DF71                 cmp     eax, 0FFFFFFFFh

.text:0002DF74                 jz      short Search_Failed

.text:0002DF76                 mov     edx, ds:NtBuildNumber

.text:0002DF7C                 cmp     word ptr [edx], 2195

.text:0002DF81                 jle     short Is2k

.text:0002DF83

.text:0002DF83 Isxp:

.text:0002DF83                 add     esi, eax

.text:0002DF85                 mov     [ebp+var_5C], esi

.text:0002DF88                 mov     [ebp+var_1C], 7

.text:0002DF8F                 push    4

.text:0002DF91                 push    offset g_XpCharCode ; xp的特征码

.text:0002DF96                 push    100h

.text:0002DF9B                 push    esi

.text:0002DF9C                 call    KilfGetAddrByCharCode ; arg_c 特征码的长度

.text:0002DF9C                                               ; arg_8 特征码的地址

.text:0002DF9C                                               ; arg_4 搜索的最大范围

.text:0002DF9C                                               ; arg_0 搜索的起始地址

.text:0002DFA1                 mov     [ebp+var_24], eax

.text:0002DFA4                 cmp     eax, 0FFFFFFFFh

.text:0002DFA7                 jz      short Search_Failed

.text:0002DFA9                 lea     ebx, [esi+eax+2]

.text:0002DFAD                 mov     [ebp+var_20], ebx

.text:0002DFB0                 mov     [ebp+var_4], 0FFFFFFFFh

.text:0002DFB7                 jmp     short SetSwapContextHook

.text:0002DFB9

.text:0002DFB9 Is2k:

.text:0002DFB9                 lea     ebx, [eax+esi]

.text:0002DFBC                 mov     [ebp+var_20], ebx

.text:0002DFBF                 mov     [ebp+var_1C], 5

.text:0002DFC6

.text:0002DFC6 Search_Failed:

.text:0002DFC6                 mov     [ebp+var_4], 0FFFFFFFFh

.text:0002DFCD                 jmp     short SetSwapContextHook

.text:0002DFCF

.text:0002DFCF SEH_Routine:    

.text:0002DFCF                 mov     eax, 1

.text:0002DFD4                 retn

.text:0002DFD5

.text:0002DFD5 SEH_Routine2:      

.text:0002DFD5                 mov     esp, [ebp-18h]

.text:0002DFD8                 mov     dword ptr [ebp-4], 0FFFFFFFFh

.text:0002DFDF                 mov     ebx, [ebp-20h]

.text:0002DFE2

.text:0002DFE2 SetSwapContextHook:

.text:0002DFE2                 test    ebx, ebx

.text:0002DFE4                 jz      Routine_Error

.text:0002DFEA                 mov     eax, 90909090h

.text:0002DFEF                 mov     [ebp+var_38], eax

.text:0002DFF2                 mov     [ebp+var_34], eax

.text:0002DFF5                 mov     [ebp+var_30], ax

.text:0002DFF9                 mov     byte ptr [ebp+var_38], 0E9h ; JMP

.text:0002DFFD                 mov     ecx, offset KlifSwapContext

.text:0002E002                 sub     ecx, ebx        ; EBX是SwapContext+offset的地址

.text:0002E004                 sub     ecx, 5

.text:0002E007                 mov     [ebp+var_38+1], ecx

.text:0002E00A

.text:0002E00A Klif_Store_SC_Addr:                     ; 保存原来的指令

.text:0002E00A                 mov     ecx, [ebp+var_1C]

.text:0002E00D                 lea     edx, [ecx+ebx]

.text:0002E010                 mov     g_SwapContextAddr, edx

.text:0002E016                 mov     esi, ebx

.text:0002E018                 mov     edi, offset g_SwapContextOpcode

.text:0002E01D                 mov     eax, ecx

.text:0002E01F                 shr     ecx, 2

.text:0002E022                 rep movsd

.text:0002E024                 mov     ecx, eax

.text:0002E026                 and     ecx, 3

.text:0002E029                 rep movsb

.text:0002E02B                 lea     ecx, [ebp+var_48]

.text:0002E02E                 push    ecx

.text:0002E02F                 push    1

.text:0002E031                 push    ebx

.text:0002E032                 call    KlifSetInterruptStauts

.text:0002E037                 test    al, al

.text:0002E039                 jz      short Routine_Error

.text:0002E03B                 mov     ecx, offset g_kernelLock

.text:0002E040                 call    KlifClearInterruptAndLock

.text:0002E045

.text:0002E045 Klif_Set_SC_Hook:                         ; detour开始

.text:0002E045                 mov     ecx, [ebp+var_1C] ; 覆盖的Opcodes Num

.text:0002E048                 lea     esi, [ebp+var_38] ; Jmp Klif+0x????

.text:0002E04B                 mov     edi, ebx

.text:0002E04D                 mov     edx, ecx

.text:0002E04F                 shr     ecx, 2

.text:0002E052                 rep movsd

.text:0002E054                 mov     ecx, edx

.text:0002E056                 and     ecx, 3

.text:0002E059                 rep movsb

.text:0002E05B                 mov     edx, eax

.text:0002E05D                 mov     ecx, offset g_kernelLock

.text:0002E062                 call    KlifUnLock

.text:0002E067                 lea     eax, [ebp+var_48]

.text:0002E06A                 push    eax

.text:0002E06B                 mov     ecx, [ebp+var_48]

.text:0002E06E                 push    ecx

.text:0002E06F                 push    ebx

.text:0002E070                 call    KlifSetInterruptStauts

.text:0002E075                 mov     al, 1

.text:0002E077                 mov     ecx, [ebp+var_10]

.text:0002E07A                 mov     large fs:0, ecx

.text:0002E081                 pop     edi

.text:0002E082                 pop     esi

.text:0002E083                 pop     ebx

.text:0002E084                 mov     esp, ebp

.text:0002E086                 pop     ebp

.text:0002E087                 retn

.text:0002E088 Routine_Error:                         

.text:0002E088                 xor     al, al

.text:0002E08A                 mov     ecx, [ebp+var_10]

.text:0002E08D                 mov     large fs:0, ecx

.text:0002E094                 pop     edi

.text:0002E095                 pop     esi

.text:0002E096                 pop     ebx

.text:0002E097                 mov     esp, ebp

.text:0002E099                 pop     ebp

.text:0002E09A                 retn

.text:0002E09A KlifSetSwapContextHook endp



二:KlifSwapContext


.text:0002DE10 KlifSwapContext:                      

.text:0002DE10                 pushf

.text:0002DE11                 pusha

.text:0002DE12                 mov     ebp, esp

.text:0002DE14                 sub     esp, 10h

.text:0002DE17                 add     esi, dword_2FC90

.text:0002DE1D                 add     edi, dword_2FC90

.text:0002DE23                 mov     eax, [esi]      ; Address of next thread

.text:0002DE25                 mov     [ebp-8], eax

.text:0002DE28                 mov     eax, [edi]      ; Address of previous thread

.text:0002DE2A                 mov     [ebp-0Ch], eax

.text:0002DE2D                 mov     ecx, offset g_kernelLock

.text:0002DE32                 call    KlifClearInterruptAndLock

.text:0002DE37                 mov     [ebp-4], eax

.text:0002DE3A                 mov     eax, [ebp-8]

.text:0002DE3D                 push    eax             ; Address of next thread

.text:0002DE3E                 call    KlifSwapContextMain ;//MAIN!!!

.text:0002DE43                 mov     edx, [ebp-4]

.text:0002DE46                 mov     ecx, offset g_kernelLock

.text:0002DE4B                 call    KlifUnLock

.text:0002DE50                 add     esp, 10h

.text:0002DE53                 popa

.text:0002DE54                 popf

.text:0002DE55                 jmp     g_OldSwapContext

.text:0002DE5B                 align 10h



三:KlifSetSwapContextHook 对应C代码


PVOID g_NtoskrnlAddr;

char  g_2003CharCode[8]    = {0xFF, 0x43, 0x10, 0xFF, 0x33, 0x83, 0x7B, 0x08};

char  g_ReleaseCharCode[8] = {0x80, 0x7E, 0x5D, 0x00, 0x74, 0x04, 0xF3, 0x90};

char  g_Bete3CharCode[8]   = {0xF7, 0x46, 0x24, 0x01, 0x00, 0x00, 0x00, 0x00};

char  g_2kCharCode[8]      = {0x26, 0xC6, 0x46, 0x2D, 0x02, 0x00, 0x00, 0x00};

char  g_xpCharCode[4]      = {0x8B, 0x0B, 0x83, 0xBB};

int   g_SwapContextOpcode[4] = {0x90909090, 0x90909090, 0x9090FF25, g_SwapContextAddr};

int   g_SwapContextAddr;

char  g_kernelLock;

BOOL KlifSetSwapContextHook()

{

 METUEX ProtectMutex;        //这个不大准确

 int    nOffset          = 0;//特征码距Ntoskrnl的偏移移

 int    nReplaceNum      = 0;//要替换SwapContext几个字节

 char*  pSwapContextAddr = 0;//要替换的地址

 

 int   nCompareNum  = 0;

 char* pCompareCode = 0;

 

 PIMAGE_DOS_HEADER     pImageDos =  (PIMAGE_DOS_HEADER)g_NtoskrnlAddr;

 PIMAGE_NT_HEADERS     pImageNt  =  (PIMAGE_NT_HEADERS)(g_NtoskrnlAddr+pImageDos->e_lfanew);

 PIMAGE_SECTION_HEADER pImageSec =  (PIMAGE_SECTION_HEADER )((char*)pImageNt+pImageNt->FileHeader.SizeOfOptionalHeader);

 

 char* pSearchAddr = (char*)g_NtoskrnlAddr + pImageSec->VirtualAddress;

 int   nSearchArea = pImageSection->SizeOfRawData;

 

 if ((WORD)g_NtoskrnlAddr               == 0x5A4D &&

     (WORD)((char*)g_NtoskrnlAddr+0x3C) == 0x4550)

 {

  return FALSE;

 }

 

 //Windows 2003

 if (*NtBuildNumber > 2600)

 {

  nReplaceNum = 5;

  int nOrgOffset = 0;

  if (*NtBuildNumber >= 3790)

  {

   nCompareNum = 9;

   pCompareCode = g_2003CharCode;

   

  }else

  {

   if (*NtBuildNumber > 3604)

   {

    nOrgOffset   = 10;

    nCompareNum  = 8;

    pCompareCode = g_ReleaseCharCode;

   }else

   {

    nOrgOffset   = 9;

    nCompareNum  = 7;

    pCompareCode = g_Bete3CharCode;

     

   }

  }

  

  nOffset = KilfGetAddrByCharCode(nCompareNum, 

                                  pCompareCode,

                                  nSearchArea,

                                  pSearchAddr);

  if (nOffset == -1)

  {

   return FALSE;

  }

  

  pSwapContextAddr = pSearchAddr + nOffset + nOrgOffset;

  

  if (((int)pSwapContextAddr) != 0xFF1043FF)

  {

   return FALSE;

  }

  

 }else

 {

  //Win2K

  nOffset = KilfGetAddrByCharCode(5,

                                  g_2kCharCode,

                                  nSearchArea,

                                  pSearchAddr);

  if (nOffset == -1)

  {

   return FALSE;

  }

  

  //WinXP

  if (*NtBuildNumber > 2395)

  {

   nOffset = KilfGetAddrByCharCode(4,

                                   g_xpCharCode,

                                   nSearchArea,

                                   pSearchAddr);   

   if (nOffset == -1)

   {

    return FALSE;

   }

   nReplaceNum      = 7;

   pSwapContextAddr = pSearchAddr + nOffset + 2;

  }else

  {

   nReplaceNum      = 5;

   pSwapContextAddr = pSearchAddr + nOffset;

  }

 }

 if (!pSwapContextAddr)

 {

  return FALSE;

 }

 char NewSwapContext[9] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};

 NewSwapContext[0]           = 0xE9;

 ((int)&NewSwapContext[1]) = (int)KlifSwapContext - (int)pSwapContextAddr - 5;


 //保存原来的指令

 g_SwapContextAddr = pSwapContextAddr + nReplaceNum;//跳回来的地址

 for(int i = nReplaceNum; i>0; i--)

 {

  g_SwapContextOpcode[i] = pSwapContextAddr[i];

 }

 

 if (!KlifSetInterruptStauts(pSwapContextAddr, 1, &ProtectMutex))

 {

  return FALSE;

 }


 KlifClearInterruptAndLock(g_kernelLock);


 //覆盖原来指令

 for(i = nReplaceNum; i>0; i–)

 {

  pSwapContextAddr[i] = NewSwapContext[i];

 }

 KlifUnLock(g_kernelLock);

 KlifSetInterruptStauts(pSwapContextAddr, ProtectMutex, &ProtectMutex);

 return TRUE;

}


阐述中存在错误,望指教,谢谢!转载请注明地址 :)



转载于:http://blog.vckbase.com/windowssky

去年年底帮别人做一个项目,了解了一下TCPMP,觉得这个软件的结构写得很好就做了些记录,今天偶然翻出来看看觉得可能对正在研究这个软件的人有点帮助就贴出来。如果转载请注明出处,谢谢。

TCPMP是一个功能强大开放式的开源多媒体播放器,
播放器主要由核心框架模块(common工程)和解码器分离器插件组成。
TCPMP
的插件非常多,、libmad我们联合几个最常用的插件(ffmpegsplitter)来说明,其中interface插件实现TCPMP的界面,由于他和媒体播放没有什么关系,这部分可以完全被替换掉,替换成自己的界面。
ffmpeg
工程是系统主要的音视频解码模块,ffmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。ffmpeg支持MPEGDivXMPEG4AC3DVFLV40多种编码,AVIMPEGOGGMatroskaASF90多种解码。很多开源播放器都用到了ffmpeg。但是ffmpeg程序解码效率不是很高,系统仅仅使用了FFmpeg的部分解码功能。
ffmpeg
主目录下主要有libavcodeclibavformatlibavutil等子目录。其中libavcodec用于存放各个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于存放内存操作等常用模块。本系统的媒体文件分离器有单独的splitter模块完成所以不需要libavformat子目录。ffmpeg目录下libavcodeclibavutil保留子目录。

libmad工程用于MP3文件解码,该工程包含两个功能模块,一个负责解析MP3文件格式,包括MPEG1音频文件 (MP1,MP2,MP3,MPA),读取每一帧音频数据;另一个负责解码MPEG1音频数据,解码代码在libmad子目录中。
libmad
是一个开源的高精度 MPEG1音频解码库,支持 MPEG-1Layer I, Layer II LayerIII,也就是 MP3)。libmad提供 24-bit PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。libmad是用的fixed-integer,通过整数模拟小数计算的,精度只能保证到小数点后第9位(大于0的最小值 0.00000000372529),虽然解码精度会有损失,但是极大提高了解码效率,特别是在嵌入式设备上也可以实现高码率MP3文件的解码。

splitter工程用于解析多种音视频文件格式。可以解析的文件格式包括:ASF媒体文件,视频文件 (AVI,DIVX)Windows波形文件 (WAV,RMP)MPEG电影文件 (MPEG,MPG,MPV)MPEG4文件 (MP4,3GP,M4A,M4B,K3G)。以上格式可以被解析但是数据编码不一定能正确解码,需要依赖系统的解码器。

common工程是核心模块,是一个开放的集数据输入、转换、音/视频解码、信号输出等功能为一体的完整的多媒体播放框架。这个框架自身不包含任何的DecodeSplit功能,这些功能由插件实现,核心模块以一个树状结构管理所有的功能模块和插件模块,实现数据Render功能,对输入、转换、输出流程的控制,接受播放过程中的操作和对事件进行处理,同时也实现系统运行中经常使用的一些共用函数,比如解码过程中经常使用的逆离散余弦变换,内存操作,界面中需要使用的多语言字符处理等。common工程的主目录下主要有:blitdyncodeoverlaypcmsoftidctwin32zlib等子目录。其中blitoverlay存放是视频信号渲染模块,pcm存放PCM音频信号转换模块,softidct存放逆离散余弦变换函数,win32存放内存操作等常用模块,dyncode这个目录的代码比较晦涩,存放的是程序运行是动态生成代码模块,针对不同的CPU指令集,PCM数据数据声道和采样率不同,视频渲染数据格式和色深等不同情况动态生成不同的优化代码(这段代码非常精彩,不能不让人佩服TCPMP作者的高超水平)。核心模块有一个上下文对象context,该对象在初始化函数bool_t Context_Init(……)中候创建了一个该对象实例。该对象实例记录管理各个功能模块,用户界面可以通过该对象和核心模块交互,管理控制播放过程。
 Context
对象说明:
typedef struct context
{
 int Version; //
版本信息
 uint32_t ProgramId;
 const tchar_t* ProgramName;  //
应用程序名称
 const tchar_t* ProgramVersion; //
程序版本号,字符串
 const tchar_t* CmdLine;   //
程序命令行信息
 void* Wnd;   //
视频渲染窗口句柄
 void* NodeLock;  //
功能模块访问临界区互斥变量
 array Node;   //
功能模块数据对象数组
 array NodeClass;  //
功能模块定义对象数组,按照系统逻辑关系组织
 array NodeClassPri; //
功能模块定义对象数组,按照系统逻辑关系和模块优先级排列
 array NodeModule;  //
外部插件模块数组
 int LoadModuleNo;  //
当前正在装载的外部插件序号
 void* LoadModule;
 array StrTable[2];  //
字符串资源数组,字符串分为
      //
给底层使用的标准字符串资源和
      //
给界面使用的显示字符串资源,两种资源用两个数组表示
 array StrBuffer;
 array StrModule;  //
未使用
 void* StrLock;  //
字符串数组访问临界区互斥变量
 uint32_t Lang;   //
当前使用语言标志
 int CodePage;   //
当前使用代码页标志
 struct pcm_soft* PCM; //PCM
音频信号转换模块
 struct blitpack* Blit; //
视频信号渲染模块
 struct node* Platform; //
得到平台相关信息
 struct node* Advanced; //
得到播放模块高级信息
 struct node* Player;  //
播放控制模块
 notify Error;   //
信息错误回调函数
 //
屏幕旋转信息,在某些系统中屏幕可以旋转90度或180
 int (HwOrientation)(void);
 void HwOrientationContext;
 bool_t TryDynamic;  //
未使用
 int SettingsPage;   //
未使用
 size_t StartUpMemory;  //
可以使用的有效内存数
 bool_t InHibernate;   //
是否进入休眠状态
 bool_t WaitDisable;   //
未使用
 int FtrId;     //
未使用
 bool_t LowMemory;  //
可以使用的有效内存数是否小于系统要求的最低要求
 //
动态代码生成中间状态及数据
 bool_t CodeFailed;
 bool_t CodeMoveBack;
 bool_t CodeDelaySlot;
 void
CodeLock;
 void* CodeInstBegin;
 void* CodeInstEnd;
 int NextCond;
 bool_t NextSet;
 bool_t NextByte;
 bool_t NextHalf;
 bool_t NextSign;
 
 uint32_t* FlushCache;  //
未使用
 void* CharConvertUTF8; //
未使用
 void* CharConvertCustom; //
未使用
 int CustomCodePage;  //
未使用
 void* CharConvertAscii; //
未使用
 void* Application;
 void* Logger;    //
未使用
 bool_t KeepDisplay;  //
是否保持背光长亮
 int DisableOutOfMemory; //
未使用

} context;
核心模块上下文指针可以通过全局函数获得context* Context();
初始化上下文对象的全局函数是bool_t Context_Init(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine,void* Application);其中Name参数为应用程序名称,Version为版本信息字符串。
释放上下文对象的全局函数是void Context_Done();
void Context_Wnd(void*);
函数将视频播放窗口句柄初始化给设备上下文。

功能模块包含定义对象和数据对象,定义对象描述功能模块相互间的逻辑结构,数据对象记录模块属性和方法。
所有的功能模块结构按一个树状结构来组织,结构关系如下,NODE是整个结构的根结点,其下为子节点,节点按类型可分为实节点,全局节点,设置节点,抽象节点。
#define CF_SIZE   0x00FFFFFF
#define CF_GLOBAL  0x01000000
#define CF_SETTINGS  0x02000000
#define CF_ABSTRACT  0x08000000
抽象节点没有对应的对象实例,类似C++的抽象基类,为了按照逻辑关系组织系统结构而存在,例如NODE就是抽象节点。全局节点全局只有一个对象的实例,如播放控制模块PLAYER_ID。设置节点表示和系统播放设置相关,比如声音均衡器模块EQUALIZER_ID,颜色控制模块COLOR_ID。实节点与抽象节点不同,指可以生成对象实例的节点,实节点没有特殊标识,一般以数据对象占用内存大小表示是否是一个实节点,创建节点时要根据该信息分配内存单元,实节点也可以有子节点,例如:MMS_ID的父节点是HTTP_ID。全局节点,设置节点和实节点可以相互组合,比如播放控制节点同时是全局节点,设置节点和实节点。节点名称后带_ID的就是实节点,否则就是抽象节点。

    NODE (根节点)
   
├─FLOW (流控制模块)
   
  ├─CODEC (解码模块)
   
    ├─EQUALIZER_ID (声音均衡器模块)
   
    ├─VBUFFER_ID (视频缓冲模块)
   
    ├─DMO DirectX Media Object
   
      ├─WMV_ID
   
      ├─WMS_ID
   
      ├─WMVA_ID
   
      ├─WMA_ID
   
      └─WMAV_ID
   
    ├─FFMPEG VIDEO FFMpeg 解码模块)
   
    └─LIBMAD_ID Libmad Mp3解码模块)
   
  ├─OUT (信号渲染模块)
   
    ├─AOUT (音频信号渲染)
   
      ├─NULLAUDIO_ID
   
      └─WAVEOUT_ID
   
    └─VOUT (视频信号渲染)
   
        ├─NULLVIDEO_ID
   
        └─OVERLAY
   
  ├─IDCT (离散余弦解码模块)
   
    └─SOFTIDCT_ID
   
  └─CODECIDCT(离散余弦解码模块,函数比IDCT要少)
   
      └─MPEG1_ID
   
├─MEDIA (媒体文件格式编码解析模块)
   
  ├─FORMAT (格式解析模块)
   
    └─FORMATBASE
   
        ├─RAWAUDIO
   
          └─MP3_ID
   
        ├─RAWIMAGE
   
        ├─ASF_ID
   
        ├─AVI_ID
   
        ├─MP4_ID
   
        ├─MPG_ID
   
        ├─NSV_ID
   
        └─WAV_ID
   
  ├─PLAYLIST (播放列表模块)
   
    ├─ASX_ID
   
    ├─M3U_ID
   
    └─PLS_ID
   
  └─STREAMPROCESS (数据流处理模块)
   
├─STREAM (数据输入模块)
   
  ├─MEMSTREAM_ID (内存数据流模块)
   
  ├─FILE_ID (文件IO模块)
   
  └─HTTP_ID (网络数据获取模块)
   
├─TIMER (定时器模块)
   
  └─SYSTIMER_ID
   
├─ASSOCIATION_ID (文件扩展名自动关联模块)
   
├─ADVANCED_ID (高级设置模块)
   
├─COLOR_ID (颜色控制模块)
   
├─PLATFORM_ID (平台信息模块)
   
├─XSCALEDRIVER_ID Intel XScale CPU 信息模块)
   
├─PLAYER_ID (播放控制模块)
   
└─PLAYER_BUFFER_ID (播放缓冲模块)
   
   
节点树状结构由若干个静态定义对象(nodedef)实例实现,
        typedef struct nodedef
  {
   int    Flags;
   int    Class;
   int    ParentClass;
   int    Priority;
   nodecreate  Create;
   nodedelete  Delete;
  } nodedef;
 Flags
表示当前节点的类型:抽象、实节点、全局、设置。
 Class
表示当前节点的标识,如MEDIA_CLASSASF_ID等等。
 ParentClass
表示当前节点的父节点标识,如SYSTIMER_ID对象的父节点是TIMER_CLASS
 Priority
表示当前节点优先级。
 Create
Delete是两个函数指针,表示该节点的创建函数和销毁函数。
 
如播放控制模块的结构定义是
 static const nodedef Player =
 {
  sizeof(player_base)|CF_GLOBAL|CF_SETTINGS,
  PLAYER_ID,
  NODE_CLASS,
  PRI_MAXIMUM+600,
  (nodecreate)Create,
  (nodedelete)Delete,
 };
 
 
绝大多数节点都有一个对应的数据对象,记录该节点的数据和方法,每一个子节点对象都是以父节点对象作为该节点一个元素,类似C++的封装继承机制。如果子节点的父节点没有数据对象,该节点可以从node节点直接继承。每一个节点都可以看成Node节点的直接或间接子节点,所以所有节点头以一个相同的node结构开头,子节点可能还有自己的属性,在继承父对象后就是子节点自己的元素。
    typedef struct node
 {
  int   Class;
  nodeenum Enum;
  nodeget  Get;
  nodeset  Set;
 } node;
 Class
表示该对象的标识,如PLAYER_ID
 Enum
是一个函数指针,指向一个函数用于枚举当前节点的属性。
 Get
是一个函数指针,得到当前节点某一属性值。
 Set
是一个函数指针,设置当前节点的某一属性数值。
 
 
节点的属性值数据特性在一个static const datatable xxxParams[] = {……};的静态数组里定义。
 typedef struct datatable
 {
  int No;
  int Type;
  int Flags;
  int Format1;
  int Format2;
 } datatable;
 
 No
表示属性的标识,如播放控制模块的#define PLAYER_PLAY 0x32 就表示控制播放器播放或暂停。
 Type
表示属性的数据类型,可用值在node.h中定义。
 Flags
是属性数据的标志,表示该数据是不是只读数据,是否有最大最小值等等,可用值在node.h中定义,如果该标志包含DF_SETUP同时不包含DF_NOSAVEDF_RDONLY属性,该属性会被记录在注册表中,下次启动时用注册表的数据初始化该属性。
 Format1
Format2是可选标志与Flags配合使用,比如如果Flags表示该属性存在最大最小值,Format1Format2可以分别表示最小和最大数值。
 
 
在在系统上下文对象中有两个元素记录节点信息array Node;array NodeClass;array是数组数据类型,Node是节点数据对象的数组,NodeClass节点对象的数组,按照系统逻辑关系组织。
 
创建节点时传入nodedef对象到节点创建函数,函数会根据nodedef信息生成对应nodeclass对象添加到NodeClass数组,同时根据nodedef信息分配数据对象的内存空间。在该节点的Create函数里面再初始化该节点的数据对象。

在所有功能模块中和界面加交互的主要就是播放控制模块struct node* Player;使用方法如下:
context* p = Context();
player* myplayer = NULL;
if(p) myplayer = (player*)(p->Player);
控制播放参数使用Set(void* This,int No,const void* Data,int Size);函数,第一个参数是播放模块指针,第二个参数是控制代码,即要进行什么操作,第三个参数是需要赋值给控制代码的数值,最后一个参数是所赋数值的占用内存的大小。
例如开始播放的代码是:
myplayer->Set(myplayer,PLAYER_PLAY,1,sizeof(int));
PLAYER_PLAY
为控制代码,表示当前控制的是播放暂停功能,数值为1表示播放为0表示暂停。
得到某一控制属性使用Get(void* This,int No,void* Data,int Size);函数,参数含义和Set函数相同。
控制代码是一组宏,定义在player.h文件中。比较重要的控制参数有
// play or pause (bool_t)
#define PLAYER_PLAY   0x32
// position in fraction (fraction)
#define PLAYER_PERCENT  0x25
// position in time (tick_t)
#define PLAYER_POSITION  0x28
// current format (format*)
#define PLAYER_FORMAT  0x2B
// current file in playlist (int)
#define PLAYER_LIST_CURRENT 0x2F
// current file index (suffled) in playlist (int)
#define PLAYER_LIST_CURRIDX 0xA2
// fullscreen mode (bool_t)
#define PLAYER_FULLSCREEN 0x3E
// stop
#define PLAYER_STOP   0xB2
// skin viewport rectangle (rect)
#define PLAYER_SKIN_VIEWPORT 0x3C
播放控制模块所有可用参数见static const datatable PlayerParams[]结构。

添加一个媒体文件到播放模块使用int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title);
第一个参数为播放模块指针,第二个参数是添加到播放模块文件队列的序号,如果是使文件成为第一个文件该参数设为0,第三个参数是媒体文件的目录和名称,第四个参数为媒体文件标题,该参数可以忽略。

核心模块也管理多语言字符串,使用函数const tchar_t* LangStr(int Class, int Id);const tchar_t* LangStrDef(int Class, int Id)可以得到对应字符串,系统字符串资源有两种,标准字符串和特殊字符集字符串。标准字符串资源文件是工程目录下的lang_std.txt文件,该文件字符串为ASCII字符,可与其他代码页字符兼容。该文件记录的是核心模块运行时需要使用的字符串,DecodeSplite模块可以处理的编码格式和文件格式也在这个文件中记录,例如lang_std.txt文件中的
MP3_0001=audio/mpeg
MP3_0002=mp1:A;mp2:A;mp3:A;mpa:A
MP3_0200=acodec/0x0055
纪录了MP3文件分离器对应的文件类型、扩展名和文件特征码。
要得到标准字符串使用函数LangStrDef,第一个参数表示字符类别,第二个参数表示字符ID。界面相关的是特殊字符集的字符串,使用函数LangStr,第一个参数表示字符类别,第二个参数表示字符ID。关于字符串资源文件结构含义将在以后的文档中说明。http://blog.csdn.net/navi_dx/archive/2007/11/14/1885780.aspx

“熊猫烧香”余波未了,“灰鸽子”又飞进了人们的视野。

  在这个领域,互联网“险情”不断。

  ——网络世界,这个表面看起来风平浪静的生态,背后无时不在发生着弱肉强食的事件,就像森林里生物链一样默默发生着变化,而外边望去却一片平静。

  商业正在改变着这个领域的生态。这个领域的生态也在改写着现实世界的商业秩序——利益的驱动,使黑客一词的含义正在悄悄地酝酿着嬗变:这些黑客们不仅创富于虚拟世界,同时也被现实的商战所利用。

  一线调查

  黑客自揭“黑色产业”链条

  关闭你电脑所有的程序(包括应用程序和操作系统内部程序),然后再联上网络,有没有发现ADSL猫的几只“绿眼睛”在不停地眨眼,电脑网线接口处的信号灯也在配合着不停地闪烁。

  如果是,你的电脑已经染上病毒,正在为别人工作——这样的电脑被黑客称之为“肉鸡”(即被黑客控制的电脑)。

  黑客控制着你的电脑并发出小小的数据包,内容可能包含着电脑中的机密,也可能指令你的电脑向其他电脑继续传播病毒。

  在中国,有上百万的网民如你一般,毫无察觉地为网络黑色产业链无偿地“贡献着力量”。

  2007年3月,被认为互联网病毒业“标志性建筑”的“灰鸽子”工作室表示,将停止研发“灰鸽子”病毒。但是由于“灰鸽子”源代码已经在网络上广为流传,“灰鸽子”的余威依旧将在互联网上肆虐。在“灰鸽子”和“熊猫烧香”之后,互联网黑色资金链条并没有发生改变。

  创富链条

  “熊猫烧香”的覆灭并没有危及黑色利益链的运转。

  “做病毒一定要低调。”

  黑客肖嘲笑着“熊猫烧香”的英雄主义,“圈内都知道,设计病毒忌讳带图标,像‘熊猫烧香’那样,形成了品牌,最终只能惹火烧身。”

  黑客肖今年30岁,在程序设计领域已经堪称“大师”级的人物,被称为“造枪人”,他可以为买家们制造出各种各样病毒——偷盗账户的、捕获“肉鸡”的、发送垃圾邮件的、发送广告的……

  在接受记者采访的前几天,黑客肖的QQ号被一位崇拜者在网上对外公布了,于是每天有几十个想购买病毒程序的下家主动来“敲门”。

  黑客肖非常谨慎,他尽量只把一个写好的程序卖给一个下家,“把枪卖得太多,传播的就越多越快,我可不能像‘熊猫烧香’那样引起民愤。”

  黑客肖有两台电脑——台式机和笔记本,在他房间的任何角落都能够上网,在接受记者采访时,他毫无表情地倚靠在沙发上,只有在打开电脑的瞬间,狡黠的表情才回到了他的脸上。

  “我们的圈子用QQ群和地下留言板进行联系,一切都是网上交易。我从来不和‘买枪’的人见面。这样只能增加风险。”“买枪人”是黑客肖的下家,也就是购买病毒的人,通常“买枪人”会在QQ群上“招标”——“想买一匹马用来做XX”,这句黑话的解释是,“想找人设计一个木马程序。”于是有能力造枪的人便去“竞标”。

  “‘买枪人’基本买两种枪。一种是他们需要的病毒,另一种是购买某个网站的漏洞,供他们入侵。”黑客肖透露,“设计简单的病毒,一般的程序员都会做,但是收入不多。原先普通的木马病毒只卖3000元,但是买家可以拿它赚取几十万元的利润——现在有了新的合作模式,即与卖家进行分账。”

  黑客肖所说的分账,有点类似出版业的版税模式,即“买枪人”的每一笔收益都给“造枪人”提成。据了解,熊猫烧香的程序设计者,每天入账收入近1万元。

  “这还不是最赚钱的,真正赚钱的是卖网站漏洞。这可不是一般程序员能做的,只有掌握了高超技艺的人才能在大企业的网站中找到漏洞。”黑客肖有些得意,“由于找漏洞多数是企业之间利用这些设计漏洞进行攻击,所以一个漏洞可以卖到几万到几十万元不等。”

  不仅如此,黑客还会被要求设计病毒的升级程序以及反杀毒程序。

  产业链已具团伙性质

  “现在每台‘肉鸡’一周的租金只要7美分。”

  “不,中国只要9分,而且是人民币。”

  在一个内部会议中,公安部网监司的一位处长正与一家网络机房工程师对话。面对时价,该处长哑口无言。1万台“肉鸡”可以发送450万个数据包,占用4.5G的带宽,能够让绝大多数网站处于瘫痪的状态。这些“肉鸡”组成了一个中等的僵尸网络。

  据中国互联网安全的最高机构——国家计算机网络应急技术处理协调中心(以下简称CNCERT/CC)的监测数据显示,目前,中国的互联网世界中,有5个僵尸网络操控的“肉鸡”规模超过10万台,个别僵尸网络能达到30万台的规模。这些僵尸网络可以被租借、买卖,黑客们每年可以有上百万元的收入。

  2006年末至2007年初,类似僵尸网络这样的互联网安全事件,呈现出爆发式的增长。CNCERT/CC接到的报警数量超过日常的两倍。

  在网络病毒被制造者卖出之后,病毒的旅行才刚刚开始。

  “以一个盗窃虚拟财产的病毒为例,买枪的人拿到程序后,通常会雇佣一个僵尸网络来传播病毒。传播出去的病毒可以偷窃用户的网络游戏的游戏币,以及可以偷盗游戏中的武器,把偷盗的序列号发回到指定的信箱中。”黑客肖说。

  “僵尸网络是传播病毒的核心,但是也有比较简单的传播病毒的方法。”黑客肖笑了笑,“你早上起床,来到一个网吧,把网吧中的30台机器都染上病毒,然后你就可以回家等着收钱了。因为,来网吧的人基本都是聊天和打游戏的,无论是盗取Q币还是游戏币都可以获益。”

  这时,每台中毒的计算机、程序设计者或者是僵尸网络的持有者,都可以拿到五分到五角之间不等的收益。

  黑色产业链发展到这一阶段,已经出现了明显的分工,“一个团伙通常有十几个人,有人负责传播病毒,有人负责卖偷盗来的虚拟货币,有人负责洗钱。”

  “对虚拟货币的洗钱方式,通常是团伙内的人,在一个网络游戏上开设许多账号,比如偷来网络游戏中的一件武器,在这些账号上被多次转移后,再卖出。游戏公司也没有办法,因为不知道哪些账号是真的游戏玩家,哪些是买卖的人。”黑客肖说。

  其他偷盗来的虚拟货币则会以批发价向下一级代理出售。

  此外,黑客们设计的其他木马程序还会被刻成光盘,批量生产,进行销售。根据“独家性”和“功能性”,价格可能几十元,也可能上千元。

  还有一些黑客组织可以提供恶意广告插件的服务,使用户的电脑弹出特定的窗口。据透露,弹出窗口每千次的售价是12元,而国内目前至少有50家恶意广告代理商,据CNCERT/CC保守估计,年产值能够达到1.08亿元。

  2006年2月,公安部门抓获黑客组织“玫瑰骑士”,他们既攻击网站又代理广告,抓获时,其流动资金已达上千万元。

  在2006年的最后一天,公安部还抓获了一家专门进行网络敲诈的传媒公司,公司内部的几个人专门从事“拒绝服务攻击”,让用户无法登录相关网站,并向网站勒索钱财。他们最先只是花2000元购买了一个攻击傀儡僵尸的软件,随后向十多家网站发起攻击,其中3家网站在很短时间内便上交了3000元的保护费。

  侵蚀现实商业

  “想买到我的终端产品很容易。”黑客肖认为黑色产业链到了这一阶段,已经半公开或者全公开了,“通常处于下游的零售商会雇人在网站上叫卖。一天可以有几千或者数万元的收入进账。”

  “或者你在中关村大街上,向那些办假证的人问,卖不卖木马,多数不会落空。因为黑色产业链的终端已经与其他IT产品的终端进行了融合。”黑客肖说,“专业出售IT产品的柜台上,也可以购买到这些产品,有时,真的产品和偷盗来的产品会被掺在一起出售。”

  “盗卖虚拟货币的,通常在网上叫卖,不过最多的是与网吧老板联合。如果你在玩游戏时想买一件武器,只要一伸手,向网吧老板购买即可。”

  “至于利润可想而知,圈内有一个典型故事,当网络游戏的研发者还在艰苦创业的时候,偷盗游戏币的人已经开上跑车、买别墅了。”黑客肖回忆道。

  此外,攻击网站也是黑色产业链最终盈利的方式之一。一位曾经遭遇过网络黑客勒索的(网站)“站长”描述了与黑客“面谈”的情景。“在企业受到攻击后,我们和黑客只在QQ上简单沟通了一下,并相约在公司会面。就在公司的会议室内,黑客的谈判代表与我们面对面地谈判,他们开价5万元。”“站长”觉得当时情景很无奈也很滑稽,“我们开始讨价还价,最后谈定3万元,他们收到钱后,解除了对网站的攻击。”

  黑客肖有时也会对目前黑色产业链蓬勃发展的局面担忧,“中国的互联网道德正面临着崩溃的局面。”

  “国外的黑客还猖獗不到中国黑客这样——写病毒,还写反杀毒程序;中国存在大量的实体病毒,而国外黑客只是写一些病毒样本,不会去真实写病毒。最可笑的是,中国的病毒可以公开叫卖,这在国外是绝对不可能的。”

  2007年3月,“灰鸽子”事件的主角——灰鸽子工作室发表声明,表示将停止研发“灰鸽子”,并随后关闭了自己的网站。

  虽然“灰鸽子”已经停止研发,但是根据安天样本捕获体系上报的数据表明,木马、后门、蠕虫、间谍软件等恶意代码的产生数量依然呈上升趋势,而“灰鸽子”只是这些恶意代码中的一小部分,因此“灰鸽子”工作室的关闭并不会对恶意程序产生的大环境造成影响。因此恶意代码的防治工作还任重而道远,网民的网络安全意识依然要提高。

  2007年 1月,一家提供在线服务网站同样遭到了类似的攻击,由于还没有出现有效的防御办法,该网站只能被动地在全国多个地方加载服务器,每两个小时,换一个地方,并为此耗费巨资增加服务器。

  “原先在互联网上创业只要十几万元,现在没有上百万元,你别来玩!”该网站“站长”阿康正在与投资方谈判,他已经明显感到虚拟世界对现实商业环境的影响。

  访谈

  决策层感觉不到互联网立法的紧迫性

  访国家计算机网络应急技术处理协调中心陈明奇博士

  《中国经营报》:“熊猫烧香”等病毒的泛滥,已经让我们意识到,黑色产业对现实商业环境产生的影响,你认为黑色产业链与现实商业社会的关系正在朝着何种趋势发展?

  陈明奇:互联网黑色产业链的存在正在摧毁大众对网络经济的信任,尤其是对网上银行等网络业务的信任。同时,相关的商业网站为了运行安全性,需要加大对网络安全的投入,这些将严重阻碍网络对商业领域的渗透。此外,更严重的是,互联网黑色产业链的存在,正在对青年的价值观产生着影响。互联网黑色产业链的暴利足以引诱青年人向其投靠,形成强有力的人才竞争。这些都将不可避免地对现实商业社会产生影响。

  《中国经营报》:中国刑法对互联网犯罪缺少明确、严格的规定,致使黑色产业链受害者成为新弱势群体,我们应该怎样加强在这一领域的立法?

  陈明奇:对待互联网犯罪,我们通常依据《刑法》第285、286、287条给予定罪。实际上,这3条应用起来非常困难。首先,在互联网上取证非常困难,因为多数攻击是病毒侵占了他人电脑,在机主不知情的情况下发动的,所以很难抓住元凶。其次,《刑法》规定的“重大经济损失”在互联网上很难认定。比如一个网站遭到攻击,用户无法登录,网站的广告流量受到影响,投资者暂停投资,这很难确定在某一个金钱范围内,但是对受害者影响却非常大。利用《刑法》第285、286、287条的漏洞,黑客们可以逃避惩罚、逍遥法外。

  欧洲的许多国家正在面临着与中国类似的互联网立法问题。德国的经验是,设立《个人数据保护法》,来防止黑客对个人电脑的入侵,已经产生了一定的威慑作用。中国需要注意现有各种法律法规与互联网现实的衔接,加强有关的司法解释,采取措施落实有关法律规定。

  《中国经营报》:针对互联网犯罪,你认为国家应建立一个怎样的协调监管、快速反应机制?

  陈明奇:一个假冒银行网站的存在可能只有一两天,但是对假网站执法则需要半个月的审批流程,这显然是无效的防御。

  目前,我国的互联网还没有一个统一的监察或者说管理机构,各个部门都是从各自角度,开展对互联网的管理,相互之间的协调机制远远落后于互联网的发展,应该完善有关的协调机制。同时,要对互联网犯罪转变观念,像欧洲国家一样,建立针对互联网犯罪的快速程序,才能使危害在发生恶劣后果之前,得以制止。

  《中国经营报》:管理层面对互联网安全认识是否存在误区?

  陈明奇:对于管理层来讲,互联网安全问题是一个隐性问题。根源在于掌握决策制定权的领导很少上网,感觉不到互联网立法的紧迫度。而大型企业有资金对安全进行大额投入,但真正能够感受到黑色产业链危害的是网民和中小企业,他们已经成为互联网上的弱势群体,同样需要救助。救助的方式可以多样,例如对符合救助标准的中小企业提供免费的网络黑名单,甚至赠送软件,限制对特殊网站的访问。现在,国家对这一问题的资金、人员投入还十分不足。本报记者索寒雪采访整理

  透视

  网络上各色人等为了利益充当着黑色链条上的不同角色

  黑客道德底线在溃败

  互联网,这个迄今为止最为人类值得骄傲的发明,正在悄悄沦丧。

  利益越来越浓,道德被抛在脑后,传统约束机制的缺失,使这个虚拟世界越来越疯狂起来。

  80%-90%联网电脑都是或者曾经是被控制的机器

  “网络实质是少数人说了算,极少数人控制着普通大众,指挥着绝大多数弱势群体。” 信息安全国家重点实验室副主任荆继武认为。

  业内资深专家估计,有80%~90%联网电脑都是或者曾经是被控制的机器,而且这其中大部分人几乎没有什么察觉。有人可能因为别的问题格式化电脑而使机器重新获得“自由”,但接着又会成为另一个黑客的“奴隶”。因为大多数帮助黑客捕获“奴隶”的木马程序根本不会使电脑表现出任何症状,普通用户也就无从发现。

  在荆继武眼里,微软和英特尔已经将电脑性能打造得如大海般深厚,而网络攻击者则像针一样潜伏在深海里。“以前在386上,潜伏一个木马程序是不容易的,会因占用大部分资源而被发现,但现在对黑客来说太简单了,往你的机器里埋伏五个木马,你一点感觉没有,因为现在CPU如此强劲,微软的操作系统又是如此复杂。”荆继武说。

  专业网络安全公司绿盟科技的黑洞产品市场经理韩永刚承认,黑客统治下的网络世界,普通网民沦为新弱势群体的现象愈演愈烈,整个大众的安全意识没有明显提高,会有更多用户机器上的漏洞被利用,而成为“奴隶”。

  国家有关部门上演网络版“无间道”

  2000年的某一天,中国互联网络信息中心收到一封来自加拿大的邮件,邮件的发件人是加拿大的一个网管,他举报中国境内某IP地址的计算机一直向他们发起攻击,已经持续了一段时间,互联网络信息中心根据信中IP地址很快查到了发起攻击的机器——信息安全国家重点实验室的一台Linux服务器,这着实让实验室工作人员吃了一惊,“我们没干啊。”

  事实上,类似上面的这种攻击对于互联网上很多网站来说是家常便饭。“每天都有,只是大小的问题。” 国家计算机网络应急技术处理协调中心的一位专家说。

  在韩永刚的生活中,时常有这样的片段:半夜一两点钟,睡得正香时,突然电话响起,客户网络遭到了攻击,而且攻击流量很大,他就必须立即赶过去处理情况。

  攻击的发起者甚至不需具备任何电脑知识,只要有一个必备条件——钞票。因为以往为技术高手专利的黑客行为,早已在网络泛滥的时代商业化,网络上各色人等为了利益充当着攻击链条上的不同角色,没有了现实社会中道德束缚的虚拟世界,虚拟公民们的行为越来越肆无忌惮。

  黑客攻击服务甚至在网络上公开出售,按照攻击机器台数标价,如果需要5000台机器的攻击,租用一次则要5000元,这5000台机器早已被种下木马,全部听黑客指令行事,交钱后随时可以发动攻击。“被控制的机器五花八门,甚至还包括很多各部委的机器。”一位业内人士透露。

  韩永刚的日常工作中,几乎每天都要与数百兆的攻击流量斗法,“政府机关、运营商、企业用户所遇到的攻击频繁发生。”而作为黑洞产品经理,其任务就是将攻击流量吸收到“黑洞”里,经过处理将攻击包过滤掉,再将正常的数据包送达目的地,但他也承认分辨这两种数据包难度不小。

  为了摸清已经事实存在的黑客产业链,国家计算机网络应急技术处理协调中心甚至还上演了网络版无间道,与专业的卧底公司合作,派他们购买黑客攻击服务,与黑客商业组织建立联系,慢慢打入这个产业链内部后,调查其中的内幕。

  商业让黑客攻击动力越来越大

  自2006年底开始,大规模的网络攻击越来越多,攻击表现出的商业目的越来越明显。

  以前的黑客事件大多数是想显示自己的能力,攻击规模也较小,但现在经济利益越来越多地掺杂进来,当有经济利益摆在那时,发动攻击的动力也就越来越大,直至发展为一个完整的商业链条。

  在韩永刚看来,网络上的经济利益越来越大,将推动网络世界变得和现实社会一样,绘成另一幅活生生的人生百态图,这里必然存在有打手、流氓和黑社会。

  早期接触网络的人们恐怕都还记得,最早的黑客很注重自己的声誉,一般不做过多的破坏。但网络上的利益变得非常诱人后,就开始有越来越多的所谓高手为了获取利益而专门编写攻击代码。

  一位曾经从事过黑客攻击的匿名人士坦言,网络早已褪去最初信息共享平台的外衣,它所承载的利益越来越大后,有利可图、不择手段的事情就会有人去干。正如现实社会一般,商战中不可避免地要出现非正当竞争手段。“有很多人希望暴富,在这里人们不用遵守现实世界的规则,出现商业目的攻击事件就不足为奇。”

  网络社会,这个与现实联系越来越紧密的虚拟世界,似乎正在进入黑暗的中世纪,各类角色的道德底线在利益诱惑下不断溃败。

  背景

  黑客嬗变

  上世纪70年代,黑客(Hacker)几乎还是一个褒义词,专指那些尽力挖掘计算机程序最大潜力的电脑精英。而英文单词Hack(劈砍)意即为干一件非常漂亮的工作。

  进入80年代,黑客已经演变为计算机侵入者的代名词。这一阶段,有一个名字不能不提——米特尼克,他在15岁时闯入北美空中防务指挥系统的计算机主机内,并且翻遍了美国指向前苏联及其盟国的所有核弹头的数据资料,然后悄无声息地溜了出来。

  此后,米特尼克不断制造着“光辉战绩”,直到美国联邦调查局在全国范围内对其进行通缉时,他还设法控制了加州的一个电话系统,以窃听追踪他的警察行踪。

  “巡游五角大楼,登录克里姆林宫,进出全球所有计算机系统,摧垮全球金融秩序和重建新的世界格局,谁也阻挡不了我们的进攻,我们才是世界的主宰。”这是米特尼克曾经的豪言壮语。

  进入21世纪后,黑客行为逐渐与经济利益联系紧密起来,在各种商业链条中,其身影若隐若现。

  利益的驱动,使黑客一词的含义正在悄悄地酝酿着嬗变,除了电脑编程高手外,营销、物流、心理方面的专业人才不断涌向黑客产业链。这种复杂的人员结构给整个社会带来的威胁,恐怕远比单一的技术人员统领黑客天下的时代要大得多。

1、 QQ、MSN、Gtalk,一个都不少。

由于聊天对象与聊天内容的不可控制性,使用即时通讯软件是降低工作效率的罪魁祸首。有调查显示,使用即时通讯软件,工作效率会降低20%。(对策:离开他们一段时间,试着专门做你手头上的工作。)

2、“总想多看一点点”——忘记上网的目的。

  本想查找工作资料,结果在网页上发现《哈利波特7》出来了;再点进去,又在网页底端看到自己喜欢的明星跟某某又传出了绯闻……点着点着,就忘记自己要上网做什么了。(对策:千万别把工作和娱乐放在一起,工作就好好工作,娱乐才能尽情地挥洒,控制对互联网新奇的诱惑吧。) 

3、长期不擦拭电脑屏幕和鼠标。

电脑屏幕已经糊了厚厚的一层灰尘,每次都要瞪大眼睛去看,费力去猜屏幕污点下面的字是什么;鼠标点起来已经非常费力了,反应也迟钝得像八十岁的老汉。这些都间接地影响了工作效率。(对策:保持干净的电脑桌面包括电脑桌面不能放置过多的图标,鼠标一定要好用,不然累死你的手。)

4、长期不清理电脑系统。

防火墙的防御力是有限的。长期不清理电脑系统的后果就是,内存被一些潜藏的垃圾程序给占用了,直接影响了电脑的运行速度。电脑运行慢了,也就降低了工作效率。 (对策:对系统垃圾即时清理,可以请一些系统垃圾清理软件来帮忙。比如:一键清理系统垃圾文件)

5、长期不整理办公桌和文件。

办公桌和文件杂乱的后果就是,想找东西的时候却找不到。以前,有调查公司专门对美国200家大公司职员做了调查,发现公司职员每年都要把6周时间浪费在寻找乱放的东西上面。(对策:养成东西归位的好习惯,现在就开始整理你的桌面的文件档案吧!)

6、长期不整理电子邮件和通讯录。

想给客户发个邮件,却记不得E-mail地址,于是在电子邮箱中一通乱找,却发现自己的邮箱早已被垃圾邮件搞得汪洋一片。想搜什么都变得很困难。再返回一堆堆名片中去找,就又陷入了第5个坏习惯。(对策:平时花些时间把电子邮件分类,设置好邮件的过滤功能。)

7、不适时保存文件。

尽管现在电脑的性能越来越高,死机现象越来越少,可是,意外地碰掉电脑插头、程序操作不当从而造成电脑关机、死机,总是不可避免的,如果不适时保存文件,那么文件就很可能会丢失,前功尽弃。(对策:关键时刻保存一下,免得一回儿痛心疾首。)

8、“不磨刀误了砍柴工”。

工作之前,不做充分的计划、准备。行动之后,才发现要么是工具准备得不充分,只得停下工作再去找文件、资料;要么工作做到一半,才发现偏离了预定的方向,只得重新开始,前功尽弃。(对策:还是习惯问题!)

9、梦想电脑有“三头六臂”。

 在同一时间,一个网页打不开,一个程序在等待,为了节约时间,就只好再打开其他网页。结果同一时间,十几个窗口同时打开着,电脑就变得“老态龙钟”,一动不动。什么都干不成。(对策:做重要事情的时候,只做一件,不然过多的程序会让你分心,我理解你的心境,PC都已经双核了,不榨干真是浪费!这心态不好。)

10、不会充分利用等待时间。

 打开一个软件,电脑迟迟没有反应,于是坐在电脑旁干等;打开一个网页,迟迟显示不出来,又打开新的网页,又是一阵干等;要打印文件,发现打印机里排着队的文件有好多,只好继续等待……一天的工作中,光是耗在电脑上的等待时间就很可观,如果不充分利用这些等待间隙的话,那自己的工作时间只好额外延长了。 (对策:等不能解决问题,因为程序卡在那里了,动手检查一下吧。)

11、“耻于下问”。

现在,电脑出现的病症越来越离奇。不是每个人都是电脑高手。身为IT中人,似乎像别人请教就显得自己太不专业了。于是就上网查找解决方案。结果,在垃圾信息的汪洋大海中奋力拼搏,折腾半天,才化解问题,浪费了工作的宝贵时间。如果问问身边人,可能几秒钟就解决问题了。(对策:我就常常被问,我发现我身边的同事是太会问,因为他们不是IT中人, 他们遇到问题就问我,这也一个“累”字啊。)

12、过分崇拜科技。

IT人很容易就会陷入科技崇拜。如果有新软件、新系统发布,IT人一定是最早尝试的。不管自己的电脑能不能撑起Vista,一定要给电脑装上。电脑负荷不了,只好不断罢工。工作也会因之延误。(对策:呵呵,这是很多的通病,总认为新出的一定更好,功能更大,其实软件适用才是根本,别再喜新厌旧了。)

13、长期端坐于电脑前面。

   一坐就是半天。工作并不会因为你的马不停蹄而加速。相反,如果总是沉浸在工作中,不适时休息,容易造成大脑的疲倦,反而降低了工作效率 。(对策:马上关闭电脑,去休息。) 

本文转载自『左岸读书_blog!』

dhlmtzx.edudh.net/oblog/

美国传播学家艾伯特梅拉比安曾提出一个公式:

  信息的全部表达=7%语调+38%声音+55%肢体语言

  我们把声音和肢体语言都作为非语言交往的符号,那么人际交往和销售过程中信息沟通就只有7%是由言语进行的。

  1.目光

  目光接触,是人际间最能传神的非言语交往。“眉目传情”、“暗送秋波”等成语形象说明了目光在人们情感的交流中的重要作用。

  在销售活动中,听者应看着对方,表示关注;而讲话者不宜再迎视对方的目光,除非两人关系已密切到了可直接“以目传情”。讲话者说完最后一句话时,才将目光移到对方的眼睛。这是在表示一种询问“你认为我的话对吗?”或者暗示对方“现在该论到你讲了”。

  在人们交往和销售过程中,彼此之间的注视还因人的地位和自信而异。推销学家在一次实验中,让两个互不相识的女大学生共同讨论问题,预先对其中一个说,她的交谈对象是个研究生,同时却告知另一个人说,她的交谈对象是个高考多次落第的中学生。观察结果,自以为自已地位高的女学生,在听和说的过程都充满自信地不住地凝视对方,而自以为地位低的女学生说话就很少注视对方。在日常生活中能观察到,往往主动者更多地注视对方,而被动者较少迎视对方的目光。

  2.衣着

  在谈判桌上,人的衣着也在传播信息与对方沟通。意大利影星索菲亚·罗兰说:“你的衣服往往表明你是哪一类型,它代表你的个性,一个与你会面的人往往自觉地根据你的衣着来判断你的为人。”

  衣着本身是不会说话的,但人们常在特定的情境中以穿某种衣着来表达心中的思想和建议要求。在销售交往中,人们总是恰当地选择与环境、场合和对手相称的服装衣着。谈判桌上,可以说衣着是销售者“自我形象”的延伸扩展。同样一个人,穿着打扮不同,给人留下的印象也完全不同,对交往对象也会产生不同的影响。

  美国有位营销专家做过一个实验,他本人以不同的打扮出现在同一地点。当他身穿西服以绅士模样出现时,无论是向他问路或问时间的人,大多彬彬有礼,而且本身看来基本上是绅士阶层的人;当他打扮成无业游民时,接近他的多半是流浪汉,或是来找火借烟的。

  3.体势

  达芬·奇曾说过,精神应该通过姿势和四肢的运动来表现。同样,销售与人际往中,人们的一举一动,都能体现特定的态度,表达特定的涵义。

  销售人员的体势会流露出他的态度。身体各部分肌肉如果绷得紧紧的,可能是由于内心紧张、拘谨,在与地位高于自己的人交往中常会如此。推销专家认为,身体的放松是一种信息传播行为。向后倾斜15度以上是极其放松。人的思想感情会从体势中反映出来,略微倾向于对方,表示热情和兴趣;微微起身,表示谦恭有礼;身体后仰,显得若无其事和轻慢;侧转身子,表示嫌恶和轻蔑;背朝人家,表示不屑理睬;拂袖离去,则是拒绝交往的表示。

  我国传统是很重视在交往中的姿态,认为这是一个人是否有教养的表现,因此素有大丈夫要“站如松,坐如钟,行如风”之说。在日本,百货商场对职员的鞠躬弯腰还有具体的标准:欢迎顾客时鞠躬30度,陪顾客选购商品时鞠躬45度,对离去的顾客鞠躬45度。

  如果你在销售过程中想给对方一个良好的第一印象,那么你首先应该重视与对方见面的姿态表现,如果你和人见面时耷着脑袋、无精打采,对方就会猜想也许自己不受欢迎;如果你不正视对方、左顾右盼,对方就可能怀疑你是否有销售诚意。

  4.声调

  有一次,意大利著名悲剧影星罗西应邀参加一个欢迎外宾的宴会。席间,许多客人要求他表演一段悲剧,于是他用意大利语念了一段“台词”,尽管客人听不懂他的“台词”内容,然而他那动情的声调和表情,凄凉悲怆,不由使大家流下同情的泪水。可一位意大利人却忍俊不禁,跑出会场大笑不止。原来,这位悲剧明星念的根本不是什么台词,而是宴席上的菜单。

  恰当的自然地运用声调,是顺利交往和销售成功的条件。一般情况下,柔和的声调表示坦率和友善,在激动时自然会有颤抖,表示同情时略为低沉。不管说什么样话,阴阳怪气的,就显得冷嘲热讽;用鼻音哼声往往表现傲慢、冷漠、恼怒和鄙视,是缺乏诚意的,会引起人不快。

  5.礼物

  礼物的真正价值是不能以经济价值衡量的,其价值在于沟通了们之间的友好情意。原始部落的礼品交换风俗的首要目的是道德,是为了在双方之间产生一种友好的确良感情。同时,人们通过礼品的交换,同其他部落氏族保持着社会交往。当你生日时送你一束鲜花,你会感到很高兴,与其说是花的清香,不如说是鲜花所带来的祝福和友情的温馨使你陶醉,而自己买来的鲜花就不会引起民如此愉悦的感受。

  在销售过程中,赠送礼物是免不了的,向对方赠送小小的礼物,可增添友谊,有利于巩固彼此的交易关系。那么大概多少钱的东西才好呢?在大多数场合,不一定是贵重的礼物会使受礼者高兴。相反,可能因为过于贵重,反而使受礼者觉得过意不去,倒不如送点富于感情的礼物,更会使销售对象欣然接受。

  6.时间

  在一些重要的场合,重要人物往往姗姗来迟,等待众人迎接,这才显得身份尊贵。然而,以迟到来抬高身份,毕竟不是一种公平的交往,这常会引起对方的不满而影响彼此之间的合作与交往。

  赴会一定要准时,如果对方约你7时见面,你准时或提前片刻到达,体现交往的诚意。如果你8点钟才到,尽管你口头上表示抱歉,也必然会使对方不悦,对方会认为你不尊重他,而无形之中为销售设下障碍。

  文化背景不同,社会地位不同的人的时间观念也有所不同。如德国人讲究准时、守时;如果应邀参加法国人的约会千万别提早到达,否则你会发觉此时只有你一个人到场。有位驻非洲某国的美国外交官应约准时前往该国外交部,过了10分钟毫无动静,他要求秘书再次通报,又过了半个小时仍没人理会他,这位外交官认为是有意怠慢和侮辱他,一怒之下拂袖而去。后来他才知道问题出在该国人的时间观念与美国人不同,并非有意漠视这位美国外交官。

  7.微笑

  微笑来自快乐,它带来的快乐也创造快乐,在销售过程中,微微笑一笑,双方都从发自内心的微笑中获得这样的信息:“我是你的朋友”,微笑虽然无声,但是它说出了如下许多意思:高兴、欢悦、同意、尊敬。作为一名成功的销售员,请你时时处处把“笑意写在脸上”。

本文转载自『左岸读书_blog!』

dhlmtzx.edudh.net/oblog/