mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
304 lines
11 KiB
Groovy
304 lines
11 KiB
Groovy
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*******************************************************************************
|
|
* fetchDependencies.gradle *
|
|
* *
|
|
* Fetches/downloads required dependencies that aren't available in the *
|
|
* standard online repositories (eg: maven) and configures a flat *
|
|
* directory-style respository that points to them. This should be run *
|
|
* immediately after cloning the Ghidra repository before any other gradle *
|
|
* tasks are run. *
|
|
* *
|
|
* usage: from the command line in the main ghidra repository directory, run *
|
|
* the following: *
|
|
* *
|
|
* gradle -I gradle/support/fetchDependencies.gradle init *
|
|
* *
|
|
* Note: When running the script, files will only be downloaded if *
|
|
* necessary (eg: they are not already in the dependencies/downloads/ *
|
|
* directory). *
|
|
* *
|
|
*******************************************************************************/
|
|
|
|
import java.util.zip.*;
|
|
import java.nio.file.*;
|
|
import java.security.MessageDigest;
|
|
import org.apache.commons.io.FileUtils;
|
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
|
|
|
initscript {
|
|
repositories { mavenCentral() }
|
|
dependencies { classpath 'commons-io:commons-io:2.8.0' }
|
|
}
|
|
|
|
ext.NUM_RETRIES = 3 // # of times to try to download a file before failing
|
|
ext.REPO_DIR = ((Script)this).buildscript.getSourceFile().getParentFile().getParentFile().getParentFile()
|
|
ext.DEPS_DIR = file("${REPO_DIR}/dependencies")
|
|
ext.DOWNLOADS_DIR = file("${DEPS_DIR}/downloads")
|
|
ext.FID_DIR = file("${DEPS_DIR}/fidb")
|
|
ext.FLAT_REPO_DIR = file("${DEPS_DIR}/flatRepo")
|
|
|
|
ext.deps = [
|
|
[
|
|
name: 'dex-tools-2.0.zip',
|
|
url: 'https://github.com/pxb1988/dex2jar/releases/download/2.0/dex-tools-2.0.zip',
|
|
sha256: '7907eb4d6e9280b6e17ddce7ee0507eae2ef161ee29f70a10dbc6944fdca75bc',
|
|
destination: {
|
|
unzip(DOWNLOADS_DIR, DOWNLOADS_DIR, 'dex-tools-2.0.zip')
|
|
FileUtils.copyDirectory(new File(DOWNLOADS_DIR, 'dex2jar-2.0/lib/'), FLAT_REPO_DIR, new WildcardFileFilter("dex-*"));
|
|
}
|
|
],
|
|
[
|
|
name: 'hfsexplorer-0_21-bin.zip',
|
|
url: 'https://sourceforge.net/projects/catacombae/files/HFSExplorer/0.21/hfsexplorer-0_21-bin.zip',
|
|
sha256: '90c9b54798abca5b12f4a678db7d0a4c970f4702cb153c11919536d0014dedbf',
|
|
destination: {
|
|
def hfsxdir = new File (DOWNLOADS_DIR, "hfsx")
|
|
hfsxdir.mkdir()
|
|
unzip (DOWNLOADS_DIR, hfsxdir, 'hfsexplorer-0_21-bin.zip')
|
|
FileUtils.copyFileToDirectory(new File(DOWNLOADS_DIR, "hfsx/lib/csframework.jar"), FLAT_REPO_DIR);
|
|
FileUtils.copyFileToDirectory(new File(DOWNLOADS_DIR, "hfsx/lib/hfsx_dmglib.jar"), FLAT_REPO_DIR);
|
|
FileUtils.copyFileToDirectory(new File(DOWNLOADS_DIR, "hfsx/lib/hfsx.jar"), FLAT_REPO_DIR);
|
|
FileUtils.copyFileToDirectory(new File(DOWNLOADS_DIR, "hfsx/lib/iharder-base64.jar"), FLAT_REPO_DIR);
|
|
}
|
|
],
|
|
[
|
|
name: 'AXMLPrinter2.jar',
|
|
url: 'https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/android4me/AXMLPrinter2.jar',
|
|
sha256: '00ed038eb6abaf6ddec8d202a3ed7a81b521458f4cd459948115cfd02ff59d6d',
|
|
destination: FLAT_REPO_DIR
|
|
],
|
|
[
|
|
name: 'yajsw-stable-12.12.zip',
|
|
url: 'https://sourceforge.net/projects/yajsw/files/yajsw/yajsw-stable-12.12/yajsw-stable-12.12.zip',
|
|
sha256: '1398fcb1e93abb19992c4fa06d7fe5758aabb4c45781d7ef306c6f57ca7a7321',
|
|
destination: file("${DEPS_DIR}/GhidraServer")
|
|
],
|
|
[
|
|
name: 'PyDev 6.3.1.zip',
|
|
url: 'https://sourceforge.net/projects/pydev/files/pydev/PyDev%206.3.1/PyDev%206.3.1.zip',
|
|
sha256: '4d81fe9d8afe7665b8ea20844d3f5107f446742927c59973eade4f29809b0699',
|
|
destination: file("${DEPS_DIR}/GhidraDev")
|
|
],
|
|
[
|
|
name: 'cdt-8.6.0.zip',
|
|
url: 'https://archive.eclipse.org/tools/cdt/releases/8.6/cdt-8.6.0.zip',
|
|
sha256: '81b7d19d57c4a3009f4761699a72e8d642b5e1d9251d2bb98df438b1e28f8ba9',
|
|
destination: file("${DEPS_DIR}/GhidraDev")
|
|
],
|
|
[
|
|
name: 'vs2012_x64.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2012_x64.fidb',
|
|
sha256: 'f26548a6df6b6963a418d8c83ac216d9e196b180d944a52b8123c457d472b7c9',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2012_x86.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2012_x86.fidb',
|
|
sha256: '0a8962cf3699d5b8d4b3a79400382462519edc26570a46b2085200e38534f900',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2015_x64.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2015_x64.fidb',
|
|
sha256: '187248f87fc1deb695bc3051b2d92f9b7482023a356821154db22478eed13088',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2015_x86.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2015_x86.fidb',
|
|
sha256: '1d05afa070e9c09b83ee15d544c8559ed0d2b53d7eac476f8f5f8849543b3812',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2017_x64.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2017_x64.fidb',
|
|
sha256: '1784ad6b25571177ff8212871867559998c6b8256bb1dbaeee864b580c1b2d6a',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2017_x86.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2017_x86.fidb',
|
|
sha256: 'bc9bf30621190e0eb56c4db5ec30ad0401ca7be0311f5a2ce3d894178eafd19c',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2019_x64.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2019_x64.fidb',
|
|
sha256: 'aab04eefd1142f7b3c3f86c8d766abe361b167b4fe4157c36fad18777b2a6fbd',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vs2019_x86.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vs2019_x86.fidb',
|
|
sha256: '0a2282ac3479ffc022e6cdb4e32e057bc10f0394cfb0f8016d7145be0167f5f7',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vsOlder_x64.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vsOlder_x64.fidb',
|
|
sha256: 'fe1856c0acad297d9ba4fb6a2df1d32ba34df766d9f1a2a16da0ca2b375e23dd',
|
|
destination: FID_DIR
|
|
],
|
|
[
|
|
name: 'vsOlder_x86.fidb',
|
|
url: 'https://github.com/NationalSecurityAgency/ghidra-data/raw/master/FunctionID/vsOlder_x86.fidb',
|
|
sha256: '46e56bc82ba68ad4e9a3c6a2e4ecd3428e2c390c7de0a379fa0165a58d46e115',
|
|
destination: FID_DIR
|
|
]
|
|
]
|
|
|
|
// Download dependencies (if necessary) and verify their hashes
|
|
DOWNLOADS_DIR.mkdirs()
|
|
deps.each {
|
|
File file = new File(DOWNLOADS_DIR, it.name)
|
|
if (!it.sha256.equals(generateHash(file))) {
|
|
download(it.url, file.path)
|
|
assert(it.sha256.equals(generateHash(file)));
|
|
}
|
|
}
|
|
|
|
// Copies the downloaded dependencies to their required destination.
|
|
// Some downloads require pre-processing before their relevant pieces can be copied.
|
|
deps.each {
|
|
if (it.destination instanceof File) {
|
|
println("Copying " + it.name + " to " + it.destination)
|
|
it.destination.mkdirs()
|
|
FileUtils.copyFile(new File(DOWNLOADS_DIR, it.name), new File(it.destination, it.name));
|
|
}
|
|
else if (it.destination instanceof Closure) {
|
|
println("Processing " + it.name)
|
|
it.destination()
|
|
}
|
|
else {
|
|
throw new GradleException("Unexpected destination type: " + it.destination)
|
|
}
|
|
}
|
|
//-------------------------------------Helper methods----------------------------------------------
|
|
|
|
/**
|
|
* Downloads a file from a URL. The download attempt will be tried NUM_RETRIES times before failing.
|
|
*
|
|
* Progress is shown on the command line in the form of the number of bytes downloaded and a
|
|
* percentage of the total.
|
|
*
|
|
* Note: We do not validate that the number of bytes downloaded matches the expected total here; any
|
|
* discrepencies will be caught when checking the SHA-256s later on.
|
|
*
|
|
* @param url the file to download
|
|
* @param filename the local file to create for the download
|
|
*/
|
|
def download(url, filename) {
|
|
|
|
println("URL: " + url)
|
|
def(InputStream istream, size) = establishConnection(url, NUM_RETRIES);
|
|
assert istream != null : " ***CONNECTION FAILURE***\n max attempts exceeded; exiting\n"
|
|
|
|
FileOutputStream ostream = new FileOutputStream(filename);
|
|
def dataBuffer = new byte[1024];
|
|
int bytesRead;
|
|
int totalRead;
|
|
while ((bytesRead = istream.read(dataBuffer, 0, 1024)) != -1) {
|
|
ostream.write(dataBuffer, 0, bytesRead);
|
|
totalRead += bytesRead
|
|
print("\r")
|
|
print(" Downloading: " + totalRead + " of " + size)
|
|
if (!size.equals("???")) {
|
|
int pctComplete = (totalRead / size) * 100
|
|
print(" (" + pctComplete + "%)")
|
|
}
|
|
print(" ") // overwrite gradle timer output
|
|
System.out.flush()
|
|
}
|
|
println()
|
|
istream.close();
|
|
ostream.close();
|
|
}
|
|
|
|
/**
|
|
* Attempts to establish a connection to the given URL
|
|
*
|
|
* @param url the URL to connect to
|
|
* @param retries the number of times to attempt to connect if there are failures
|
|
* @return the InputStream for the URL, and the size of the download in bytes as a string
|
|
*/
|
|
def establishConnection(url, retries) {
|
|
for (int i = 0; i < retries; i++) {
|
|
try {
|
|
if (i == 0) {
|
|
println(" Connecting...")
|
|
}
|
|
else {
|
|
println(" Connecting (" + (i+1) + "/" + retries + ")...")
|
|
}
|
|
URLConnection conn = new URL(url).openConnection();
|
|
conn.setRequestMethod("HEAD");
|
|
def size = conn.getContentLengthLong();
|
|
if (size == -1) {
|
|
size = "???"
|
|
}
|
|
return [new BufferedInputStream(new URL(url).openStream()), size];
|
|
}
|
|
catch (Exception e) {
|
|
println(" Connection error! " + e)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unzips a file to a directory
|
|
*
|
|
* @param sourceDir the directory where the zip file resides
|
|
* @param targetDir the directory where the unzipped files should be placed
|
|
* @param zipFileName the name of the file to unpack
|
|
*/
|
|
def unzip(sourceDir, targetDir, zipFileName) {
|
|
def zip = new ZipFile(new File(sourceDir, zipFileName))
|
|
zip.entries().findAll { !it.directory }.each { e ->
|
|
(e.name as File).with { f ->
|
|
if (f.parentFile != null) {
|
|
File destPath = new File(targetDir.path, f.parentFile.path)
|
|
destPath.mkdirs()
|
|
File targetFile = new File(destPath.path, f.name)
|
|
targetFile.withOutputStream { w ->
|
|
w << zip.getInputStream(e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
zip.close()
|
|
}
|
|
|
|
/**
|
|
* Generates the SHA-256 hash for the given file
|
|
*
|
|
* @param file the file to generate the SHA-256 hash for
|
|
* @return the generated SHA-256 hash, or null if the file does not exist
|
|
*/
|
|
def generateHash(file) {
|
|
if (!file.exists()) {
|
|
return null
|
|
}
|
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
md.update(Files.readAllBytes(Paths.get(file.path)));
|
|
byte[] digest = md.digest();
|
|
StringBuilder sb = new StringBuilder();
|
|
for (byte b : digest) {
|
|
sb.append(String.format("%02x", b));
|
|
}
|
|
return sb.toString();
|
|
}
|