jtpereyda / boofuzz Goto Github PK
View Code? Open in Web Editor NEWA fork and successor of the Sulley Fuzzing Framework
License: GNU General Public License v2.0
A fork and successor of the Sulley Fuzzing Framework
License: GNU General Public License v2.0
Property attributes of boofuzz.request.Request
have no setters implemented, causing AttributeErrors to
be raised when attempting to set the values of these attributes during fuzzing.
Affected attributes:
Request.mutant_index
- Caused an unhandled exception during fuzzingRequest.name
- Noticed when fixing mutant_index
Implement basic setters for the above properties
Idea: Add a log_test or log_test_run method to IFuzzLogger. This will provide a header describing a test run. Advantages:
Also record the boofuzz version number, and the test suite version number if possible.
If the target dies and refuses connections, socket_connection.py
will throw an exception and crash Boofuzz.
Handle this more gracefully.
pytest-bdd has a Makefile on which you can run make
and make test
to run all the tests. This is very nice. It would be nice to have something similar for boofuzz.
Consider basing it off of this one: https://github.com/jtpereyda/ezoutlet/blob/master/CONTRIBUTING.rst
Edit: Make sure to include a note about the expectation of professional code reviews on all pull requests. Not all users may be familiar with this. Find an article or something that briefly and effectively introduces the process.
See Bob Martin's "Clean Code", introduction to Chapter 16:
No, this is not an activity of nastiness or arrogance. What I am about to do is nothing more and nothing less than a professional review. It is something that we should all be comfortable doing. And it is something we should welcome when it is done for us. It is only through critiques like these that we will learn. Doctors do it. Pilots do it. Lawyers do it. And we programmers need to learn how to do it too.
A requirement for protocol fuzzing is to have protocol header(s) split into various fixed size fields (e.g. 4b, 20b, 8b etc.) for protocol options. These fields require to be defined separately, specified as fuzzable or non-fuzzable and afterwards assembled together to be sent to the target socket.
Currently Boofuzz pads the bitfield to the next byte boundary, therefore introducing unneeded bytes into the protocol header.
Consider an IPv6 header test case:
s_bit_field(0b0110, 4, fuzzable = False, name = "Version")
s_bit_field(0b00000000, 8, name = "Traffic_Class")
s_bit_field(0b00000000000000000000, 20, name = "Flow_Label")
s_bit_field(0b0000000000100000, 16, name = "Payload_Length")
s_bit_field(0x06, 8, name = "Next_Header")
s_bit_field(0xff, 8, name = "Hop_Limit")
s_binary("0x2a071180000200000000000000bb0001", name = "Source_Address")
s_binary("0x2a071180000200000000000000bb0002", name = "Destination_Address")
A Bofuzz feature supporting this would be highly required in order to ensure raw binary (e.g. L2,L3) protocol fuzzing.
More information on feature discussion is here: https://groups.google.com/forum/#!topic/boofuzz/7rZeBYnEyV0
Issue reported by @Ramzeth, see Gitter chats here.
@Ramzeth You bring up a good point... Boofuzz doesn't currently support a scenario where the receive step should be skipped
In order to avoid changing the default behavior for other scenarios, I think another setting would be warranted... We could add a self._receive_after_send variable. The code would look like:
if self._receive_after_send: self.last_recv = self.targets[0].recv(10000) if self._check_data_received_each_request: self._fuzz_data_logger.log_check("Verify some data was received from the target.")
Then Session would get a new keyword parameter receive_after_send=True
If you use fuzz_single_case() to fuzz all of the test cases, it will take longer and longer as you get into higher numbers. Each time the method is called, it iterates up to the desired point in the list. This can add non-trivial delay with a big enough test.
Options:
There are many cases where fuzzing a single field will exceed a size limit.
For example, if a field in an IP or UDP packet gets super oversized, the UDP checksum becomes undefined. Now we have to make the checksum function fudge in an undefined checksum.
Another exmaple is when the sockets layer has a datagram limit, and a UDP packet gets fuzzed to be too big. We then truncate it at the sockets layer, but now it doesn't even have a checksum!
This is good because it stretches size limits, but it's problematic because the packet will break in other ways. We'd probably rather make our test cases meet the maximum size of any given field. But then we'd want separate test cases to exceed the size limit.
Hi, when I try to install boofuzz on ubuntu 14.04, I get this error:-
Cleaning up...
Removing temporary dir /tmp/pip_build_root...
Exception:
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/pip/basecommand.py", line 122, in main
status = self.run(options, args)
File "/usr/lib/python2.7/dist-packages/pip/commands/install.py", line 278, in run
requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
File "/usr/lib/python2.7/dist-packages/pip/req.py", line 1198, in prepare_files
do_download,
File "/usr/lib/python2.7/dist-packages/pip/req.py", line 1376, in unpack_url
self.session,
File "/usr/lib/python2.7/dist-packages/pip/download.py", line 572, in unpack_http_url
download_hash = _download_url(resp, link, temp_location)
File "/usr/lib/python2.7/dist-packages/pip/download.py", line 433, in _download_url
for chunk in resp_read(4096):
File "/usr/lib/python2.7/dist-packages/pip/download.py", line 421, in resp_read
chunk_size, decode_content=False):
File "/usr/share/python-wheels/urllib3-1.7.1-py2.py3-none-any.whl/urllib3/response.py", line 225, in stream
data = self.read(amt=amt, decode_content=decode_content)
File "/usr/share/python-wheels/urllib3-1.7.1-py2.py3-none-any.whl/urllib3/response.py", line 174, in read
data = self._fp.read(amt)
File "/usr/lib/python2.7/httplib.py", line 573, in read
s = self.fp.read(amt)
File "/usr/lib/python2.7/socket.py", line 380, in read
data = self._sock.recv(left)
File "/usr/lib/python2.7/ssl.py", line 341, in recv
return self.read(buflen)
File "/usr/lib/python2.7/ssl.py", line 260, in read
return self._sslobj.read(len)
SSLError: The read operation timed out
What could be the problem??
Provide a foundation for behavior, as well as details in how it applies.
Detail ideas:
Add a feature to retry failures and determine whether boofuzz can reproduce them.
A binary search approach that moves backwards starting with just the failing case, then it and one before, then 4 cases, then 8... may be a good start (I thought Sulley already did this but I can't find any such script).
According to the documentation s_binary() is fuzzable, however it doesn't seems to support the fuzzable=True/False keyword and is not fuzzed during execution.
If it is not fuzzable then how can I represent and fuzz a binary or hex value that I extract directly from a pcap file with network communication?
Tox seems like an excellent tool for simplifying testing. It will build virtual environments for us, which also means we don't have to put it all in .travis.yml. And tests run on user machines will be closer to what Travis does without having to make your own virtualenv.
PR #121 revealed that the old unit tests, listed in unit_tests.py
, are not run as part of tox. Fix this.
Add a .is_valid_msg()
method to the IFuzzable
interface.
The method should return True if the data given to it can be parsed as a valid message; false otherwise.
A simple way to improve the thoroughness of a fuzzer is to parse the reply messages received to ensure they meet expected values.
I can think of three steps to implementation:
original_value
property. The data to be checked could be compared with the Request
object's `original_value.valid_values
parameter to fuzz primitives. This parameter would be a list of valid values that the primitive could take. If the values could be variable-length, things could get complicated.Request
object.Fuzzing activities would also be helped by a deserialize()
method, providing an object representation of the message. This object could be used to check various properties of the reply. That might be more than is needed typically, and comes close to allowing for a full protocol functional test, perhaps beyond the needs of a fuzzer.
For now, a procedure that parses a message for validity might be enough. A typical post_send
method could parse against the expected message and a range of known error messages. Any message following outside this domain could be considered erroneous.
Hello everyone,I find if i pass a my own defined algorithm function to s_checksum,it may cause a error. so i check the code,and do some changes as blow:
in checksum.py:
add algorithm argument's type judgement
if type(self._algorithm) is not str:
self.checksum_lengths[self._algorithm.name] = self._length
and in _get_dummy_value function :
def _get_dummy_value(self):
if type(self._algorithm) is str:
return self.checksum_lengths[self._algorithm] * '\x00'
else:
return self.checksum_lengths[self._algorithm.name] * '\x00'
hope it would be helpful or anyone have good idea?
The post_send API is defined by a function, and it's not exceedingly intuitive. Consider using an interface instead.
Inspired by this talk: The End Of Object Inheritance & The Beginning Of A New Modularity
Essentially, Python is not defined to handle functions this way. It can, but the language lends itself to using objects rather than just functions. One of the speakers argues that objects/interfaces are better than functions for this case.
The current way of registering post_send is also pretty clunky. We could take a more event handler style approach and allow multiple functions.
I'm not sure what the best approach is. This should be settled after we see more use cases.
Make Request
objects have at least one special mutation: an empty message.
Do the same for Block
.
Also, there is lots of duplicate code between Request
and Block
which would be nice to clean out.
Investigate. Occurred at least twice, intermittently, on Windows 10 machine. boofuzz v0.0.3.dev13
C:\Users\josh\code\boofuzz>python -m tox
GLOB sdist-make: C:\Users\josh\code\boofuzz\setup.py
py27 inst-nodeps: C:\Users\josh\code\boofuzz\.tox\dist\boofuzz-0.0.3.dev13.zip
py27 installed: backports.ssl-match-hostname==3.5.0.1,boofuzz==0.0.3.dev13,certifi==2016.2.28,check-manifest==0.31,colorama==0.3.7,enum34==1.1.2,Flask==0.10.1,funcsigs==1.0.0,future==0.15.2,glob2==0.4.1,impacket==0.9.14,itsdangerous==0.24,Jinja2==2.8,Mako==1.0.4,MarkupSafe==0.23,mock==2.0.0,ordereddict==1.1,parse==1.6.6,parse-type==0.3.4,pbr==1.9.0,py==1.4.31,pydot2==1.0.33,pyparsing==2.1.1,pyserial==3.0.1,pytest==2.9.1,pytest-bdd==2.16.1,six==1.10.0,tornado==4.0.2,Werkzeug==0.11.5
py27 runtests: PYTHONHASHSEED='449'
py27 runtests: commands[0] | python -m pytest
============================= test session starts =============================
platform win32 -- Python 2.7.11, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: C:\Users\josh\code\boofuzz, inifile:
plugins: bdd-2.16.1
collected 106 items
unit_tests\test_ez_outlet_reset.py ................
unit_tests\test_fuzz_logger.py .............
unit_tests\test_fuzz_logger_text.py .......................
unit_tests\test_helpers_ip_str_to_bytes.py ...................
unit_tests\test_helpers_udp_checksum.py ........
unit_tests\test_serial_connection_generic.py ...............
unit_tests\test_socket_connection.py .ssssss..F..
================================== FAILURES ===================================
____________________ TestSocketConnection.test_tcp_client _____________________
self = <unit_tests.test_socket_connection.TestSocketConnection testMethod=test_tcp_client>
def test_tcp_client(self):
"""
Given: A SocketConnection 'tcp' object and a TCP server.
When: Calling SocketConnection.open(), .send(), .recv(), and .close()
Then: send() returns RAW_L3_MAX_PAYLOAD.
and: Sent and received data is as expected.
"""
data_to_send = bytes('uuddlrlrba')
# Given
server = MiniTestServer()
server.bind()
t = threading.Thread(target=server.serve_once)
t.daemon = True
t.start()
uut = SocketConnection(host=socket.gethostname(), port=server.active_port, proto='tcp')
uut.logger = logging.getLogger("SulleyUTLogger")
# When
uut.open()
send_result = uut.send(data=data_to_send)
> received = uut.recv(10000)
unit_tests\test_socket_connection.py:265:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <boofuzz.socket_connection.SocketConnection object at 0x00000000187B6940>
max_bytes = 10000
def recv(self, max_bytes):
"""
Receive up to max_bytes data from the target.
:param max_bytes: Maximum number of bytes to receive.
:type max_bytes: int
:return: Received data.
"""
try:
if self.proto in ['tcp', 'ssl']:
> data = self._sock.recv(max_bytes)
E error: [Errno 10054] An existing connection was forcibly closed by the remote host
boofuzz\socket_connection.py:136: error
---------------------------- Captured stderr call -----------------------------
Exception in thread Thread-1:
Traceback (most recent call last):
File "c:\python27\Lib\threading.py", line 801, in __bootstrap_inner
self.run()
File "c:\python27\Lib\threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Users\josh\code\boofuzz\unit_tests\test_socket_connection.py", line 174, in serve_once
self.received = client_socket.recv(10000)
error: [Errno 10035] A non-blocking socket operation could not be completed immediately
=============== 1 failed, 99 passed, 6 skipped in 2.07 seconds ================
ERROR: InvocationError: 'C:\\Users\\josh\\code\\boofuzz\\.tox\\py27\\Scripts\\python.EXE -m pytest'
___________________________________ summary ___________________________________
ERROR: py27: commands failed
ITargetConnection.recv
has a max_bytes
parameter that is mostly an annoyance. In existing code, it has been set to some arbitrarily high number. We should eliminate the parameter, give it a helpful default, or something.
The first five lines of this example are all boilerplate: https://github.com/jtpereyda/boofuzz-ftp/blob/master/ftp.py (permalink)
This is way more boilerplate than necessary. We can add a default logger to FuzzLogger, or even to Session. And the Target class is pretty meaningless in this example. If we make Session the direct manager for connections (and process monitors, etc.), we can nix this Target middle man altogether.
And if we go really stir crazy, we can even make sleep_time default to zero!
The brave new boilerplate may look like:
session = sessions.Session(
connection=SocketConnection("127.0.0.1", 8021, proto='tcp'))
https://github.com/minimaxir/big-list-of-naughty-strings
Consider: Is there a way to include dependencies like this without hard-coding them in?
Travis currently deploys to PyPI when it gets a tag from Github. However, the release checklist (see CONTRIBUTING.rst) still requires manual interaction:
Ideally, whenever we merge a PR to master, Travis would do these steps for us and push out a new dev release.
It's nice to have a way to quickly identify the nature of a test case variant. Should describe the variant in an abbreviated way, perhaps with a link to the full data if it's long.
Elements of unique identification:
Ideas for representation:
Currently, boofuzz config is done by writing a Python script. When writing such scripts, it is natural to add command line arguments. This results in a lot of redundant fixture code.
One solution is to make the CLI standardized, and each fuzz definition script would be included via a command line parameter.
Config file:
Ideas:
Use .hgrc / .gitconfig file formats:
[testopts]
definitions = udp.py
post_fail = ez_outlet
[ez_outlet]
ip = 192.168.1.103
Parameters could be automatically passed to constructor.
Before fuzzing, run a test case without mutations (test case zero) and verify all checks pass. Consider adding an option to test the reset functionality as well by resetting before this initial test case.
A more advanced approach for complex protocols is to use a number of such tests to discover protocol interoperability.
Boofuzz, pydbg, and pydasm seemd to install fine but process monitor errors with no CancelIoEx
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\marcus\Desktop\boofuzz-master\boofuzz-master>python pr
ocess_monitor.py
Traceback (most recent call last):
File "process_monitor.py", line 11, in
from boofuzz import utils
File "C:\Documents and Settings\marcus\Desktop\boofuzz-master\boofuzz-master\b
oofuzz_init_.py", line 27, in
from .serial_connection import SerialConnection
File "C:\Documents and Settings\marcus\Desktop\boofuzz-master\boofuzz-master\b
oofuzz\serial_connection.py", line 4, in
from . import serial_connection_low_level
File "C:\Documents and Settings\marcus\Desktop\boofuzz-master\boofuzz-master\b
oofuzz\serial_connection_low_level.py", line 2, in
import serial
File "C:\Python27\lib\site-packages\serial_init_.py", line 27, in
from serial.serialwin32 import Serial
File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 15, in
from serial import win32
File "C:\Python27\lib\site-packages\serial\win32.py", line 182, in
CancelIoEx = stdcall_libraries['kernel32'].CancelIoEx
File "C:\Python27\lib\ctypes_init.py", line 375, in getattr
func = self.getitem(name)
File "C:\Python27\lib\ctypes_init_.py", line 380, in getitem
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'CancelIoEx' not found
Update: I downloaded a Vista VM and it installed perfectly on that.
The usage text for process_monitor.py
does not make it clear which arguments are required. Fix this.
It returns 212992 on my Ubuntu 15.04 VM, about an order of magnitude over the theoretical maximum of 65507.
Process monitor is able to re-start a process, but it will not work unless the process is already running when the test starts. The current workflow is:
Make the process monitor start the process if it does not already exist. This would give a new, shorter workflow:
This begs the question: Can we start the process monitor automatically if the targert is localhost? I don't see why not. Or we could even run it as a local library instead of a separate service (I don't know if it'd be worth the effort or not).
PR #45 revealed some deficiencies in the procmon restart process. Right now, the user of a procmon must decide whether to stop a process before starting it.
But probably, if you called Session.restart_target, you just want it to actually restart. So to decide whether to stop it, you need to know if it's still running, which is information that lives in the procmon, not Session.
Proposed solution: Give the procmon a restart() method that decides whether to stop first. Call restart from Session, and remove the stop_first parameter from Session.restart_target.
It seems like IFuzzable.fuzzable
would always be true, but the property "fuzzable" means something different than the interface "fuzzable."
Rename.
Perhaps a name like "should_fuzz" or "skip" would be more helpful.
As I understand, the crash_threshold is used to limit the number of mutations of a single primitive if the target continues to crash. E.g. if mutations in the primitive "test" already caused 3 crashes skip the rest of the mutations and continue with the next primitive.
In my case after this happens the fuzzing session is aborted entirely. Specifically in sessions.py:595
self._skip_after_cur_test_case = True
a flag is set that causes the loop in sessions.py:818 to break:
if self._skip_after_cur_test_case:
self._skip_after_cur_test_case = False
break
Therefore, the iterator no longer yields anything. I guess this behaviour is not intended?
The wheels being built and deployed appear to be outdated. After cleaning and rebuilding, they are still missing all sub-folders of boofuzz. Not good!
Fix and redeploy.
For every kind of block/primitive/etc: Add a parameter that allows the user to inject a few custom cases. Parameter should probably be an iterable.
It would be handy to navigate through test cases and view detailed logs from the web GUI.
Also slick: If the logs could be dynamically fed to the web page, with logs wizzing by in a nifty little text window, while finished cases are added to a tree view on the left.
A lot of fluff in BasePrimitive could be removed if we just made mutatable things iterable.
The interface is functionally the same: You get only one mutation at a time.
The interface would now be:
While we're modifying things, make all member variables private if possible.
If somebody is really in a mood for pain, they could implement SocketConnection
's raw-l2
and raw-l3
options on Windows.
One way to do this might be with WinPcap. References:
If I understand correctly, WinPcap would allow behavior similar to SOCK_RAW
, in which MACs must be supplied by the user (but not CRCs). To emulate SOCK_DGRAM
may require custom code to figure out the right MAC addresses based on the included IP packet information.
When there is no restart_target method, boofuzz waits 300 seconds and tries again. This is not always desirable.
One user has reported and I've personally seen errors like this before:
Traceback (most recent call last):
File "Z:\ftp\ftp.py", line 119, in <module>
sess.fuzz()
File "C:\Python27\lib\site-packages\boofuzz\sessions.py", line 414, in fuzz
self._fuzz_current_case(*fuzz_args)
File "C:\Python27\lib\site-packages\boofuzz\sessions.py", line 868, in _fuzz_current_case
self.transmit(target, self.fuzz_node, edge)
File "C:\Python27\lib\site-packages\boofuzz\sessions.py", line 739, in transmit
self.last_recv = self.targets[0].recv(10000)
File "C:\Python27\lib\site-packages\boofuzz\sessions.py", line 110, in recv
data = self._target_connection.recv(max_bytes=max_bytes)
File "C:\Python27\lib\site-packages\boofuzz\socket_connection.py", line 143, in recv
data = self._sock.recv(max_bytes)
socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host
This seems to indicate a crash of the unit under test, which boofuzz should report as an error instead of crashing itself.
I don't know if there are times when this is valid behavior; perhaps in some protocols.
If anyone has any further details or ideas, please post.
grep -rni --exclude-dir .git --exclude-dir .idea "random"
Randomness in tests reduces repeatability. Remove randomness, or else provide a means to record and repeat seed values.
Update 2021-02-24: Using a repeatable seed is probably easier and better.
If you don't give Session the session_filename
attribute, it will crash.
$ python ftp.py
Traceback (most recent call last):
File "ftp.py", line 58, in <module>
main()
File "ftp.py", line 11, in main
session = sessions.Session(sleep_time=0.0, fuzz_data_logger=logger)
File "/home/joshpere/code/fuzz/boofuzz/boofuzz/sessions.py", line 241, in __init__
self.import_file()
File "/home/joshpere/code/fuzz/boofuzz/boofuzz/sessions.py", line 442, in import_file
with open(self.session_filename, "rb") as f:
TypeError: coercing to Unicode: need string or buffer, NoneType found
Add a simple check at the beginning of import_file()
. This is what export_file()
already does:
if self.session_filename is None:
return
The latest wheel doesn't have Windows binaries.
This might not be worth maintaining, in which case the quick fix is to remove the binary subsection from INSTALL.rst.
PR #96 fixed a Windows build issue. Investigate and deploy a Windows automated build. Looks like AppVeyor and Buildkite are options.
The web GUI is a nice page to show off
If you don't specify a post_send
function, boofuzz will tell you... on every single test.
[2016-07-20 19:19:30,638] Test Step: Calling post_send function:
[2016-07-20 19:19:30,652] Info: No post_send callback registered.
Consider doing the same if the sleep time is zero seconds:
[2016-07-20 19:19:30,656] Test Step: Sleep between tests.
[2016-07-20 19:19:30,669] Info: sleeping for 0.000000 seconds
I was having some problems, but after some testing I figured out that boofuzz performs only three fuzzing iterations of a primitive, when it is not receiving data from the target.
However, I want to fuzz each and every field of my protocol, even if the request doesn't generate response, or at least to revert the last fuzzed primitive to its default and to continue with the next one.
Currently Boofuzz jumps over to the next request (or finishes the fuzzing of the request), when no response is received in three fuzz attempts of a primitive and because of this it never reaches the fields (primitives) that are further down the protocol.
I can manually disable fuzzing of primitives, thus forcing it to fuzz the fields I want, but it would have been much easier to just let it fuzz all fields overnight.
Also now it never reaches past the first three attempts and thus the primitive is not extensively fuzzed. For example some value which is past the first three attempts may generate response or even trigger crash, however currently it never reaches for it as Boofuzz quits after three attempts.
Is this expected behaviour and how do I make it fuzz through all values of the primitives, when the target doesn't responds to the requests?
I am fuzzing a binary protocol and my sample request looks like this:
s_initialize("request")
s_byte(0x03, name="version", fuzzable=True)
s_byte(0x00, name="reserved", fuzzable=True)
s_binary("0x0016", name="length")
s_word(0x1111, name="pdu_type", endian=">", fuzzable=True)
s_dword(0x00000000, name="data", endian=">", fuzzable=True)
We have a file, ez_outlet_reset.py
, that now has its own package, ezoutlet
. Switch to the package.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.