Repository: PowerLZY/Bold-Falcon
Branch: master
Commit: dfdd974c2950
Files: 525
Total size: 4.9 MB
Directory structure:
gitextract_a6c_s16r/
├── .codeclimate.yml
├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── __init__.py
├── _config.yml
├── agent/
│ ├── agent.py
│ └── agent.sh
├── analyzer/
│ ├── android/
│ │ ├── __init__.py
│ │ ├── analyzer.py
│ │ ├── config/
│ │ │ └── hooks.json
│ │ ├── lib/
│ │ │ ├── __init__.py
│ │ │ ├── api/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── adb.py
│ │ │ │ └── screenshot.py
│ │ │ ├── common/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abstracts.py
│ │ │ │ ├── constants.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── results.py
│ │ │ │ └── utils.py
│ │ │ └── core/
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── packages.py
│ │ │ └── startup.py
│ │ └── modules/
│ │ ├── __init__.py
│ │ ├── auxiliary/
│ │ │ ├── __init__.py
│ │ │ └── screenshots.py
│ │ └── packages/
│ │ ├── __init__.py
│ │ ├── apk.py
│ │ └── default_browser.py
│ ├── darwin/
│ │ ├── __init__.py
│ │ ├── analyzer.py
│ │ ├── lib/
│ │ │ ├── __init__.py
│ │ │ ├── common/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config.py
│ │ │ │ ├── hashing.py
│ │ │ │ ├── rand.py
│ │ │ │ └── results.py
│ │ │ ├── core/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── constants.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── signatures.yml
│ │ │ │ │ └── types.yml
│ │ │ │ ├── filetimes.py
│ │ │ │ ├── host.py
│ │ │ │ ├── osx.py
│ │ │ │ └── packages.py
│ │ │ └── dtrace/
│ │ │ ├── __init__.py
│ │ │ ├── apicalls.d
│ │ │ ├── apicalls.py
│ │ │ ├── autoprobes.py
│ │ │ ├── common.py
│ │ │ ├── dtruss.py
│ │ │ ├── dtruss.sh
│ │ │ ├── follow_children.d
│ │ │ ├── ipconnections.d
│ │ │ └── ipconnections.py
│ │ └── modules/
│ │ ├── __init__.py
│ │ └── packages/
│ │ ├── __init__.py
│ │ ├── app.py
│ │ ├── bash.py
│ │ ├── macho.py
│ │ └── zip.py
│ ├── linux/
│ │ ├── analyzer.py
│ │ ├── lib/
│ │ │ ├── __init__.py
│ │ │ ├── api/
│ │ │ │ ├── __init__.py
│ │ │ │ └── process.py
│ │ │ ├── common/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abstracts.py
│ │ │ │ ├── constants.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── hashing.py
│ │ │ │ ├── results.py
│ │ │ │ └── utils.py
│ │ │ └── core/
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ └── startup.py
│ │ └── modules/
│ │ ├── __init__.py
│ │ ├── auxiliary/
│ │ │ ├── __init__.py
│ │ │ ├── lkm.py
│ │ │ └── stap.py
│ │ └── packages/
│ │ ├── __init__.py
│ │ └── generic.py
│ └── windows/
│ ├── analyzer.py
│ ├── bin/
│ │ ├── procmon.pmc
│ │ ├── rules.yarac
│ │ └── zer0m0n-x64.sys
│ ├── lib/
│ │ ├── __init__.py
│ │ ├── api/
│ │ │ ├── __init__.py
│ │ │ ├── process.py
│ │ │ └── screenshot.py
│ │ ├── common/
│ │ │ ├── __init__.py
│ │ │ ├── abstracts.py
│ │ │ ├── constants.py
│ │ │ ├── decide.py
│ │ │ ├── defines.py
│ │ │ ├── exceptions.py
│ │ │ ├── hashing.py
│ │ │ ├── rand.py
│ │ │ ├── registry.py
│ │ │ └── results.py
│ │ └── core/
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── driver.py
│ │ ├── ioctl.py
│ │ ├── packages.py
│ │ ├── pipe.py
│ │ ├── privileges.py
│ │ └── startup.py
│ └── modules/
│ ├── __init__.py
│ ├── auxiliary/
│ │ ├── __init__.py
│ │ ├── dbgview.py
│ │ ├── disguise.py
│ │ ├── dumptls.py
│ │ ├── human.py
│ │ ├── installcert.py
│ │ ├── procmon.py
│ │ ├── reboot.py
│ │ ├── recentfiles.py
│ │ ├── screenshots.py
│ │ └── zer0m0n.py
│ └── packages/
│ ├── __init__.py
│ ├── applet.py
│ ├── bin.py
│ ├── com.py
│ ├── cpl.py
│ ├── dll.py
│ ├── doc.py
│ ├── exe.py
│ ├── ff.py
│ ├── generic.py
│ ├── hta.py
│ ├── hwp.py
│ ├── ie.py
│ ├── jar.py
│ ├── js.py
│ ├── jse.py
│ ├── msi.py
│ ├── pdf.py
│ ├── ppt.py
│ ├── ps1.py
│ ├── pub.py
│ ├── python.py
│ ├── reboot.py
│ ├── vbs.py
│ ├── wsf.py
│ ├── xls.py
│ └── zip.py
├── conf/
│ ├── auxiliary.conf
│ ├── avd.conf
│ ├── cuckoo.conf
│ ├── cuckooml.conf
│ ├── detection.conf
│ ├── esx.conf
│ ├── kvm.conf
│ ├── memory.conf
│ ├── physical.conf
│ ├── processing.conf
│ ├── qemu.conf
│ ├── reporting.conf
│ ├── virtualbox.conf
│ ├── vmware.conf
│ ├── vpn.conf
│ ├── vsphere.conf
│ └── xenserver.conf
├── cuckoo.py
├── data/
│ ├── __init__.py
│ ├── android/
│ │ ├── anti-vm/
│ │ │ ├── fake-build.prop
│ │ │ ├── fake-cpuinfo
│ │ │ └── fake-drivers
│ │ ├── apps/
│ │ │ ├── ImportContacts.apk
│ │ │ ├── Superuser.apk
│ │ │ └── de.robv.android.xposed.installer_v33_36570c.apk
│ │ ├── binaries/
│ │ │ └── su
│ │ ├── create_guest_avd.sh
│ │ └── hooking/
│ │ ├── Droidmon.apk
│ │ └── EmulatorAntiDetect.apk
│ ├── guids.txt
│ ├── html/
│ │ ├── base-report.html
│ │ ├── base-web.html
│ │ ├── browse.html
│ │ ├── error.html
│ │ ├── graphic/
│ │ │ └── logo.html
│ │ ├── js/
│ │ │ └── functions.js
│ │ ├── pagination-menu.html
│ │ ├── pagination-rpp.html
│ │ ├── report.html
│ │ ├── sections/
│ │ │ ├── behavior.html
│ │ │ ├── dropped.html
│ │ │ ├── errors.html
│ │ │ ├── file.html
│ │ │ ├── info.html
│ │ │ ├── network.html
│ │ │ ├── screenshots.html
│ │ │ ├── signatures.html
│ │ │ ├── static.html
│ │ │ ├── url.html
│ │ │ └── volatility.html
│ │ ├── submit.html
│ │ └── success.html
│ ├── mitm.py
│ ├── models/
│ │ ├── MalConv/
│ │ │ ├── malconvtest.py
│ │ │ └── pretrained_malconv.pth
│ │ ├── apistats/
│ │ │ ├── extract_apifeatures.py
│ │ │ └── standard.txt
│ │ └── strings_ngram/
│ │ ├── XGB_model.pkl
│ │ └── raw_train_sample.csv
│ ├── peutils/
│ │ └── UserDB.TXT
│ ├── src/
│ │ └── binpackage/
│ │ ├── Makefile
│ │ └── execsc.c
│ ├── strace.stp
│ ├── test-internet.vbs
│ ├── whitelist/
│ │ └── domain.txt
│ └── yara/
│ ├── binaries/
│ │ ├── embedded.yar
│ │ ├── shellcodes.yar
│ │ └── vmdetect.yar
│ ├── memory/
│ │ ├── .gitignore
│ │ └── index_memory.yar
│ └── urls/
│ └── .gitignore
├── distributed/
│ ├── alembic.ini
│ ├── app.py
│ ├── distributed/
│ │ ├── __init__.py
│ │ ├── api.py
│ │ ├── app.py
│ │ ├── db.py
│ │ ├── exception.py
│ │ └── views/
│ │ ├── __init__.py
│ │ └── api.py
│ ├── instance.py
│ ├── migration/
│ │ ├── env.py
│ │ ├── script.py.mako
│ │ └── versions/
│ │ ├── 151400d38e03_node_status_timestamp_index.py
│ │ ├── 166078eb1311_change_node_id_to_name.py
│ │ ├── 2aa59981b59d_node_task_not_unique.py
│ │ ├── 37c08c9655bb_initial_database.py
│ │ ├── 3cc1509b7fdc_node_status.py
│ │ ├── 3d1d8fd2cdbb_timestamps.py
│ │ ├── 4b86bc0d40aa_node_mode.py
│ │ ├── 4d0a2590e997_node_task_index.py
│ │ └── 69ecf07a99b_finished_to_status.py
│ ├── requirements.txt
│ └── settings.py
├── docs/
│ ├── .debug.yml
│ ├── CNAME
│ ├── Gemfile
│ ├── Makefile
│ ├── README.md
│ ├── _config.yml
│ └── book/
│ ├── DevelopmentDocumentation.md
│ ├── Installation.md
│ ├── Introduction.md
│ ├── README.md
│ ├── Usage.md
│ └── 设计文档.md
├── docs-old/
│ ├── AUTHORS
│ ├── CHANGELOG
│ ├── LICENSE
│ ├── README
│ └── book/
│ └── src/
│ ├── Makefile
│ ├── conf.py
│ ├── customization/
│ │ ├── auxiliary.rst
│ │ ├── index.rst
│ │ ├── machinery.rst
│ │ ├── packages.rst
│ │ ├── processing.rst
│ │ ├── reporting.rst
│ │ └── signatures.rst
│ ├── development/
│ │ ├── code_style.rst
│ │ ├── development_notes.rst
│ │ └── index.rst
│ ├── faq/
│ │ └── index.rst
│ ├── finalremarks/
│ │ └── index.rst
│ ├── index.rst
│ ├── installation/
│ │ ├── guest/
│ │ │ ├── agent.rst
│ │ │ ├── cloning.rst
│ │ │ ├── creation.rst
│ │ │ ├── index.rst
│ │ │ ├── network.rst
│ │ │ ├── requirements.rst
│ │ │ └── saving.rst
│ │ ├── guest_physical/
│ │ │ ├── creation.rst
│ │ │ ├── index.rst
│ │ │ ├── network.rst
│ │ │ ├── requirements.rst
│ │ │ └── saving.rst
│ │ ├── host/
│ │ │ ├── configuration.rst
│ │ │ ├── configuration_android.rst
│ │ │ ├── index.rst
│ │ │ ├── installation.rst
│ │ │ └── requirements.rst
│ │ ├── index.rst
│ │ └── upgrade.rst
│ ├── introduction/
│ │ ├── index.rst
│ │ ├── license.rst
│ │ ├── sandboxing.rst
│ │ └── what.rst
│ └── usage/
│ ├── api.rst
│ ├── clean.rst
│ ├── dist.rst
│ ├── index.rst
│ ├── packages.rst
│ ├── results.rst
│ ├── start.rst
│ ├── submit.rst
│ ├── utilities.rst
│ └── web.rst
├── examples/
│ ├── cuckooml.ipynb
│ ├── cuckooml.py
│ ├── detectiontest.py
│ ├── getjsondata.py
│ ├── instance.py
│ ├── loader.py
│ ├── ml.py
│ └── rundetectiontest.py
├── lib/
│ ├── __init__.py
│ └── cuckoo/
│ ├── __init__.py
│ ├── common/
│ │ ├── __init__.py
│ │ ├── abstracts.py
│ │ ├── colors.py
│ │ ├── compare.py
│ │ ├── config.py
│ │ ├── constants.py
│ │ ├── defines.py
│ │ ├── dns.py
│ │ ├── exceptions.py
│ │ ├── irc.py
│ │ ├── logo.py
│ │ ├── netlog.py
│ │ ├── objects.py
│ │ ├── utils.py
│ │ ├── virustotal.py
│ │ └── whitelist.py
│ └── core/
│ ├── __init__.py
│ ├── database.py
│ ├── guest.py
│ ├── plugins.py
│ ├── resultserver.py
│ ├── rooter.py
│ ├── scheduler.py
│ └── startup.py
├── modules/
│ ├── __init__.py
│ ├── auxiliary/
│ │ ├── __init__.py
│ │ ├── mitm.py
│ │ ├── services.py
│ │ └── sniffer.py
│ ├── detection/
│ │ ├── __init__.py
│ │ ├── apistats.py
│ │ ├── malconv.py
│ │ ├── model.py
│ │ └── strings.py
│ ├── machinery/
│ │ ├── __init__.py
│ │ ├── avd.py
│ │ ├── esx.py
│ │ ├── kvm.py
│ │ ├── physical.py
│ │ ├── qemu.py
│ │ ├── virtualbox.py
│ │ ├── vmware.py
│ │ ├── vsphere.py
│ │ └── xenserver.py
│ ├── processing/
│ │ ├── __init__.py
│ │ ├── analysisinfo.py
│ │ ├── apkinfo.py
│ │ ├── baseline.py
│ │ ├── behavior.py
│ │ ├── buffer.py
│ │ ├── cuckooml.py
│ │ ├── debug.py
│ │ ├── droidmon.py
│ │ ├── dropped.py
│ │ ├── dumptls.py
│ │ ├── googleplay.py
│ │ ├── memory.py
│ │ ├── network.py
│ │ ├── platform/
│ │ │ ├── __init__.py
│ │ │ ├── linux.py
│ │ │ └── windows.py
│ │ ├── procmemory.py
│ │ ├── screenshots.py
│ │ ├── snort.py
│ │ ├── static.py
│ │ ├── strings.py
│ │ ├── suricata.py
│ │ ├── targetinfo.py
│ │ └── virustotal.py
│ └── reporting/
│ ├── __init__.py
│ ├── elasticsearch.py
│ ├── jsondump.py
│ ├── moloch.py
│ ├── mongodb.py
│ └── reporthtml.py
├── oldweb/
│ ├── .gitignore
│ ├── analysis/
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ ├── templatetags/
│ │ │ ├── __init__.py
│ │ │ └── analysis_tags.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── compare/
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── dashboard/
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ ├── static/
│ │ ├── css/
│ │ │ ├── lightbox.css
│ │ │ └── style.css
│ │ └── js/
│ │ ├── app.js
│ │ ├── bootstrap-fileupload.js
│ │ ├── hexdump.js
│ │ ├── jquery.js
│ │ └── lightbox.js
│ ├── submission/
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── templates/
│ │ ├── analysis/
│ │ │ ├── admin/
│ │ │ │ └── index.html
│ │ │ ├── behavior/
│ │ │ │ ├── _api_call.html
│ │ │ │ ├── _chunk.html
│ │ │ │ ├── _processes.html
│ │ │ │ ├── _search.html
│ │ │ │ ├── _search_results.html
│ │ │ │ ├── _tree.html
│ │ │ │ ├── _tree_process.html
│ │ │ │ └── index.html
│ │ │ ├── buffers/
│ │ │ │ └── index.html
│ │ │ ├── dropped/
│ │ │ │ └── index.html
│ │ │ ├── export.html
│ │ │ ├── import.html
│ │ │ ├── index.html
│ │ │ ├── memory/
│ │ │ │ ├── _apihooks.html
│ │ │ │ ├── _callbacks.html
│ │ │ │ ├── _devicetree.html
│ │ │ │ ├── _gdt.html
│ │ │ │ ├── _idt.html
│ │ │ │ ├── _malfind.html
│ │ │ │ ├── _messagehooks.html
│ │ │ │ ├── _modscan.html
│ │ │ │ ├── _netscan.html
│ │ │ │ ├── _pslist.html
│ │ │ │ ├── _sockscan.html
│ │ │ │ ├── _ssdt.html
│ │ │ │ ├── _svcscan.html
│ │ │ │ ├── _timers.html
│ │ │ │ ├── _yarascan.html
│ │ │ │ └── index.html
│ │ │ ├── network/
│ │ │ │ ├── _dns.html
│ │ │ │ ├── _hosts.html
│ │ │ │ ├── _http.html
│ │ │ │ ├── _icmp.html
│ │ │ │ ├── _irc.html
│ │ │ │ ├── _snort.html
│ │ │ │ ├── _suricata.html
│ │ │ │ ├── _tcp.html
│ │ │ │ ├── _udp.html
│ │ │ │ └── index.html
│ │ │ ├── overview/
│ │ │ │ ├── _file.html
│ │ │ │ ├── _info.html
│ │ │ │ ├── _screenshots.html
│ │ │ │ ├── _signatures.html
│ │ │ │ ├── _summary.html
│ │ │ │ ├── _url.html
│ │ │ │ └── index.html
│ │ │ ├── pending.html
│ │ │ ├── procmemory/
│ │ │ │ └── index.html
│ │ │ ├── report.html
│ │ │ ├── search.html
│ │ │ ├── search_results.html
│ │ │ └── static/
│ │ │ ├── _antivirus.html
│ │ │ ├── _pe32.html
│ │ │ ├── _strings.html
│ │ │ └── index.html
│ │ ├── base.html
│ │ ├── compare/
│ │ │ ├── _info.html
│ │ │ ├── _summary_table.html
│ │ │ ├── both.html
│ │ │ ├── hash.html
│ │ │ └── left.html
│ │ ├── dashboard/
│ │ │ └── index.html
│ │ ├── error.html
│ │ ├── footer.html
│ │ ├── header.html
│ │ ├── standalone_error.html
│ │ ├── submission/
│ │ │ ├── complete.html
│ │ │ ├── index.html
│ │ │ └── status.html
│ │ └── success.html
│ └── web/
│ ├── __init__.py
│ ├── headers.py
│ ├── local_settings.py
│ ├── secret_key.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── requirements.txt
└── utils/
├── api.py
├── community.py
├── darwin/
│ ├── bootstrap_guest.sh
│ └── bootstrap_host.sh
├── db_migration/
│ ├── alembic.ini
│ ├── env.py
│ ├── script.py.mako
│ └── versions/
│ ├── from_0_6_to_1_1.py
│ ├── from_1_1_to_1_2-added_states.py
│ ├── from_1_1_to_1_2-extend_file_type.py
│ ├── from_1_2_to_1_3-add_task_owner.py
│ ├── from_1_2_to_2_0-guest_status.py
│ ├── from_1_2_to_2_0-machine_options.py
│ ├── from_1_2_to_2_0-processing-column.py
│ └── from_1_2_to_2_0-taken-route.py
├── dnsserve.py
├── machine.py
├── process.py
├── process2.py
├── rawdb.py
├── rooter.py
├── service.sh
├── setup.sh
├── smtp_sinkhole.py
├── start-distributed.sh
├── stats.py
├── stop-distributed.sh
├── submit.py
├── suricata.sh
└── vpncheck.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .codeclimate.yml
================================================
languages:
Python: true
exclude_paths:
- "tests/*"
================================================
FILE: .gitignore
================================================
# Ignore Database
db/cuckoo.db
# Ignore logs
log/*.log
# Ignore analyses
storage/*
# Ignore Python byte code
*.pyc
# Ignore certificates
*.pem
*.cert
# Ignore OS generated files
.DS_Store*
.AppleDouble
ehthumbs.db
Icon?
Thumbs.db
# Ignore development files
docs/book/src/_build/
.idea/
.project
.pydevproject
# Ignore Django secret_key
web/web/secret_key.py
# Ignore yara rules
data/yara/index_*.yar
# Ignore venv
venv
# Ignore sample data
sample_data/
# Ignore Jupyter Notebook examples' checkpoints
examples/.ipynb_checkpoints
/data/monitor/
/modules/signatures/
================================================
FILE: .travis.yml
================================================
branches:
only:
- master
language: python
python:
- 2.7
before_install:
- sudo apt-get update -qq
- sudo apt-get install python-dev python-libvirt libffi-dev libssl-dev
- wget http://downloads.sourceforge.net/project/ssdeep/ssdeep-2.12/ssdeep-2.12.tar.gz
- tar -zxvf ssdeep-2.12.tar.gz
- cd ssdeep-2.12
- ./configure && make
- sudo make install
- cd ..
install:
- pip install -r requirements.txt
script:
- python utils/community.py -wafb monitor
- python cuckoo.py --debug --test
#- nosetests
================================================
FILE: LICENSE.txt
================================================
BSD 3-Clause License
Copyright 2021-2024, Zhengyang Li. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
```shell
____ _ _ _____ _
| __ ) ___ | | __| | | ___|_ _| | ___ ___ _ __
| _ \ / _ \| |/ _` |_____| |_ / _` | |/ __/ _ \| '_ \
| |_) | (_) | | (_| |_____| _| (_| | | (_| (_) | | | |
|____/ \___/|_|\__,_| |_| \__,_|_|\___\___/|_| |_|
```
# 毕方智能云沙箱
毕方智能云沙箱(***Bold-Falcon***)是一个开源的自动化恶意软件分析系统。它用于自动运行和分析文件,并收集全面的分析结果,概述恶意软件在独立操作系统中运行时所做的工作。我们的工作是二次开发开源cuckoo沙箱,包括**更新项目结构**,**重写整个前端的用户交互**和**添加基于机器学习的检测模块**,
使恶意软件分析系统可以**思考**。

**`说明文档`** https://powerlzy.github.io/Bold-Falcon/
**`开发文档`** https://boldfalcon.readthedocs.io
#### 下载源码
```shell
$ git clone https://github.com/PowerLZY/Bold-Falcon
```
#### 开源资料
+ [cuckoo](https://github.com/cuckoosandbox/cuckoo) Cuckoo Sandbox is an automated dynamic malware analysis system
+ [cuckoo-modified](https://github.com/spender-sandbox/cuckoo-modified) Modified edition of cuckoo
+ [cuckooDroid](https://github.com/idanr1986/cuckoo-droid) CuckooDroid - Automated Android Malware Analysis with Cuckoo Sandbox.
+ [docker-cuckoo](https://github.com/blacktop/docker-cuckoo) Cuckoo Sandbox Dockerfile
+ [cuckooautoinstall](https://github.com/buguroo/cuckooautoinstall) Auto Installer Script for Cuckoo Sandbox
+ [cuckooML](https://github.com/honeynet/cuckooml) CuckooML: Machine Learning for Cuckoo Sandbox
+ [Panda-Sandbox](https://github.com/PowerLZY/Panda-Sandbox) Cuckoo python3 (Unfinished)
+ [HaboMalHunter](https://github.com/Tencent/HaboMalHunter#readme_cn) HaboMalHunter is a sub-project of Habo Malware Analysis System
+ [cuckoosegg](https://github.com/chrisante/cuckoosegg)
#### 源码分析
+ [cuckoo技术分析全景图](https://cloud.tencent.com/developer/article/1597020)
+ [cuckoo沙箱源码分析上](https://bbs.pediy.com/thread-260038.htm)
+ [cuckoo沙箱源码分析中](https://bbs.pediy.com/thread-260087.htm)
+ [cuckoo沙箱源码分析后](https://bbs.pediy.com/thread-260252.htm)
+ [腾讯哈勃Linux沙箱源码分析上](https://zhuanlan.zhihu.com/p/54756592)
+ [腾讯哈勃Linux沙箱源码分析下](https://zhuanlan.zhihu.com/p/54756845)
### 开源样本
如果你想要获取更多的`恶意样本`请访问查询:
**推荐:**
- [Blue Hexagon Open Dataset for Malware AnalysiS (BODMAS)](https://whyisyoung.github.io/BODMAS/)
- [EMBER](https://github.com/elastic/ember) - Endgame Malware BEnchmark for Research
- [Malware Training Sets: A machine learning dataset for everyone](http://marcoramilli.blogspot.cz/2016/12/malware-training-sets-machine-learning.html) ([data](https://github.com/marcoramilli/MalwareTrainingSets))
- [SoReL-20M](https://github.com/sophos-ai/SOREL-20M) - Sophos-ReversingLabs 20 Million dataset.
- [Virusshare](https://virusshare.com/)
**其他:**
+ [Samples of Security Related Dats](http://www.secrepo.com/)
+ [DARPA Intrusion Detection Data Sets](https://www.ll.mit.edu/ideval/data/)
+ [Stratosphere IPS Data Sets](https://stratosphereips.org/category/dataset.html)
+ [Open Data Sets](http://csr.lanl.gov/data/)
+ [Data Capture from National Security Agency](http://www.westpoint.edu/crc/SitePages/DataSets.aspx)
+ [The ADFA Intrusion Detection Data Sets](https://www.unsw.adfa.edu.au/australian-centre-for-cyber-security/cybersecurity/ADFA-IDS-Datasets)
+ [NSL-KDD Data Sets](https://github.com/defcom17/NSL_KDD)
+ [Malicious URLs Data Sets](https://sysnet.ucsd.edu/projects/url)
+ [Multi-Source Cyber-Security Events](http://csr.lanl.gov/data/cyber1/)
+ [Malware Training Sets: A machine learning dataset for everyone](http://marcoramilli.blogspot.cz/2016/12/malware-training-sets-machine-learning.html)
如果你想要获取更多的`良性样本`请在如下等网络自行爬取:
- [portablefreeware](http://www.portablefreeware.com/)
- [onlyfreewares](http://www.onlyfreewares.com/)
- [snapfiles](https://www.snapfiles.com/new/list-whatsnew.html)
- [downloadcrew](https://downloadcrew.com/)
- [chocolatey](https://chocolatey.org/)
#### 项目结构更新
- [x] 整理工程目录打包lib:(common,core),Modules(辅助功能、虚拟机、处理、签名、机器学习模型检测)
- [x] 省略\CWD目录:添加 analyzer、db、examples、Mal_sample、sample_data、storage、log等目录
#### 最近更新
+ 学习内容
- [x] [预训练TF-IDF模型加载优化 100s -> 2s](https://thiagomarzagao.com/2015/12/08/saving-TfidfVectorizer-without-pickles/)
- [ ] Linux沙箱及检测方案
- [ ] ATT&CK Navigator layer for Cuckoo's TTPs.pyattack
- [ ] 添加 《Dynamic Malware Analysis with Feature Engineering and Feature Learning》 动态分析检测模型
- [ ] 添加 MaliciousMacroBot(mmbot)office宏病毒检测方案
+ 设计文档
+ [x] 参考文献记录(设计依据)
+ [x] 国内沙箱深度调研
+ [x] 图标+起名
+ 家族签名模块
- [x] [cuckoo 社区签名库](https://github.com/cuckoosandbox/community)
- [x] [cuckoo的行为签名](https://www.secpulse.com/archives/75180.html)
- [ ] 添加挖矿+使用自定义签名
+ 机器学习模块
- [x] 数据集:kaggle microsoft 10000个软件、挖矿软件 6000个;
- [x] 报告显示内容:模型检测图展示、使用特征展示、预测威胁得分;
- [x] 静态检测引擎:string、malconv;
- [x] 动态检测引擎:API调用序列;
- [x] 定义基类Dectection、Instance等;
- [x] 添加Smaple——malware,200个json report样本;
- [ ] 添加 《Dynamic Malware Analysis with Feature Engineering and Feature Learning》 动态分析检测模型
+ 后期需求
+ [ ] 环境打包,Docker\shells安装
+ [ ] blog解析文档编写
+ [ ] 虚拟机管理:libvirt+高并发虚拟机
+ [ ] 沙箱内存管理:MemScrimper: Time- and Space-Efficient Storage of *Malware* Sandbox Memory Dumps (2018 DIVMA)
+ [ ] 3.3.5 REST API(Cuckoo docs) wsgi应用程序
#### 常见问题
+ Machine * status gurumeditation
- 找到虚拟机安装目录下VBox.log日志文件
- 在日志文件中找到ProcessID, ```kill - 9 ProcessID```
+ python 2/3 joblib.dump() 和 joblib.load()
- 不同python版本的pickle.dump()和pickle.load()是可以相互转换和支持的
- 在python3中,您应该使用较低的协议号来编写pickle数据 ```pickle.dump(your_object, your_file, protocol=2)```
+ Pytorch Cpu 导入 Gpu 训练的模型
- `model.load(model_path, map_location='cpu')`
+ Sphinx-readthedocs 开发文档自动生成
- `sphinx-quickstart`
- `sphinx-apidoc -o ./source ../Bold-Falcon`
- `python -m sphinx -T -E -b html -d _build/doctrees -D language=en . _build/html`
================================================
FILE: __init__.py
================================================
name = "1111ScorecardBundle"
================================================
FILE: _config.yml
================================================
theme: jekyll-theme-cayman
================================================
FILE: agent/agent.py
================================================
# Copyright (C) 2010-2013 Claudio Guarnieri.
# Copyright (C) 2014-2018 Cuckoo Foundation.
# Copyright (C) 2020-2021 PowerLZY.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import os
import sys
import time
import socket
import string
import random
import platform
import subprocess
import ConfigParser
from StringIO import StringIO
from zipfile import ZipFile
from SimpleXMLRPCServer import SimpleXMLRPCServer
BIND_IP = "0.0.0.0"
BIND_PORT = 8000
STATUS_INIT = 0x0001
STATUS_RUNNING = 0x0002
STATUS_COMPLETED = 0x0003
STATUS_FAILED = 0x0004
class Agent(object):
"""
Bold-Falcon agent, it runs inside guest.
:param system: platform
:param analyzer_path: analyzer path
:param int analyzer_pid: analyzer pid
:param error_message: None
:param current_status: STATUS_INIT = 0x0001,STATUS_RUNNING = 0x0002,STATUS_COMPLETED = 0x0003,STATUS_FAILED = 0x0004
:param analyzer_folder: upload analyzer folder
:param results_folder: results folder
"""
def __init__(self):
self.system = platform.system().lower()
self.analyzer_path = ""
self.analyzer_pid = 0
self.error_message = None
self.current_status = STATUS_INIT
self.analyzer_folder = ""
self.results_folder = ""
def _initialize(self):
'''
initialize agent.py
'''
if not self.analyzer_folder:
random.seed(time.time())
container = "".join(random.choice(string.ascii_lowercase) for x in range(random.randint(5, 10)))
if self.system == "windows":
system_drive = os.environ["SYSTEMDRIVE"] + os.sep
self.analyzer_folder = os.path.join(system_drive, container)
elif self.system == "linux" or self.system == "darwin":
self.analyzer_folder = \
os.path.join(os.environ.get("HOME", os.environ.get("PWD", "/tmp")), container)
else:
self.error_message = "Unable to identify operating system"
return False
try:
os.makedirs(self.analyzer_folder)
except OSError as e:
self.error_message = e
return False
return True
def get_status(self):
"""
Get current status.
:return: status.
"""
return self.current_status
def get_error(self):
"""
Get error message
:return: error message.
"""
return str(self.error_message)
def add_malware(self, data, name):
"""
Get analysis data.
:param data: analysis data.
:param name: file name.
:return: operation status.
"""
data = data.data
if self.system == "windows":
root = os.environ["TEMP"]
elif self.system == "linux" or self.system == "darwin":
root = "/tmp"
else:
self.error_message = \
"Unable to write malware to disk because the operating " \
"system could not be identified."
return False
file_path = os.path.join(root, name)
try:
with open(file_path, "wb") as sample:
sample.write(data)
except IOError as e:
self.error_message = \
"Unable to write sample to disk: {0}".format(e)
return False
return True
def add_config(self, options):
"""
Creates analysis.conf file from current analysis options.
:param options: current configuration options, dict format.
:return: operation status.
"""
if not isinstance(options, dict):
return False
config = ConfigParser.RawConfigParser()
config.add_section("analysis")
try:
for key, value in options.items():
# Options can be UTF encoded.
if isinstance(value, basestring):
try:
value = value.encode("utf-8")
except UnicodeEncodeError:
pass
config.set("analysis", key, value)
config_path = os.path.join(self.analyzer_folder, "analysis.conf")
with open(config_path, "wb") as config_file:
config.write(config_file)
except Exception as e:
self.error_message = e
return False
return True
def add_analyzer(self, data):
"""Add analyzer.
:param data: analyzer data.
:return: operation status.
"""
data = data.data
if not self._initialize():
return False
try:
zip_data = StringIO()
zip_data.write(data)
with ZipFile(zip_data, "r") as archive:
archive.extractall(self.analyzer_folder)
finally:
zip_data.close()
self.analyzer_path = os.path.join(self.analyzer_folder, "analyzer.py")
return True
def execute(self):
"""
Execute analysis.
:return: analyzer PID.
"""
if not self.analyzer_path or not os.path.exists(self.analyzer_path):
return False
try:
proc = subprocess.Popen([sys.executable, self.analyzer_path],
cwd=os.path.dirname(self.analyzer_path))
self.analyzer_pid = proc.pid
except OSError as e:
self.error_message = e
return False
self.current_status = STATUS_RUNNING
return self.analyzer_pid
def complete(self, success=True, error="", results=""):
"""
Complete analysis.
:param success: success status.
:param error: error status.
"""
if success:
self.current_status = STATUS_COMPLETED
else:
self.current_status = STATUS_FAILED
if error:
self.error_message = error
self.results_folder = results
return True
if __name__ == "__main__":
try:
if not BIND_IP:
BIND_IP = socket.gethostbyname(socket.gethostname())
print("[+] Starting agent on %s:%s ..." % (BIND_IP, BIND_PORT))
# Disable DNS lookup, by Scott D.
def FakeGetFQDN(name=""):
return name
socket.getfqdn = FakeGetFQDN
server = SimpleXMLRPCServer((BIND_IP, BIND_PORT), allow_none=True)
server.register_instance(Agent())
server.serve_forever()
except KeyboardInterrupt:
server.shutdown()
================================================
FILE: agent/agent.sh
================================================
#!/bin/bash
# Copyright (C) 2010-2013 Claudio Guarnieri.
# Copyright (C) 2014-2016 Cuckoo Foundation.
# Copyright (C) 2020-2021 PowerLZY.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
FILEPATH=$(readlink -f ${0%})
FILEPATHDIR=$(dirname $FILEPATH)
cd /tmp/
python $FILEPATHDIR/agent.py >$FILEPATHDIR/agent.stdout 2>$FILEPATHDIR/agent.stderr &
================================================
FILE: analyzer/android/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/analyzer.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import os
import logging
import pkgutil
import shutil
import sys
import xmlrpclib
import time
from lib.core.packages import choose_package
from lib.common.exceptions import CuckooError, CuckooPackageError
from lib.common.abstracts import Package, Auxiliary
from lib.common.constants import PATHS
from lib.core.config import Config
from lib.core.startup import init_logging
from modules import auxiliary
logging.disable(level=logging.DEBUG)
log = logging.getLogger()
class Analyzer(object):
def __init__(self):
self.config = None
self.target = None
def complete(self):
"""End analysis."""
log.info("Analysis completed")
def get_options(self):
"""Get analysis options.
@return: options dict.
"""
# The analysis package can be provided with some options in the
# following format:
# option1=value1,option2=value2,option3=value3
#
# Here we parse such options and provide a dictionary that will be made
# accessible to the analysis package.
options = {}
if self.config.options:
try:
# Split the options by comma.
fields = self.config.options.strip().split(",")
except ValueError as e:
log.warning("Failed parsing the options: %s", e)
else:
for field in fields:
# Split the name and the value of the option.
try:
key, value = field.strip().split("=")
except ValueError as e:
log.warning("Failed parsing option (%s): %s", field, e)
else:
# If the parsing went good, we add the option to the
# dictionary.
options[key.strip()] = value.strip()
return options
def prepare(self):
# Initialize logging.
init_logging()
# Parse the analysis configuration file generated by the agent.
self.config = Config(cfg="analysis.conf")
# We update the target according to its category. If it's a file, then
# we store the path.
if self.config.category == "file":
self.target = os.path.join("/data/local/tmp", str(self.config.file_name))
shutil.copyfile("config/hooks.json", "/data/local/tmp/hooks.json")
# If it's a URL, well.. we store the URL.
else:
self.target = self.config.target
def run(self):
self.prepare()
log.info("Starting analyzer from: {0}".format(os.getcwd()))
log.info("Storing results at: {0}".format(PATHS["root"]))
log.info("Target is: {0}".format(self.target))
# If no analysis package was specified at submission, we try to select
# one automatically.
if not self.config.package:
log.info("No analysis package specified, trying to detect it automagically")
# If the analysis target is a file, we choose the package according
# to the file format.
if self.config.category == "file":
package = choose_package(self.config.file_type, self.config.file_name)
# If it's an URL, we'll just use the default Internet Explorer
# package.
else:
package = "default_browser"
# If we weren't able to automatically determine the proper package,
# we need to abort the analysis.
if not package:
raise CuckooError("No valid package available for file type: {0}".format(self.config.file_type))
log.info("Automatically selected analysis package \"%s\"", package)
# Otherwise just select the specified package.
else:
package = self.config.package
# Generate the package path.
package_name = "modules.packages.%s" % package
# Try to import the analysis package.
try:
__import__(package_name, globals(), locals(), ["dummy"], -1)
# If it fails, we need to abort the analysis.
except ImportError:
raise CuckooError("Unable to import package \"{0}\", does not exist.".format(package_name))
# Initialize the package parent abstract.
Package()
# Enumerate the abstract's subclasses.
try:
package_class = Package.__subclasses__()[0]
except IndexError as e:
raise CuckooError("Unable to select package class (package={0}): {1}".format(package_name, e))
# Initialize the analysis package.
pack = package_class(self.get_options())
# Initialize Auxiliary modules
Auxiliary()
prefix = auxiliary.__name__ + "."
for loader, name, ispkg in pkgutil.iter_modules(auxiliary.__path__, prefix):
if ispkg:
continue
# Import the auxiliary module.
try:
__import__(name, globals(), locals(), ["dummy"], -1)
except ImportError as e:
log.warning("Unable to import the auxiliary module "
"\"%s\": %s", name, e)
# Walk through the available auxiliary modules.
aux_enabled = []
for module in Auxiliary.__subclasses__():
# Try to start the auxiliary module.
try:
aux = module()
aux.start()
except (NotImplementedError, AttributeError):
log.warning("Auxiliary module %s was not implemented",
aux.__class__.__name__)
continue
except Exception as e:
log.warning("Cannot execute auxiliary module %s: %s",
aux.__class__.__name__, e)
continue
finally:
log.info("Started auxiliary module %s",
aux.__class__.__name__)
aux_enabled.append(aux)
# Start analysis package. If for any reason, the execution of the
# analysis package fails, we have to abort the analysis.
try:
pack.start(self.target)
except NotImplementedError:
raise CuckooError("The package \"{0}\" doesn't contain a run "
"function.".format(package_name))
except CuckooPackageError as e:
raise CuckooError("The package \"{0}\" start function raised an "
"error: {1}".format(package_name, e))
except Exception as e:
raise CuckooError("The package \"{0}\" start function encountered "
"an unhandled exception: "
"{1}".format(package_name, e))
time_counter = 0
while True:
time_counter += 1
if time_counter == int(self.config.timeout):
log.info("Analysis timeout hit, terminating analysis")
break
try:
# The analysis packages are provided with a function that
# is executed at every loop's iteration. If such function
# returns False, it means that it requested the analysis
# to be terminate.
if not pack.check():
log.info("The analysis package requested the "
"termination of the analysis...")
break
# If the check() function of the package raised some exception
# we don't care, we can still proceed with the analysis but we
# throw a warning.
except Exception as e:
log.warning("The package \"%s\" check function raised "
"an exception: %s", package_name, e)
finally:
# Zzz.
time.sleep(1)
try:
# Before shutting down the analysis, the package can perform some
# final operations through the finish() function.
pack.finish()
except Exception as e:
log.warning("The package \"%s\" finish function raised an "
"exception: %s", package_name, e)
# Terminate the Auxiliary modules.
for aux in aux_enabled:
try:
aux.stop()
except (NotImplementedError, AttributeError):
continue
except Exception as e:
log.warning("Cannot terminate auxiliary module %s: %s",
aux.__class__.__name__, e)
# Let's invoke the completion procedure.
self.complete()
return True
if __name__ == "__main__":
success = False
error = ""
try:
# Initialize the main analyzer class.
analyzer = Analyzer()
# Run it and wait for the response.
success = analyzer.run()
# This is not likely to happen.
except KeyboardInterrupt:
error = "Keyboard Interrupt"
# If the analysis process encountered a critical error, it will raise a
# CuckooError exception, which will force the termination of the analysis
# weill notify the agent of the failure. Also catched unexpected
# exceptions.
except Exception as e:
# Store the error.
error = str(e)
# Just to be paranoid.
if len(log.handlers) > 0:
log.critical(error)
else:
sys.stderr.write("{0}\n".format(e))
# Once the analysis is completed or terminated for any reason, we report
# back to the agent, notifying that it can report back to the host.
finally:
# Establish connection with the agent XMLRPC server.
server = xmlrpclib.Server("http://127.0.0.1:8000")
server.complete(success, error, PATHS["root"])
================================================
FILE: analyzer/android/config/hooks.json
================================================
{
"hookConfigs": [
{
"class_name": "android.telephony.TelephonyManager",
"method": "getDeviceId",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getSubscriberId",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getLine1Number",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getNetworkOperator",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getNetworkOperatorName",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getSimOperatorName",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.net.wifi.WifiInfo",
"method": "getMacAddress",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getSimCountryIso",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getSimSerialNumber",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getNetworkCountryIso",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "getDeviceSoftwareVersion",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.os.Debug",
"method": "isDebuggerConnected",
"thisObject": false,
"type": "fingerprint"
},
{
"class_name": "android.app.SharedPreferencesImpl$EditorImpl",
"method": "putString",
"thisObject": false,
"type": "globals"
},
{
"class_name": "android.app.SharedPreferencesImpl$EditorImpl",
"method": "putBoolean",
"thisObject": false,
"type": "globals"
},
{
"class_name": "android.app.SharedPreferencesImpl$EditorImpl",
"method": "putInt",
"thisObject": false,
"type": "globals"
},
{
"class_name": "android.app.SharedPreferencesImpl$EditorImpl",
"method": "putLong",
"thisObject": false,
"type": "globals"
},
{
"class_name": "android.app.SharedPreferencesImpl$EditorImpl",
"method": "putFloat",
"thisObject": false,
"type": "globals"
},
{
"class_name": "android.content.ContentValues",
"method": "put",
"thisObject": false,
"type": "globals"
},
{
"class_name": "java.net.URL",
"method": "openConnection",
"thisObject": true,
"type": "network"
},
{
"class_name": "org.apache.http.impl.client.AbstractHttpClient",
"method": "execute",
"thisObject": false,
"type": "network"
},
{
"class_name": "android.app.ContextImpl",
"method": "registerReceiver",
"thisObject": false,
"type": "binder"
},
{
"class_name": "android.app.ActivityThread",
"method": "handleReceiver",
"thisObject": false,
"type": "binder"
},
{
"class_name": "android.app.Activity",
"method": "startActivity",
"thisObject": false,
"type": "binder"
},
{
"class_name": "dalvik.system.BaseDexClassLoader",
"method": "findResource",
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.BaseDexClassLoader",
"method": "findLibrary",
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.DexFile",
"method": "loadDex",
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.DexClassLoader",
"method": null,
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.BaseDexClassLoader",
"method": "findResources",
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.DexFile",
"method": "loadClass",
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.DexFile",
"method": null,
"thisObject": false,
"type": "dex"
},
{
"class_name": "dalvik.system.PathClassLoader",
"method": null,
"thisObject": false,
"type": "dex"
},
{
"class_name": "java.lang.reflect.Method",
"method": "invoke",
"thisObject": false,
"type": "reflection"
},
{
"class_name": "javax.crypto.spec.SecretKeySpec",
"method": null,
"thisObject": false,
"type": "crypto"
},
{
"class_name": "javax.crypto.Cipher",
"method": "doFinal",
"thisObject": true,
"type": "crypto"
},
{
"class_name": "javax.crypto.Mac",
"method": "doFinal",
"thisObject": false,
"type": "crypto"
},
{
"class_name": "android.app.ApplicationPackageManager",
"method": "setComponentEnabledSetting",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.app.NotificationManager",
"method": "notify",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.util.Base64",
"method": "decode",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.telephony.TelephonyManager",
"method": "listen",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.util.Base64",
"method": "encode",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.util.Base64",
"method": "encodeToString",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.net.ConnectivityManager",
"method": "setMobileDataEnabled",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.content.BroadcastReceiver",
"method": "abortBroadcast",
"thisObject": false,
"type": "generic"
},
{
"class_name": "android.telephony.SmsManager",
"method": "sendTextMessage",
"thisObject": false,
"type": "sms"
},
{
"class_name": "android.telephony.SmsManager",
"method": "sendMultipartTextMessage",
"thisObject": false,
"type": "sms"
},
{
"class_name": "java.lang.Runtime",
"method": "exec",
"thisObject": false,
"type": "runtime"
},
{
"class_name": "java.lang.ProcessBuilder",
"method": "start",
"thisObject": true,
"type": "runtime"
},
{
"class_name": "java.io.FileOutputStream",
"method": "write",
"thisObject": false,
"type": "runtime"
},
{
"class_name": "java.io.FileInputStream",
"method": "read",
"thisObject": false,
"type": "runtime"
},
{
"class_name": "android.app.ActivityManager",
"method": "killBackgroundProcesses",
"thisObject": false,
"type": "runtime"
},
{
"class_name": "android.os.Process",
"method": "killProcess",
"thisObject": false,
"type": "runtime"
},
{
"class_name": "android.content.ContentResolver",
"method": "query",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.content.ContentResolver",
"method": "registerContentObserver",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.content.ContentResolver",
"method": "insert",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.accounts.AccountManager",
"method": "getAccountsByType",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.accounts.AccountManager",
"method": "getAccounts",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.location.Location",
"method": "getLatitude",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.location.Location",
"method": "getLongitude",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.content.ContentResolver",
"method": "delete",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.media.AudioRecord",
"method": "startRecording",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.media.MediaRecorder",
"method": "start",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.os.SystemProperties",
"method": "get",
"thisObject": false,
"type": "content"
},
{
"class_name": "android.app.ApplicationPackageManager",
"method": "getInstalledPackages",
"thisObject": false,
"type": "content"
},
{
"class_name": "libcore.io.IoBridge",
"method": "open",
"thisObject": false,
"type": "file"
}
],
"trace": false
}
================================================
FILE: analyzer/android/lib/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/lib/api/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/lib/api/adb.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import logging
import os
import subprocess
from lib.common.utils import send_file
log = logging.getLogger(__name__)
def install_sample(path):
"""Install the sample on the emulator via adb"""
log.info("Installing sample in the device: %s", path)
try:
args = ["/system/bin/sh", "/system/bin/pm", "install", path]
output = subprocess.check_output(args)
except subprocess.CalledProcessError as e:
log.error("Error installing sample: %r", e)
return
log.info("Installed sample: %r", output)
def execute_sample(package, activity):
"""Execute the sample on the emulator via adb"""
try:
package_activity = "%s/%s" % (package, activity)
args = [
"/system/bin/sh", "/system/bin/am", "start",
"-n", package_activity,
]
output = subprocess.check_output(args)
except subprocess.CalledProcessError as e:
log.error("Error executing package activity: %r", e)
return
log.info("Executed package activity: %r", output)
def dump_droidmon_logs(package):
xposed_logs = "/data/data/de.robv.android.xposed.installer/log/error.log"
if not os.path.exists(xposed_logs):
log.info("Could not find any Xposed logs, skipping droidmon logs.")
return
tag = "Droidmon-apimonitor-%s" % package
tag_error = "Droidmon-shell-%s" % package
log_xposed, log_success, log_error = [], [], []
for line in open(xposed_logs, "rb"):
if tag in line:
log_success.append(line.split(":", 1)[1])
if tag_error in line:
log_error.append(line.split(":", 1)[1])
log_xposed.append(line)
send_file("logs/xposed.log", "\n".join(log_xposed))
send_file("logs/droidmon.log", "\n".join(log_success))
send_file("logs/droidmon_error.log", "\n".join(log_error))
def execute_browser(url):
"""Start URL intent on the emulator."""
try:
args = [
"/system/bin/sh", "/system/bin/am", "start",
"-a", "android.intent.action.VIEW",
"-d", url,
]
output = subprocess.check_output(args)
except subprocess.CalledProcessError as e:
log.error("Error starting browser intent: %r", e)
return
log.info("Intent returned: %r", output)
def take_screenshot(filename):
try:
subprocess.check_output(["/system/bin/screencap", "-p",
"/sdcard/%s" % filename])
except subprocess.CalledProcessError as e:
log.error("Error creating screenshot: %r", e)
return
return "/sdcard/%s" % filename
================================================
FILE: analyzer/android/lib/api/screenshot.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import math
import filecmp
try:
import ImageChops
from PIL import Image
HAVE_PIL = True
except:
try:
from PIL import ImageChops
from PIL import Image
HAVE_PIL = True
except:
HAVE_PIL = False
class Screenshot:
"""Get screenshots."""
def have_pil(self):
"""Is Python Image Library installed?
@return: installed status.
"""
return HAVE_PIL
def equal_old(self, img1, img2):
"""Compares two screenshots using Root-Mean-Square Difference (RMS).
@param img1: screenshot to compare.
@param img2: screenshot to compare.
@return: equal status.
"""
if not HAVE_PIL:
return None
image1 = Image.open(img1)
image2 = Image.open(img2)
# To get a measure of how similar two images are, we use
# root-mean-square (RMS). If the images are exactly identical,
# this value is zero.
diff = ImageChops.difference(image1, image2)
h = diff.histogram()
sq = (value*((idx % 256)**2) for idx, value in enumerate(h))
sum_of_squares = sum(sq)
rms = math.sqrt(sum_of_squares/float(image1.size[0] * image2.size[1]))
# Might need to tweak the threshold.
return rms < 8
def equal(self, img1, img2):
return filecmp.cmp(img1, img2)
================================================
FILE: analyzer/android/lib/common/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/lib/common/abstracts.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
class Package(object):
"""Base abstact analysis package."""
def __init__(self, options={}):
"""@param options: options dict."""
self.options = options
self.pids = []
def set_pids(self, pids):
"""Update list of monitored PIDs in the package context.
@param pids: list of pids.
"""
self.pids = pids
def start(self):
"""Run analysis packege.
@param path: sample path.
@raise NotImplementedError: this method is abstract.
"""
raise NotImplementedError
def check(self):
"""Check.
@raise NotImplementedError: this method is abstract.
"""
raise NotImplementedError
def finish(self):
"""Finish run.
@raise NotImplementedError: this method is abstract.
"""
raise NotImplementedError
class Auxiliary(object):
pass
================================================
FILE: analyzer/android/lib/common/constants.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import os
import string
import random
def _rand_string(a, b):
return "".join(random.choice(string.ascii_lowercase) for x in xrange(random.randint(a, b)))
ROOT = os.path.join("/data/local/tmp", _rand_string(6, 10))
PATHS = {
"root" : ROOT,
"logs" : os.path.join(ROOT, "logs"),
"files" : os.path.join(ROOT, "files"),
"shots" : os.path.join(ROOT, "shots"),
"memory" : os.path.join(ROOT, "memory"),
"drop" : os.path.join(ROOT, "drop")
}
================================================
FILE: analyzer/android/lib/common/exceptions.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
class CuckooError(Exception):
pass
class CuckooPackageError(Exception):
pass
================================================
FILE: analyzer/android/lib/common/results.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import logging
import socket
import time
from lib.core.config import Config
log = logging.getLogger(__name__)
BUFSIZE = 1024*1024
def upload_to_host(file_path, dump_path):
nc = infd = None
try:
nc = NetlogFile(dump_path)
infd = open(file_path, "rb")
buf = infd.read(BUFSIZE)
while buf:
nc.send(buf, retry=False)
buf = infd.read(BUFSIZE)
except Exception as e:
log.error("Exception uploading file %s to host: %s", file_path, e)
finally:
if infd:
infd.close()
if nc:
nc.close()
class NetlogConnection(object):
def __init__(self, proto=""):
config = Config(cfg="analysis.conf")
self.hostip, self.hostport = config.ip, config.port
self.sock = None
self.proto = proto
def connect(self):
# Try to connect as quickly as possible. Just sort of force it to
# connect with a short timeout.
while not self.sock:
try:
s = socket.create_connection((self.hostip, self.hostport), 0.1)
s.sendall(self.proto)
except socket.error:
time.sleep(0.1)
continue
self.sock = s
def send(self, data, retry=True):
if not self.sock:
self.connect()
try:
self.sock.sendall(data)
except socket.error as e:
if retry:
self.connect()
self.send(data, retry=False)
else:
raise
except Exception as e:
log.error("Unhandled exception in NetlogConnection: %s", str(e))
# We really have nowhere to log this, if the netlog connection
# does not work, we can assume that any logging won't work either.
# So we just fail silently.
self.close()
def close(self):
try:
self.sock.close()
except Exception:
pass
class NetlogFile(NetlogConnection):
def __init__(self, filepath):
self.filepath = filepath
NetlogConnection.__init__(self, proto="FILE\n{0}\n".format(self.filepath))
self.connect()
class NetlogHandler(logging.Handler, NetlogConnection):
def __init__(self):
logging.Handler.__init__(self)
NetlogConnection.__init__(self, proto="LOG\n")
self.connect()
def emit(self, record):
msg = self.format(record)
self.send("{0}\n".format(msg))
================================================
FILE: analyzer/android/lib/common/utils.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
from lib.common.results import NetlogFile
def send_file(name, data):
"""Send file to result server"""
nf = NetlogFile(name)
nf.sock.sendall(data)
nf.close()
================================================
FILE: analyzer/android/lib/core/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/lib/core/config.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import ConfigParser
class Config:
def __init__(self, cfg):
"""@param cfg: configuration file."""
config = ConfigParser.ConfigParser(allow_no_value=True)
config.read(cfg)
for section in config.sections():
for name, raw_value in config.items(section):
try:
value = config.getboolean(section, name)
except ValueError:
try:
value = config.getint(section, name)
except ValueError:
value = config.get(section, name)
setattr(self, name, value)
================================================
FILE: analyzer/android/lib/core/packages.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
def choose_package(file_type, file_name):
"""Choose analysis package due to file type and file extension.
@param file_type: file type.
@return: package or None.
"""
if not file_type:
return None
file_type = file_type.lower()
file_name = file_name.lower()
if "apk" in file_name:
return "apk"
elif "zip" in file_type:
return "apk"
# elif "DEX" in file_type:
# return "dex"
else:
return "apk"
================================================
FILE: analyzer/android/lib/core/startup.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import os
import logging
from lib.common.constants import PATHS
from lib.common.results import NetlogHandler
log = logging.getLogger()
def create_folders():
"""Create folders in PATHS."""
for name, folder in PATHS.items():
if os.path.exists(folder):
continue
try:
os.makedirs(folder)
except OSError:
pass
def init_logging():
"""Initialize logger."""
formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
sh = logging.StreamHandler()
sh.setFormatter(formatter)
log.addHandler(sh)
nh = NetlogHandler()
nh.setFormatter(formatter)
log.addHandler(nh)
log.setLevel(logging.DEBUG)
================================================
FILE: analyzer/android/modules/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/modules/auxiliary/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/modules/auxiliary/screenshots.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import time
import logging
import StringIO
from threading import Thread
from lib.common.abstracts import Auxiliary
from lib.common.results import NetlogFile
from lib.api.adb import take_screenshot
from lib.api.screenshot import Screenshot
log = logging.getLogger(__name__)
SHOT_DELAY = 2
class Screenshots(Auxiliary, Thread):
"""Take screenshots."""
def __init__(self):
Thread.__init__(self)
self.do_run = True
def stop(self):
"""Stop screenshotting."""
self.do_run = False
def run(self):
"""Run screenshotting.
@return: operation status.
"""
img_counter = 0
img_last = None
while self.do_run:
time.sleep(SHOT_DELAY)
try:
filename = "screenshot%s.jpg" % str(img_counter)
img_current = take_screenshot(filename)
if img_last:
if Screenshot().equal(img_last, img_current):
continue
file = open(img_current, 'r')
tmpio = StringIO.StringIO(file.read())
# now upload to host from the StringIO
nf = NetlogFile("shots/%s.jpg" % str(img_counter).rjust(4, "0"))
for chunk in tmpio:
nf.sock.sendall(chunk)
nf.close()
file.close()
img_counter += 1
img_last = img_current
except IOError as e:
log.error("Cannot take screenshot: %s", e)
continue
return True
================================================
FILE: analyzer/android/modules/packages/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
================================================
FILE: analyzer/android/modules/packages/apk.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
import logging
from lib.api.adb import dump_droidmon_logs, execute_sample, install_sample
from lib.common.abstracts import Package
log = logging.getLogger(__name__)
class Apk(Package):
"""Apk analysis package."""
def __init__(self, options={}):
super(Apk, self).__init__(options)
self.package, self.activity = options.get("apk_entry", ":").split(":")
def start(self, path):
install_sample(path)
execute_sample(self.package, self.activity)
def check(self):
return True
def finish(self):
dump_droidmon_logs(self.package)
return True
================================================
FILE: analyzer/android/modules/packages/default_browser.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
# Originally contributed by Check Point Software Technologies, Ltd.
from lib.common.abstracts import Package
from lib.api.adb import execute_browser
class default_browser(Package):
"""Default Browser analysis package."""
def __init__(self, options={}):
super(default_browser, self).__init__(options)
def start(self, target):
execute_browser(target)
def check(self):
return True
def finish(self):
return True
================================================
FILE: analyzer/darwin/__init__.py
================================================
================================================
FILE: analyzer/darwin/analyzer.py
================================================
#!/usr/bin/env python
# Copyright (C) 2015 Dmitry Rodionov
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE file for details.
import logging
from sys import stderr
from hashlib import sha256
from xmlrpclib import Server
from traceback import format_exc
from os import path, getcwd, makedirs
from lib.common.config import Config
from lib.common.hashing import hash_file
from lib.common.results import NetlogHandler, upload_to_host
from lib.core.constants import PATHS
from lib.core.packages import choose_package_class
from lib.core.osx import set_wallclock
from lib.core.host import CuckooHost
class Macalyzer(object):
"""Cuckoo OS X analyser.
"""
log = logging.getLogger()
target = None
files_to_upload = []
uploaded_hashes = []
def __init__(self, host, configuration=None):
self.config = configuration
self.host = host
def bootstrap(self):
_create_result_folders()
_setup_logging()
self._detect_target()
def run(self):
"""Run analysis.
"""
self.bootstrap()
self.log.debug("Starting analyzer from %s", getcwd())
self.log.debug("Storing results at: %s", PATHS["root"])
package = self._setup_analysis_package()
if self.config.clock:
set_wallclock(self.config.clock)
self._analysis(package)
return self._complete()
def _complete(self):
for f in self.files_to_upload:
self._upload_file(f)
return True
#
# Implementation details
#
def _detect_target(self):
if self.config.category == "file":
self.target = path.join("/tmp/", str(self.config.file_name))
else: # It's not a file, but a URL
self.target = self.config.target
def _setup_analysis_package(self):
# Do we have a suggestion about an analysis package?
if self.config.package:
suggestion = self.config.package
elif self.config.category != "file":
suggestion = "url"
else:
suggestion = None
# Try to figure out what analysis package to use with this target
kwargs = {"suggestion" : suggestion}
package_class = choose_package_class(self.config.file_type,
self.config.file_name, **kwargs)
if not package_class:
raise Exception("Could not find an appropriate analysis package")
# Package initialization
kwargs = {
"options" : self.config.get_options(),
"timeout" : self.config.timeout
}
return package_class(self.target, self.host, **kwargs)
def _analysis(self, package):
package.start()
self.files_to_upload = package.touched_files
def _upload_file(self, filepath):
if not path.isfile(filepath):
return
# Check whether we've already dumped this file - in that case skip it
try:
hashsum = hash_file(sha256, filepath)
if sha256 in self.uploaded_hashes:
return
except IOError as e:
self.log.info("Error dumping file from path \"%s\": %s", filepath, e)
return
filename = "%s_%s" % (hashsum[:16], path.basename(filepath))
upload_path = path.join("files", filename)
try:
upload_to_host(filepath, upload_path)
self.uploaded_hashes.append(hashsum)
except IOError as e:
self.log.error("Unable to upload dropped file at path \"%s\": %s", filepath, e)
def _create_result_folders():
for _, folder in PATHS.items():
if path.exists(folder):
continue
try:
makedirs(folder)
except OSError:
pass
def _setup_logging():
""" Initialize logger. """
logger = logging.getLogger()
formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
stream = logging.StreamHandler()
stream.setFormatter(formatter)
logger.addHandler(stream)
netlog = NetlogHandler()
netlog.setFormatter(formatter)
logger.addHandler(netlog)
logger.setLevel(logging.DEBUG)
if __name__ == "__main__":
success = False
error = ""
try:
config = Config(cfg="analysis.conf")
cuckoo = CuckooHost(config.ip, config.port)
analyzer = Macalyzer(cuckoo, config)
success = analyzer.run()
except KeyboardInterrupt:
error = "Keyboard Interrupt"
except Exception as err:
error_exc = format_exc()
error = str(err)
if len(analyzer.log.handlers):
analyzer.log.exception(error_exc)
else:
stderr.write("{0}\n".format(error_exc))
# Once the analysis is completed or terminated for any reason, we report
# back to the agent, notifying that it can report back to the host.
finally:
# Establish connection with the agent XMLRPC server.
server = Server("http://127.0.0.1:8000")
server.complete(success, error, PATHS["root"])
================================================
FILE: analyzer/darwin/lib/__init__.py
================================================
================================================
FILE: analyzer/darwin/lib/common/__init__.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
================================================
FILE: analyzer/darwin/lib/common/config.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import ConfigParser
class Config:
def __init__(self, cfg):
"""@param cfg: configuration file."""
config = ConfigParser.ConfigParser(allow_no_value=True)
config.read(cfg)
for section in config.sections():
for name, raw_value in config.items(section):
if name == "file_name":
value = config.get(section, name)
else:
try:
value = config.getboolean(section, name)
except ValueError:
try:
value = config.getint(section, name)
except ValueError:
value = config.get(section, name)
setattr(self, name, value)
def get_options(self):
"""Get analysis options.
@return: options dict.
"""
# The analysis package can be provided with some options in the
# following format:
# option1=value1,option2=value2,option3=value3
#
# Here we parse such options and provide a dictionary that will be made
# accessible to the analysis package.
options = {}
if hasattr(self, "options") and len(self.options) > 0:
try:
# Split the options by comma.
fields = self.options.split(",")
except ValueError:
pass
else:
for field in fields:
# Split the name and the value of the option.
try:
# Sometimes, we have a key without a value (i.e. it's a
# command line argument), so we can't use the
# `key, value = field.split("=", 1)` style here
parts = field.split("=", 1)
except ValueError:
pass
else:
key = parts[0].strip()
arg_prefix = "arg-"
if not key.startswith(arg_prefix):
# If the parsing went good, we add the option to the
# dictionary.
value = parts[1].strip()
options[key] = value
elif len(key) > len(arg_prefix):
# Remove "arg-" prefix from the key
key = key[4:]; parts[0] = key
# Add this key (with a value maybe) to the args
if "args" not in options: options["args"] = []
options["args"] += parts
return options
================================================
FILE: analyzer/darwin/lib/common/hashing.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
BUFSIZE = 1024*1024
def hash_file(method, path):
"""Calculates an hash on a file by path.
@param method: callable hashing method
@param path: file path
@return: computed hash string
"""
f = open(path, "rb")
h = method()
while True:
buf = f.read(BUFSIZE)
if not buf:
break
h.update(buf)
return h.hexdigest()
================================================
FILE: analyzer/darwin/lib/common/rand.py
================================================
import random
import string
def random_string(minimum, maximum=None):
if maximum is None:
maximum = minimum
count = random.randint(minimum, maximum)
return "".join(random.choice(string.ascii_letters) for x in xrange(count))
def random_integer(digits):
start = 10 ** (digits - 1)
end = (10 ** digits) - 1
return random.randint(start, end)
================================================
FILE: analyzer/darwin/lib/common/results.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import time
import socket
import logging
from config import Config
log = logging.getLogger(__name__)
BUFSIZE = 1024*1024
def upload_to_host(file_path, dump_path):
nc = infd = None
try:
nc = NetlogFile(dump_path)
infd = open(file_path, "rb")
buf = infd.read(BUFSIZE)
while buf:
nc.send(buf, retry=False)
buf = infd.read(BUFSIZE)
except Exception as e:
log.error("Exception uploading file %s to host: %s", file_path, e)
finally:
if infd:
infd.close()
if nc:
nc.close()
class NetlogConnection(object):
def __init__(self, proto=""):
config = Config(cfg="analysis.conf")
self.hostip, self.hostport = config.ip, config.port
self.sock, self.file = None, None
self.proto = proto
def connect(self):
i = 1
# this can loop forever, if we can't connect the whole analysis is useless anyways
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((self.hostip, self.hostport))
s.sendall(self.proto)
except:
time.sleep(i)
i = min(i + 1, 60)
else:
self.sock = s
self.file = s.makefile()
break
def send(self, data, retry=True):
if not self.sock: self.connect()
try:
self.sock.sendall(data)
except socket.error as e:
if retry:
self.connect()
self.send(data, retry=False)
else:
raise
except Exception as e:
log.error("Unhandled exception in NetlogConnection: %s", str(e))
# We really have nowhere to log this, if the netlog connection
# does not work, we can assume that any logging won't work either.
# So we just fail silently.
self.close()
def close(self):
try:
self.file.close()
self.sock.close()
except Exception:
pass
class NetlogFile(NetlogConnection):
def __init__(self, filepath):
self.filepath = filepath
NetlogConnection.__init__(self, proto="FILE\n{0}\n".format(self.filepath))
self.connect()
class NetlogHandler(logging.Handler, NetlogConnection):
def __init__(self):
logging.Handler.__init__(self)
NetlogConnection.__init__(self, proto="LOG\n")
self.connect()
def emit(self, record):
msg = self.format(record)
self.send("{0}\n".format(msg))
================================================
FILE: analyzer/darwin/lib/core/__init__.py
================================================
================================================
FILE: analyzer/darwin/lib/core/constants.py
================================================
# Copyright (C) 2014-2016 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import os
from tempfile import gettempdir
from ..common.rand import random_string
ROOT = os.path.join(gettempdir() + os.sep, random_string(6, 10))
PATHS = {
"root" : ROOT,
"logs" : os.path.join(ROOT, "logs"),
"files" : os.path.join(ROOT, "files"),
"shots" : os.path.join(ROOT, "shots"),
"memory" : os.path.join(ROOT, "memory"),
"drop" : os.path.join(ROOT, "drop")
}
================================================
FILE: analyzer/darwin/lib/core/data/signatures.yml
================================================
system:
is_success_condition: "retval == 0"
args:
- {name: "command", type: "char *"}
retval_type: "int"
category: "foobar"
printf:
is_success_condition: "retval > 0"
args:
- {name: "format", type: "char *"}
retval_type: "int"
category: "foobar"
dlopen:
is_success_condition: "retval > 0"
args:
- {name: "path", type: "char *"}
- {name: "mode", type: "int"}
retval_type: "void *"
category: "foobar"
library: "libdyld"
dlsym:
is_success_condition: "retval > 0"
args:
- {name: "handle", type: "void *"}
- {name: "symbol", type: "char *"}
retval_type: "void *"
category: "foobar"
library: "libdyld"
fprintf:
is_success_condition: "retval > 0"
args:
- {name: "stream", type: "void *"}
- {name: "format", type: "char *"}
retval_type: "int"
category: "foobar"
open:
is_success_condition: "retval > 0"
args:
- {name: "path", type: "char *"}
- {name: "oflag", type: "int"}
retval_type: "int"
category: "file"
fopen:
is_success_condition: "retval > 0"
args:
- {name: "filename", type: "char *"}
- {name: "mode", type: "char *"}
retval_type: "void *"
category: "file"
freopen:
is_success_condition: "retval > 0"
args:
- {name: "filename", type: "char *"}
- {name: "mode", type: "char *"}
- {name: "stream", type: "void *"}
retval_type: "void *"
category: "file"
rename:
is_success_condition: "retval == 0"
args:
- {name: "old", type: "char *"}
- {name: "new", type: "char *"}
- {name: "state", type: "void *"}
- {name: "flags", type: "uint64_t"}
retval_type: "int"
category: "file"
copyfile:
is_success_condition: "retval == 0"
args:
- {name: "from", type: "char *"}
- {name: "to", type: "char *"}
- {name: "state", type: "void *"}
- {name: "flags", type: "uint32_t"}
retval_type: "int"
category: "file"
remove:
is_success_condition: "retval == 0"
args:
- {name: "path", type: "char *"}
retval_type: "int"
category: "file"
unlink:
is_success_condition: "retval == 0"
args:
- {name: "path", type: "char *"}
retval_type: "int"
category: "file"
execve:
is_success_condition: "retval != -1"
args:
- {name: "path", type: "char *"}
- {name: "argv", type: "void *"}
- {name: "envp", type: "void *"}
retval_type: "int"
category: "process"
__ignore__: true
fork:
is_success_condition: "retval >= 0"
args: []
retval_type: "int"
category: "process"
socket:
is_success_condition: "retval > 0"
args:
- {name: "domain", type: "int"}
- {name: "type", type: "int"}
- {name: "protocol", type: "int"}
retval_type: "int"
category: "network"
# Signatures for tests.
# Please don't remove them. Thanks!
rb_isalpha:
is_success_condition: "retval != 0"
args:
- {name: "character", type: "char"}
retval_type: "int"
category: "foobar"
atoi:
is_success_condition: "1==1"
args:
- {name: "str", type: "char *"}
retval_type: "int"
category: "foobar"
library: "libsystem_c"
================================================
FILE: analyzer/darwin/lib/core/data/types.yml
================================================
# ===============================================
# Basic types
#
int: &int
# We will print it with something like printf("%d", value)
printf_specifier: "%d"
# Is it a native C type (on OS X)?
native: Yes
# Alternative name for backward compatibility
integer: *int
unsigned int: &unsigned-int
printf_specifier: "%ld"
native: Yes
long: &long
printf_specifier: "%l"
native: Yes
unsigned long: &unsigned-long
printf_specifier: "%lu"
native: Yes
unsigned long long: &unsigned-long-long
printf_specifier: "%llu"
native: Yes
size_t: *unsigned-long
char: &char
printf_specifier: '"%c"'
native: Yes
float: &float
printf_specifier: "%f"
native: Yes
double: &double
printf_specifier: "%f"
native: Yes
#
# Raw pointers: just dump their values (in *decimal* since dtrace will output
# JSON that doesn't accept hex values)
#
"void *":
<<: *unsigned-long-long
cast: "unsigned long long"
#
# Strings
#
"char *": &char-pointer
printf_specifier: '"%S"'
native: No
template: |-
!!(${ARG}) ? copyinstr((uint64_t)${ARG}) : ""
#
# Arbitrary buffers
#
#buffer: &buffer
# printf_specifier: '"%S"'
# native: No
# template: |-
# ${ARG} != (int64_t)NULL ? stringof(copyin(${ARG}, ${SIZE_ARG})) : ""
#
# Fixed length C types
#
int8_t: &int8_t
printf_specifier: "%d"
native: Yes
uint8_t: &uint8_t
printf_specifier: "%u"
native: Yes
int16_t: &int16_t
printf_specifier: "%d"
native: Yes
uint16_t: &uint16_t
printf_specifier: "%u"
native: Yes
int32_t: &int32_t
printf_specifier: "%d"
native: Yes
uint32_t: &uint32_t
printf_specifier: "%u"
native: Yes
int64_t: &int64_t
printf_specifier: "%lld"
native: Yes
uint64_t: &uint64_t
printf_specifier: "%llu"
native: Yes
#
# Structures for tests.
# Please don't remove them. Thanks!
#
test_t:
native: No
struct:
hash: "int"
base: "test_internal_t *"
description: "char *"
test_internal_t:
native: No
struct:
abc: "double *"
hfa: "size_t"
sss: "char *"
test_extra_t:
native: No
struct:
foo: int
bar: uint64_t
#
# Your custom data types
#
================================================
FILE: analyzer/darwin/lib/core/filetimes.py
================================================
# Copyright (c) 2009, David Buxton
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tools to convert between Python datetime instances and Microsoft times.
"""
from calendar import timegm
# http://support.microsoft.com/kb/167296
# How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS = 10000000
def dt_to_filetime(dt, delta_from_utc):
"""Converts a datetime to Microsoft filetime format.
>>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0))
'128930364000000000'
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc))
'116444736000000000'
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0))
'116444736000000000'
>>> dt_to_filetime(datetime(2009, 7, 25, 23, 0, 0, 100))
128930364000001000
"""
dt += delta_from_utc
ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
return ft + (dt.microsecond * 10)
================================================
FILE: analyzer/darwin/lib/core/host.py
================================================
#!/usr/bin/env python
# Copyright (C) 2015 Dmitry Rodionov
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE file for details.
import yaml
import socket
import logging
import bson
from os import path
from datetime import datetime
from subprocess import check_output, CalledProcessError
from filetimes import dt_to_filetime
log = logging.getLogger(__name__)
class CuckooHost(object):
""" Sending analysis results back to the Cuckoo Host.
Currently it only supports sending results about API calls via send_api(),
see `apicalls` module.
"""
sockets = {
# Each target process has its own results server on the host, so
# we setup as many sockets as we have targets to analyse.
}
descriptions = {
# We don't want to explain APIs every single time they're about to be
# send to the host, so we explain them once and then just refer to them
# via an unique ID.
}
launch_times = {
# Since Cuckoo host expects us to send relative times, we remember when
# every target was launched.
}
human_readable_info = {
# Here goes all the additional information about APIs like category,
# arguments names and so on. See date/apis.json for more details.
}
def __init__(self, host_ip, host_port):
self.ip = host_ip
self.port = host_port
self._load_human_readable_info()
def send_api(self, thing):
""" Sends a new API notification to the Cuckoo host """
pid = thing.pid
api = thing.api
# We're required to report results of tracing a target process to
# *its own* result server. So create a communication socket...
if pid not in self.sockets:
self.sockets[pid] = self._create_socket()
if not self.sockets[pid]:
raise Exception("CuckooHost error: could not create socket.")
# ... and don't forget to explain every single API call again
self.descriptions.setdefault(pid, ["__process__", "__thread__"])
self._send_new_process(thing)
try:
lookup_idx = self.descriptions[pid].index(api)
except ValueError:
self.descriptions[pid].append(api)
lookup_idx = len(self.descriptions[pid]) - 1
self._send_api_description(lookup_idx, thing)
# Here's an api object:
# {
# "I" : (int),
# "T" : (int),
# "t" : (int)