From f15898512aa8716fca3f150494ff219d1a31a55a Mon Sep 17 00:00:00 2001 From: apriestman <apriestman@basistech.com> Date: Fri, 19 Jun 2020 07:55:43 -0400 Subject: [PATCH] Image is not created by auto_db_java if it's already in the database. --- bindings/java/jni/auto_db_java.cpp | 14 ++++ bindings/java/jni/auto_db_java.h | 1 + bindings/java/jni/dataModel_SleuthkitJNI.cpp | 65 +++++++++++++++++-- bindings/java/jni/dataModel_SleuthkitJNI.h | 20 +++++- .../src/org/sleuthkit/datamodel/Image.java | 4 ++ .../sleuthkit/datamodel/SleuthkitCase.java | 11 +++- .../org/sleuthkit/datamodel/SleuthkitJNI.java | 60 ++++++++++++++--- win32/tsk_jni/tsk_jni.vcxproj | 16 ++--- 8 files changed, 164 insertions(+), 27 deletions(-) diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index eb009c7d3..5dd64a30d 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -990,6 +990,11 @@ TskAutoDbJava::openImage(const char* a_deviceId) uint8_t TskAutoDbJava::addImageDetails(const char* deviceId) { + // The image has already been added to the database + if (m_curImgId > 0) { + return 0; + } + string md5 = ""; string sha1 = ""; string collectionDetails = ""; @@ -1465,6 +1470,15 @@ TskAutoDbJava::setTz(string tzone) m_curImgTZone = tzone; } +/** + * Set the object ID for the data source + */ +void +TskAutoDbJava::setDatasourceObjId(int64_t img_id) +{ + m_curImgId = img_id; +} + TSK_RETVAL_ENUM TskAutoDbJava::processFile(TSK_FS_FILE * fs_file, const char *path) { diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h index a4a55e509..1cba1ae3b 100644 --- a/bindings/java/jni/auto_db_java.h +++ b/bindings/java/jni/auto_db_java.h @@ -44,6 +44,7 @@ class TskAutoDbJava :public TskAuto { virtual void closeImage(); void close(); virtual void setTz(string tzone); + virtual void setDatasourceObjId(int64_t img_id); virtual TSK_FILTER_ENUM filterVs(const TSK_VS_INFO * vs_info); virtual TSK_FILTER_ENUM filterVol(const TSK_VS_PART_INFO * vs_part); diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index e4dac6466..f4da3b4b6 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -15,6 +15,10 @@ #include "tsk/img/img_writer.h" #include "tsk/img/raw.h" #include "auto_db_java.h" +#if HAVE_LIBEWF +#include "tsk/img/ewf.h" +#include "tsk/img/tsk_img_i.h" +#endif #include "jni.h" #include "dataModel_SleuthkitJNI.h" #include <locale.h> @@ -905,7 +909,7 @@ JNIEXPORT void JNICALL TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "runAddImgNat: Invalid TskAutoDbJava object passed in"); + "runOpenAndAddImgNat: Invalid TskAutoDbJava object passed in"); return; } @@ -914,7 +918,7 @@ JNIEXPORT void JNICALL if (NULL != deviceId) { device_id = (const char *) env->GetStringUTFChars(deviceId, &isCopy); if (NULL == device_id) { - setThrowTskCoreError(env, "runAddImgNat: Can't convert data source id string"); + setThrowTskCoreError(env, "runOpenAndAddImgNat: Can't convert data source id string"); return; } } @@ -933,7 +937,7 @@ JNIEXPORT void JNICALL GetStringUTFChars(jsPath, &isCopy); if (imagepaths8[i] == NULL) { setThrowTskCoreError(env, - "runAddImgNat: Can't convert path strings."); + "runOpenAndAddImgNat: Can't convert path strings."); // @@@ should cleanup here paths that have been converted in imagepaths8[i] return; } @@ -997,11 +1001,12 @@ JNIEXPORT void JNICALL * @param process the add-image process created by initAddImgNat * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) * @param a_img_info image info object +* @param img_id The object ID of the image in the database * @param timeZone the timezone the image is from */ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, - jclass obj, jlong process, jstring deviceId, jlong a_img_info, jstring timeZone, jstring imageWriterPathJ) { + jclass obj, jlong process, jstring deviceId, jlong a_img_info, jlong img_id, jstring timeZone, jstring imageWriterPathJ) { TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { @@ -1020,6 +1025,9 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, } } + // Set the data source object ID + tskAuto->setDatasourceObjId(img_id); + // Set the time zone. if (env->GetStringLength(timeZone) > 0) { const char *time_zone = env->GetStringUTFChars(timeZone, &isCopy); @@ -1291,8 +1299,8 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat(JNIEnv * env, } // env->NewStringUTF(img_ptrs[i]) #if HAVE_LIBEWF - if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { - IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; if (ewf_info->md5hash_isset) { return env->NewStringUTF(ewf_info->md5hash); } @@ -1301,6 +1309,51 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat(JNIEnv * env, return env->NewStringUTF(""); } +/* +* Get the sha1 hash of an image. +*/ +JNIEXPORT jstring JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getSha1HashForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + // env->NewStringUTF(img_ptrs[i]) +#if HAVE_LIBEWF + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; + if (ewf_info->sha1hash_isset) { + return env->NewStringUTF(ewf_info->sha1hash); + } + } +#endif + return env->NewStringUTF(""); +} + +/* +* Get the collection details of an image. +*/ +JNIEXPORT jstring JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getCollectionDetailsForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + // env->NewStringUTF(img_ptrs[i]) +#if HAVE_LIBEWF + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; + ewf_get_details(ewf_info); + } +#endif + return env->NewStringUTF(""); +} /* * Open the volume system at the given offset diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.h b/bindings/java/jni/dataModel_SleuthkitJNI.h index 6e580f9f9..ed49532dc 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.h +++ b/bindings/java/jni/dataModel_SleuthkitJNI.h @@ -194,10 +194,10 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runOpenAndAddIm /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: runAddImgNat - * Signature: (JLjava/lang/String;JLjava/lang/String;Ljava/lang/String;)V + * Signature: (JLjava/lang/String;JJLjava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat - (JNIEnv *, jclass, jlong, jstring, jlong, jstring, jstring); + (JNIEnv *, jclass, jlong, jstring, jlong, jlong, jstring, jstring); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI @@ -367,6 +367,22 @@ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSectorSizeF JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat (JNIEnv *, jclass, jlong); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getSha1HashForImageNat + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSha1HashForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getCollectionDetailsForImageNat + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getCollectionDetailsForImageNat + (JNIEnv *, jclass, jlong); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: closeImgNat diff --git a/bindings/java/src/org/sleuthkit/datamodel/Image.java b/bindings/java/src/org/sleuthkit/datamodel/Image.java index 045c14d81..1b920333a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Image.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Image.java @@ -129,6 +129,10 @@ public synchronized long getImageHandle() throws TskCoreException { return imageHandle; } + + synchronized void setImageHandle(long imageHandle) { + this.imageHandle = imageHandle; + } @Override public Content getDataSource() { diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 2c2e6f320..4933d492a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -5920,7 +5920,16 @@ public Image addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size connection.executeUpdate(preparedStatement); // Create the new Image object - return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, displayName, + String name = displayName; + if (name == null || name.isEmpty()) { + if (imagePaths.size() > 0) { + String path = imagePaths.get(0); + name = (new java.io.File(path)).getName(); + } else { + name = ""; + } + } + return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, name, imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, savedSize); } catch (SQLException ex) { if (!imagePaths.isEmpty()) { diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index 056006e41..e77d6132f 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -541,6 +541,31 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws */ public void run(String deviceId, String[] imageFilePaths, int sectorSize, AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { + Image img = addImageToDatabase(skCase, imageFilePaths, sectorSize, "", "", "", "", deviceId); + run(deviceId, img, sectorSize, addDataSourceCallbacks); + } + + /** + * Starts the process of adding an image to the case database. + * Either AddImageProcess.commit or AddImageProcess.revert MUST be + * called after calling AddImageProcess.run. + * + * @param deviceId An ASCII-printable identifier for the + * device associated with the image that + * should be unique across multiple cases + * (e.g., a UUID). + * @param image The image object (has already been added to the database) + * @param sectorSize The sector size (use '0' for autodetect). + * @param addDataSourceCallbacks The callbacks to use to send data to ingest (may do nothing). + * + * @throws TskCoreException if a critical error occurs within the + * SleuthKit. + * @throws TskDataException if a non-critical error occurs within + * the SleuthKit (should be OK to continue + * the process) + */ + public void run(String deviceId, Image image, int sectorSize, + AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { dbHelper = new JniDbHelper(skCase, addDataSourceCallbacks); getTSKReadLock(); try { @@ -550,7 +575,8 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize, throw new TskCoreException("Add image process already started"); } if (!isCanceled) { //with isCanceled being guarded by this it will have the same value everywhere in this synchronized block - imageHandle = openImage(imageFilePaths, sectorSize, false, caseDbIdentifier); + //imageHandle = openImage(imageFilePaths, sectorSize, false, caseDbIdentifier); + imageHandle = image.getImageHandle(); tskAutoDbPointer = initAddImgNat(dbHelper, timezoneLongToShort(timeZone), addUnallocSpace, skipFatFsOrphans); } if (0 == tskAutoDbPointer) { @@ -558,7 +584,7 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize, } } if (imageHandle != 0) { - runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, timeZone, imageWriterPath); + runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, image.getId(), timeZone, imageWriterPath); } } finally { releaseTSKReadLock(); @@ -877,33 +903,43 @@ private static long openImage(String[] imageFiles, int sSize, boolean useCache, } } - public static Image addImageToDatabase(SleuthkitCase skCase, String imagePath, int sectorSize, + public static Image addImageToDatabase(SleuthkitCase skCase, String[] imagePaths, int sectorSize, String timeZone, String md5, String sha1, String sha256, String deviceId) throws TskCoreException { // Open the image - long imageHandle = openImgNat(new String[]{imagePath}, 1, sectorSize); + long imageHandle = openImgNat(imagePaths, 1, sectorSize); // Get the fields stored in the native code - List<String> paths = Arrays.asList(getPathsForImageNat(imageHandle)); + List<String> computedPaths = Arrays.asList(getPathsForImageNat(imageHandle)); long size = getSizeForImageNat(imageHandle); long type = getTypeForImageNat(imageHandle); long computedSectorSize = getSectorSizeForImageNat(imageHandle); if (StringUtils.isEmpty(md5)) { md5 = getMD5HashForImageNat(imageHandle); } + if (StringUtils.isEmpty(sha1)) { + sha1 = getSha1HashForImageNat(imageHandle); + } + // Sleuthkit does not currently generate any SHA256 hashes. Set to empty + // string for consistency. + if (sha256 == null) { + sha256 = ""; + } + String collectionDetails = getCollectionDetailsForImageNat(imageHandle); // Now save to database CaseDbTransaction transaction = skCase.beginTransaction(); try { Image img = skCase.addImage(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), computedSectorSize, - size, null, paths, + size, null, computedPaths, timeZone, md5, sha1, sha256, deviceId, transaction); + if (!StringUtils.isEmpty(collectionDetails)) { + skCase.setAcquisitionDetails(img, collectionDetails); + } transaction.commit(); - // TODO may keep open - would need to add to cache here - closeImgNat(imageHandle); - + img.setImageHandle(imageHandle); // TODO cache this so we can close it return img; } catch (TskCoreException ex) { transaction.rollback(); @@ -2028,7 +2064,7 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native void runOpenAndAddImgNat(long process, String deviceId, String[] imgPath, int splits, String timezone) throws TskCoreException, TskDataException; - private static native void runAddImgNat(long process, String deviceId, long a_img_info, String timeZone, String imageWriterPath) throws TskCoreException, TskDataException; + private static native void runAddImgNat(long process, String deviceId, long a_img_info, long image_id, String timeZone, String imageWriterPath) throws TskCoreException, TskDataException; private static native void stopAddImgNat(long process) throws TskCoreException; @@ -2071,6 +2107,10 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native long getSectorSizeForImageNat(long imgHandle); private static native String getMD5HashForImageNat(long imgHandle); + + private static native String getSha1HashForImageNat(long imgHandle); + + private static native String getCollectionDetailsForImageNat(long imgHandle); private static native void closeImgNat(long imgHandle); diff --git a/win32/tsk_jni/tsk_jni.vcxproj b/win32/tsk_jni/tsk_jni.vcxproj index 3e32c21b5..05e12e243 100755 --- a/win32/tsk_jni/tsk_jni.vcxproj +++ b/win32/tsk_jni/tsk_jni.vcxproj @@ -151,8 +151,8 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(ProjectDir)\..\..\tsk\hashdb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;_USRDLL;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(ProjectDir)\..\..\tsk\hashdb;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;_USRDLL;HAVE_LIBEWF;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> @@ -182,8 +182,8 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;_USRDLL;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;_USRDLL;HAVE_LIBEWF;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <PrecompiledHeader> @@ -212,8 +212,8 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(ProjectDir)\..\..\tsk\hashdb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(ProjectDir)\..\..\tsk\hashdb;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;HAVE_LIBEWF;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> @@ -275,8 +275,8 @@ <ClCompile> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> - <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;TSK_JNI_EXPORTS;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(JDK_HOME)\include;$(JDK_HOME)\include\win32;$(ProjectDir)\..\..;$(LIBEWF_HOME)\include;$(LIBEWF_HOME)\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;TSK_JNI_EXPORTS;HAVE_LIBEWF;_CRT_SECURE_NO_WARNINGS;WINVER=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> -- GitLab