Git Product home page Git Product logo

ingram's Introduction

Ingram
Platform Python Version GitHub Github Checks GitHub Last Commit (master) Languages Count

简体中文 | English

简介

主要针对网络摄像头的漏洞扫描框架,目前已集成海康、大华、宇视、dlink等常见设备

run

安装

请在 Linux 或 Mac 系统使用,确保安装了3.8及以上版本的Python,尽量不要使用3.11,因为对许多包的兼容不是很好

  • 克隆该仓库:
git clone https://github.com/jorhelp/Ingram.git
  • 进入项目目录,创建一个虚拟环境,并激活该环境:
cd Ingram
pip3 install virtualenv
python3 -m virtualenv venv
source venv/bin/activate
  • 安装依赖:
pip3 install -r requirements.txt

至此安装完毕!

运行

  • 由于是在虚拟环境中配置,所以,每次运行之前,请先激活虚拟环境:source venv/bin/activate

  • 你需要准备一个目标文件,比如 targets.txt,里面保存着你要扫描的 IP 地址,每行一个目标,具体格式如下:

# 你可以使用井号(#)来进行注释

# 单个的 IP 地址
192.168.0.1

# IP 地址以及要扫描的端口
192.168.0.2:80

# 带 '/' 的IP段
192.168.0.0/16

# 带 '-' 的IP段
192.168.0.0-192.168.255.255
  • 有了目标文件之后就可直接运行:
python3 run_ingram.py -i 你要扫描的文件 -o 输出文件夹
  • 端口: 如果target.txt文件中指定了目标的端口,比如: 192.168.6.6:8000,那么会扫描该目标的8000端口

否则的话,默认只扫描常见端口(定义在 Ingram/config.py 中),若要批量扫描其他端口,需自行指定,例如:

python3 run_ingram.py -i 你要扫描的文件 -o 输出文件夹 -p 80 81 8000
  • 默认并发数目为 300,可以根据机器配置及网速通过 -t 参数来自行调控:
python3 run_ingram.py -i 你要扫描的文件 -o 输出文件夹 -t 500
  • 支持中断恢复,不过并不会实时记录当前运行状态,而是间隔一定时间,所以并不能准确恢复到上次的运行状态。如果扫描因为网络或异常而中断,可以通过重复执行上次的扫描命令来继续扫描

  • 所有参数:

optional arguments:
  -h, --help            show this help message and exit
  -i IN_FILE, --in_file IN_FILE
                        the targets will be scan
  -o OUT_DIR, --out_dir OUT_DIR
                        the dir where results will be saved
  -p PORTS [PORTS ...], --ports PORTS [PORTS ...]
                        the port(s) to detect
  -t TH_NUM, --th_num TH_NUM
                        the processes num
  -T TIMEOUT, --timeout TIMEOUT
                        requests timeout
  -D, --disable_snapshot
                        disable snapshot
  --debug

端口扫描器

  • 我们可以利用强大的端口扫描器来获取活动主机,进而缩小 Ingram 的扫描范围,提高运行速度,具体做法是将端口扫描器的结果文件整理成 ip:port 的格式,并作为 Ingram 的输入

  • 这里以 masscan 为例简单演示一下(masscan 的详细用法这里不再赘述),首先用 masscan 扫描 80 或 8000-8008 端口存活的主机:masscan -p80,8000-8008 -iL 目标文件 -oL 结果文件 --rate 8000

  • masscan 运行完之后,将结果文件整理一下:grep 'open' 结果文件 | awk '{printf"%s:%s\n", $4, $3}' > targets.txt

  • 之后对这些主机进行扫描:python run_ingram.py -i targets.txt -o out

微信提醒(已移除)

  • (可选) 扫描时间可能会很长,如果你想让程序扫描结束的时候通过微信发送一条提醒的话,你需要按照 wxpusher 的指示来获取你的专属 UIDAPP_TOKEN,并将其写入 run_ingram.py:
# wechat
config.set_val('WXUID', '这里写uid')
config.set_val('WXTOKEN', '这里写token')

结果

.
├── not_vulnerable.csv
├── results.csv
├── snapshots
└── log.txt
  • results.csv 里保存了完整的结果, 格式为: ip,端口,设备类型,用户名,密码,漏洞条目:
Ingram
  • not_vulnerable.csv 中保存的是没有暴露的设备

  • snapshots 中保存了部分设备的快照:

Ingram

实时预览 (由于部分原因已移除)

  • 可以直接通过浏览器登录来预览

  • 如果想批量查看,我们提供了一个脚本 show/show_rtsp/show_all.py,不过它还有一些问题:

Ingram

免责声明

本工具仅供安全测试,严禁用于非法用途,后果与本团队无关

鸣谢 & 引用

Thanks to Aiminsun for CVE-2021-36260
Thanks to chrisjd20 for hidvision config file decryptor
Thanks to mcw0 for DahuaConsole

ingram's People

Contributors

andyco2008 avatar anmaiy avatar avikowy avatar haoke98 avatar jorhelp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ingram's Issues

Can you update?

The tool currently only scans for vulnerabilities on http port. you can scan for vulnerabilities on software (hkvision, dahua pc)

One of the best tools i've used but one question.

how do i change default passwords attack inputs these are shown in log.txt: "passwords=['admin', 'admin12345', 'asdf1234', 'abc12345', '12345admin', '12345abc']". is it possible to change them? or even increase their amount? if not this tool would be perfect if it had this function.

I have the following problems during use,thank!

Snipaste_2022-08-07_23-00-54
$ python run_ingram.py --in test_in --out test_out --all --th 230 --nosnap
Traceback (most recent call last):
File "C:\Users\tt\Ingram\run_ingram.py", line 68, in
run(args)
File "C:\Users\tt\Ingram\run_ingram.py", line 48, in run
for line in f:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 1519: illegal multibyte sequence

kill process

the process ends with a "kill" error, what could be the problem? target costs 300

cant cancel paused job

i ran Ingram but decided to stop the job with Ctrl C it then went into paused.... now i cant start another job how do i cancel the paused session and start a new session... am a newbie please forgive

求端点继续的功能

加入机器死了,重新启动相同命令,从最后一次的目标、端口开始
而不重新开始

谢谢

pwd found

how i get new pwd file

Default passwords are not working
All dvr do recommend changing the default password

1

1

Output

Hi. you have an incorrect output to the file. It is different from the one in the readme. There is too much additional information about "cve_2017_7921" in the output file

_cve-2017-7921,["['IP CAMERA', 'CST', 'DST', 'CST-9:00:00', '(W', 'davinci', 'HIKVISION DS-123', 'time.windows.com', 'members.dyndns.org', 'dynupdate.no-ip.com', 'www.hik-online.com', 'public', 'private', 'public', 'admin', '12345', '22222', '2d', '222', 'mainStream', 'Profile_1', 'VideoSourceToken', 'AudioSourceConfigToken', 'VideoEncoderToken_1', 'MainAudioEncoderToken', 'VideoAnalyticsToken', 'PTZToken', 'AudioOutputConfigToken', 'AudioDecoderConfigToken', 'subStream', 'Profile_2', 'VideoSourceToken', 'AudioSourceConfigToken', 'VideoEncoderToken_2', 'MainAudioEncoderToken', 'VideoAnalyticsToken', 'PTZToken', 'AudioOutputConfigToken', 'AudioDecoderConfigToken', 'eualarm.hik-online.com', '12345678', 'onvif://www.onvif.org_.. and so on

Thanks

The problems will be solved after some time

Hey guys, we've been so busy with graduation and job hunting lately that we can't get around to optimizing this program.

After a while, we'll work through each bug in the project, so please make do with it now.

Wish us luck!

2 issues - cve_2021_36260 not found & snapshots all cam

Hi Jorhelp
I have 2 issues. Please check them for me. Thanks so much

  1. I try to execute the following command in readme.md
    ./run_ingram.py --in TARGET --out OUT_DIR --hik_weak --cve_2017_7921 --**cve-2021_36260** --th_num 80
    but It's say cve_2021_36260's not found
    How can I solved this?
  2. I want to snapshot all cams info in 1 result found. How can I get this? Default I see in "camera.py" snap just 1 cam info
    Thanks for Your Support

How to disable snapshots

Hi im scanning a lot of ips and getting a lot of screenshots that i dont want. Is there any option to disable snapshots?

When running, only seems to check for open ports in log

``
[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.220 detect 80 is open

[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.219 detect 80 is open

[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.218 detect 80 is open

[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.217 detect 80 is open

[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.216 detect 80 is open

[2022-11-23 00:45:00][INFO][detect.port_detect] 12.42.140.215 detect 80 is open

[2022-11-23 00:45:16][INFO][run_ingram.] Ingram done!
``

What seems to be the issue? The log is same format as this, but I cut it down to show you a sample

your File have Virus ?!

$ git clone [email protected]:jorhelp/Ingram.git
Cloning into 'Ingram'...
The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
[email protected]: Permission denied (publickey).
fatal: Could not read from remote repository.

cve 2021-33044 do not show any result

cve 2021-33044 for dahua do not give snapshot or the username and passowrd. All the fields are empty but in your picture I see them. so what is worng?

无法运行,有“ValueError: negative shift count”错误提示

运行错误提示:
Exception in thread Thread-1 (_cal_total):
Traceback (most recent call last):
File "K:\Python310\lib\threading.py", line 1016, in _bootstrap_inner
self.run()
File "K:\Python310\lib\threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "K:\Ingram\Ingram\data.py", line 54, in _cal_total
self.add_total(net.get_ip_seg_len(strip_line))
File "K:\Ingram\Ingram\utils\net.py", line 16, in get_ip_seg_len
return IPy.IP(ip_seg, make_net=True).len()
File "K:\Python310\lib\site-packages\IPy.py", line 259, in init
self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion)
File "K:\Python310\lib\site-packages\IPy.py", line 1632, in _prefixlenToNetmask
return ((2<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
ValueError: negative shift count
error occurred, see the log.txt for more information.

mac m1 安装依赖报错

python3.10 -m pip install -r requirements.txt
....
error: subprocess-exited-with-error

× Building wheel for unicorn (pyproject.toml) did not run successfully.
│ exit code: 1
╰─> [1 lines of output]
error: [Errno 2] No such file or directory: 'cmake'
[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for unicorn
Building wheel for colored-traceback (pyproject.toml) ... done
Created wheel for colored-traceback: filename=colored_traceback-0.3.0-py3-none-any.whl size=4608 sha256=ef79b2254252fdfde6740b9cccba0135baeab08d101828da951641f4f487d268
Stored in directory: /Users/../Library/Caches/pip/wheels/0e/84/b5/8f43516633cca93567bf206c690474547a29f1f1a2e9a3882d
Successfully built IPy pwn capstone intervaltree colored-traceback
Failed to build unicorn
ERROR: Could not build wheels for unicorn, which is required to install pyproject.toml-based projects

no results

there is no results after this command :
./run_ingram.py --in_file port80 --out_file results --all --th_num 80

{Help} Can add example target.txt

Hi
Can add example target.txt
{"ip":"...","ports":[80,81,82,86,161,554,8082,9020,50080]}
{"ip":"...","ports":[161,2222,8082,8880,9990]}

Not doing anything

Hello.
I have recently reinstalled my Mac due to drive issues.
Ingram worked flawlessly before resetting it and updating it to the latest beta version (public beta Sonoma),but now, with the targets file it will just do the spinning animation and won't count any hit, no nothing.Thoughts?
Thanks
Screenshot 2023-07-16 at 16 38 30

提示target.txt 文件不存在,是我target.txt 文件放错位置了,还是target.txt 文件里的IP地址格式不对。

(.venv) C:\Users\Administrator\Ingram>python run_ingram.py -i target.txt -o sda

      _    __..-:┑

║`====╩=╩=======╣ |
==#║_______███_/ @ ║ |
╚===.===╦==╦=╤==╩#█
_║::║/ _______
║::║| |
| .-----. .-----. .----. .---.-. .--------.
║::║| | | | | | _ | | | | _ | | |
║::║
\ |
______| |
|| |__ | || |.| ||||
██ |
___|
██
██
██
██

the input file target.txt does not exists!

target.txt文件我就如下这样保存的:

你可以使用井号(#)来进行注释

IP 地址以及要扫描的端口

带 '-' 的IP段

212.67.1.2-212.81.255.255

纯小白,学习一天了 最后到进行到the input file target.txt does not exists! 不知道怎么回事了,前面磕磕绊绊但都解决了。

Chinese --> English translate documentation

Hi, can you translate the documentation of the tool to english?
I think that is to stupid to make a github tool where the documentation in the github is on chinese.
In my opinion, you should write on english to can understand how it works.
Most of the people dont understand chinese like me

Port autodetect doesnt work. only hikvision cams captured. IP list accepts only IP addresses without specifying ports and without ranges

Port autodetection works only at 80 port. Other ports are not detectable by your scanner.
When I use -p key as python3 run_ingram.py -i masscan_results.txt -o out -t 50 -p 80 81 8080 8081 8000 8001 8002 8003 8004 8005 8006 8007 8008 it scans every IP address with all marked ports. It takes much more time.
IP list cant accept IP ranges or IP addresses with port specified (for example 0.0.0.0:1080)
Please revert back masscan support, or make IP list with ports acceptable.
one more thing: no screenshots for any dahua or any other cams except hikvision. The results.csv contains a lot of dahua and other cams, but only hikvision cams are captured. The results.csv doesn't contain dahua logins and passwords. Absolutely.
Previous version was more functional and workable.

OSError: [Errno 25] Inappropriate ioctl for device

When I use the script on the backgroud of CentOS7 via nohup, but it has occurred some errors like below:

nohup python3 run_ingram.py -i targets.txt -o 218 -t 1248 &

Traceback (most recent call last):
  File "/home/projects/Ingram/run_ingram.py", line 10, in <module>
    from Ingram.utils import config
  File "/home/projects/Ingram/Ingram/utils/__init__.py", line 4, in <module>
    from Ingram.utils.logo import logo
  File "/home/projects/Ingram/Ingram/utils/logo.py", line 351, in <module>
    logo = generate_logo()
  File "/home/projects/Ingram/Ingram/utils/logo.py", line 329, in generate_logo
    if font_width + icon_width + 2 < os.get_terminal_size()[0]:
OSError: [Errno 25] Inappropriate ioctl for device

but when i run it directly with console , everything is ok.
How can i fix this and run it on backgroud correctly?

new errors

hi sir sorri but have another problem plz look at error am gettin since yesterday

[⇘] 182/326(55.8%) Found 7 Time: 6s/11s Traceback (most recent call last):
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 40, in
main()
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 35, in main
xor = xore( decrypt(open( sys.argv[1],'rb').read()) )
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 18, in decrypt
cipher = AES.new(key, AES.MODE_ECB)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/AES.py", line 59, in init
blockalgo.BlockAlgo.init(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/blockalgo.py", line 141, in init
self._cipher = factory.new(key, *args, **kwargs)
SystemError: PY_SSIZE_T_CLEAN macro must be defined for '#' formats
[⇖] 306/326(93.9%) Found 8 Time: 11s/12s Traceback (most recent call last):
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 40, in
main()
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 35, in main
xor = xore( decrypt(open( sys.argv[1],'rb').read()) )
File "/home/kali/Desktop/Ingram/scan/lib/decrypt_configure.py", line 18, in decrypt
cipher = AES.new(key, AES.MODE_ECB)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/AES.py", line 95, in new
return AESCipher(key, *args, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/AES.py", line 59, in init
blockalgo.BlockAlgo.init(self, _AES, key, *args, **kwargs)
File "/usr/local/lib/python3.10/dist-packages/Crypto/Cipher/blockalgo.py", line 141, in init
self._cipher = factory.new(key, *args, **kwargs)
SystemError: PY_SSIZE_T_CLEAN macro must be defined for '#' formats
[⇘] 326/326(100.0%) Found 9 Time: 17s/17s

Snapshot

There are many screenshots that do not load (instead of a full-fledged snapshot, a file of 43-45 bytes in size). View the already existing bruteforsers in the public domain, maybe they will tell you how to fix it

Thank you for your attention.

No DVR capture for CVE-2021-33044

Hi Jorhelp, are there any possibilities to have DVR snapshots for CVE-2021-33044 with zoomeye dork (":4c66" +title:"WEB SERVICE" +port:"80") as example. In most cases cameras password are the same as DVRs.
In console: ./Console.py --logon loopback --rhost 192.168.57.20 --proto dhip --rport 80
config RemoteDevice

I have the following problems during use, how to solve them?

Killed
Killed
Killed
Killed
Exception ignored in: <_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
Killed
Killed
Exception ignored in: <_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
Exception ignored in: <_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe
BrokenPipeError: [Errno 32] Broken pipe
Killed
Exception ignored in: <_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
Killed
BrokenPipeError: [Errno 32] Broken pipe
Killed

swapped colors on snapshot taken via --hik_weak and some ideas

when scanned host match both conditions - cve-2017-7921 and weak password 2 snapshots are generated, for snapshot triggered by cve colors are ok, but same camera snapshot made made as filename IP_PORT_login_password.jpg have disorted colors. is that happen everywhere or i have bad config (currently using ubuntu lxc container on proxmox)
comparision
xxx xxx xxx xxx_80-admin-12345abc
xxx xxx xxx xxx_80-cve_2017_7921

how you download snapshots for hikvision with known password when url below doesnt work? how about this? http://login:password@IP:PORT/ISAPI/Streaming/channels/101/picture

i noticed jpegs generated with --hik_weak have quality set to 75 while i guess downloaded with http://IP:PORT/onvif-http/snapshot?auth=YWRtaW46MTEK in url are quality 80 so i guess its untouched.

some thoughts:

idea1. usualy there is more than one camera behind nat or in same subnet but many times while some are vulnerable some dont, how about to add option which will check already discovered password on vuln cam against others found on same ip/subnet, if password wasnt already changed by "visitors" there is high probability it will match to all other cameras/nvr used in same network.

idea2. with help of zoomeye ([almost] free alternative to shodan) users can generate list of ip:port targets for scan, noticed hikvision cameras usually set port forward via upnp on ports: 80-88,8080-8088 (depend of amount of cameras), by using filter https://www.zoomeye.org/searchResult?q=iconhash%3A%20%2289b932fcc47cf4ca3faadb0cfdef89cf%22%20%2Bservice%3A%22http%22 a search of favicon which is used by hikvision at login page, it will also return all clones from gray market since these are runnin with borrowed fw.

to narrow results only to vulnerable fw versions i use "Last-Modified" variable from http header. versions proven vulnerable to cve-2017-7921: "Last-Modified: Tue, 17 May 2016 10:22:58 GMT" "Last-Modified: Thu, 24 Mar 2016 05:58:21 GMT" "Last-Modified: Thu, 19 Mar 2015 09:24:20 GMT" there is 6 maybe 7 more which can be easily extracted using curl -H which will save headers then grep by "Last-Modified"

idea3. since scanner randomize targets result file will be also filled with random unsorted ip addresses, maybe nothing but i use something like this on result file to get rid of duplicates:
cat results.csv |grep -v 2021- |cut --delimiter=, -f1,2,3,4 |sort -V |uniq
result will contain only ip,port,login,password so can really help in batch import into vms (sort -V will gill give "natural" order of ip addresses - eg 35. before 105. etc)

idea4. it would be nice if not vulnerable targets (but confirmed to be an ip camera could be saved into separate log file for example not_vulnerable.csv for further research. who knows if such db will not be useful in future. now all targets which were not cracked are just skipped.

issue(5) for cve-2021-36260 i think there is no timeout (or does not work) and in case when remote host accepts connection but does not close - process will hang forever waiting for something - if multiple targets will keep connection opened scan speed will drasticaly drop.

question(6) is there any way to resume scan (there is paused.conf file - i think from masscan so probably only that part could be resumed)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.