Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checksec: RFG, SafeSEH, GS support #11

Merged
merged 4 commits into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
add_executable(winchecksec main.cc Checksec.cc)

target_link_libraries(winchecksec wintrust)
target_link_libraries(winchecksec imagehlp)
88 changes: 80 additions & 8 deletions Checksec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <winnt.h>
#include <wincrypt.h>
#include <softpub.h>
#include <imagehlp.h>

#include <ostream>
#include <codecvt>
Expand All @@ -24,30 +25,62 @@ void Checksec::process() {
uint32_t ntSignature;



filestream_.read( (char*)&imageDosHeader, sizeof(imageDosHeader) );
if( imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE ) {
string msg = "Not a valid DOS header.";
throw msg;
throw "Not a valid DOS header.";
}

filestream_.read( (char*)&imageDosHeaderExtra, sizeof(imageDosHeaderExtra) );

filestream_.seekg( imageDosHeader.e_lfanew, ios_base::beg );
filestream_.read( (char*)&ntSignature, sizeof(ntSignature) );
if( ntSignature!= IMAGE_NT_SIGNATURE ) {
string msg = "Not a valid NT Signature.";
throw msg;
throw "Not a valid NT Signature.";
}
filestream_.read( (char*)&imageFileHeader, sizeof(imageFileHeader) );

// TODO(ww): We should probably guard this with imageFileHeader.SizeOfOptionalHeader != 0,
// since object files lack an optional header.
if ( !imageFileHeader.SizeOfOptionalHeader ) {
throw "Missing optional header.";
}

filestream_.read( (char*)&imageOptionalHeader, sizeof(imageOptionalHeader) );

imageCharacteristics_ = imageFileHeader.Characteristics;
dllCharacteristics_ = imageOptionalHeader.DllCharacteristics;

// https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_data_directory
IMAGE_DATA_DIRECTORY dir = imageOptionalHeader.DataDirectory[10];

if ( !dir.VirtualAddress || !dir.Size ) {
throw "No IMAGE_LOAD_CONFIG_DIRECTORY in the PE.";
}

LOADED_IMAGE *loadedImage = ImageLoad(filepath_.c_str(), NULL);

IMAGE_SECTION_HEADER sectionHeader = {0};

// Find the section that contains the load config directory.
// This should always be .rdata, but there's no telling with Windows.
for (uint64_t i = 0; i < loadedImage->NumberOfSections; i++) {
if (loadedImage->Sections[i].VirtualAddress < dir.VirtualAddress
&& loadedImage->Sections[i].VirtualAddress > sectionHeader.VirtualAddress)
{
sectionHeader = loadedImage->Sections[i];
}
}

size_t loadConfigOffset = dir.VirtualAddress
- sectionHeader.VirtualAddress
+ sectionHeader.PointerToRawData;

size_t loadConfigSize = (dir.Size < sizeof(loadConfig_)) ? dir.Size : sizeof(loadConfig_);

// After all that, we can finally read the load config directory.
filestream_.seekg(loadConfigOffset, ios_base::beg);
filestream_.read((char *) &loadConfig_, loadConfigSize);

ImageUnload(loadedImage);

}


Expand All @@ -65,6 +98,9 @@ Checksec::operator json() const {
{ "nx", isNX() },
{ "seh", isSEH() },
{ "cfg", isCFG() },
{ "rfg", isRFG() },
{ "safe_seh", isSafeSEH() },
{ "gs", isGS() },
{ "authenticode", isAuthenticode() },
{ "path", filepath_ },
};
Expand Down Expand Up @@ -142,17 +178,53 @@ const bool Checksec::isAuthenticode() const {
return status == ERROR_SUCCESS;
}

const bool Checksec::isRFG() const {
// NOTE(ww): a load config under 148 bytes implies the absence of the GuardFlags field.
if (loadConfig_.Size < 148) {
cerr << "Warn: short load config, assuming no RFG" << endl;
return false;
}

// https://xlab.tencent.com/en/2016/11/02/return-flow-guard/
return (loadConfig_.GuardFlags & 0x00020000)
&& (loadConfig_.GuardFlags & 0x00040000 || loadConfig_.GuardFlags & 0x00080000);
}

const bool Checksec::isSafeSEH() const {
// NOTE(ww): a load config under 112 bytes implies the absence of the SafeSEH fields.
if (loadConfig_.Size < 112) {
cerr << "Warn: short load config, assuming no SafeSEH" << endl;
return false;
}

return isSEH() && loadConfig_.SEHandlerTable != 0 && loadConfig_.SEHandlerCount != 0;
}

const bool Checksec::isGS() const {
// NOTE(ww): a load config under 96 bytes implies the absence of the SecurityCookie field.
if (loadConfig_.Size < 96) {
cerr << "Warn: short load config, assuming no GS" << endl;
return false;
}

// TODO(ww): Handle the edge case where the user defines a custom entry point
// and fails to call __security_init_cookie().
return loadConfig_.SecurityCookie != 0;
}

ostream& operator<<( ostream& os, Checksec& self ) {
json j = self.operator json();
os << "Dyanmic Base : " << j["dynamicBase"] << endl;
os << "Dynamic Base : " << j["dynamicBase"] << endl;
os << "ASLR : " << j["aslr"] << endl;
os << "High Entropy VA : " << j["highEntropyVA"] << endl;
os << "Force Integrity : " << j["forceIntegrity"] << endl;
os << "Isolation : " << j["isolation"] << endl;
os << "NX : " << j["nx"] << endl;
os << "SEH : " << j["seh"] << endl;
os << "CFG : " << j["cfg"] << endl;
os << "RFG : " << j["rfg"] << endl;
os << "SafeSEH : " << j["safe_seh"] << endl;
os << "GS : " << j["gs"] << endl;
os << "Authenticode : " << j["authenticode"] << endl;
return os;
}
Expand Down
17 changes: 11 additions & 6 deletions Checksec.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef CHECKSEC_H
#define CHECKSEC_H

#include <Windows.h>

#include <string>
#include <iostream>
#include <fstream>
Expand Down Expand Up @@ -29,7 +31,6 @@ class Checksec {
}



json toJson() const;

const bool isDynamicBase() const;
Expand All @@ -41,18 +42,22 @@ class Checksec {
const bool isSEH() const;
const bool isCFG() const;
const bool isAuthenticode() const;
const bool isRFG() const;
const bool isSafeSEH() const;
const bool isGS() const;

operator json() const;
friend ostream& operator<<( ostream& os, Checksec& );


private:

void process();
string filepath_;
ifstream filestream_;
uint16_t imageCharacteristics_ = 0;
uint16_t dllCharacteristics_ = 0;
void process();
string filepath_;
ifstream filestream_;
uint16_t imageCharacteristics_ = 0;
uint16_t dllCharacteristics_ = 0;
IMAGE_LOAD_CONFIG_DIRECTORY loadConfig_ = {0};
};

} // namespace
Expand Down
4 changes: 2 additions & 2 deletions main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int main( int argc, char* argv[] ) {
cout << csec << endl;
}

} catch( string& x1 ) {
} catch( const char *x1 ) {
cerr << x1 << endl;
usage(argv);
return -__LINE__;
Expand All @@ -54,4 +54,4 @@ int main( int argc, char* argv[] ) {


return 0;
}
}