☆ 1. GNU Global 简介

GNU Global 是一种源代码标记系统,可以帮助程序员在代码库中快速定位函数、变量、宏定义等。GNU Global 可以生成索引文件,用于快速跳转到定义、引用、函数调用等地方,从而提高代码阅读和编辑的效率。

GNU global 具有以下优势:

  1. 多语言支持:在默认情况 GNU Global 支持 C, C++, Yacc, Java, PHP4 和 assembly 语言,在安装拓展的情况下可以支持 50多种语言。
  2. 完整性:GNU global 可以标记更多符号的位置,如定义、引用、结构体、类,宏定义等。
  3. 高效性:GNU global 生成高效的 tag 数据库,节省了磁盘空间,使得查询速度更快,同时也支持增量更新索引文件,这意味着当源代码发生变化时,只需要重新生成变更的部分,而不需要重新生成整个索引文件。
  4. 可集成:可以在 Emacs, Vi /vim, Less viewer, Bash shell, 浏览器环境下正常使用
  5. 持续更新: https://www.gnu.org/software/global/whatsnew.html

更多信息请参考: https://www.gnu.org/software/global/globaldoc_toc.html 1.3 Features

☆ 2. GNU Global 的安装

下载地址: https://ftp.gnu.org/pub/gnu/global/global-6.6.9.tar.gz

https://www.gnu.org/software/global/download.html 中说明了源码编译方法:

1
2
3
% sh reconf.sh 
% ./configure
% make

Fedora/Debian 等系统可以使用仓库直接安装

1
sudo dnf install global-ctags global cscope

如果不安装 global-ctags 在 Fedora 下可能会报 Cannot enable custom plug-in parser for GNU GLOBAL 的错误。

如果是系统仓库安装路径可能有变化,Fedora 上的 gtags.vim 路径为 /usr/share/gtags/gtags.vim , gtags-cscope 由 global 安装包提供不用另外安装了。

1
2
3
4
5
6
7
8
9
10
11
sudo dnf whatprovides gtags-cscope

global-6.6.5-7.fc37.x86_64 : Source code tag system
仓库 :@System
匹配来源:
文件名 :/usr/bin/gtags-cscope

global-6.6.5-7.fc37.x86_64 : Source code tag system
仓库 :fedora
匹配来源:
文件名 :/usr/bin/gtags-cscope

☆ 3. GNU global 和 cscope, vim 集成

vim 的 cscope 支持可能需要重新编译 vim,可以通过下面的命令查询。

1
2
vim --version | grep cscope
+cscope +localmap +ruby/dyn +wildignore

GNU global 提供了两个 vim 插件用于和 vim 结合,分别为 gtags.vim 和 gtags-cscope.vim,可以直接拷贝到 vim 的插件目录使用。

1
2
cp /usr/local/share/gtags/gtags.vim $HOME/.vim/plugin
cp /usr/local/share/gtags/gtags-cscope.vim $HOME/.vim/plugin

gtags-cscope.vim 定义了一组快捷键和以前的 cscope_maps.vim 一致

1
2
3
4
5
6
7
8
:nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>
:nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR>
:nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR>
:nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR>
:nmap <C-\>e :cs find e <C-R>=expand("<cword>")<CR><CR>
:nmap <C-\>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
:nmap <C-\>i :cs find i <C-R>=expand("<cfile>")<CR><CR>
:nmap <C-\>a :cs find a <C-R>=expand("<cword>")<CR><CR>

gtags-cscope.vim 的使用注释里有详细的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
" Usage
" -----
" First of all, you must execute gtags(1) at the root of source directory
" to make tag files. Assuming that your source directory is '/var/src',
" it is neccessary to execute the following commands.
"
" [Load vim]
" $ cd /var/src
" $ gtags
" $ vim
" [Load gtags-cscope]
" :GtagsCscope <ENTER> (in vim command line)
"
" Basic command
" -------------
" Then you can use cs commands except for the 'd'(2) command.
" Profitable commands are assigned to keys like follows:
"
" explanation command
" ----------------------------------------------------------
" Find symbol :cs find 0 or s
" Find definition :cs find 1 or g
" Find functions called by this function (not implemented)
" Find reference :cs find 3 or c
" Find text string :cs find 4 or t
" Find egrep pattern :cs find 6 or e
" Find path :cs find 7 or f
" Find include file :cs find 8 or i
" Find assignments :cs find 9 or a
"
" You can move tag list using:
" Go to the next tag :tn
" Go to the previous tag :tp
" Pop tag stack :pop

除了不能使用 :cs find d 命令,可以使用其他所有的命令,gtags-cscope.vim 使用快捷键替代了输入命令,常用的快捷键的含义如下:

1
2
3
4
5
<C-\>g - 查看光标下符号的定义
<C-\>s - 查看光标下符号
<C-\>c - 查看光标下符号的引用
<C-\>f - 查找光标下的文件
<C-\>i - 查找哪些文件 include 了本文件

<C-\>g 是同时按下 Ctrl 和 \ ,接着再按 g , 其他的同理。

在 .vimrc 中添加下面的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if has("cscope")
" To use the default key/mouse mapping:
let GtagsCscope_Auto_Map = 1

" To deterring interruption:
let GtagsCscope_Keep_Alive = 1

set cscopetag
set csprg=/usr/bin/gtags-cscope
set csto=1
set nocsverb

" Set enviroment values
let $GTAGSLABEL='native-pygments'

endif

☆ 4. GUN global 的使用方法

切换到源码目录,执行下面的命令将生成 Global 所需要的索引文件。

1
2
find . -type f -name "*.h" -o -name "*.hpp" -o -name "*.hh" -o -name "*.hxx" -o -name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.cxx" > gtags.files
gtags -f gtags.files

上面的命令执行完毕后,会生成 GPATH、GRTAGS、GTAGS 几个索引文件。

在 vim 中执行 :GtagsCscope <ENTER> 即可加载索引文件,最后就是使用快捷键愉快地浏览源码文件了。

参考链接

由于伟大的X火长城的存在,在境内进行 DNS 解析请求返回的报文不一定是靠谱的,有必要使用远程的服务器进行DNS 解析来缓解这个问题。根据 Google 的文档如果设置了 socks5 代理,URL 中的 hostname 将由代理服务器解析。

1
2
The --proxy-server="socks5://myproxy:8080" flag tells Chrome to send all http:// and https:// URL requests through the SOCKS proxy server "myproxy:8080",
using version 5 of the SOCKS protocol. The hostname for these URLs will be resolved by the proxy server, and not locally by Chrome.

但是由于 chrome 浏览器的 DNS prefetching 特性,即使设置了 socks5 代理 DNS prefetching 特性也会使用本地网络进行 DNS 解析。

1
2
3
4
The --proxy-server flag applies to URL loads only. 
There are other components of Chrome which may issue DNS resolves directly and hence bypass this proxy server.
The most notable such component is the "DNS prefetcher".
Hence if DNS prefetching is not disabled in Chrome then you will still see local DNS requests being issued by Chrome despite having specified a SOCKS v5 proxy server.

下面是禁用 DNS prefetching 的方法,新版本的 chrome 已经取消了直接针对 DNS prefetching 设置的选项,有一个预加载网页的选项,需要把这个选项关闭,具体设置方法为:

1
设置 -> 隐私设置和安全性 -> Cookie 及其他网站数据 -> 预加载网页,以便实现更快速的浏览和搜索

禁用 DNS prefetching 的方案是脆弱的,Chrome 可能在其他的地方使用 raw dns requsts ( Chrome 发送 DNS 请求报文), 绕过代理服务器直接使用本地网络进行 DNS 解析。

1
2
Disabling DNS prefetching would solve this problem, 
however it is a fragile solution since once needs to be aware of all the areas in Chrome which issue raw DNS requests.

所以最终的解决方案是使用 Google 文档中的命令行参数 --host-resolver-rules= ,经过测试可以使用下面的命令行来解决 DNS 远程解析

1
google-chrome --proxy-server="socks5://127.0.0.1:9999" --host-resolver-rules="MAP * 0.0.0.0, EXClUDE 127.0.0.1"

–host-resolver-rules 将阻止Chrome 使用本地网络发起 DNS 请求,具体的方式是将 DNS 请求转向一个不存在的地址,注意需要排除 sock5 代理的地址。

在实际使用过程中发现,如果不指定 –host-resolver-rules 命令行参数,有少量 DNS 请求会使用本地网络,比如 safebrowsing.googleapis.com

参考资料

https://www.chromium.org/developers/design-documents/network-stack/socks-proxy/

source: https://blog.samaltman.com/how-to-be-successful

I’ve observed thousands of founders and thought a lot about what it takes to make a huge amount of money or to create something important. Usually, people start off wanting the former and end up wanting the latter.

Here are 13 thoughts about how to achieve such outlier success. Everything here is easier to do once you’ve already reached a baseline degree of success (through privilege or effort) and want to put in the work to turn that into outlier success. [1] But much of it applies to anyone.

1. Compound yourself

Compounding is magic. Look for it everywhere. Exponential curves are the key to wealth generation.

A medium-sized business that grows 50% in value every year becomes huge in a very short amount of time. Few businesses in the world have true network effects and extreme scalability. But with technology, more and more will. It’s worth a lot of effort to find them and create them.

You also want to be an exponential curve yourself—you should aim for your life to follow an ever-increasing up-and-to-the-right trajectory. It’s important to move towards a career that has a compounding effect—most careers progress fairly linearly.

You don’t want to be in a career where people who have been doing it for two years can be as effective as people who have been doing it for twenty—your rate of learning should always be high. As your career progresses, each unit of work you do should generate more and more results. There are many ways to get this leverage, such as capital, technology, brand, network effects, and managing people.

It’s useful to focus on adding another zero to whatever you define as your success metric—money, status, impact on the world, or whatever. I am willing to take as much time as needed between projects to find my next thing. But I always want it to be a project that, if successful, will make the rest of my career look like a footnote.

Most people get bogged down in linear opportunities. Be willing to let small opportunities go to focus on potential step changes.

I think the biggest competitive advantage in business—either for a company or for an individual’s career—is long-term thinking with a broad view of how different systems in the world are going to come together. One of the notable aspects of compound growth is that the furthest out years are the most important. In a world where almost no one takes a truly long-term view, the market richly rewards those who do.

Trust the exponential, be patient, and be pleasantly surprised.

2. Have almost too much self-belief

Self-belief is immensely powerful. The most successful people I know believe in themselves almost to the point of delusion.

Cultivate this early. As you get more data points that your judgment is good and you can consistently deliver results, trust yourself more.

If you don’t believe in yourself, it’s hard to let yourself have contrarian ideas about the future. But this is where most value gets created.

I remember when Elon Musk took me on a tour of the SpaceX factory many years ago. He talked in detail about manufacturing every part of the rocket, but the thing that sticks in memory was the look of absolute certainty on his face when he talked about sending large rockets to Mars. I left thinking “huh, so that’s the benchmark for what conviction looks like.”

Managing your own morale—and your team’s morale—is one of the greatest challenges of most endeavors. It’s almost impossible without a lot of self-belief. And unfortunately, the more ambitious you are, the more the world will try to tear you down.

Most highly successful people have been really right about the future at least once at a time when people thought they were wrong. If not, they would have faced much more competition.

Self-belief must be balanced with self-awareness. I used to hate criticism of any sort and actively avoided it. Now I try to always listen to it with the assumption that it’s true, and then decide if I want to act on it or not. Truth-seeking is hard and often painful, but it is what separates self-belief from self-delusion.

This balance also helps you avoid coming across as entitled and out of touch.

3. Learn to think independently

Entrepreneurship is very difficult to teach because original thinking is very difficult to teach. School is not set up to teach this—in fact, it generally rewards the opposite. So you have to cultivate it on your own.

Thinking from first principles and trying to generate new ideas is fun, and finding people to exchange them with is a great way to get better at this. The next step is to find easy, fast ways to test these ideas in the real world.

“I will fail many times, and I will be really right once” is the entrepreneurs’ way. You have to give yourself a lot of chances to get lucky.

One of the most powerful lessons to learn is that you can figure out what to do in situations that seem to have no solution. The more times you do this, the more you will believe it. Grit comes from learning you can get back up after you get knocked down.

4. Get good at “sales”

Self-belief alone is not sufficient—you also have to be able to convince other people of what you believe.

All great careers, to some degree, become sales jobs. You have to evangelize your plans to customers, prospective employees, the press, investors, etc. This requires an inspiring vision, strong communication skills, some degree of charisma, and evidence of execution ability.

Getting good at communication—particularly written communication—is an investment worth making. My best advice for communicating clearly is to first make sure your thinking is clear and then use plain, concise language.

The best way to be good at sales is to genuinely believe in what you’re selling. Selling what you truly believe in feels great, and trying to sell snake oil feels awful.

Getting good at sales is like improving at any other skill—anyone can get better at it with deliberate practice. But for some reason, perhaps because it feels distasteful, many people treat it as something unlearnable.

My other big sales tip is to show up in person whenever it’s important. When I was first starting out, I was always willing to get on a plane. It was frequently unnecessary, but three times it led to career-making turning points for me that otherwise would have gone the other way.

5. Make it easy to take risks

Most people overestimate risk and underestimate reward. Taking risks is important because it’s impossible to be right all the time—you have to try many things and adapt quickly as you learn more.

It’s often easier to take risks early in your career; you don’t have much to lose, and you potentially have a lot to gain. Once you’ve gotten yourself to a point where you have your basic obligations covered you should try to make it easy to take risks. Look for small bets you can make where you lose 1x if you’re wrong but make 100x if it works. Then make a bigger bet in that direction.

Don’t save up for too long, though. At YC, we’ve often noticed a problem with founders that have spent a lot of time working at Google or Facebook. When people get used to a comfortable life, a predictable job, and a reputation of succeeding at whatever they do, it gets very hard to leave that behind (and people have an incredible ability to always match their lifestyle to next year’s salary). Even if they do leave, the temptation to return is great. It’s easy—and human nature—to prioritize short-term gain and convenience over long-term fulfillment.

But when you aren’t on the treadmill, you can follow your hunches and spend time on things that might turn out to be really interesting. Keeping your life cheap and flexible for as long as you can is a powerful way to do this, but obviously comes with tradeoffs.

6. Focus

Focus is a force multiplier on work.

Almost everyone I’ve ever met would be well-served by spending more time thinking about what to focus on. It is much more important to work on the right thing than it is to work many hours. Most people waste most of their time on stuff that doesn’t matter.

Once you have figured out what to do, be unstoppable about getting your small handful of priorities accomplished quickly. I have yet to meet a slow-moving person who is very successful.

7. Work hard

You can get to about the 90th percentile in your field by working either smart or hard, which is still a great accomplishment. But getting to the 99th percentile requires both—you will be competing with other very talented people who will have great ideas and be willing to work a lot.

Extreme people get extreme results. Working a lot comes with huge life trade-offs, and it’s perfectly rational to decide not to do it. But it has a lot of advantages. As in most cases, momentum compounds, and success begets success.

And it’s often really fun. One of the great joys in life is finding your purpose, excelling at it, and discovering that your impact matters to something larger than yourself. A YC founder recently expressed great surprise about how much happier and more fulfilled he was after leaving his job at a big company and working towards his maximum possible impact. Working hard at that should be celebrated.

It’s not entirely clear to me why working hard has become a Bad Thing in certain parts of the US, but this is certainly not the case in other parts of the world—the amount of energy and drive exhibited by entrepreneurs outside of the US is quickly becoming the new benchmark.

You have to figure out how to work hard without burning out. People find their own strategies for this, but one that almost always works is to find work you like doing with people you enjoy spending a lot of time with.

I think people who pretend you can be super successful professionally without working most of the time (for some period of your life) are doing a disservice. In fact, work stamina seems to be one of the biggest predictors of long-term success.

One more thought about working hard: do it at the beginning of your career. Hard work compounds like interest, and the earlier you do it, the more time you have for the benefits to pay off. It’s also easier to work hard when you have fewer other responsibilities, which is frequently but not always the case when you’re young.

8. Be bold

I believe that it’s easier to do a hard startup than an easy startup. People want to be part of something exciting and feel that their work matters.

If you are making progress on an important problem, you will have a constant tailwind of people wanting to help you. Let yourself grow more ambitious, and don’t be afraid to work on what you really want to work on.

If everyone else is starting meme companies, and you want to start a gene-editing company, then do that and don’t second guess it.

Follow your curiosity. Things that seem exciting to you will often seem exciting to other people too.

9. Be willful

A big secret is that you can bend the world to your will a surprising percentage of the time—most people don’t even try, and just accept that things are the way that they are.

People have an enormous capacity to make things happen. A combination of self-doubt, giving up too early, and not pushing hard enough prevents most people from ever reaching anywhere near their potential.

Ask for what you want. You usually won’t get it, and often the rejection will be painful. But when this works, it works surprisingly well.

Almost always, the people who say “I am going to keep going until this works, and no matter what the challenges are I’m going to figure them out”, and mean it, go on to succeed. They are persistent long enough to give themselves a chance for luck to go their way.

Airbnb is my benchmark for this. There are so many stories they tell that I wouldn’t recommend trying to reproduce (keeping maxed-out credit cards in those nine-slot three-ring binder pages kids use for baseball cards, eating dollar store cereal for every meal, battle after battle with powerful entrenched interest, and on and on) but they managed to survive long enough for luck to go their way.

To be willful, you have to be optimistic—hopefully this is a personality trait that can be improved with practice. I have never met a very successful pessimistic person.

10. Be hard to compete with

Most people understand that companies are more valuable if they are difficult to compete with. This is important, and obviously true.

But this holds true for you as an individual as well. If what you do can be done by someone else, it eventually will be, and for less money.

The best way to become difficult to compete with is to build up leverage. For example, you can do it with personal relationships, by building a strong personal brand, or by getting good at the intersection of multiple different fields. There are many other strategies, but you have to figure out some way to do it.

Most people do whatever most people they hang out with do. This mimetic behavior is usually a mistake—if you’re doing the same thing everyone else is doing, you will not be hard to compete with.

11. Build a network

Great work requires teams. Developing a network of talented people to work with—sometimes closely, sometimes loosely—is an essential part of a great career. The size of the network of really talented people you know often becomes the limiter for what you can accomplish.

An effective way to build a network is to help people as much as you can. Doing this, over a long period of time, is what lead to most of my best career opportunities and three of my four best investments. I’m continually surprised how often something good happens to me because of something I did to help a founder ten years ago.

One of the best ways to build a network is to develop a reputation for really taking care of the people who work with you. Be overly generous with sharing the upside; it will come back to you 10x. Also, learn how to evaluate what people are great at, and put them in those roles. (This is the most important thing I have learned about management, and I haven’t read much about it.) You want to have a reputation for pushing people hard enough that they accomplish more than they thought they could, but not so hard they burn out.

Everyone is better at some things than others. Define yourself by your strengths, not your weaknesses. Acknowledge your weaknesses and figure out how to work around them, but don’t let them stop you from doing what you want to do. “I can’t do X because I’m not good at Y” is something I hear from entrepreneurs surprisingly often, and almost always reflects a lack of creativity. The best way to make up for your weaknesses is to hire complementary team members instead of just hiring people who are good at the same things you are.

A particularly valuable part of building a network is to get good at discovering undiscovered talent. Quickly spotting intelligence, drive, and creativity gets much easier with practice. The easiest way to learn is just to meet a lot of people, and keep track of who goes on to impress you and who doesn’t. Remember that you are mostly looking for rate of improvement, and don’t overvalue experience or current accomplishment.

I try to always ask myself when I meet someone new “is this person a force of nature?” It’s a pretty good heuristic for finding people who are likely to accomplish great things.

A special case of developing a network is finding someone eminent to take a bet on you, ideally early in your career. The best way to do this, no surprise, is to go out of your way to be helpful. (And remember that you have to pay this forward at some point later!)

Finally, remember to spend your time with positive people who support your ambitions.

12. You get rich by owning things

The biggest economic misunderstanding of my childhood was that people got rich from high salaries. Though there are some exceptions—entertainers for example —almost no one in the history of the Forbes list has gotten there with a salary.

You get truly rich by owning things that increase rapidly in value.

This can be a piece of a business, real estate, natural resource, intellectual property, or other similar things. But somehow or other, you need to own equity in something, instead of just selling your time. Time only scales linearly.

The best way to make things that increase rapidly in value is by making things people want at scale.

13. Be internally driven

Most people are primarily externally driven; they do what they do because they want to impress other people. This is bad for many reasons, but here are two important ones.

First, you will work on consensus ideas and on consensus career tracks. You will care a lot—much more than you realize—if other people think you’re doing the right thing. This will probably prevent you from doing truly interesting work, and even if you do, someone else would have done it anyway.

Second, you will usually get risk calculations wrong. You’ll be very focused on keeping up with other people and not falling behind in competitive games, even in the short term.

Smart people seem to be especially at risk of such externally-driven behavior. Being aware of it helps, but only a little—you will likely have to work super-hard to not fall in the mimetic trap.

The most successful people I know are primarily internally driven; they do what they do to impress themselves and because they feel compelled to make something happen in the world. After you’ve made enough money to buy whatever you want and gotten enough social status that it stops being fun to get more, this is the only force I know of that will continue to drive you to higher levels of performance.

This is why the question of a person’s motivation is so important. It’s the first thing I try to understand about someone. The right motivations are hard to define a set of rules for, but you know it when you see it.

Jessica Livingston and Paul Graham are my benchmarks for this. YC was widely mocked for the first few years, and almost no one thought it would be a big success when they first started. But they thought it would be great for the world if it worked, and they love helping people, and they were convinced their new model was better than the existing model.

Eventually, you will define your success by performing excellent work in areas that are important to you. The sooner you can start off in that direction, the further you will be able to go. It is hard to be wildly successful at anything you aren’t obsessed with.

[1] A comment response I wrote on HN:

One of the biggest reasons I’m excited about basic income is the amount of human potential it will unleash by freeing more people to take risks.
Until then, if you aren’t born lucky, you have to claw your way up for awhile before you can take big swings. If you are born in extreme poverty, then this is super difficult :(

It is obviously an incredible shame and waste that opportunity is so unevenly distributed. But I’ve witnessed enough people be born with the deck stacked badly against them and go on to incredible success to know it’s possible.

I am deeply aware of the fact that I personally would not be where I am if I weren’t born incredibly lucky.

Thanks to Brian Armstrong, Greg Brockman, Dalton Caldwell, Diane von Furstenberg, Maddie Hall, Drew Houston, Vinod Khosla, Jessica Livingston, Jon Levy, Luke Miles (6 drafts!), Michael Moritz, Ali Rowghani, Michael Seibel, Peter Thiel, Tracy Young and Shivon Zilis for reviewing drafts of this, and thanks especially to Lachy Groom for help writing it.

以下是 DeepL 的翻译

标题: 如何获得成功

我观察了数以千计的创始人,并思考了很多关于赚取巨额金钱或创造重要事物所需的东西。通常情况下,人们一开始希望得到前者,最后却希望得到后者。

这里有13个关于如何实现这种超常成功的想法。一旦你已经达到了成功的基本程度(通过特权或努力),并想投入工作将其转化为离群的成功,这里的一切就更容易做到。[1] 但其中大部分内容适用于任何人。

  1. 复利自己

复利是一种魔法。到处寻找它。指数曲线是创造财富的关键。

一个中等规模的企业,如果每年的价值增长50%,就会在很短的时间内变得巨大。世界上很少有企业拥有真正的网络效应和极端的可扩展性。但随着技术的发展,会有越来越多的企业。 值得花大力气去寻找它们,创造它们。

你也想让自己成为一条指数曲线–你的目标应该是让你的生活遵循一条不断增加的向上和向右的轨迹。重要的是,要朝着具有复合效应的职业发展–大多数职业的发展是相当线性的。

你不希望在一个职业中,做了两年的人可以和做了二十年的人一样有效–你的学习率应该一直很高。随着你事业的发展,你所做的每一个单位的工作应该产生越来越多的结果。有很多方法可以获得这种杠杆作用,如资本、技术、品牌、网络效应和管理人。

不管你把什么定义为你的成功指标–金钱、地位、对世界的影响,或者其他什么,专注于再加一个零是很有用的。我愿意在项目之间花尽可能多的时间来寻找我的下一件事。但我总是希望它是一个项目,如果成功,将使我职业生涯的其余部分看起来像一个脚注。

大多数人在线性机会中陷入困境。要愿意让小的机会消失,以专注于潜在的步骤变化。

我认为商业上最大的竞争优势–无论是对公司还是对个人的职业生涯–是长期的思考,对世界上不同的系统如何结合起来有一个广泛的看法。复合增长的一个显著方面是,最远的年份是最重要的。在这个世界上,几乎没有人采取真正的长期观点,市场对那些这样做的人给予了丰厚的回报。

相信指数,要有耐心,并获得惊喜。

  1. 拥有几乎太多的自信心

自信的力量是巨大的。我认识的最成功的人对自己的信念几乎达到了妄想的程度。

尽早培养这种信念。当你得到更多的数据,证明你的判断力是好的,而且你能持续提供结果时,就更加相信自己。

如果你不相信自己,就很难让自己对未来有逆向的想法。但这是最能创造价值的地方。

我记得很多年前,埃隆-马斯克带我参观了SpaceX的工厂。他详细谈论了火箭的每一个部分的制造,但让我记忆犹新的是,当他谈到将大型火箭送往火星时,他脸上露出了绝对肯定的表情。我离开时想:”啊,原来这就是信念的基准。”

管理你自己的士气–以及你的团队的士气–是大多数工作的最大挑战之一。如果没有足够的自信心,这几乎是不可能的。不幸的是,你越有雄心壮志,世界就越想把你打倒。

大多数高度成功的人至少有一次在人们认为他们错了的时候,对未来的看法是真正正确的。如果不是这样,他们会面临更多的竞争。

自信必须与自我意识相平衡。我曾经讨厌任何形式的批评,并主动回避它。现在,我试着总是在假设它是真实的情况下听取它,然后决定是否要采取行动。寻求真理是困难的,而且常常是痛苦的,但它是区分自信心和自欺欺人的原因。

这种平衡也有助于你避免表现出有权和不合群的样子。

  1. 学会独立思考

创业精神是很难教的,因为原创性思维是很难教的。学校的设置不是为了教这个–事实上,它通常奖励相反的东西。所以你必须自己培养它。

从第一原则出发思考并试图产生新的想法是很有趣的,而找到与之交流的人是在这方面做得更好的一个好方法。下一步是找到简单、快速的方法,在现实世界中测试这些想法。

“我会失败很多次,而我真的会对一次 “是企业家的方式。你必须给自己很多机会来获得幸运。

要学习的最有力的课程之一是,你可以在似乎没有解决方案的情况下想出办法。你这样做的次数越多,你就会越相信它。勇气来自于学习你可以在被击倒后重新站起来。

  1. 善于 “销售”

光有自信心是不够的,你还必须能够说服其他人相信你的想法。

所有伟大的事业,在某种程度上,都成为销售工作。你必须向客户、未来的员工、媒体、投资者等宣扬你的计划。这需要一个鼓舞人心的愿景,强大的沟通技巧,一定程度的魅力,以及执行能力的证明。

善于沟通,特别是书面沟通,是一项值得投资的工作。我对清晰沟通的最佳建议是,首先确保你的思路清晰,然后使用平实、简明的语言。

做好销售的最好方法是真正相信你所销售的东西。销售你真正相信的东西感觉很好,而试图销售蛇油的感觉很糟糕。

擅长销售就像提高其他技能一样,任何人都可以通过刻意练习而变得更好。但出于某种原因,也许是因为它让人感到厌恶,许多人把它当作无法学习的东西。

我的另一个重要的销售建议是在重要的时候亲自出面。当我刚开始工作时,我总是愿意坐飞机。这经常是不必要的,但有三次它导致了我的职业生涯的转折点,否则就会走到另一个方向。

5.让人容易承担风险

大多数人高估了风险,低估了回报。承担风险是很重要的,因为你不可能一直都是正确的,你必须尝试很多东西,并在你学到更多东西时迅速适应。

在你职业生涯的早期,往往更容易承担风险;你没有什么损失,而你有可能获得很多。一旦你让自己的基本义务得到保障,你就应该试着让自己容易承担风险。寻找你可以做的小赌注,如果你错了,你会损失1倍,但如果成功了,会赚100倍。然后朝着这个方向做一个更大的赌注。

不过,不要积攒太久。在YC,我们经常注意到那些在谷歌或Facebook工作了很长时间的创始人的一个问题。当人们习惯了舒适的生活,可预测的工作,以及无论做什么都能成功的声誉时,就很难将其抛在脑后(人们有一种难以置信的能力,总是将自己的生活方式与明年的工资相匹配)。即使他们真的离开了,返回的诱惑也很大。将短期利益和便利性置于长期成就之上是很容易的,也是人类的天性。

但是,当你不在跑步机上时,你可以跟随你的直觉,把时间花在那些可能变成真正有趣的事情上。在尽可能长的时间内保持你的生活廉价和灵活是一个强大的方法,但显然是有代价的。

  1. 专注

专注是工作中的一种力量倍增器。

我见过的几乎每个人都会花更多的时间来思考该专注于什么。在正确的事情上工作比在许多时间上工作要重要得多。大多数人把大部分时间浪费在不重要的事情上。

一旦你想清楚了要做什么,就要势不可挡地迅速完成你那一小撮优先事项。我还没有见过一个行动缓慢的人非常成功。

  1. 努力工作

你可以通过聪明或努力工作达到你所在领域的第90个百分点,这仍然是一个伟大的成就。但是,要想达到第99百分位数,则需要两者兼备–你将与其他非常有才华的人竞争,他们会有很好的想法,并愿意付出很多努力。

极端的人得到极端的结果。大量工作伴随着巨大的生活权衡,决定不这样做是完全理性的。但它也有很多优点。就像在大多数情况下一样,势头会越来越大,成功会带来成功。

而且它往往非常有趣。生活中最大的乐趣之一是找到你的目的,在这方面表现出色,并发现你的影响比你自己更重要。一位YC的创始人最近表示非常惊讶,在离开大公司的工作后,他变得更加快乐和充实,并为自己的最大影响力而努力。在这一点上努力工作应该得到赞许。

我并不完全清楚为什么在美国的某些地方,努力工作已经成为一件坏事,但在世界其他地方肯定不是这样的–美国以外的企业家所表现出来的能量和动力正迅速成为新的基准。

你必须弄清楚如何努力工作而不至于筋疲力尽。人们为此找到自己的策略,但有一个几乎总是有效的策略是找到你喜欢的工作,与你喜欢花很多时间的人一起工作。

我认为那些假装你可以在职业上取得巨大成功而不需要大部分时间(在你生命中的某个时期)工作的人是在做一件坏事。事实上,工作耐力似乎是长期成功的最大预测因素之一。

关于努力工作还有一个想法:在你的职业生涯开始时就努力工作。艰苦的工作就像利息一样复利,你越早做,你就有越多的时间来获得回报。当你有较少的其他责任时,也更容易努力工作,这在你年轻的时候经常发生,但并不总是这样。

  1. 大胆一点

我相信,做一个艰难的创业公司比做一个容易的创业公司更容易。人们希望成为令人兴奋的事情的一部分,并感到他们的工作很重要。

如果你在一个重要的问题上取得了进展,你将会有一个持续的尾巴,人们想要帮助你。让自己变得更有野心,不要害怕从事你真正想做的工作。

如果其他人都在创办备忘录公司,而你想创办一家基因编辑公司,那就去做,不要猜测。

遵循你的好奇心。对你来说似乎很兴奋的事情,往往也会让其他人感到兴奋。

9.要有意志力

一个很大的秘密是,你可以在令人惊讶的时间内使世界屈服于你的意志,大多数人甚至没有尝试,而只是接受事情是他们的方式。

人们有一种巨大的能力来使事情发生。自我怀疑、过早放弃和不够努力等因素结合在一起,使大多数人无法达到接近其潜力的程度。

要求得到你想要的东西。你通常不会得到它,而且往往拒绝会很痛苦。但当这一方法奏效时,它的效果出奇地好。

几乎总是这样,那些说 “我要一直走下去,直到成功为止,不管有什么挑战,我都要把它们解决掉 “的人,并且是认真的,会继续取得成功。他们坚持了足够长的时间,给自己一个机会,让幸运降临到他们身上。

Airbnb是我在这方面的基准。他们有很多故事,我不建议尝试复制(把刷爆的信用卡放在孩子们用来装棒球卡的九槽三环夹子里,每顿都吃一元店的麦片,与强大的利益集团进行一场又一场的斗争,等等),但他们设法生存了足够长的时间,让运气顺着他们。

要想成为有意志力的人,你必须要乐观–希望这是一个可以通过实践来改善的人格特质。我从未见过一个非常成功的悲观主义者。

  1. 很难与之竞争

大多数人都明白,如果公司难以与之竞争,就更有价值。这很重要,显然也是事实。

但这对作为个人的你也是如此。如果你所做的事情可以由别人来做,那么最终就会由别人来做,而且花的钱更少。

变得难以与之竞争的最好方法是建立杠杆。例如,你可以通过个人关系,通过建立一个强大的个人品牌,或者通过在多个不同领域的交叉点上获得优势来做到这一点。还有许多其他策略,但你必须想出一些办法来做。

大多数人做他们所交往的大多数人做的事。这种模仿行为通常是个错误–如果你做的是别人都在做的事情,你将不难与之竞争。

  1. 建立一个网络

伟大的工作需要团队。发展一个由有才华的人组成的工作网络–有时是紧密的,有时是松散的–是伟大事业的一个重要组成部分。你所认识的真正有才华的人的网络的大小往往成为你能取得成就的限制因素。

建立网络的一个有效方法是尽可能地帮助别人。在很长一段时间内,这样做是导致我最好的职业机会和我四个最好的投资中的三个的原因。我不断感到惊讶,因为我十年前帮助一位创始人的事情,经常有好事发生在我身上。

建立网络的最好方法之一是建立一个真正照顾到与你合作的人的声誉。要过分慷慨地分享好处;这将会给你带来10倍的回报。另外,学会如何评估人们的长处,并让他们担任这些职务。(这是我在管理方面学到的最重要的东西,而我并没有读过很多这方面的书)。你要有一个口碑,那就是把人逼得够狠,使他们的成就超过他们的想象,但又不至于让他们倦怠。

每个人在某些方面都比其他人强。用你的优势来定义你自己,而不是你的弱点。承认你的弱点并想办法解决它们,但不要让它们阻止你做你想做的事。”我不能做X,因为我不擅长Y”,这是我经常从企业家那里听到的,而且几乎总是反映出缺乏创造力。弥补你的弱点的最好方法是雇用互补的团队成员,而不是仅仅雇用那些擅长你所做的同样事情的人。

建立网络的一个特别有价值的部分是要善于发现未被发现的人才。通过练习,快速发现智慧、动力和创造力变得更加容易。最简单的学习方法就是认识很多人,并跟踪谁会给你留下深刻印象,谁不会。记住,你主要是在寻找改进的速度,不要高估经验或当前的成就。

当我遇到新的人时,我试着总是问自己:”这个人是一个自然的力量吗?” 这是一个相当好的启发式方法,可以找到那些有可能完成伟大事业的人。

发展网络的一个特殊情况是找到知名人士为你下注,最好是在你职业生涯的早期。要做到这一点,毫不奇怪,最好的办法是不遗余力地提供帮助。(请记住,你必须在以后的某个时间点上把这些钱交出来!)。

最后,记得把你的时间花在支持你雄心壮志的积极人士身上。

  1. 你通过拥有东西而致富

我童年时最大的经济误区是人们通过高薪致富。虽然有一些例外–例如娱乐界人士–但在福布斯榜单的历史上,几乎没有人是靠工资获得的。

你通过拥有快速增值的东西来获得真正的财富。

这可以是一个企业的一部分,房地产,自然资源,知识产权,或其他类似的东西。但无论如何,你需要拥有某些东西的股权,而不是仅仅出售你的时间。时间只是线性扩展的。

制造快速增值的东西的最好方法是大规模制造人们想要的东西。

  1. 要有内部驱动力

大多数人主要是受外部驱动;他们做他们所做的事是因为他们想给其他人留下深刻印象。这有很多不好的原因,但这里有两个重要的原因。

首先,你将在协商一致的想法和协商一致的职业轨道上工作。 你会非常关心–比你意识到的要多得多–其他人是否认为你在做正确的事情。这可能会阻止你做真正有趣的工作,即使你做了,别人也会做。

第二,你通常会把风险计算弄错。你会非常专注于跟上别人的步伐,在竞争性游戏中不掉队,即使是在短期内。

聪明人似乎特别容易出现这种外在驱动的行为。意识到这一点有帮助,但只是一点点–你很可能要付出超强的努力才不会落入模仿的陷阱。

我所知道的最成功的人主要是由内部驱动的;他们所做的事情是为了给自己留下深刻印象,也是因为他们感到有必要在这个世界上有所作为。在你赚够了钱,可以买到你想要的任何东西,得到了足够的社会地位,不再以获得更多的东西为乐趣之后,这是我所知道的唯一的力量,会继续推动你达到更高的表现水平。

这就是为什么一个人的动机问题是如此重要。这是我试图了解一个人的第一件事。正确的动机很难定义一套规则,但当你看到它时,你就会知道它。

杰西卡-利文斯顿和保罗-格雷厄姆是我这方面的标杆。YC在最初几年被广泛嘲笑,在他们刚开始的时候几乎没有人认为它会大获成功。但他们认为,如果它能成功,对世界来说是件好事,而且他们喜欢帮助人,他们坚信他们的新模式比现有模式更好。

最终,你将通过在对你很重要的领域进行出色的工作来定义你的成功。你越早朝这个方向起步,你就能走得越远。你很难在你不痴迷的事情上取得巨大的成功。

[1] 我在HN上写的一个评论回应。

我对基本收入感到兴奋的最大原因之一是,它将通过释放更多的人去冒险而释放出大量的人类潜力。
在此之前,如果你不是天生的幸运儿,你必须在大摇大摆之前努力奋斗一阵子。如果你出生在极端贫困地区,那么这就超级困难了。

机会分配如此不均,显然是一种难以置信的耻辱和浪费。但是,我已经目睹了足够多的人出生在对他们非常不利的环境中,并取得了令人难以置信的成功,所以我知道这是可能的。

我深深地意识到,如果我不是生来就非常幸运,我个人就不会有现在的成就。

感谢布莱恩-阿姆斯特朗、格雷格-布罗克曼、道尔顿-考德威尔、戴安-冯-弗斯滕伯格、麦迪-霍尔、德鲁-休斯顿、维诺德-科斯拉、杰西卡-利文斯顿、乔恩-利维、卢克-迈尔斯(6稿!)、迈克尔-莫里茨、阿里-罗格尼、迈克尔-塞贝尔、彼得-泰尔、特蕾西-杨和希文-齐利斯对本稿的审核,特别感谢拉奇-格罗姆帮助撰写。

source: https://www.swyx.io/learn-in-public/

  • a habit of creating learning exhaust
    • Write blogs and tutorials and cheatsheets.
    • Speak at meetups and conferences.
    • Ask and answer things on Stackoverflow or Reddit.
      • Avoid the walled gardens like Slack and Discord, they’re not public.
    • Make Youtube videos or Twitch streams.
    • Start a newsletter.
    • Draw cartoons (people loooove cartoons!)
  • Whatever your thing is, make the thing you wish you had found when you were learning.
  • Don’t judge your results by “claps” or retweets or stars or upvotes
  • Oh you think you’re done? Don’t stop there:
    • Enjoyed a coding video? Reach out to the speaker/instructor and thank them, and ask questions.
    • Make PR’s to libraries you use.
    • Make your own libraries no one will ever use.
    • Clone stuff you like, from scratch, to see how they work.
    • Teach workshops.
    • Go to conferences and summarize what you learned.
  • The subheading under this rule would be: Try your best to be right, but don’t worry when you’re wrong.
    • People think you suck? Good. You agree. Ask them to explain, in detail, why you suck
    • You want to just feel good or you want to be good?
      • Then go away and prove them wrong. Of course, if they get abusive block them.
  • At some point you’ll get some support behind you. People notice genuine learners. They’ll want to help you.
    • Don’t tell them, but they just became your mentors.
    • This is very important: Pick up what they put down
    • Because you learn in public. By teaching you, they teach many.

使用 Logseq 写笔记已经大半年了,Logseq 是这么多年来除了 vim 之外,唯一可以在使用上螺旋上升的软件。我把 Logseq 推荐给媳妇,她用来写会议纪要,写备忘录,管理待办事项,也用得挺好。

Logseq 的成功之处在于它降低了记录的成本,不需要大段大段的文字,也不需要太多的文章结构,随时都可以记录。Logseq 比较严重的问题是它的使用太灵活了,前后记录的标准、格式、标签等要素可能不一致,容易造成笔记凌乱。笔记一凌乱就没法有效的聚合,笔记的有效聚合形成一个有意义的想法群是 Logseq 等双链软件和其他笔记软件相比最有价值的地方。

我这半年来总结出笔记系统成功的关键是: 在同一个地方,用同样的格式和一致的标准记录你的洞见。但是由于 Logseq 缺乏有效的工作流,没法自动化提示必要的步骤,只能通过不断的练习来强化学习。另外 Logseq 中 page 和 tag 只是起到过滤笔记的作用,最关键的笔记之间的链接却很容易被忽视,tag 的数量一多又会开始笔记凌乱了。

总而言之,如果只是把 Logseq 作为备忘,或者简单作为信息记录,挺好用也不需要学习太多的东西,但是如果需要使用 Logseq 来实践卡片笔记法等 PKM 理论,恐怕还是需要花费一些功夫仔细琢磨一番。PKM 方面的书籍我强力推荐 《卡片笔记写作法》一书,中文翻译很不错,虽然书名是谈写作,里面的内容却涉及很广,我自己阅读后收益良多。

就实践卡片笔记法来说,浮墨是一个比较理想的工具,「少即是多,多则惑」,去掉一切不需要的功能,重点才能突出。Logseq 的野心挺大,不想只做一个双链笔记本,还想做得更多,这就要求我们想清楚使用其的主要目的是什么。现实中并不存在一个 all in one 的理想工具,什么工具合适就可以使用什么工具,各位施主不能 「着相」了。

上面说的是一些「道」层面的东西, Logseq 在「术」方面的东西也不少,下面将介绍一些前两篇尚未涉及的功能和使用技巧。

☆ 使用 properties

https://docs.logseq.com/#/page/term%2Fproperties

在 Logseq 中有两种 properties, page properties 和 block properties。

  • Page properties 在第一个 block 写 property:: value
  • Block properties 在每个 block 内使用 property:: value

在使用 block properties 时,需要输入 shift + Enter 来进行换行,换行后输入 :: Logseq 会自动提示已经使用过的 property。理解 properties 可以简单的把 block 看成数据库的一条记录, properties 就是不同的字段,可以通过 Logseq 的 query 来查询相关记录。在使用 properties 时可以引用 page 或者 tag,可以通过这种方法来索引这条记录。

例如,我将书籍的 block 都添加了 category:: Books 的属性,后续可以使用 {{query (property category Books) }} 来查询所有书籍了,感觉上有点类似 notion 的表格。

☆ 使用 /Scheduled 制定计划

使用 Schedule 的功能的方法比较简单,在写完一条 block 后,输入 shift + enter 换行后,输入 /Scheduled 在弹出的界面中选择时间周期即可。

在 Schedule 规定的时间内,Logseq 的 Journals 页面可以看到相关的内容。

☆ 使用模板 (Template)

https://docs.logseq.com/#/page/templates

使用模板的好处是可以避免重复性的劳动,比如你希望在 Journals 页面里按照 [[每日工作]] [[每日心得]] 的固定格式来记录,那么你需要在每天重复输入一次。但如果你制作一个固定模板,就可以使用 /Template 命令来完成自动化的输入。

制作模板的过程可以参考 Logseq 官方的动画,简单说就是写一个 block,这个 block 包含了必要的结构,在 block 前面的圆点点击鼠标右键 -> Make template,再输入模板的名字就完成了。在后续的使用过程中,输入 /Template 后在弹出的界面选择对应的模板名就可以完成自动化的输入。

☆ 一些好用的插件

插件可以拓展 Logseq 的功能,使用插件的方法在 Logseq 使用小结 (二) 中有详细介绍,这里就不再细说。

Logseq Plugin Tabs

https://github.com/pengx17/logseq-plugin-tabs

Plugin Tabs 像打开浏览器一样打开 pages 或者 blocks,对于内容比较多的 page,这个插件非常实用。

logseq-plugin-mark-map

https://github.com/vipzhicheng/logseq-plugin-mark-map

Mark map 可以自动化地把你的笔记用导图的形式输出,方便展示和记忆。

☆ 一些使用上的小技巧

  • Logseq content wide mode 可以使用快捷键切换 t w
  • Logseq 的 property 可以通过输入 :: 来触发自动补全操作,非常方便。
  • Logseq 使用快捷键 Cmd + Shift + i 可以打开 devtools
  • Logseq v0.68,自动补全提供两个快捷键 mod+pmod+n 比较方便了
  • Logseq 可以使用 command + command - 来放大或者缩小 Logseq 的字体大小
  • Logseq 0.7.1 新功能:Copy & Paste with rich-text formats,如果粘贴时不想保留格式 mod + shift + v
  • Logseq 移动光标的快捷键:mod + p 向上移动光标,mod + n 向下移动光标
  • Logseq Markdown 语法支持 Horizontal Rules,可以使用 ---分隔卡片笔记。

mod 在 windows 和 linux 系统中是 ctrl, 在 macOS 系统中是 command

☆ 参考链接

https://www.usmacd.com/2022/02/21/logseq/
https://www.usmacd.com/2022/03/07/logseq2/

  • Random Notes (漫游笔记) 的技术原理是用新视角去审视旧想法,和自己以前的想法不期而遇的感觉非常奇妙。总体来说人是会不断进步的,经过一段时间后再去重温自己某个时刻的想法,可能以前的一些疑惑现在已经没有了,也可能对某个问题有了更进一步的思路,这是促使想法迭代的一种方法。

  • 想要做到想法的自然迭代,必要的条件是可以马上回到过去的某个时刻,而且那个时刻的上下文没有丢失,如果上下文丢失意味着已经看不懂以前的笔记了,自然也就没有了迭代的可能性。

  • 卡片盒笔记法要求用完整的句子精要简述想法,笔记之间的连接则补充了上下文,为想法迭代创造了有利条件。

  • 写永久笔记时,应该假设读者对文本背后的思想,原文背景一无所知,只具备相关的领域知识。这里的读者其实也包括未来的自己,我们写下永久笔记后,很快就会将其的上下文遗忘,就想从没有见过该笔记的其他读者一样。

  • 我们应该寻找与我们观点相反的论点或者事实来挑战我们的既有思维,但是受「确认偏差」的影响,我们会不经意的忽略这些信息,那些和我们观点一致的信息有很大吸引力,因为这些信息可以证明自己很博学。解决 「确认偏差」的方法:把寻找证实性信息变为收集所有信息,不需要关注这些信息支持的具体观点。

  • 我们使用卡片盒笔记法,对习惯最重要的转变是将注意力从个别项目的预设立场变成思考笔记之间的开放性联系。通过一段时间的训练,在阅读过程中我们可以轻松找出与预设观点相悖的信息。当这些信息改变我们对某些问题的看法时,我们会感到相当兴奋,从而慢慢喜欢上这种感觉。

  • 在实践卡片笔记法的过程中,会遇上一整天都没写一张卡片的时候,而有的时候则一天写十多张卡片。限制每天写笔记的数量可以让写卡片盒笔记的过程更加轻松,避免了过于放松的状态,也避免了过于紧绷的状态,有利于我们保持良好的状态。另一个好处则是让你选择笔记内容时更加挑剔,有利于提高笔记的质量。

  • 你可以将目标设置为每天写3条笔记,或者每年出一本书,仍然可以在合理的时间内积累大量的想法。

  • 卡片笔记法属于自下而上的发散思维,发散思维有个问题,就是无法把握发散的度。为了应对这个问题,卢曼提出的办法是仔细思考笔记之间的联系,写笔记时参考文献笔记和自己以前写的笔记,定期自上而下制作索引卡片,这些做法的目的是避免想法过于太发散。

  • 思维需要发散,但是领域需要聚焦,我们在自己的专业,财务等领域是需要不断精进的,这两个方面似乎是矛盾的。有人建议使用分类法,把关注的领域分类,在日常工作学习中不断完善,但这种方法很容易回到资料归档的老路。

  • 最近的研究发现可以使用 P.A.R.A. 来组织顶层结构,统领需要精进的领域,在具体想法的记录上则继续使用卡片笔记法。这种方法使用 P.A.R.A. 不断推动自己在关注领域的取得进展,又能在关键的思考上使用卡片笔记法,自用一段时间感觉不错。

  • 关于笔记管理存在几个误区,1. 笔记软件/博客系统,我先后使用过 EverNote,WizNote,VNote,CSDN blog,Google blogspot, WordPress 2. 笔记格式,我先后使用过 txt, orgmode, markdown 其实这些都不是关键,除了折腾还是折腾。以前我常说一个段子,你以为你在写博客,其实你在折腾 Wordpress 插件。

  • 我以前思考这个问题认为是笔记缺乏组织结构,实践结果表明良好的分类确实有一定帮助,但是不是最重要的。最重要的是在同一个地方,用同样的格式和一致的标准记录你的洞见。

  • 把笔记保存在同一个地方避免了笔记分散,迁移也比较方便。同样的格式和一致的标准则为笔记之间的联系与聚合创造了有利条件。如果笔记之间格式不一致,粒度不一致,不容易产生新的想法。另外,当我们以一致的标准写笔记时阻力是最小的,关注的是笔记的内容,形式等外在的东西则变得不再重要。

  • 新的观点、新的洞见、新的想法,是笔记系统中最有价值的东西。笔记系统应该侧重于知识的积累而不是信息的积累。

  • 各种笔记软件,博客系统,笔记格式的切换都会带来信息丢失。信息丢失容易造成心理负担,我从 2005 年开始写文章到现在已经丢失了 50%以上,这些文章散落在互联网的各个角落,非常令人痛心。

  • 卢曼的卡片盒笔记系统使用固定编号的方法标记唯一的一条笔记,这种编号系统有很大的优势。
    a) 固定编号使笔记之间能够相互连接。
    b) 固定编号使笔记能够任意的内部分叉 (internal branching),内部生长 (internal growth)。
    c) 由于缺乏系统的顺序,需要维护关键字登记册,登记册用于来查找笔记,固定编号对于登记册来说是不可或缺的。
    d) 我们从文献中摘录的书目信息需要保存在索引卡片中,在写笔记的时候可以用固定编号连接指定的书籍、论文、文章等。

  • 卢曼的卡片盒笔记系统不使用预先设定的系统的次序,不按照主题、子主题的方式存放笔记,而是使用固定编号的系统结构。
    a) 卢曼认为卡片盒笔记系统和我们自己是不断发展的,不可能提前几十年就把笔记束缚在某个次序上。
    b) 使用固定编号的方法,将减少我们安排笔记位置的复杂性,使我们可以创造高度复杂的笔记卡片。
    c) 使用固定编号是任意基于内容的系统结构的抽象,将使我们的卡片和笔记系统达到一个更高类型的秩序。

未完,待续 …

有关金融市场方面的书我个人的图书馆里至少有1200本。它们主要是讨论证券分析、期权策略、期货策略、技术分析以及其他相关方面的书。这些书大多都包含相当不错的构想,其中约2%是真正优秀的作品。然而,它们大多存在一个共同的问题:试图推销一种“战胜市场”的方法,很多方法甚至没有经过实际市场的完整测试。

如何正确的绘制趋势线

使用以下方法绘制趋势线可以提供一致而精确的结果,而且绝对不会产生错误的信号:

  1. 选择考虑的时间:长期(数月至数年)、中期(数个星期至数月)或短期(数天至数个星期)。
  2. 上升趋势线:在考虑的时间内,以最低的低点为起点,向右上方绘制一条直线,连接最高点前的某一个低点,使这条直线在两个低点之间未穿越任何价位。延伸这条直线而经过最高点(这是指水平轴上的位置而言)。趋势线经过所考虑的最高点以后,它可能穿越某些价位。事实上,这是趋势发生变化的一种现象。
  3. 下降趋势线:在考虑的时间内,以最高的高点为起点,向右下方绘制一条直线,连接最低点前的某一个高点,而使这条直线在两个高点之间未穿越任何价位。延伸这条直线而经过最低点。

这个方法的优点在于其明确性,并可以用来判断趋势是否可能变动或已经变动。

确立趋势变动的 123 法则

  • 股价突破趋势线
  • 上升趋势回档后,不创新高;下降趋势反弹后,不创新低
  • 上升趋势跌破上一个回档低点,下降趋势突破上一个反弹高点

风险报酬比很不错的 2B法则

上升趋势中价格创新高后,并未持续上涨,而后又跌破先前的高点,则趋势很可能反转。

具有统计学优势的四天准则

在中期走势中,当市场在高点或者低点以连续四天下跌或上涨的走势而呈反转时,趋势可能发生变化。 (和趋势方向相反的连续四根K线)

四天准则辅助准则

当中期走势发展到相当程度以后,如果出现顺势的四天(或者以上)排列,随后出现第一天的逆趋势行情,经常代表趋势变动的顶部或者底部。

三天高低价准则

三天的高低价准则,是运用最近三天的盘中高价与低价。当价格发生反转而穿越三天的高价或低价时,则做多或卖空,并以第三天的低价或高价作为止损点。 这个准则必须配合另一个确认原则。

缺口准则

缺口准则很简单,当趋势线上方或者下方出现缺口时,它反映重大变化(消息面或基本面),并显示趋势可能发生变动。该准则并不需要2B,也不需要经过试探(趋势线)。
然而,缺口必须穿越趋势线,该准则才有效。 这个准则必须配合另一个确认原则。

0. 下载 Visual Studio Community 2022

下载地址:https://aka.ms/vs/17/release/vs_community.exe

从微软网站下载并安装 Visual Studio Community 的时候速度只有2 K,一下午没能下载成功。咨询同事小钻风,指出可能是 DNS 问题,将 DNS 切换成阿里的 DNS 223.5.5.5 后下载速度达到 4 M。中国开发者网络总是个问题,大家都挺不容易的。

1. 编译 winafl

  1. 下载 DynamoRIO 解压到 D:\DynamoRIO
  2. 下载 cmake 解压到 D:\cmake
  3. 从 Github 下载 winafl 编译 intel PT 需要同步 third_party 的源码
1
2
3
git clone https://github.com/googleprojectzero/winafl.git
cd winafl
git submodule update --init --recursive
  1. 从菜单中选择 X64 native tool command prompt for VS 2022,执行编译命令
1
2
3
4
mkdir build64
cd build64
D:\cmake\bin\cmake.exe -G"Visual Studio 17 2022" -A x64 .. -DDynamoRIO_DIR=D:\DynamoRIO\cmake -DINTELPT=1
D:\cmake\bin\cmake.exe --build . --config Release

-DINTelPT=1 参数将编译 Intel PT 模式的支持,编译完成后,可以正常运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
.\build64\bin\Release>.\afl-fuzz.exe

WinAFL 1.16b by <ifratric@google.com>
Based on AFL 2.43b by <lcamtuf@google.com>

.\afl-fuzz.exe [ afl options ] -- [instrumentation options] -- \path\to\fuzzed_app [ ... ]

Required parameters:

-i dir - input directory with test cases
-o dir - output directory for fuzzer findings
-t msec - timeout for each run

Instrumentation type:

-D dir - directory with DynamoRIO binaries (drrun, drconfig)
-w winafl - Path to winafl.dll
-P - use Intel PT tracing mode
-Y - enable the static instrumentation mode

Execution control settings:

-f file - location read by the fuzzed program (stdin)
-m limit - memory limit for the target process
-p - persist DynamoRIO cache across target process restarts
-c cpu - the CPU to run the fuzzed program

Fuzzing behavior settings:

-d - quick & dirty mode (skips deterministic steps)
-n - fuzz without instrumentation (dumb mode)
-x dir - optional fuzzer dictionary (see README)

Other stuff:

-I msec - timeout for process initialization and first run
-T text - text banner to show on the screen
-M \ -S id - distributed mode (see parallel_fuzzing.txt)
-C - crash exploration mode (the peruvian rabbit thing)
-e - expert mode to run WinAFL as a DynamoRIO tool
-l path - a path to user-defined DLL for custom test cases processing
-V - show version number and exit

Attach:

-A module - attach to the process that loaded the provided module

For additional tips, please consult afl_docs\README.


C:\Users\zhouzhen\Downloads\winafl\build64\bin\Release>

2. 检查 DynamoRIO 模式是否正常工作

使用 IDA Pro 对 test_gdiplus.exe 进行反汇编,target_offset 为 0x10E0

1
2
3
.text:00000001400010E0 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00000001400010E0 main proc near ; CODE XREF: __scrt_common_main_seh(void)+107↓p
.text:00000001400010E0 ; DATA XREF: .rdata:0000000140002B6C↓o ...

先用 drrun.exe 测试 winafl 是否正常工作,其实就是循环执行 10 次目标程序。

1
2
3
D:\DynamoRIO\bin64\drrun.exe -c winafl.dll -debug ^
-target_module test_gdiplus.exe -target_offset 0x10E0 -fuzz_iterations 10 ^
-nargs 2 -- test_gdiplus.exe input.bmp

执行命令后,在当前目录下会生成一个 log 文件,文件名为 afl.test_gdiplus.exe.14172.0000.proc.log,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Module loaded, dynamorio.dll
Module loaded, winafl.dll
Module loaded, drx.dll
Module loaded, drreg.dll
Module loaded, drmgr.dll
Module loaded, drwrap.dll
Module loaded, test_gdiplus.exe
Module loaded, gdiplus.dll
Module loaded, VCRUNTIME140.dll
Module loaded, msvcp_win.dll
Module loaded, win32u.dll
Module loaded, ucrtbase.dll
Module loaded, KERNELBASE.dll
Module loaded, gdi32full.dll
Module loaded, KERNEL32.dll
Module loaded, msvcrt.dll
Module loaded, IMM32.dll
Module loaded, RPCRT4.dll
Module loaded, GDI32.dll
Module loaded, combase.dll
Module loaded, USER32.dll
Module loaded, ntdll.dll
In pre_fuzz_handler
Module loaded, UxTheme.dll
Module loaded, OLEAUT32.dll
Module loaded, bcrypt.dll
Module loaded, SECHOST.dll
Module loaded, ADVAPI32.dll
Module loaded, WindowsCodecs.dll
Module loaded, MSCTF.dll
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
In pre_fuzz_handler
In post_fuzz_handler
Everything appears to be running normally.
Coverage map follows:

Everything appears to be running normally. 表明 winafl 自己认为没有出错。 log 文件中还包含了其他有用信息,比如说目标程序加载的模块。这些加载的模块可以用来设置 -coverage_module来统计你所关注的 coverage。

使用 drrun.exe 验证成功后,可以执行下面命令测试 winafl 是否正常工作。

1
2
3
afl-fuzz.exe -i in -o out -D D:\DynamoRIO\bin64 -t 20000 ^
-- -coverage_module gdiplus.dll -coverage_module WindowsCodecs.dll -fuzz_iterations 5000 ^
-target_module test_gdiplus.exe -target_offset 0x10E0 -nargs 2 -- test_gdiplus.exe @@

执行后上面的命令后,如果发现 total paths 的数字在不断变化, winafl 就已经可以正常工作了,恭喜 😄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+- process timing -------------------------------------+- overall results ----+
| run time : 0 days, 0 hrs, 0 min, 1 sec | cycles done : 0 |
| last new path : 0 days, 0 hrs, 0 min, 1 sec | total paths : 2 |
| last uniq crash : none seen yet | uniq crashes : 0 |
| last uniq hang : none seen yet | uniq hangs : 0 |
+- cycle progress --------------------+- map coverage -+----------------------+
| now processing : 0 (0.00%) | map density : 1.65% / 1.65% |
| paths timed out : 0 (0.00%) | count coverage : 1.00 bits/tuple |
+- stage progress --------------------+ findings in depth --------------------+
| now trying : arith 8\8 | favored paths : 1 (50.00%) |
| stage execs : 388/389 (99.74%) | new edges on : 2 (100.00%) |
| total execs : 619 | total crashes : 0 (0 unique) |
| exec speed : 353.3/sec | total tmouts : 0 (0 unique) |
+- fuzzing strategy yields -----------+---------------+- path geometry -------+
| bit flips : 0/56, 1/55, 0/53 | levels : 2 |
| byte flips : 0/7, 0/6, 0/4 | pending : 2 |
| arithmetics : 0/0, 0/0, 0/0 | pend fav : 1 |
| known ints : 0/0, 0/0, 0/0 | own finds : 1 |
| dictionary : 0/0, 0/0, 0/0 | imported : n/a |
| havoc : 0/0, 0/0 | stability : 99.63% |
| trim : 0.00%/1, 0.00% +-----------------------+
^C----------------------------------------------------+ [cpu000001: 14%]

3. DynamoRIO 模式 winafl.dll 各个参数的具体含义

winafl.dll 是 DynamoRIO client (instrumentation) code,参数挺多 Winafl 的 README 中介绍了下面这些参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-covtype         - the type of coverage being recorded. Supported options are
bb (basic block, default) or edge.

-coverage_module - module for which to record coverage. Multiple module flags
are supported.

-target_module - module which contains the target function to be fuzzed.
Either -target_method or -target_offset need to be
specified together with this option.

-target_method - name of the method to fuzz in persistent mode. For this to
work either the method needs to be exported or the symbols
for target_module need to be available. Otherwise use
-target_offset instead.

-target_offset - offset of the method to fuzz from the start of the module.

-fuzz_iterations - Maximum number of iterations for the target function to run
before restarting the target process.

-nargs - Number of arguments the fuzzed method takes. This is used
to save/restore the arguments between runs.

-call_convention - The default calling convention is cdecl on 32-bit x86
platforms and Microsoft x64 for Visual Studio 64-bit
applications. Possible values:
* fastcall: fastcall
* ms64: Microsoft x64 (Visual Studio)
* stdcall: cdecl or stdcall
* thiscall: thiscall

-debug - Debug mode. Does not try to connect to the server. Outputs
a log file containing loaded modules, opened files and
coverage information.

-logdir - specifies in which directory the log file will be written
(only to be used with -debug).

-thread_coverage - If set, WinAFL will only collect coverage from a thread
that executed the target function
  • -nargs target method 或者 target offset 的参数个数
  • -fuzz_iterations 重启进程前,循环执行 target method 或者 target offset 的次数
  • -target_moudle fuzz 的目标模块,需要和 target method 或者 target offset 一起使用
  • -target_method persistent mode 下 fuzz 的函数名(方法名),需要有符号 (symbols)
  • -target_offset fuzz 的函数(方法)相对于模块起始地址的偏移
  • -coverage_module 统计覆盖率的模块,可以统计多个模块的 coverage,即指定多个 -coverage_module 参数
  • -covtype bb 或者 edge,bb (basic block 为默认值)
  • -thread_coverage 用于多线程程序,只统计一个线程执行 target function 的覆盖率变化

4. WinAFL intel PT 模式使用

Intel PT (Processor Tracing) 是 Intel CPU 的一个特性,从 Intel 第5代处理器开始支持,5th generation 的微架构为 Broadwell。相关说明可以参考:https://www.intel.com/content/www/us/en/support/articles/000056730/processors.html

Windows 10 从 v1809 开始提供了 Intel PT 的驱动,但目前没有公开的文档描述此驱动,也没有提供官方的 API。 Alex Ionescu 提供了 winipt library 和 Intel PT 驱动交互,WinAFL 利用 winipt 来获得 Intel PT 的 Processo Tracing 信息。

WinAFL 使用 Intel PT 模式,和 DynamoRIO 不同需要指定 -P , WinAFL Intel PT 模式除了和DynamoRIO 相同的参数外额外添加了下面几个参数:

  • -trace_size 每次执行 traget 手机的 trace 信息的字节数,必须是2 的次方并且大于 4096
  • -decode 处理 trace 信息的解码器,有三个可选值:tip, tip_reffull (full 为默认值)
  • -nopersistent_trace 由于性能的缘故,WinAFL 每次循环执行 target 是不重启程序,这个选项可以强制 WinAFL 每次执行 target 都重启,一般是为了调试才会启用这个选项
  • -trace_cache_size trace 信息的 cache 字节数,和 full 解码器一起使用

5. 检查 Intel PT 模式是否正常工作

1
2
winaflpt-debug.exe -debug -coverage_module gdipluss.dll -coverage_module WindowsCodecs.dll ^
-fuzz_iterations 10 -target_module test_gdiplus.exe -target_offset 0x10E0 -nargs 2 -- test_gdiplus.exe @@

上面命令其实和检查 DynamoRIO 模式是否正常工作的命令基本一致,执行命令后输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Module loaded: test_gdiplus.exe
Module loaded: ntdll.dll
Module loaded: KERNEL32.DLjL
Module loaded: KERNELBASE.dll
Module loaded: ucrtbase.dll
Module loaded: gdiplus.dll
Module loaded: msvcrt.dll
Module loaded: combase.dll
Module loaded: VCRUNTIME140.dll
Module loaded: RPCRT4.dll
Module loaded: USER32.dll
Module loaded: win32u.dll
Module loaded: GDI32.dll
Module loaded: gdi32full.dll
Module loaded: msvcp_win.dll
Module loaded: IMM32.DLL
iteration 0
Module loaded: uxtheme.dll
Module loaded: msctf.dll
Module loaded: oleaut32.dll
Module loaded: sechost.dll
Iteration finished normally
iteration 1
Iteration finished normally
iteration 2
Iteration finished normally
iteration 3
Iteration finished normally
iteration 4
Iteration finished normally
iteration 5
Iteration finished normally
iteration 6
Iteration finished normally
iteration 7
Iteration finished normally
iteration 8
Iteration finished normally
iteration 9
Iteration finished normally
Coverage map (hex):
00000000000000000000000000000000
00000000000000000000000000000000

6. 用 Intel PT 模式 fuzz

1
2
3
afl-fuzz.exe -i in -o out -P -t 20000 -- -coverage_module gdiplus.dll ^
-coverage_module WindowsCodecs.dll -fuzz_iterations 5000 ^
-target_module test_gdiplus.exe -target_offset 0x10E0 -nargs 2 -- test_gdiplus.exe @@

用上面的命令调用 afl-fuzz,发现可以正常工作,界面如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
               WinAFL 1.16b based on AFL 2.43b (test_gdiplus.exe)

+- process timing -------------------------------------+- overall results ----+
| run time : 0 days, 0 hrs, 0 min, 15 sec | cycles done : 0 |
| last new path : 0 days, 0 hrs, 0 min, 1 sec | total paths : 9 |
| last uniq crash : none seen yet | uniq crashes : 0 |
| last uniq hang : none seen yet | uniq hangs : 0 |
+- cycle progress --------------------+- map coverage -+----------------------+
| now processing : 0 (0.00%) | map density : 1.50% / 3.67% |
| paths timed out : 0 (0.00%) | count coverage : 1.40 bits/tuple |
+- stage progress --------------------+ findings in depth --------------------+
| now trying : havoc | favored paths : 1 (11.11%) |
| stage execs : 2332/8192 (28.47%) | new edges on : 5 (55.56%) |
| total execs : 3350 | total crashes : 0 (0 unique) |
| exec speed : 221.4/sec | total tmouts : 0 (0 unique) |
+- fuzzing strategy yields -----------+---------------+- path geometry -------+
| bit flips : 0/48, 0/47, 1/45 | levels : 2 |
| byte flips : 0/6, 0/5, 0/3 | pending : 9 |
| arithmetics : 1/335, 0/75, 0/0 | pend fav : 1 |
| known ints : 0/28, 1/168, 1/120 | own finds : 8 |
| dictionary : 0/0, 0/0, 0/0 | imported : n/a |
| havoc : 0/0, 0/0 | stability : 65.28% |
| trim : 0.00%/1, 0.00% +-----------------------+
+-----------------------------------------------------+ [cpu000001: 23%]

7. Winafl 的多核使用情况

执行 Winafl 的 PT 模式,使用任务管理器 -> 打开资源监视器 -> CPU 可以观察到只有一个 CPU core 跑满 100%

参考资料

https://github.com/googleprojectzero/winafl/blob/master/README.md
https://github.com/googleprojectzero/winafl/blob/master/readme_dr.md
https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md

source: https://null2root.github.io/blog/2022/07/21/When-Hypervisor-Met-Snapshot-Fuzzing.html

1. Introduction

Hypervisor was known as hard target to fuzz over several years. Even though, lots of prior pioneers( Peter Hlavaty, Chaitin Tech, StarLabs, Peleg Hadar and Ophir Harpaz and many others ) doing amazing work to overcome this limit and found interesting bugs.

But it is not an easy topic for some fresh newbie like me to starts from the bottom. I think manual code( or binary ) auditing and static analysis could be only considerable options if I start my research a few years ago without What The Fuzz.

What The Fuzz( a.k.a WTF ) is a snapshot-based fuzzer targeting Windows Ring-3 and Ring-0 component. WTF’s snapshot is based on memory dump both kernel and user-land. Because of that, WTF can not emulate any functionality which requires anything not within in-memory and can not create new process or thread because memory dump limited on single process. But WTF support breakpoint Handler which you can set breakpoint on any address within memory dump and if fuzz execution reaches that address, pre-defined breakpoint handler will be executed. Based on this, we can trying to overcome some limitation on WTF such as file access.

I really love WTF’s flexibility and its potential, and I am going to show one of example usage of WTF on targeting Virtualbox to prove how awesome it is.

2. Developing Fuzz Module

First, you should define your own fuzz module for your target. There are few examples on github( fuzzer_hevd.cc, fuzzer_tlv_server.cc ) and blog post( ret2systems, Doar-e ).

My Target was Virtualbox’s SVGA component.

SVGA is something like “JavaScript of hypervisors“. Because of its complex nature, many hypervisor bugs are oriented by SVGA. And that’s the reason why I choose it as a first target.

VirtualBox have a function called vmsvgaR3FifoLoop. It waits until guest submit new command data through GPA. So this is a good spot( or should I call it source? ) to take a snapshot.

I set a breakpoint on vmsvgaR3FifoLoop+4E2 to take snapshot. It is same position as here, start of switch-case routine for SVGA and SVGA3D command.

blahblah1

After creating snapshot, I had to decide make it run multiple times in single execution or not. Because SVGA is consist of various commands like define, update, delete something…, I thought fuzz campaign must handle multiple SVGA commands during single round.

First, I had to define structure to contains multiple testcases. Luckily, 0vercl0k already write nice example to doing that. So I did same thing as below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#define SVGA_CMD_BODY_MAX 0xA000

const uint32_t SvgaCmdList[] = {1, 3, 19, 22, 25, 29, 30, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 1040, 1041, 1042, 1043, 1044, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081};

const uint32_t SvgaCmdListLen = 58;

struct SvgaCmd_t {
uint32_t SvgaCmdCode;
std::vector<uint8_t> SvgaBody;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SvgaCmd_t, SvgaCmdCode, SvgaBody);
};

struct SvgaCmds_t {
std::vector<SvgaCmd_t> SvgaCmds;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(SvgaCmds_t, SvgaCmds);
};

struct {
std::deque<SvgaCmd_t> SvgaCmds;
CpuState_t Context;
uint8_t SvgaCmdBody[SVGA_CMD_BODY_MAX];
uint32_t SvgaCmdBodySize;

void RestoreGprs(Backend_t *B) {
const auto &C = Context;
B->Rsp(C.Rsp);
B->Rip(C.Rip);
B->Rax(C.Rax);
B->Rbx(C.Rbx);
B->Rcx(C.Rcx);
B->Rdx(C.Rdx);
B->Rsi(C.Rsi);
B->Rdi(C.Rdi);
B->R8(C.R8);
B->R9(C.R9);
B->R10(C.R10);
B->R11(C.R11);
B->R12(C.R12);
B->R13(C.R13);
B->R14(C.R14);
B->R15(C.R15);
}
} GlobalState;

SvgaCmds_t Deserialize(const uint8_t *Buffer, const size_t BufferSize) {
const auto &Root = json::json::parse(Buffer, Buffer + BufferSize);
return Root.get<SvgaCmds_t>();
}

bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) {
GlobalState.SvgaCmds.clear();

const auto &Root = Deserialize(Buffer, BufferSize);
for(auto SvgaCmd : Root.SvgaCmds) {
GlobalState.SvgaCmds.emplace_back(std::move(SvgaCmd));
}

return true;
}

It is almost identical as example.

Next question was, how can I insert data into snapshot? I had to find a insertion point and proper memory address. Luckily( again ), VirtualBox using a function called vmsvgaR3FifoGetCmdPayload to receive command data from guest to host. I define a breakpoint handler in Init() callback function as below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//
// unsigned __int8 *__fastcall vmsvgaR3FifoGetCmdPayload(
// unsigned int cbPayloadReq,
// volatile unsigned int *pFIFO,
// unsigned int offCurrentCmd,
// unsigned int offFifoMin,
// unsigned int offFifoMax,
// unsigned __int8 *pbBounceBuf,
// unsigned int *pcbAlreadyRead,
// PDMTHREAD *pThread,
// VGAState *pThis,
// VMSVGAR3STATE *pSVGAState,
// PDMDEVINSR3 *pDevIns)
//
if(!g_Backend->Setbreakpoint("VBoxDD!vmsvgaR3FifoGetCmdPayload", [](Backend_t *Backend) {
uint32_t cbPayloadReq = Backend->GetArg(0);
Gva_t pbBounceBuf_gva = Backend->GetArgGva(5);
Gva_t pcbAlreadyRead_gva = Backend->GetArgGva(6);

if(cbPayloadReq > GlobalState.SvgaCmdBodySize) {
DebugPrint("cbpayloadReq({:#x}) > SvgaCmdBodySize({:#x}), restore context and goto next round\n", cbPayloadReq, GlobalState.SvgaCmdBodySize);
return GlobalState.RestoreGprs(Backend);
}

if(cbPayloadReq > u32PbBounceBufMaxSize) {
const uint64_t RetAddr = Backend->VirtRead8(Gva_t(Backend->Rsp()));
DebugPrint("check this, RetAddr = {:#x}\n", RetAddr);
std::abort();
}

if(!Backend->VirtWriteDirty(pbBounceBuf_gva, GlobalState.SvgaCmdBody, cbPayloadReq)) {
fmt::print("Failed to write pbBounceBuf, pbBounceBuf = {:#x}, cbPayloadReq = {:#x}, SvgaCmdBodySize = {:#x}\n",
pbBounceBuf_gva.U64(), cbPayloadReq, GlobalState.SvgaCmdBodySize);
std::abort();
}

if(!Backend->VirtWriteStructDirty(pcbAlreadyRead_gva, &cbPayloadReq)) {
fmt::print("Faile to write pcbAlreadyRead, pcbAlreadyRead = {:#x}\n", pcbAlreadyRead_gva.U64());
std::abort();
}

Backend->SimulateReturnFromFunction(pbBounceBuf_gva.U64());

})) {
fmt::print("Failed to Setbreakpoint on VBoxDD!vmsvgaR3FifoGetCmdPayload\n");
return false;
}

I also had to define end point of execution. After some reversing, I found two spots and using it as end point.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Gva_t SvgaLoopEnd = Gva_t(g_Dbg.GetSymbol("VBoxDD!vmsvgaR3FifoLoop") + 0x24AB);
if(!g_Backend->Setbreakpoint(SvgaLoopEnd, [](Backend_t *Backend) {
DebugPrint("loop end reached, restore context\n");
GlobalState.RestoreGprs(g_Backend);
})) {
fmt::print("Failed to Setbreakpoint on SvgaLoopEnd\n");
return false;
}

const Gva_t SvgLoopEnd2 = Gva_t(g_Dbg.GetSymbol("VBoxDD!vmsvgaR3FifoLoop") + 0x24B2);
if(!g_Backend->Setbreakpoint(SvgLoopEnd2, [](Backend_t *Backend) {
DebugPrint("loop end2 reached, restore context\n");
GlobalState.RestoreGprs(g_Backend);
})) {
fmt::print("Failed to Setbreakpoint on SvgLoopEnd2\n");
return false;
}

As I said above, I create a snapshot on vmsvgaR3FifoLoop+4E2. So if I restore register context, next execution flow starts from there. Because of that, I had to parse new testcase using breakpoint Handler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const Gva_t SvgaLoopStart = Gva_t(g_Dbg.GetSymbol("VBoxDD!vmsvgaR3FifoLoop") + 0x4e2);
if(!g_Backend->Setbreakpoint(SvgaLoopStart, [](Backend_t *Backend) {
if(GlobalState.SvgaCmds.size() == 0) {
DebugPrint("testcase deque empty! goto next round...\n");
return Backend->Stop(Ok_t());
}

auto &Testcase = GlobalState.SvgaCmds.front();

while (Testcase.SvgaCmdCode == 1045) {
GlobalState.SvgaCmds.pop_front();

if(GlobalState.SvgaCmds.size() == 0) {
DebugPrint("testcase deque empty during cmd filtering! goto next round...\n");
return Backend->Stop(Ok_t());
}

Testcase = GlobalState.SvgaCmds.front();
}

Backend->Rbx(Testcase.SvgaCmdCode);

DebugPrint("SvgaCmdCode = {:#x}\n", Testcase.SvgaCmdCode);

if(Testcase.SvgaCmdCode >= 1040) {

DebugPrint("Have to avoid AssertBreak(pHdr->size < pThis->svga.cbFIFO), write {:#x} on first DWORD\n", Testcase.SvgaBody.size());

const uint32_t Svga3dCmdSize = Testcase.SvgaBody.size();
if(Svga3dCmdSize >= 0x200000) {
fmt::print("Svga3dCmdSize({:#x}) > cbFIFO, abort\n", Svga3dCmdSize);
std::abort();
}

memcpy(&GlobalState.SvgaCmdBody[0], &Svga3dCmdSize, 4);
memcpy(&GlobalState.SvgaCmdBody[4], Testcase.SvgaBody.data(), Testcase.SvgaBody.size());
GlobalState.SvgaCmdBodySize = Testcase.SvgaBody.size() + 4;
}
else {
memcpy(GlobalState.SvgaCmdBody, Testcase.SvgaBody.data(), Testcase.SvgaBody.size());
GlobalState.SvgaCmdBodySize = Testcase.SvgaBody.size();
}

GlobalState.SvgaCmds.pop_front();
})) {
fmt::print("Failed to Setbreakpoint on SvgaLoopStart\n");
return false;
}

After some investigation, I found CreateDeviceEx call in vmsvga3dContextDefine keep causing CR3 context switching and I didn’t found any way to handling it using breakpoint handler. So I just blacklisting it( SVGA_3D_CMD_CONTEXT_DEFINE = 1045 ).

I disclose almost every part of fuzz module except mutation part. I just use libfuzzer mutator to mutate body data of SVGA command and pick one of command in array randomly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
explicit CustomMutator_t(std::mt19937_64 &Rng, const size_t TestcaseMaxSize)
: Rng_(Rng), TestcaseMaxSize_(TestcaseMaxSize) {

// set maximum size for multiple (mutated) testcase( Default = 1MB )
ScratchBuffer__ = std::make_unique<uint8_t[]>(_1MB);
ScratchBuffer_ = {ScratchBuffer__.get(), _1MB};

BodyMutator_ = make_unique<LibfuzzerMutator_t>(Rng, TestcaseMaxSize);
}

// skip for brevity...

std::string Mutate(uint8_t *Data, const size_t DataLen, const size_t MaxSize) {
auto Root = Deserialize(Data, DataLen);
auto &SvgaCmds = Root.SvgaCmds;

for(auto &SvgaCmd : SvgaCmds) {

//
// 50%
//
if(GetUint32(0, 1) == 1) {
uint32_t SvgaCmdRandomIdx = GetUint32(0, SvgaCmdListLen);
SvgaCmd.SvgaCmdCode = SvgaCmdList[SvgaCmdRandomIdx];
}

memcpy(GlobalMutBuffer, SvgaCmd.SvgaBody.data(), SvgaCmd.SvgaBody.size());
size_t NewTestcaseSize = BodyMutator_->Mut_.Mutate(GlobalMutBuffer, SvgaCmd.SvgaBody.size(), MaxSize);
SvgaCmd.SvgaBody.resize(NewTestcaseSize);
memcpy(SvgaCmd.SvgaBody.data(), GlobalMutBuffer, NewTestcaseSize);
}

json::json Serialized;
to_json(Serialized, Root);
return Serialized.dump();
}

I also define generator function. It is very useful if you are too lazy to create random input like me ^~^.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
std::string GenerateTestcase() {
SvgaCmds_t Root;
const auto N = GetUint32(1, 10);

for (size_t Idx = 0; Idx < N; Idx++) {
SvgaCmd_t SvgaCmd;

SvgaCmd.SvgaCmdCode = SvgaCmdList[GetUint32(0, SvgaCmdListLen)];
SvgaCmd.SvgaBody.resize(GetUint32(0x100, 0x400));

if(GetUint32(0, 1) == 0) {
for(int i = 0; i < SvgaCmd.SvgaBody.size(); i++) {
SvgaCmd.SvgaBody[i] = 0x41;
}
}
else {
for(int i = 0; i < SvgaCmd.SvgaBody.size(); i++) {
SvgaCmd.SvgaBody[i] = (uint8_t)GetUint32(0, 255);
}
}

Root.SvgaCmds.emplace_back(SvgaCmd);
}

json::json Serialized;
to_json(Serialized, Root);
return Serialized.dump();
}

std::string GetNewTestcase(const Corpus_t &Corpus) override {

//
// 20%
//
if(GetUint32(1, 5) == 1) {
return GenerateTestcase();
}

const Testcase_t *Testcase = Corpus.PickTestcase();

if (!Testcase) {
fmt::print("The corpus is empty, generate random one\n");
return GenerateTestcase();
}

//
// Copy the input in a buffer we're going to mutate.
//

memcpy(ScratchBuffer_.data(), Testcase->Buffer_.get(),
Testcase->BufferSize_);

// return Mutate(ScratchBuffer_.data(), Testcase->BufferSize_, TestcaseMaxSize_);

return Mutate(ScratchBuffer_.data(), Testcase->BufferSize_, u32PbBounceBufMaxSize - sizeof(uint32_t));
}

Aaaaannnd, this is everything you need to fuzz! I skip some formal code for brevity, but I think you can easily find what you need to define fully working fuzz module.

…..Ooooh wait, I forgot something. After some hours of struggle, I define a blacklist which cause CR3 context switching. I just put it on the end of Init() function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
bool SetupVBoxBlacklistHooks() {
if(!g_Backend->SetBreakpoint("VBoxRT!RTLogLoggerEx", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VBoxRT!RTLogLoggerEx\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("VBoxVMM!PDMCritSectLeave", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VBoxVMM!PDMCritSectLeave\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("VBoxC!util::AutoLockBase::release", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VBoxC!util::AutoLockBase::release\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("VBoxC!util::AutoLockBase::acquire", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VBoxC!util::AutoLockBase::acquire\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("VirtualBoxVM!UIFrameBufferPrivate::NotifyChange", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VirtualBoxVM!UIFrameBufferPrivate::NotifyChange\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("VBoxRT!SUPSemEventWaitNoResume", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on VBoxRT!SUPSemEventWaitNoResume\n");
std::abort();
}

if(!g_Backend->SetBreakpoint("d3d9!CBaseDevice::Release", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on d3d9!CBaseDevice::Release\n");
}

if(!g_Backend->SetBreakpoint("d3d9!CD3DBase::Clear", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on d3d9!CD3DBase::Clear\n");
}

if(!g_Backend->SetBreakpoint("d3d9!CMipMap::SetAutoGenFilterType", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on d3d9!CMipMap::SetAutoGenFilterType\n");
}

if(!g_Backend->SetBreakpoint("d3d9!CMipMap::GenerateMipSubLevels", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on d3d9!CMipMap::GenerateMipSubLevels\n");
}

if(!g_Backend->SetBreakpoint("USER32!GetDC", [](Backend_t *Backend) {
Backend->SimulateReturnFromFunction(0x1337);
})) {
fmt::print("Failed to SetBreakpoint on USER32!GetDC\n");
}

return true;
}

Yep. That’s really everything. I use this fuzz module several hours and it founds interesting crash.

3. Vulnerability( TL;DR )

Crash occurred in vmsvga3dSurfaceCopy function( PageHeap needed ).

blahblah2

This function trying to copy surface data from one to another using surface id and there’s no boundary check between surface, so it become exploitable wildcopy vulnerability in heap memory.

spot the bug

This vulnerability patched in 6.1.36 release at July, 2022.

4. Conclusion

I think importance of snapshot fuzzing is, it makes researcher to focus on target itself.

Unlike other fuzzers based on runtime and DBI are often create (very) unreasonable side effect or need lots of time to create working harness. The concept of snapshot fuzzing makes it possible to reduce this waste of time.

可以量化一下,有时间考虑程序实现。

  • 涨/跌停板数
  • 涨停封版成功率
  • 涨跌股票比率
  • 大盘成交量额
  • 融资余额情况
  • 北向资金净流入 (参考)
0%