最近 Linux 内核爆出了一个严重的安全漏洞,非root用户可以通过该漏洞的 exploit 获取root权限。这并不罕见,值得一提的是这个补丁看起来如此平常以至于我们绝大多数人都不会以为这是安全问题。

先看这个问题的补丁,就是下面这个:

  static int perf_swevent_init(struct perf_event *event)
 {
-    int event_id = event->attr.config;
+    u64 event_id = event->attr.config;

     if (event->attr.type != PERF_TYPE_SOFTWARE)
         return -ENOENT;
我们第一眼的感觉就是这大概只是修复了编译器报的一个小警告吧,怎么会引起如此严重的安全问题呢? 在没打补丁的代码中 event_id 是个**带符号**的整型,而且就在下面不远处的两行代码中只检查了其上界: [c] if (event_id >= PERF_COUNT_SW_MAX) return -ENOENT; [/c] 而如果传递进来的 event->attr.config 值正好设置了符号位,那么 event_id 就会变成负值,而且能躲过上面的检查。 负值意味着什么呢?再继续看后面的代码: [c] if (!event->parent) { int err; err = swevent_hlist_get(event); if (err) return err; atomic_inc(&perf_swevent_enabled[event_id]); event->destroy = sw_perf_event_destroy; } [/c] 意味着数组越界!这时你应该身上开始冒冷汗了。继续,数组 perf_swevent_enabled[] 在 RHEL6 上的定义是: [c] atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; [/c] 而 atomic_t 基本上就是int,也就是说 perf_swevent_enabled[] 是整型数组,那么用 event_id 访问该数组时会把 event_id 的值乘以4再加上数组的起始地址。很简单哈! 好,通过 System.map 文件我们可以得到 perf_swevent_enabled 的地址:
ffffffff81f360c0 B perf_swevent_enabled

那么当 event->attr.config == 0xffffffff (即有符号的-1)时,在 x86_64 上面我们最终会得到:

0xffffffffffffffff * 4 + 0xffffffff81f360c0 == 0xFFFFFFFF81F360BC

同理,当 event->attr.config == 0xfffffffe 时我们得到:

0xfffffffffffffffe * 4 + 0xffffffff81f360c0 == 0xFFFFFFFF81F360B8

所以上述的 atomic_inc() 其实增加的是前面两个地址中存放的值,而这俩地址都指向内核空间(参见 Documentation/x86/x86_64/mm.txt)!这时你应该感到紧张了。。。

后面更有趣的事情发生在 sw_perf_event_destroy() 函数中,它是在 perf_event_open() 返回的 fd 被关闭时被调用,RHEL6 上其定义如下:

[c]
static void sw_perf_event_destroy(struct perf_event *event)
{
u64 event_id = event->attr.config;

    WARN_ON(event->parent);

    atomic_dec(&perf_swevent_enabled[event_id]);
    swevent_hlist_put(event);

}
[/c]

很明显的不同是,event_id 这次是无符号的类型。那么,同上,当 event->attr.config == 0xffffffff 时我们得到:

0xffffffff * 4 + 0xffffffff81f360c0 == 0x0000000381F360BC

当 event->attr.config == 0xfffffffe 时我们得到:

0xfffffffe * 4 + 0xffffffff81f360c0 == 0x0000000381F360B8

所以这里的 atomic_dec() 实际上减小的是用户空间地址内的值。

上面是“基础知识”,带着这些知识我们看 exploit 代码究竟做了什么,代码片段如下:

[c]

define BASE 0x380000000

define SIZE 0x010000000

assert((map = mmap((void)BASE, SIZE, 3, 0x32, 0,0)) == (void)BASE);
memset(map, 0, SIZE);
sheep(-1); sheep(-2); // sheep will just invoke perf_event_open
// syscall with attr.config set to the param
for (i = 0; i < SIZE/4; i++) if (map[i]) {
assert(map[i+1]);
break;
}
[/c]

它首先会 mmap() 起始地址是 0x380000000 的一块内存区域。然后分别以 attr.config 为 -1 和 -2 调用两次 perf_event_open()。根据前面的计算,它实际上分别增加了 0xFFFFFFFF81F360BC 和 0xFFFFFFFF81F360B8 两处内存的值,减少了 0x0000000381F360BC 和 0x0000000381F360B8 的值。后面的 for 循环则是找出被减少的内存地址,这样一来也就可以算出 perf_swevent_enabled[] 数组的地址(System.map 并不总是存在,如果存在而且可读我们当然可以直接去读这个值)。

知道这个地址我们就可以操纵内核中某处的32bit的值,把其值加一。正因为如此,作者巧妙地选择了中断描述符表——一个16字节描述符的数组,它的地址可以通过 sidt 指令获取。它其中的描述符结构定义如下:

Offset     Size     Description
0     2     Offset low bits (0..15)
2     2     Selector (Code segment selector)
4     1     Zero
5     1     Type and Attributes (same as before)
6     2     Offset middle bits (16..31)
8     4     Offset high bits (32..63)
12     4     Zero

这里最有趣的是 offset 为8 的地方,在 x86_64 上面其值为 0xffffffff。作者选择的中断描述符是 0x4,所以相对于中断描述符表它的偏移实际上是 0x48。现在的任务就成了通过 perf_swevent_enabled[] 来计算出该中断描述符中偏移为8的内存地址,并对其加一!下面的代码就是做的这个工作:

[c]
sheep(-i + (((idt.addr&0xffffffff)-0x80000000)/4) + 16);
[/c]

i 是我们前面在 for 循环中搜到的 perf_swevent_enabled[] 的一个偏移,idt.addr 是中断描述符表的绝对内核地址,取其低32位并减去 0x80000000 是为了得到低28位作为偏移,除以4是因为数组是int,最后加的16就是 0x4 中断描述符中的偏移(4已经除去了),所以最终sheep()里面的参数就是我们想要的偏移,这样以来内核就替我们把 0x4 中断描述符中的偏移为 8 的 0xffffffff 加上了1,也就成了0,也就成了用户空间的地址!所以后面的 int 0x4 其实就会跳转到用户空间早已经设置好的代码!!!

而这段代码比较生涩,但其意思就是更改当前进程的 uid/gid 为0来提升权限,所以最终取得一个有 root 权限的 shell!整个攻击大功告成!

注:上面的链接可能不能用,exploit 代码也可以在这里看到:https://gist.github.com/onemouth/5625174

以前没结婚自己怎么过都能凑合,现在结婚了不得不考虑以后的生活。目前国内的情况一天比一天糟,而且它只会变得更糟。我对这个国家的未来不报任何希望,所以决定移民。

现求推荐海外,尤其是比较容易移民的国家(比如澳大利亚、新西兰、美国、加拿大)的工作,Linux 底层开发相关的即可,最好是内核开发。公司只要提供 relocation 。我给开源项目贡献的补丁见下。

Linux kernel:
http://goo.gl/AfPJw
http://goo.gl/NhVg3

dracut:
http://goo.gl/Nj4OI
http://goo.gl/U2sMw

iproute2:
http://goo.gl/W4TX4

kexec-tools:
http://goo.gl/6CzvP
http://goo.gl/zgTwV

util-linux:
http://goo.gl/dsOlm

还有一些零碎的补丁就不献丑了。

稍微正式的简历可以看我的 linkedin,正式的简历可以发邮件给你。我的邮箱是:xiyou.wangcong@gmail.com。

国内的猎头就别浪费时间了。

本文讲的是 korg 内核中的 openvswitch,而非 openvswitch.org 上提供的openvswitch模块。

之前讲过 VXLAN,但是一直没有动手实践。其实配置 VXLAN 要比配置 GRE tunnel 简单很多,你所需要做的只要两步:

1. 添加 VXLAN 设备(需要比较新的 iproute2 和内核支持):

# ./ip/ip li add vxlan0 type vxlan id 42 group 239.1.1.1 local 10.16.43.214 mtu 1500 dev eth0

其中,10.16.43.214 是配置在 eth0 (或其它连接两台主机的设备)上的本地IP地址。

2. 把 vxlan0 添加到 openvswitch 的 bridge 中去:

# ovs-vsctl add-port ovsbr0 vxlan0

最终的结果如下所示:

# ./ip/ip -d li show vxlan0
7: vxlan0:  mtu 1500 qdisc noqueue
state UNKNOWN mode DEFAULT
    link/ether d2:93:a2:08:10:1e brd ff:ff:ff:ff:ff:ff
    vxlan id 42 group 239.1.1.1 local 10.16.43.214 dev eth0 port 32768
61000 ageing 300
# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr AC:16:2D:05:14:3A
          inet addr:10.16.43.214  Bcast:10.16.47.255  Mask:255.255.248.0
          inet6 addr: 2620:52:0:102f:ae16:2dff:fe05:143a/64 Scope:Global
          inet6 addr: fe80::ae16:2dff:fe05:143a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:386702 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1505515 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:42804806 (40.8 MiB)  TX bytes:1202562852 (1.1 GiB)
          Interrupt:20 Memory:fbc00000-fbc20000

# ifconfig vxlan0
vxlan0    Link encap:Ethernet  HWaddr D2:93:A2:08:10:1E
          inet6 addr: fe80::d093:a2ff:fe08:101e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:346746 errors:0 dropped:0 overruns:0 frame:0
          TX packets:752833 errors:36 dropped:0 overruns:0 carrier:36
          collisions:0 txqueuelen:0
          RX bytes:18081642 (17.2 MiB)  TX bytes:1139323399 (1.0 GiB)

# ovs-vsctl show
2dce25d0-ac18-4626-905d-c4995464785d
    Bridge "ovsbr0"
        Port "vnet0"
            Interface "vnet0"
        Port "vxlan0"
            Interface "vxlan0"
        Port "vnet1"
            Interface "vnet1"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
    ovs_version: "1.7.0"

另外一台机器上基本上完全相同,结果如下:

# ./ip/ip -d li show vxlan0
8: vxlan0:  mtu 1450 qdisc noqueue
state UNKNOWN mode DEFAULT
    link/ether 92:8b:0f:8b:31:f1 brd ff:ff:ff:ff:ff:ff
    vxlan id 42 group 239.1.1.1 local 10.16.43.215 dev eth0 port 32768
61000 ageing 300
# ifconfig vxlan0
vxlan0    Link encap:Ethernet  HWaddr 92:8B:0F:8B:31:F1
          inet6 addr: fe80::908b:fff:fe8b:31f1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:752833 errors:0 dropped:0 overruns:0 frame:0
          TX packets:346770 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1128783737 (1.0 GiB)  TX bytes:22940562 (21.8 MiB)

# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr E8:39:35:30:1D:1E
          inet addr:10.16.43.215  Bcast:10.16.47.255  Mask:255.255.248.0
          inet6 addr: 2620:52:0:102f:ea39:35ff:fe30:1d1e/64 Scope:Global
          inet6 addr: fe80::ea39:35ff:fe30:1d1e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1535775 errors:0 dropped:0 overruns:0 frame:0
          TX packets:346951 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1210625058 (1.1 GiB)  TX bytes:41681835 (39.7 MiB)
          Interrupt:17

# ovs-vsctl show
71f0f455-ccc8-4781-88b2-4b663dd48c5f
    Bridge "ovsbr0"
        Port "vnet1"
            Interface "vnet1"
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "vnet0"
            Interface "vnet0"
        Port "vxlan0"
            Interface "vxlan0"
    ovs_version: "1.7.0"

配置好了之后,你可以尝试用 tcpdump 在 eth0 上捕捉一下包试试,你会发现 L2 是直接封装到 UDP 包中的:

05:39:05.107906 IP 10.16.43.215.52908 > 10.16.43.214.otv: UDP, length
106
        0x0000:  ac16 2d05 143a e839 3530 1d1e 0800 4500
        0x0010:  0086 abd0 0000 4011 62ca 0a10 2bd7 0a10
        0x0020:  2bd6 ceac 2118 0072 0000 0800 0000 0000
        0x0030:  2a00 5254 00f7 e2e7 5254 0090 5117 0800
        0x0040:  4500 0054 0000 4000 4001 f152 c0a8 6402
        0x0050:  c0a8 6403 0800 073c 3e05 0009 ca8f 0f51
        0x0060:  0000 0000 1102 0900 0000 0000 1011 1213
        0x0070:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
        0x0080:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
        0x0090:  3435 3637

1. 排在第一位的毫无疑问是《黑客帝国》三部曲。关键词:矩阵。

2. 《盗梦空间 Inception》。关键词:虚拟化。

3. 《云图 Cloud Atlas》。关键词:并行。

4. 《恐怖游轮 Triangle》。关键词:递归。

5. 《源代码 Source Code》。关键词:重入。

6. 《二维世界 Flatland》。关键词:二维。

我把它们汇总成了一个豆列,欢迎补充。

众所周知,C语言数组下标是从0开始,其它很多语言皆如此。而 FORTRAN 则是数组下标从1开始的典范。所以就有数组下标是从1开始好还是从0开始好之争。连《C专家编程》中都如此调侃:

数组的下标应该是从0还是从1开始?我提议的妥协方案是0.5,可惜他们未予认真考虑便一口回绝。—— Stan Kelly-Bootle
仔细思考一下这个问题很有意思,建议你不妨自己思考一下再继续往下看。

其实这个问题早在 1982 年就已经由计算机科学领域的大师 Edsger Dijkstra 研究过并得出了一个结论。他手写了一篇名为《Why numbering should start at zero》的论文,关键部分截图和大体翻译如下:

表示一个自然数子序列,比如 2, 3, …, 12,不用中间那三个点,有四种方式可供我们选择:

a) 2<= i < 13

b) 1 < i <= 12

c) 2<= i <= 12

d) 1 < i < 13

有什么道理选其中一种而不选别的吗?是的,的确有。观察到 a) 和 b) 的优势是两边的边界值之差正好是子序列的长度。作为一个推论,下面的观察也成立:在它们两个当中,两个子序列是邻接的就意味着一个的上界等于另外一个的下界。这些观察并没有让我们从a) 和 b) 之中做出选择,所以我们从头开始。

一定存在一个最小的自然数。排除掉下界——就像 b) 和 d) 那样——就会迫使一个从最小的自然数开始的子序列的下界进入非自然数领域。这很难看,所以对于下界我们更喜欢<=,就像 a) 和 c) 那样。现在,考虑一下从最小的自然数开始的子序列:包含上界就会迫后者不那么像自然数(译者注:作者的意思是自然数是一个有下界没有上界的集合),当序列缩小成空序列时。这很难看,所以对于上界我们更喜欢<,就像 a) 和 d) 那样。我们得出结论, a) 是我们最喜欢的。

当处理长度为 N 的序列时,我们期望通过下标来区分它的元素。下一个令人烦恼的问题就是,我们该给它的第一个元素赋予什么样下标值?坚持 a) 的方式,当下标从1开始时,下标范围为 1<= i < N+1;当下标从0开始时则是更好看的 0<= i < N。所以,让我们的序数从0开始:一个元素的序数(下标)等于序列中在它前面的元素个数。这个故事的寓意是我们最好尊重0最为一个最自然的数——过了这么多个世纪!


顺便说一句,Python 选择了 a),所以 range(0,3) 返回的是序列是 0,1,2。

在一个大风的夜里

走在古城的小巷子里

推门走进一家客栈

几个陌生人围坐在一起

炭火烧得通红

坐下来喝一杯热茶

听他们讲自己的故事

任凭门外风声四起

遇到过一个又一个不靠谱的姑娘,也因为种种原因错过了一个又一个没有缘分的姑娘,终于在合适的时间遇到一个合适而且靠谱的好姑娘!

和这个姑娘是在云南大理认识的,当时她正好一路搭车从山东来到了云南,而我正好从北京搬去大理常住……我们从那时一直交往到现在,一起去过全国很多地方,走了一圈最后发现还是最喜欢大理,而且很快还要回大理。

我这个人喜欢四处乱走,最近两年基本上每六个月就要搬一次家。你们搬家最多就是从城西搬到城东,而我搬家基本上是从中国的华北搬到西南…… 我不喜欢住在大城市,我认为城市从来没有让人们的生活变得更美好,只会让人更加迷失自我。而且,我从来没有买房子的打算,在一个地方住上几年不动对于我来说简直难以忍受。所以,和我这样的人在一起是需要很大的勇气的,要是换了别的姑娘早就被吓跑了,就算姑娘不跑也被姑娘的妈妈给拉跑了……

其实,真正让我感动的不是刚认识就觉得彼此很合适,也不是姑娘她不要钱不要车不要房子,而是,她能为了我一点一点去改变,为了在一起不会轻易就放弃。认识我之前她什么饭都不会做,现在已经鸡鸭鱼肉样样都行了,任性的脾气也改了不少。这比什么都重要。

我的姑娘就是这个样,就像我们结婚证上的那样,美丽又大方!

What Every Computer Scientist Should Know About Floating-Point Arithmetic

What Every Programmer Should Know About Memory

What every programmer absolutely, positively needs to know about encodings and character sets to work with text

Latency Numbers Every Programmer Should Know

What every programmer should know about time

What Every Programmer Should Know About Races

What Every C Programmer Should Know About Undefined Behavior

What every data programmer needs to know about disks

本文译自:http://theoatmeal.com/comics/tesla,图片均来自此处。也可以参考本人之前写的文章《这些男人来自地球》。


Geek 们熬夜通宵把世界给拆了,这样就可以把添加好的新功能一起装好放回去了。

他们修补那些没有损坏的东西。





















**

Geek 们放弃了他们周围的世界,因为他们忙于焊接一个新的世界。

他们痴迷,很多时候,甚至受苦。

一百多年前,一个塞尔维亚裔美国科学家,名叫尼古拉·特斯拉,开始修补一些没有损坏的东西。

在那个时候,世界上大部分人还在使用蜡烛照明,一个被称为交流电的系统诞生了。到今天,这已经是地球上每个家庭的电力了。

**
























把人类带入第二次工业革命,我们应该感谢谁?


尼古拉·特斯拉。


可每个人都认为:“我觉得托马斯·爱迪生才是电力时代之父。”


不!应该是尼古拉·特斯拉!


当多数人想到托马斯·爱迪生的时候,他们想到的是发明电灯泡的人。


爱迪生并没有发明电灯泡!他改进了电灯泡的主意,有22 个人在他之前就已经是先驱了。


爱迪生只是找到了销售电灯泡的方法!

实际上,特斯拉在他早期的职业生涯中曾经为爱迪生工作。

爱迪生许诺给他相当于现在的一百万美元的钱,去修复他的直流电发电机和电动机。

特斯拉修好了爱迪生的机器,当他去要许诺给他的钱时,爱迪生嘲笑他,并且说:

“特斯拉,你不懂我们的美国式幽默。”

爱迪生是一个很好的例子,本身不是 geek 却活跃在 geek 领域。

他相信他的发明的价值可以通过赚的钱来衡量。他既不是数学家,也不是科学家。因为他相信他可以雇用别人来帮他完成。

他不是 geek,他是 CEO。

特斯拉以发现令人惊讶的东西,而且忘了把它们记录下来而闻名。


爱迪生以只要他的雇员发现了什么东西他就匆忙往专利局去跑而闻名。


当他和爱迪生闹僵后,特斯拉去忙他的交流电电力系统了。


这对爱迪生和他之间的仇恨更是火上浇油,因为爱迪生那个时候正在向世界兜售他的直流电系统。


爱迪生的直流电系统每平方英里就需要一个发电机,而且不能把电力输送很远。交流电用的是更细的电线,更高的电压,能把电力进行远距离传输。


那爱迪生做了什么?

在爱迪生实验室附近住的邻居们开始注意到,他们的宠物消失了。这是因为学生们每捉到一只活的狗和猫,爱迪生就付给他们 25 分钱。

然后他把这些猫和狗放到展览中,然后公开地给它们通特斯拉的交流电。

他的目标是公开污蔑特斯拉的交流电,从而让公众相信家庭使用太危险了。

简而言之,爱迪生真正领先的东西是变态。

可曾听说过一个叫马可尼的人?

因为发明了无线电报他获得了诺贝尔物理学奖。

你可知道他做的所有东西都是基于特斯拉之前做的工作?在马可尼因为发了第一封无线电报而出名以后,特斯拉回复:

“马可尼是好样的。让他继续吧。他使用了我的17项专利。”

基本上,特斯拉是最和蔼的发明家。

可曾听说过雷达?


一个名叫沃特森·瓦特的英国科学家被公认为于1935年发明了雷达。


你能猜猜谁在1917年就有了这个主意?


尼古拉·特斯拉!

在第一次世界大战之初,他把它扔给了美国海军,当时世界正被迫屈服于德国的潜水艇的淫威。

不幸的是,托马斯**·爱迪生是当时美国海军的研发部领导,他设法让他们相信雷达在战争中没有实际用处。**

干得好,爱迪生!你这个自负、弄巧成拙的混蛋!

威廉·伦琴被归功于发现了X射线。

你能猜一猜哪个留八字胡的发明家比他捷足先登而且没有获得任何功劳?

尼古拉·**讨厌的·**特斯拉!

当X射线被发现之初,人们相信它能治疗失明和其它疾病,而特斯拉警告说X射线可能是危险的,他拒绝将它用于医学试验。

爱迪生(当黑他的机会就在眼前,我是不会错过的),却在从事X射线的人体试验。他的一个员工,Clarency Dally,接受了太多的放射性以至于为了挽救他的生命不得不对他的胳膊进行截肢。但这没有起作用,他最终还是死于纵隔肿瘤。

Dally 被认为是第一个死于放射试验的美国人。——最终,爱迪生发明了一些原创的东西!

除了杀死他的助手,爱迪生自己也近乎失明,因为眼睛反复接受X射线。当后来被问起X射线时,爱迪生如此回答:

“别和我提X射线,我怕它们。”——**托马斯·爱迪生**,1903年

大傻逼!

想知道谁在尼加拉瀑布上建造了第一个水力发电站,从而向世界证明了这种电力是可行的能量来源?

尼古拉·特斯拉!

谁在低温工程发明之前的近半个世纪就在试验它?

特斯拉!

谁在一百多年前就持有以后发展晶体管用到的专利?

特斯拉!

谁是第一个记录来自外太空的无线电波的人?

特斯拉!

谁发现了地球的共振频率?

特斯拉!

谁建造了一个地震机当它启动时差点摧毁整个纽约周边地区?

特斯拉!

听说过球状闪电?

它是球形外表的闪电,移动缓慢,徘徊在地面上几英尺。

它是一种及其稀少的现象,即使是今天也没有一个科学家能在实验室中成功造出它来。

哦,当然除了特斯拉在19世纪90年代成功了。

可曾想知道谁发明了远程控制?

特斯拉!

氖管照明?

特斯拉!

现代发电机?

特斯拉!

无线通信?

特斯拉!

你知道当你家需要电的时候它怎样能从地球电离层流下来给所有东西进行无线充电?

好吧,这是特斯拉发明了但没有和外界分享的东西,很可能他怕哪个缺乏创见的混蛋偷走他的专利。

毫无疑问,特斯拉是一个天才。

他能说八种语言:塞尔维亚语,英语,捷克语,德语,法语,匈牙利语,意大利语和拉丁语。

(我们多数人只会一种语言,而且也很糟糕)

他能记住整本书,而且随时能背出来。

(我们多数人连自己的密码都记不住)

他能完全在头脑中幻想出机器设备,而且不用写出来就可以建造它们。

(我们多数人只会花时间去幻想裸体的女人,和油腻的三明治这些东西)

让人印象更深刻的是,这个男人活到了86岁,一生独身。

尽管身高两米,而且很受女人欢迎,特斯拉拒绝约会,因为他认为这会打扰他的工作。

特斯拉(看照片):一个很帅的伙计,一生拒绝性感,因为他忙着在公寓中制造人造闪电。

P.S. **托马斯·爱迪生娶了一个16岁的女孩。**

P.P.S. 我发誓,这是我在漫画中最后一次黑爱迪生。

拥有难以置信的头脑和那么多发明,特斯拉应该很有钱很出名,对吧?

很不幸,不是!

特斯拉生活在一个急功近利的时代,当时我们不需要射电天文学,我们需要的是电灯泡和面包烤箱。

特斯拉的贡献不是添加性的,而是革命性的!

特斯拉奉献给世界的一个最后的礼物是纽约附近的一座塔楼,可以给整个地球提供无线能源。当了解到没有办法控制这个能源也就无法赚钱后,曾经资助建造这座塔的人关闭了它。

这种贪得无厌折磨着特斯拉大部分职业生涯,他花了很多精力去应付。并且,特斯拉也经历了混乱时代,我们通常称之为“精神失常”。

特斯拉时常产生幻觉,有时分不清现实和想象,这也是为什么他多年来一直独自待在实验室,夜以继日地工作。

特斯拉在孤独和伤心中去世,死于纽约一家宾馆的房间。他曾经以牛奶和国立饼干公司的饼干为生,在他最后的一个采访中,他展示了一个独特的个性:

“我曾经养过数千只鸽子好几年。但是只有一只鸽子,一只漂亮的鸟,纯白色,翅膀上有一些浅灰色。这只与众不同。它是母的。我发现她出现在所有地方,无论我在哪里她都能找到我。当我想她的时候我只要想一下,叫她,她就会飞向我。她了解我,我也了解她。

我爱上了那只鸽子。

我爱她就像一个男人爱一个女人,而且她也爱我。

当我看到她时,我知道她要告诉我,她快死了。然后,我从她那里得到消息,她眼睛中发出一道光,一道强光。”

靠饼干为生而且和一个想象中发出激光的鸽子对话?!

这就是特斯拉给人类做出的贡献的回报?!

亲爱的**尼古拉·特斯拉**,

我很抱歉,

非常非常抱歉!

你是生错了时间的人,

你是史上最伟大的 geek!

而且,当描述**托马斯·爱迪生时,英文中没有足够的名词去补充“人渣”这个词,但我会尝试一下:**

(省略若干脏字)

对于那些专门修补没有损坏的东西的人,考虑一下我的方式:

谢谢你,**尼古拉·特斯拉**!

我需要你的帮助!我正在尝试筹款去购买特斯拉的旧实验室,把它变成一个博物馆。