1178 lines
55 KiB
Ruby
Executable File
1178 lines
55 KiB
Ruby
Executable File
#!/usr/bin/ruby -w
|
|
#
|
|
# Copyright (C) 1999-2014 Rockstar Games. All Rights Reserved.
|
|
#
|
|
require 'getoptlong'
|
|
require 'Logger'
|
|
require 'singleton'
|
|
require 'fileutils'
|
|
require 'csv'
|
|
require 'pp'
|
|
require 'zlib'
|
|
require 'sys/proctable'
|
|
include Sys
|
|
|
|
|
|
def message_box(message, title, type)
|
|
# Create a Windows MessageBox.
|
|
# 0 = 'OK' Button
|
|
# 1 = 'OK' 'Cancel' Buttons
|
|
# 2 = 'Abort' 'Retry' 'Ignore' Buttons
|
|
# 3 = 'Yes' 'No' 'Cancel' Buttons
|
|
# 4 = 'Yes' 'No' Buttons
|
|
# 5 = 'Retry' 'Cancel' Buttons
|
|
# 6 = 'Cancel' 'Try Again' 'Continue'
|
|
#######################
|
|
# 16 = 'OK' Button with 'Error' Symbol
|
|
# ... SEE ABOVE EXAMPLES
|
|
# 22 = 'Cancel' 'Try Again' 'Continue' Buttons with 'Error'
|
|
#######################################
|
|
# 32 = 'OK' Button with 'Question' Symbol
|
|
# ... SEE ABOVE EXAMPLES
|
|
# 38 = 'Cancel' 'Try Again' 'Continue' Buttons with 'Question'
|
|
#########################################
|
|
# 48 = 'OK' Button with 'Warning' Symbol
|
|
# ... SEE ABOVE EXAMPLES
|
|
# 54 = 'Cancel' 'Try Again' 'Continue' Buttons with 'Warning'
|
|
########################################
|
|
# 64 = 'OK' Button with 'Info' Symbol
|
|
# ... SEE ABOVE EXAMPLES
|
|
# 70 = 'Cancel' 'Try Again' 'Continue' Buttons with 'Info'
|
|
######################################
|
|
mb = Win32API.new("user32", "MessageBox", ['i','p','p','i'], 'i')
|
|
mb.call(0, message, title, type)
|
|
end
|
|
|
|
|
|
# Setting up
|
|
RAGE_DIR = ENV['RAGE_DIR']
|
|
RS_CODEBRANCH = ENV['RS_CODEBRANCH']
|
|
RS_BUILDBRANCH = ENV['RS_BUILDBRANCH']
|
|
RS_TOOLSROOT = ENV['RS_TOOLSROOT']
|
|
USER_PROFILE = ENV['USERPROFILE']
|
|
RAGE_3RDPARTY = ENV['RAGE_3RDPARTY'].gsub("\\", "/")
|
|
USER_DESKTOP = USER_PROFILE + "/Desktop"
|
|
GAME = "GTAV"
|
|
X86DIR = ENV['ProgramFiles(x86)']
|
|
RS_TITLE = ENV['RS_TITLE'] || GAME
|
|
|
|
|
|
PROTECTION_PATH = RS_TOOLSROOT + "/script/coding/protection"
|
|
GENERATED_PATH = PROTECTION_PATH + "/generated"
|
|
GUARDSCRIPT_PATH = PROTECTION_PATH + "/gtav_pc.gsml"
|
|
ARXAN_VERSION = "11.0.0"
|
|
GUARDIT_VERSION_DIR = "/" + ARXAN_VERSION
|
|
GUARDIT_ROOT = RAGE_3RDPARTY + "/bin/Arxan/GuardIT" + GUARDIT_VERSION_DIR
|
|
GUARDIT_PATH = GUARDIT_ROOT + "/plugins/com.arxan.guardit_" + ARXAN_VERSION
|
|
COMMON_RUBY_PATH = RS_TOOLSROOT + "/script/coding/protection/common/ruby"
|
|
DUMPBIN_PATH = "dumpbin.exe"
|
|
$LOAD_PATH << COMMON_RUBY_PATH
|
|
ENV['PATH'] = ENV['PATH'] + ";" + ENV['ProgramFiles(x86)'] + "/Microsoft Visual Studio 11.0/VC/bin;" + ENV['ProgramFiles(x86)'] + "/Microsoft Visual Studio 11.0/Common7/IDE"
|
|
require 'RsgLog'
|
|
|
|
# Defines
|
|
|
|
LOG_NAME = ""
|
|
DEFAULT_GUARD_PATTERN_DEBUGGING = ""
|
|
DEFAULT_GUARD_DEBUGGING = false
|
|
DEFAULT_OBFUSCATION_LEVEL = "2"
|
|
DEFAULT_VERSION = 0
|
|
DEFAULT_VERBOSE = false
|
|
DEFAULT_DEBUG = false
|
|
DEFAULT_RELEASE = ""
|
|
DEFAULT_OVERRIDE = ""
|
|
DEFAULT_ARCH = "x64"
|
|
DEFAULT_CLOBBER = false
|
|
DEFAULT_PORTCULLIS = false
|
|
DEFAULT_FASTPROTECT = false
|
|
DEFAULT_NETWORK_TAMPERFY = false
|
|
CURR_TIME = Time.now.strftime("%Y%m%d.%H.%M.%S")
|
|
DEFAULT_TAMPERFY = false
|
|
DEFAULT_GUARD_DISABLE = false
|
|
RANDOM_NUMBER = rand(2147483647).to_s
|
|
GUARD_OBFUSCATION_CONFIG_FILE= PROTECTION_PATH + "/guardobfuscations.txt"
|
|
|
|
# </globals>
|
|
|
|
# <loggingSetup>
|
|
|
|
|
|
# Make sure that our generated directory is created
|
|
# First our protection path
|
|
begin
|
|
Dir.mkdir(PROTECTION_PATH)
|
|
rescue Exception =>e
|
|
puts e.message
|
|
end
|
|
# Then our generated path
|
|
begin
|
|
Dir.mkdir(GENERATED_PATH)
|
|
rescue Exception =>e
|
|
puts e.message
|
|
end
|
|
|
|
|
|
|
|
class MultiDelegator
|
|
def initialize(*targets)
|
|
@targets = targets
|
|
end
|
|
|
|
def self.delegate(*methods)
|
|
methods.each do |m|
|
|
define_method(m) do |*args|
|
|
@targets.map { |t| t.send(m, *args) }
|
|
end
|
|
end
|
|
self
|
|
end
|
|
|
|
class <<self
|
|
alias to new
|
|
end
|
|
end
|
|
|
|
# </loggingSetup>
|
|
|
|
# <banner>
|
|
LOG_SEVERITY_LEVEL = Logger::INFO
|
|
log = RsgLog::RsgLog.instance()
|
|
log.SetParameters(GENERATED_PATH,CURR_TIME, LOG_SEVERITY_LEVEL)
|
|
log.info(" _ _ _ _ _ _ _ _ _ _ _ _ _ ");
|
|
log.info(" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ ");
|
|
log.info(" ( R | o | c | k | s | t | a | r ) ( G | a | m | e | s )");
|
|
log.info(" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ ");
|
|
log.info(" Protection Script");
|
|
log.info("");
|
|
# </banner>
|
|
|
|
# <commandLineArguments>
|
|
|
|
|
|
class Parameters
|
|
|
|
attr_reader :verbose
|
|
attr_reader :verbose
|
|
attr_reader :debug
|
|
attr_reader :release
|
|
attr_reader :guardscript
|
|
attr_reader :guarditroot
|
|
attr_reader :architecture
|
|
attr_reader :clobber
|
|
attr_reader :portcullis
|
|
attr_reader :tamperfy
|
|
attr_reader :inputOverride
|
|
attr_reader :fastProtect
|
|
attr_reader :guardDisable
|
|
attr_reader :networkTamperfy
|
|
attr_reader :guardDebugging
|
|
attr_reader :guardPatternDebugging
|
|
attr_reader :version
|
|
def initialize
|
|
@verbose = DEFAULT_VERBOSE #false
|
|
@debug = DEFAULT_DEBUG #false
|
|
@release = DEFAULT_RELEASE #nil
|
|
@guardscript = GUARDSCRIPT_PATH
|
|
@guarditroot = GUARDIT_PATH
|
|
@architecture = DEFAULT_ARCH
|
|
@clobber = DEFAULT_CLOBBER
|
|
@portcullis = DEFAULT_PORTCULLIS
|
|
@tamperfy = DEFAULT_TAMPERFY
|
|
@inputOverride = DEFAULT_OVERRIDE
|
|
@fastProtect = DEFAULT_FASTPROTECT
|
|
@guardDisable = DEFAULT_GUARD_DISABLE
|
|
@guardPatternDebugging = DEFAULT_GUARD_PATTERN_DEBUGGING
|
|
@networkTamperfy = DEFAULT_NETWORK_TAMPERFY
|
|
@version = DEFAULT_VERSION
|
|
@opts = GetoptLong.new(
|
|
|
|
[ '--arch', '-a', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--clobber', '-c', GetoptLong::NO_ARGUMENT],
|
|
[ '--debug', '-d', GetoptLong::NO_ARGUMENT],
|
|
[ '--version', '-e', GetoptLong::REQUIRED_ARGUMENT],
|
|
[ '--fastProtect', '-f', GetoptLong::NO_ARGUMENT],
|
|
[ '--guardscript', '-g', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--help', '-h', GetoptLong::NO_ARGUMENT],
|
|
[ '--inputOverride', '-i', GetoptLong::REQUIRED_ARGUMENT],
|
|
[ '--portcullis', '-l', GetoptLong::NO_ARGUMENT],
|
|
[ '--networkTamperfy', '-n', GetoptLong::NO_ARGUMENT],
|
|
[ '--guarditroot', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--release', '-r', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--tamperfy', '-t', GetoptLong::NO_ARGUMENT],
|
|
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
|
[ '--guardDisable', '-x', GetoptLong::NO_ARGUMENT],
|
|
[ '--guardDebugging', '-y', GetoptLong::NO_ARGUMENT],
|
|
[ '--guardPatternDebugging', '-z', GetoptLong::REQUIRED_ARGUMENT]
|
|
)
|
|
end
|
|
|
|
def PrintHelp()
|
|
RsgLog.instance().info("Usage: ruby protectit.rb -r <release> [options] ");
|
|
RsgLog.instance().info(" ");
|
|
RsgLog.instance().info("-h --help Display this ");
|
|
RsgLog.instance().info("-v --verbose Displays the output from GuardIT to stdout ");
|
|
RsgLog.instance().info("-d --debug Displays how the command line arguments are parsed, and");
|
|
RsgLog.instance().info(" skips running GuardIT ");
|
|
RsgLog.instance().info("-r --release Required. Supported releases are: ");
|
|
RsgLog.instance().info(" bankrelease beta debug final release master ");
|
|
RsgLog.instance().info(" steambeta steamfinal diskrelease diskfinal ");
|
|
RsgLog.instance().info(" noupdate ");
|
|
RsgLog.instance().info(" preloaderrelease preloaderfinal preloaderdebug ");
|
|
RsgLog.instance().info(" Default is bankrelease ");
|
|
RsgLog.instance().info("-g -guardscript Override the guardscript to specify your own. ");
|
|
RsgLog.instance().info(" Default is generated from ");
|
|
RsgLog.instance().info(" $(RS_BUILDBRANCH)/protection/gtav.gsml ");
|
|
RsgLog.instance().info("-p -guarditroot Specifies the root path of GuardIT, in case somebody ");
|
|
RsgLog.instance().info(" wants to be rebellious and install it somewhere else ");
|
|
RsgLog.instance().info("-a -arch Specifies the architecture of GuardIT used. ");
|
|
RsgLog.instance().info(" Supported architectures are: x64 (default) x86 ");
|
|
RsgLog.instance().info(" Default is x64 ");
|
|
RsgLog.instance().info("-c --clobber Takes the generated protected executable and overwrites");
|
|
RsgLog.instance().info(" the copy in $(RS_BUILDBRANCH). NOT SUPPORTED YET ");
|
|
RsgLog.instance().info("-l --portcullis Enables the Portcullis Protections ");
|
|
RsgLog.instance().info("-t --tamperfy Swaps out notify user tamper actions with fo-real ones ");
|
|
RsgLog.instance().info("-x --guardDisable Swaps out notify user tamper actions with empty ");
|
|
RsgLog.instance().info(" function calls. Cannot be used with -t ");
|
|
RsgLog.instance().info("-n --networkTamperfy Swaps out notify user tamper actions with telemetry ");
|
|
RsgLog.instance().info(" actions ");
|
|
|
|
end
|
|
def ParseArguments()
|
|
@opts.each do |opt, arg|
|
|
case opt
|
|
when '--help'
|
|
PrintHelp()
|
|
exit
|
|
when '--verbose'
|
|
@verbose = true
|
|
when '--debug'
|
|
@debug = true
|
|
when '--release'
|
|
case arg
|
|
when /release/i,/bankrelease/i,/debug/i,/final/i,/beta/i, /master/i, /steamfinal/i, /steammaster/i, /steambeta/i, /diskrelease/i, /diskfinal/i, /preloaderfinal/i, /preloadrrelease/i, /noupdate/i
|
|
@release = arg
|
|
else
|
|
RsgLog.instance().error("Invalid release [#{arg}] specified")
|
|
exit
|
|
end
|
|
when '--guardscript'
|
|
@guardscript = arg
|
|
when '--guarditroot'
|
|
@guarditroot = arg
|
|
when '--tamperfy'
|
|
@tamperfy = true
|
|
when '--networkTamperfy'
|
|
@networkTamperfy = true
|
|
when '--inputOverride'
|
|
@inputOverride = arg
|
|
when '--guardDebugging'
|
|
@guardDebugging = true
|
|
when '--guardPatternDebugging'
|
|
@guardPatternDebugging = arg
|
|
when '--guardDisable'
|
|
@guardDisable = true
|
|
when '--version'
|
|
@version = arg
|
|
when '--arch'
|
|
case arg
|
|
when /x64/i,/x86/i
|
|
@architecture = arg
|
|
else
|
|
RsgLog.instance().error("Invalid architecture [#{arg}] specified")
|
|
exit
|
|
end
|
|
when '--clobber'
|
|
@clobber = true
|
|
when '--fastProtect'
|
|
@fastProtect = true
|
|
when '--portcullis'
|
|
@portcullis = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class GuardScriptFiller
|
|
@@knownFunctionsThatCrashSteam = ["DirectInput8Create", "CreateProcessW", "CreateProcessA", "LoadLibraryExW","LoadLibraryExA", "LoadLibraryW", "LoadLibraryA", "GetStringTypeW", "ShellExecuteExA", "DispatchMessageA", "PeekMessageA", "SetCapture", "ReleaseCapture","ClipCursor","GetKeyState","ShowCursor","SetCursorPos", "GetCursorPos", "GetRawInputData", "RegisterRawInputDevices", "FreeLibrary","CoCreateInstance", "PeekMessageW", "DispatchMessageW","Direct3DCreate9", "GetMessageW", "SetCursor","GetAsyncKeyState", "GetKeyboardState"]
|
|
|
|
class DllItem
|
|
attr_reader :dllName
|
|
attr_reader :functions
|
|
def initialize
|
|
@dllName = ""
|
|
@functions = Array.new
|
|
end
|
|
|
|
def setName(name)
|
|
@dllName = name
|
|
end
|
|
def addFunction(name)
|
|
@functions.push(name)
|
|
end
|
|
end
|
|
|
|
|
|
def self.GetGuardObfuscationCommands(fileContents)
|
|
enabledGuards = Array.new
|
|
foundMatchingGuard = false
|
|
guardName = ""
|
|
fileContents.each_line do |line|
|
|
if foundMatchingGuard == true
|
|
if line =~ /<disable>/
|
|
if line =~ /true/
|
|
# If it's disabled, just move on
|
|
else
|
|
# Otherwise lets add it to our queue
|
|
# RsgLog::RsgLog.instance().debug("Pushing #{guardName}")
|
|
if guardName =~ /OBF/
|
|
RsgLog::RsgLog.instance().warn("Not adding obfuscation guard #{guardName} to the mix")
|
|
else
|
|
enabledGuards.push(guardName)
|
|
end
|
|
end
|
|
# Indicate that we've found it, so we don't
|
|
# Add it later
|
|
foundMatchingGuard = false
|
|
end
|
|
elsif line =~ /guard_cmd name/
|
|
if foundMatchingGuard == true
|
|
# This means that the previously found guard
|
|
# didn't have an associated disable command
|
|
# which means we should add it to our queue
|
|
#enabledGuards.push(guardName)
|
|
if guardName =~ /OBF/
|
|
RsgLog::RsgLog.instance().warn("Not adding obfuscation guard #{guardName} to the mix")
|
|
else
|
|
enabledGuards.push(guardName)
|
|
end
|
|
end
|
|
foundMatchingGuard = true
|
|
|
|
guardCmdRegex = line.scan(/.*guard_cmd name=\"(.*)\".*/)
|
|
# Now lets fetch the guard name
|
|
guardCmdRegex.each { |match|
|
|
guardName = match[0]
|
|
}
|
|
end
|
|
end
|
|
|
|
returnString = ""
|
|
# Read JSON from a file, iterate over objects
|
|
imageCommandRegex = fileContents.scan(/.*image_cmd name=\"(.*)\".*/)
|
|
imageCommandName = imageCommandRegex[0]
|
|
|
|
obfConfigFile = open(GUARD_OBFUSCATION_CONFIG_FILE)
|
|
obfConfigContents = obfConfigFile.read
|
|
obfConfigContents.each_line { |line|
|
|
RsgLog::RsgLog.instance().debug("Reading #{line.strip}")
|
|
lineParts = line.split(",")
|
|
obfLevel = lineParts[0]
|
|
currGuardArray = Array.new
|
|
lineParts.drop(1).each do |guard|
|
|
guard = guard.strip
|
|
# do the same general thing to all elements except the first
|
|
# RsgLog::RsgLog.instance().debug("Dropping #{guard} from the main list for a custom obfuscation level")
|
|
if enabledGuards.delete(guard) == nil
|
|
# Indicate that we couldn't find it
|
|
RsgLog::RsgLog.instance().warn("Couldn't find #{guard} in the list of enabled guards")
|
|
else
|
|
currGuardArray.push(guard)
|
|
end
|
|
end
|
|
returnString = returnString + CreateObfuscationCommand(obfLevel,currGuardArray,imageCommandName)
|
|
}
|
|
pp enabledGuards
|
|
returnString = returnString + CreateObfuscationCommand(2, enabledGuards,imageCommandName)
|
|
return returnString
|
|
end
|
|
|
|
def self.CreateObfuscationCommand(level, guards, imageCommandName)
|
|
if guards.length == 0
|
|
return ""
|
|
end
|
|
|
|
returnString = ""
|
|
nowTime = rand(2147483647).to_s
|
|
returnString = returnString +
|
|
returnString = returnString + "\n<guard_cmd name=\"GUARD_OBFUSCATIONS_LEVEL_#{level}_#{nowTime}\">"
|
|
returnString = returnString + "\n <obfuscation>"
|
|
returnString = returnString + "\n <protected_range>"
|
|
returnString = returnString + "\n <include>"
|
|
|
|
# Now lets get programmatic with it, adding all of our queued guards
|
|
guards.each { |guard|
|
|
if guard =~ /OBF/
|
|
RsgLog::RsgLog.instance().warn("Not adding obfuscation guard #{guard} to the mix")
|
|
else
|
|
returnString = returnString + "\n <range>"
|
|
returnString = returnString + "\n <image_name>#{imageCommandName}</image_name>"
|
|
returnString = returnString + "\n <guard_name>#{guard}</guard_name>"
|
|
returnString = returnString + "\n </range>"
|
|
end
|
|
}
|
|
# Close out our included regions
|
|
returnString = returnString + "\n </include>"
|
|
returnString = returnString + "\n </protected_range>"
|
|
returnString = returnString + "\n <level>#{level}</level>"
|
|
returnString = returnString + "\n <debug>false</debug>"
|
|
returnString = returnString + "\n <disable>false</disable>"
|
|
returnString = returnString + "\n </obfuscation>"
|
|
returnString = returnString + "\n</guard_cmd>"
|
|
end
|
|
|
|
def self.GetRevolvingProbability(fileContents)
|
|
guardCmdRegex = fileContents.scan(/.*guard_cmd name=\"(.*REVOLVING.*)\".*/)
|
|
RsgLog::RsgLog.instance().info("Found #{guardCmdRegex.length} number of revolving guards")
|
|
return (1.5 / guardCmdRegex.length).to_s[0..5]
|
|
end
|
|
|
|
def self.GetRevolvingLocationIndex(fileContents)
|
|
|
|
end
|
|
|
|
def self.GetRevolvingGuards(fileContents)
|
|
guardCmdRegex = fileContents.scan(/.*guard_cmd name=\"(.*REVOLVING.*)\".*/)
|
|
returnString = ""
|
|
guardCmdRegex.each { |match|
|
|
returnString = returnString + "\n <range>"
|
|
returnString = returnString + "\n <image_name>gtav_pc</image_name>"
|
|
returnString = returnString + "\n <guard_name>#{match}</guard_name>"
|
|
returnString = returnString + "\n </range>"
|
|
}
|
|
return returnString
|
|
end
|
|
|
|
def self.GetQAPrintCommands(fileContents)
|
|
#<guard_cmd name="CHLRP_0013_CHK_AF">
|
|
guardCmdRegex = fileContents.scan(/.*guard_cmd name=\"(.*(CHK|ADB).*)\".*/)
|
|
returnString = ""
|
|
guardCmdRegex.each { |match|
|
|
if match[0] =~ /PRTCL/
|
|
next
|
|
end
|
|
returnString = returnString + "\n<print_cmd name=\"print_#{match[0]}_has_run\">"
|
|
returnString = returnString + "\n <range>"
|
|
returnString = returnString + "\n <image_name>gtav_pc</image_name>"
|
|
returnString = returnString + "\n <guard_name>#{match[0]}</guard_name>"
|
|
returnString = returnString + "\n <guard_symbol_name>has_run_expected</guard_symbol_name>"
|
|
returnString = returnString + "\n </range>"
|
|
returnString = returnString + "\n <disable>false</disable>"
|
|
returnString = returnString + "\n</print_cmd>"
|
|
|
|
if match[1] == "CHK"
|
|
returnString = returnString + "\n<print_cmd name=\"print_#{match[0]}_key\">"
|
|
returnString = returnString + "\n <range>"
|
|
returnString = returnString + "\n <image_name>gtav_pc</image_name>"
|
|
returnString = returnString + "\n <guard_name>#{match[0]}</guard_name>"
|
|
returnString = returnString + "\n <guard_symbol_name>key</guard_symbol_name>"
|
|
returnString = returnString + "\n </range>"
|
|
returnString = returnString + "\n <disable>false</disable>"
|
|
returnString = returnString + "\n</print_cmd>"
|
|
end
|
|
}
|
|
return returnString
|
|
end
|
|
|
|
def self.GetHookDebug(fileContents, parameters)
|
|
|
|
inputFileRegex = Regexp.new('<input_file>(.*)<\/input_file>')
|
|
matchData = inputFileRegex.match(fileContents)
|
|
|
|
filePath = matchData[1]
|
|
cmd = "#{DUMPBIN_PATH} /imports #{filePath}"
|
|
output = `#{cmd}`
|
|
|
|
dllItems = Array.new
|
|
|
|
currDLLItem = DllItem.new()
|
|
output.each_line do |line|
|
|
|
|
if line =~ /Import Address Table/i
|
|
next
|
|
elsif line =~ /Import Name Table/i
|
|
next
|
|
elsif line =~ /time date stamp/i
|
|
next
|
|
elsif line =~ /Index of first forwarder reference/i
|
|
next
|
|
elsif line =~ /Ordinal/i
|
|
next
|
|
elsif line =~ /Dump of file/i
|
|
next
|
|
elsif line.strip.empty?
|
|
next
|
|
elsif line =~ /\.dll/i
|
|
if currDLLItem.dllName != ""
|
|
dllItems.push(currDLLItem)
|
|
end
|
|
currDLLItem = DllItem.new()
|
|
currDLLItem.setName(line.strip)
|
|
elsif line.strip == "Summary"
|
|
break
|
|
elsif currDLLItem.dllName.empty? == false
|
|
functionLine = line.strip
|
|
functionNameRegex = Regexp.new('[0-9a-fA-F]* (.*)')
|
|
matchData = functionNameRegex.match(functionLine)
|
|
if matchData == nil
|
|
# puts "No bueno RegExp for #{functionLine}"
|
|
else
|
|
functionName = matchData[1]
|
|
currDLLItem.addFunction(functionName)
|
|
end
|
|
else
|
|
next
|
|
end
|
|
end
|
|
|
|
trueOutput = ""
|
|
potentialOutput = ""
|
|
guardIteration = 0
|
|
socialClubInstallationLocations = ["MAIN_INITGAMEHEAP_ADDALLOCATOR"]
|
|
gameInstallationLocations = ["CSYSTEM_INIT_INIT_MEMORY_BUCKETS"]
|
|
launcherInstallationLocations = ["CFSM_CONSTRUCTOR"]
|
|
|
|
dllItems.each { |x|
|
|
if x.functions.size > 0
|
|
x.functions.each { |y|
|
|
if filePath =~ /socialclub.dll/i || parameters.release =~ /steam/i || parameters.guardscript =~ /launcher/i
|
|
# Handle things that make Steam sad.
|
|
if @@knownFunctionsThatCrashSteam.include?(y) == false
|
|
|
|
if filePath =~ /socialclub.dll/i
|
|
trueOutput = trueOutput + "\n <guard_cmd name=\"SCDLL_#{guardIteration}_HKD_A\"> "
|
|
elsif parameters.guardscript =~ /launcher/i
|
|
trueOutput = trueOutput + "\n <guard_cmd name=\"LNCHR_#{guardIteration}_HKD_A\"> "
|
|
else
|
|
trueOutput = trueOutput + "\n <guard_cmd name=\"GTAVB_#{guardIteration}_HKD_A\"> "
|
|
end
|
|
trueOutput = trueOutput + "\n <hook_detection> "
|
|
trueOutput = trueOutput + "\n <action> "
|
|
trueOutput = trueOutput + "\n <notify_user> "
|
|
if filePath =~ /socialclub.dll/i
|
|
trueOutput = trueOutput + "\n <message>SCDLL_#{guardIteration}_HKD_A</message> "
|
|
elsif parameters.guardscript =~ /launcher/i
|
|
trueOutput = trueOutput + "\n <message>LNCHR_#{guardIteration}_HKD_A</message> "
|
|
else
|
|
trueOutput = trueOutput + "\n <message>GTAVB_#{guardIteration}_HKD_A</message> "
|
|
end
|
|
trueOutput = trueOutput + "\n <exit_code>#{guardIteration}</exit_code> "
|
|
trueOutput = trueOutput + "\n </notify_user> "
|
|
trueOutput = trueOutput + "\n </action> "
|
|
trueOutput = trueOutput + "\n <invocation> "
|
|
trueOutput = trueOutput + "\n <locationSet> "
|
|
trueOutput = trueOutput + "\n <include> "
|
|
|
|
if filePath =~ /socialclub.dll/i
|
|
socialClubInstallationLocations.each { |installationLoc|
|
|
trueOutput = trueOutput + "\n <location> "
|
|
trueOutput = trueOutput + "\n <image_name>rgsc</image_name> "
|
|
trueOutput = trueOutput + "\n <location_name>#{installationLoc}</location_name> "
|
|
trueOutput = trueOutput + "\n </location> "
|
|
}
|
|
elsif parameters.guardscript =~ /launcher/i
|
|
launcherInstallationLocations.each { |installationLoc|
|
|
trueOutput = trueOutput + "\n <location> "
|
|
trueOutput = trueOutput + "\n <image_name>launcher</image_name> "
|
|
trueOutput = trueOutput + "\n <location_name>#{installationLoc}</location_name> "
|
|
trueOutput = trueOutput + "\n </location> "
|
|
}
|
|
else
|
|
gameInstallationLocations.each { |installationLoc|
|
|
trueOutput = trueOutput + "\n <location> "
|
|
trueOutput = trueOutput + "\n <image_name>gtav_pc</image_name> "
|
|
trueOutput = trueOutput + "\n <location_name>#{installationLoc}</location_name> "
|
|
trueOutput = trueOutput + "\n </location> "
|
|
}
|
|
end
|
|
|
|
trueOutput = trueOutput + "\n </include> "
|
|
trueOutput = trueOutput + "\n </locationSet> "
|
|
trueOutput = trueOutput + "\n </invocation> "
|
|
trueOutput = trueOutput + "\n <hook_targets> "
|
|
trueOutput = trueOutput + "\n <target>"
|
|
trueOutput = trueOutput + "\n <module_name>#{x.dllName}</module_name>"
|
|
trueOutput = trueOutput + "\n <function_name>#{y}</function_name>"
|
|
trueOutput = trueOutput + "\n </target>"
|
|
trueOutput = trueOutput + "\n </hook_targets> "
|
|
trueOutput = trueOutput + "\n <mode>default</mode> "
|
|
trueOutput = trueOutput + "\n <instances>0</instances> "
|
|
trueOutput = trueOutput + "\n <debug>false</debug> "
|
|
trueOutput = trueOutput + "\n <disable>false</disable> "
|
|
trueOutput = trueOutput + "\n </hook_detection> "
|
|
trueOutput = trueOutput + "\n </guard_cmd> "
|
|
guardIteration +=1
|
|
end
|
|
end
|
|
}
|
|
end
|
|
}
|
|
trueOutput = trueOutput + "\n"
|
|
|
|
return trueOutput
|
|
end
|
|
|
|
def self.GetHookTargets(fileContents, parameters)
|
|
|
|
inputFileRegex = Regexp.new('<input_file>(.*)<\/input_file>')
|
|
matchData = inputFileRegex.match(fileContents)
|
|
|
|
if parameters.inputOverride == ""
|
|
filePath = matchData[1]
|
|
else
|
|
filePath = parameters.inputOverride
|
|
end
|
|
|
|
cmd = "#{DUMPBIN_PATH} /imports \"#{filePath}\""
|
|
output = `#{cmd}`
|
|
|
|
dllItems = Array.new
|
|
|
|
currDLLItem = DllItem.new()
|
|
output.each_line do |line|
|
|
|
|
if line =~ /Import Address Table/i
|
|
next
|
|
elsif line =~ /Import Name Table/i
|
|
next
|
|
elsif line =~ /time date stamp/i
|
|
next
|
|
elsif line =~ /Index of first forwarder reference/i
|
|
next
|
|
elsif line =~ /Ordinal/i
|
|
next
|
|
elsif line =~ /Dump of file/i
|
|
next
|
|
elsif line.strip.empty?
|
|
next
|
|
elsif line =~ /\.dll/i
|
|
if currDLLItem.dllName != ""
|
|
dllItems.push(currDLLItem)
|
|
end
|
|
currDLLItem = DllItem.new()
|
|
currDLLItem.setName(line.strip)
|
|
elsif line.strip == "Summary"
|
|
break
|
|
elsif currDLLItem.dllName.empty? == false
|
|
functionLine = line.strip
|
|
functionNameRegex = Regexp.new('[0-9a-fA-F]* (.*)')
|
|
matchData = functionNameRegex.match(functionLine)
|
|
if matchData == nil
|
|
# puts "No bueno RegExp for #{functionLine}"
|
|
else
|
|
functionName = matchData[1]
|
|
currDLLItem.addFunction(functionName)
|
|
end
|
|
else
|
|
next
|
|
end
|
|
end
|
|
|
|
trueOutput = ""
|
|
potentialOutput = ""
|
|
dllItems.each { |x|
|
|
if x.functions.size > 0
|
|
x.functions.each { |y|
|
|
skip = false
|
|
if filePath =~ /socialclub.dll/i || parameters.release =~ /steam/i
|
|
# Handle things that make Steam sad.
|
|
if @@knownFunctionsThatCrashSteam.include?(y)
|
|
skip = true
|
|
end
|
|
end
|
|
if !skip
|
|
trueOutput = trueOutput + "\n <target>"
|
|
trueOutput = trueOutput + "\n <module_name>#{x.dllName}</module_name>"
|
|
trueOutput = trueOutput + "\n <function_name>#{y}</function_name>"
|
|
trueOutput = trueOutput + "\n </target>"
|
|
end
|
|
}
|
|
end
|
|
}
|
|
trueOutput = trueOutput + "\n"
|
|
|
|
return trueOutput
|
|
end
|
|
|
|
def self.Populate(fileContents, parameters)
|
|
newContents = fileContents
|
|
newContents = newContents.gsub("$OBFUSCATION_LEVEL", DEFAULT_OBFUSCATION_LEVEL )
|
|
newContents = newContents.gsub("$RS_CODEBRANCH", RS_CODEBRANCH )
|
|
|
|
if parameters.release =~ /release/i || parameters.release =~ /noupdate/i
|
|
newContents = newContents.gsub("$TINYXML_RELEASE", "release" )
|
|
elsif parameters.release =~ /debug/i
|
|
newContents = newContents.gsub("$TINYXML_RELEASE", "debug" )
|
|
elsif parameters.release =~ /final/i
|
|
newContents = newContents.gsub("$TINYXML_RELEASE", "final" )
|
|
end
|
|
|
|
#if parameters.guardscript =~ /prelauncher/i || parameters.guardscript =~ /bootstrap/i
|
|
# # The space afterwarsd is intentional. This prevents it from
|
|
# # picking up "prerelease" and setting it as release.
|
|
# if parameters.release =~ /release/i
|
|
# newContents = newContents.gsub("$RELEASE", "release" )
|
|
# elsif parameters.release =~ /debug/i
|
|
# newContents = newContents.gsub("$RELEASE", "debug" )
|
|
# elsif parameters.release =~ /final/i
|
|
# newContents = newContents.gsub("$RELEASE", "final" )
|
|
# end
|
|
#else£
|
|
|
|
#if parameters.release =~ /^master/i
|
|
# newContents = newContents.gsub("$FRAMEWORKRELEASE", "master" )
|
|
# newContents = newContents.gsub("$RELEASE", "mastereuropean" )
|
|
# newContents = newContents.gsub("$FILENAME", "final_eu" )
|
|
#else
|
|
#if parameters.release =~ /mastereuropean/i
|
|
# newContents = newContents.gsub("$FRAMEWORKRELEASE", "master" )
|
|
#else
|
|
# newContents = newContents.gsub("$FRAMEWORKRELEASE", parameters.release )
|
|
#end
|
|
newContents = newContents.gsub("$RELEASE", parameters.release )
|
|
newContents = newContents.gsub("$FILENAME", parameters.release )
|
|
#end
|
|
#end
|
|
|
|
if parameters.inputOverride != ""
|
|
newContents = newContents.gsub(/<input_file>.*<\/input_file>/ , "<input_file>#{parameters.inputOverride}</input_file>")
|
|
end
|
|
|
|
|
|
|
|
newContents = newContents.gsub("$RS_BUILDBRANCH", RS_BUILDBRANCH )
|
|
newContents = newContents.gsub("$RAGE_DIR", RAGE_DIR )
|
|
newContents = newContents.gsub("$RAGE_3RDPARTY", RAGE_3RDPARTY )
|
|
newContents = newContents.gsub("$GUARDIT_PATH", GUARDIT_PATH )
|
|
newContents = newContents.gsub("$PROTECTION_PATH", PROTECTION_PATH )
|
|
newContents = newContents.gsub("$CURR_TIME", CURR_TIME )
|
|
newContents = newContents.gsub("$RS_TITLE", RS_TITLE )
|
|
newContents = newContents.gsub("$PORTCULLIS_ENABLE", parameters.portcullis ? "false" : "true" )
|
|
newContents = newContents.gsub("$ARXAN_VERSION", ARXAN_VERSION )
|
|
newContents = newContents.gsub("\\", "/" )
|
|
newContents = newContents.gsub("$BACKSLASH", "\\" )
|
|
newContents = newContents.gsub("$SEED_VALUE", RANDOM_NUMBER )
|
|
newContents = newContents.gsub("$FAST_PROTECT", parameters.fastProtect ? "true" : "false" )
|
|
newContents = newContents.gsub("$HOOK_TARGETS", GetHookTargets(newContents, parameters))
|
|
newContents = newContents.gsub("$HOOK_GUARD_DEBUG", GetHookDebug(newContents, parameters))
|
|
newContents = newContents.gsub("$REVOLVING_GUARDS", GetRevolvingGuards(newContents))
|
|
newContents = newContents.gsub("$QA_PRINT_COMMANDS", GetQAPrintCommands(newContents))
|
|
newContents = newContents.gsub("$GUARD_OBFUSCATION", GetGuardObfuscationCommands(newContents))
|
|
|
|
locationNumbers = (1..99).to_a.shuffle
|
|
revolvingIdx = 0
|
|
while(newContents.include?("$REVOLVING_LOCATION_GEN") == true)
|
|
formattedNum = sprintf("%03d", locationNumbers[revolvingIdx])
|
|
revolvingIdx = (revolvingIdx % 99) + 1
|
|
newContents = newContents.sub("$REVOLVING_LOCATION_GEN", "REVOLVING_LOCATION_#{formattedNum}")
|
|
end
|
|
|
|
# Now we need to replace the DWORDS to something reliable.
|
|
# This is where having a version number is important
|
|
# in addition to being able to parse out the token
|
|
randomDwordsArray = fileContents.scan(/.*\$RANDOM_DWORD_(.*)<\/action_param>/)
|
|
randomDwordsArray += fileContents.scan(/.*\$RANDOM_DWORD_(.*)<\/exit_code>/)
|
|
versionCrc = Zlib.crc32(parameters.version.to_s)
|
|
RsgLog::RsgLog.instance().info("Version XOR Value: #{versionCrc.to_i.to_s(16)}");
|
|
randomDwordsArray.each { |randomDwordIdentifier|
|
|
|
|
identifierCrc = Zlib.crc32(randomDwordIdentifier[0])
|
|
output = "#{randomDwordIdentifier},0x#{identifierCrc.to_s(16).rjust(8,"0")}"
|
|
# puts output
|
|
thisCrc = identifierCrc.to_i ^ versionCrc.to_i
|
|
#puts "[A,#{randomDwordIdentifier}]\t\t[I,#{identifierCrc.to_i.to_s(16)}]\t\t[V,#{versionCrc.to_i.to_s(16)}]\t\t[T,#{thisCrc.to_i.to_s(16)}]"
|
|
RsgLog::RsgLog.instance().info("#{randomDwordIdentifier},#{identifierCrc.to_i.to_s(16)},#{thisCrc.to_i.to_s(16)}")
|
|
#puts "Replacing $RANDOM_DWORD_#{randomDwordIdentifier} with 0x#{thisCrc.to_i.to_s(16)}"
|
|
# This is a total hack. It wsn't matching only the whole string, so I added the < qualifier to be sure
|
|
# that strings only replaced themselves
|
|
newContents = newContents.gsub("$RANDOM_DWORD_#{randomDwordIdentifier}<","0x#{thisCrc.to_i.to_s(16)}<")
|
|
}
|
|
|
|
#doneSubbing = false
|
|
#while doneSubbing == false do
|
|
# randStr = rand(2147483647).to_s(16)
|
|
# test = newContents.sub!("$RANDOM_DWORD", "0x#{randStr}" )
|
|
# if test == nil
|
|
# doneSubbing = true
|
|
# else
|
|
# newContents = test
|
|
# end
|
|
#end
|
|
|
|
netContentsPatternDebugging = ""
|
|
if parameters.guardDebugging == true
|
|
newContents = newContents.gsub("<debug>false</debug>", "<debug>true</debug>")
|
|
elsif parameters.guardPatternDebugging != ""
|
|
foundDebug = false
|
|
foundMatchingGuard = false
|
|
patternSplit = parameters.guardPatternDebugging.split(";")
|
|
newContents.each_line do |line|
|
|
if foundMatchingGuard == true
|
|
if line =~ /<debug>/
|
|
netContentsPatternDebugging+=line.gsub("false", "true")
|
|
foundMatchingGuard = false
|
|
else
|
|
netContentsPatternDebugging+=line
|
|
end
|
|
elsif line =~ /guard_cmd name/
|
|
patternSplit.each { |p|
|
|
if line =~ /#{p}/
|
|
foundMatchingGuard = true
|
|
end
|
|
}
|
|
netContentsPatternDebugging+=line
|
|
else
|
|
netContentsPatternDebugging+=line
|
|
end
|
|
end
|
|
newContents = netContentsPatternDebugging
|
|
|
|
end
|
|
|
|
|
|
|
|
newContentsDeux = ""
|
|
if parameters.tamperfy == true
|
|
|
|
# Lets open our CSV file
|
|
#@tamperHash = {}
|
|
#CSV.foreach("ProtectIT.TamperActions.csv") do |row|
|
|
# guardName, guardAction = row
|
|
# @tamperHash[guardName] = guardAction
|
|
#end
|
|
foundNotify = false
|
|
oldMessage = ""
|
|
oldTabs = ""
|
|
# What are the steps here?
|
|
# Iterate line by line, looking for things we care about
|
|
newContents.each_line do |line|
|
|
if foundNotify == true
|
|
if line =~ /<message>(.*)<\/message>/
|
|
oldMessage = $1
|
|
elsif line =~ /<\/notify_user>/
|
|
# This is where we do the work of indexing into our hash to see what tamper action we should take
|
|
# In the event to which one doesn't exist, it probably seems safe to default to fail
|
|
|
|
# First lets set this to false so we still get the rest of the file
|
|
foundNotify = false
|
|
# Good, now lets see if the thing exists in our hash
|
|
newContentsDeux +="#{oldTabs}<fail/>\n"
|
|
end
|
|
elsif line =~ /(.*)<notify_user>/
|
|
oldTabs = $1
|
|
foundNotify = true
|
|
else
|
|
newContentsDeux +=line
|
|
end
|
|
end
|
|
return newContentsDeux
|
|
elsif (parameters.guardDisable == true || parameters.networkTamperfy == true)
|
|
foundNotify = false
|
|
oldMessage = ""
|
|
oldTabs = ""
|
|
maintainOriginal = true
|
|
bufferOld = ""
|
|
# What are the steps here?
|
|
# Iterate line by line, looking for things we care about
|
|
newContents.each_line do |line|
|
|
if foundNotify == true
|
|
bufferOld+=line
|
|
if line =~ /<message>(.*)<\/message>/
|
|
oldMessage = $1
|
|
elsif line =~ /<\/notify_user>/
|
|
# First lets set this to false so we still get the rest of the file
|
|
foundNotify = false
|
|
# This is where we do the work of indexing into our hash to see what tamper action we should take
|
|
# In the event to which one doesn't exist, it probably seems safe to default to fail
|
|
|
|
if oldMessage =~ /enc/i
|
|
newContentsDeux+=bufferOld
|
|
else
|
|
randomNumber = rand(2147483647).to_s(16).upcase
|
|
# Good, now lets see if the thing exists in our hash
|
|
newContentsDeux +="#{oldTabs}<call>\n"
|
|
if parameters.networkTamperfy == true
|
|
newContentsDeux +="#{oldTabs}\t<action_proc>TamperAction_SendTelemetry</action_proc>\n"
|
|
else
|
|
newContentsDeux +="#{oldTabs}\t<action_proc>TamperAction_None</action_proc>\n"
|
|
end
|
|
newContentsDeux +="#{oldTabs}\t<action_param>0x#{randomNumber}</action_param>\n"
|
|
newContentsDeux +="#{oldTabs}</call>\n"
|
|
end
|
|
end
|
|
elsif line =~ /(.*)<notify_user>/
|
|
bufferOld = line
|
|
oldTabs = $1
|
|
foundNotify = true
|
|
else
|
|
newContentsDeux +=line
|
|
end
|
|
end
|
|
return newContentsDeux
|
|
else
|
|
return newContents
|
|
end
|
|
end
|
|
end
|
|
|
|
# </commandLineArguments>
|
|
|
|
|
|
|
|
log.info("Parsing arguments")
|
|
parameters = Parameters.new
|
|
parameters.ParseArguments()
|
|
|
|
if parameters.release == ""
|
|
parameters.PrintHelp()
|
|
log.error("Invalid release [#{parameters.release}] specified")
|
|
exit
|
|
end
|
|
|
|
if parameters.debug == true
|
|
log.SetLevel(Logger::DEBUG)
|
|
end
|
|
|
|
if parameters.tamperfy && parameters.guardDisable && parameters.networkTamperfy
|
|
parameters.PrintHelp()
|
|
log.error("Cannot use both the tamperfy (-t) or networktamperfy (-n) and the guardDisable (-x), as you're trying to use two different flags to affect the tamper actions. You'll have to chose one. Exiting.");
|
|
exit
|
|
elsif parameters.tamperfy && parameters.networkTamperfy
|
|
parameters.PrintHelp()
|
|
log.error("Cannot use both the tamperfy (-t) and the networktamperfy (-n) , as you're trying to use two different flags to affect the tamper actions. You'll have to chose one. Exiting.");
|
|
exit
|
|
end
|
|
|
|
# <print command line arguments for good measure"
|
|
log.debug("Verbose \t#{parameters.verbose}");
|
|
log.debug("Debug \t#{parameters.debug}");
|
|
log.debug("Guardscript \t#{parameters.guardscript}");
|
|
log.debug("GuardIT Root \t#{parameters.guarditroot}");
|
|
log.debug("Release \t#{parameters.release}");
|
|
log.debug("Architecture \t#{parameters.architecture}");
|
|
log.debug("Clobber \t#{parameters.clobber}");
|
|
log.debug("Portcullis \t#{parameters.portcullis}");
|
|
log.debug("Tamperfy \t#{parameters.tamperfy}");
|
|
log.debug("Network Tamp \t#{parameters.networkTamperfy}");
|
|
log.debug("Guard Disable\t#{parameters.guardDisable}");
|
|
|
|
# Now we actually have to do the work of building the guardscript together
|
|
|
|
# Open up the file
|
|
log.info("Opening template guardscript")
|
|
templateGs = File.new(parameters.guardscript, "r")
|
|
if(templateGs)
|
|
else
|
|
log.error("Unable to open path to guardscript '#{parameters.guardscript}'")
|
|
end
|
|
|
|
templateGsContents = templateGs.read
|
|
|
|
# Now lets fill it in with our tokens
|
|
log.info("Generating guardscript")
|
|
populatedGsContents = GuardScriptFiller.Populate(templateGsContents,parameters)
|
|
# Now that we've got the generated, lets write it out to disk.
|
|
# Create a file name so that we can debug it accordingly
|
|
|
|
generatedGsFileName = CURR_TIME + ".gsml"
|
|
|
|
log.info("Writing out generated guardscript to disk")
|
|
|
|
File.open(GENERATED_PATH + "/" + generatedGsFileName,'w') do |outputFile|
|
|
outputFile.puts populatedGsContents
|
|
end
|
|
|
|
log.info("Writing out have list to disk")
|
|
# First lets create the command
|
|
if parameters.debug == false
|
|
generatedHaveFileName = CURR_TIME + ".haveList"
|
|
haveCommand = "p4 have #{RS_CODEBRANCH}/..."
|
|
haveOutput = `#{haveCommand}`
|
|
File.open(GENERATED_PATH + "/" + generatedHaveFileName,'w') do |outputFile|
|
|
outputFile.puts haveOutput
|
|
end
|
|
end
|
|
|
|
log.info("Running GuardIT")
|
|
guardItCommand = GUARDIT_PATH
|
|
#if parameters.architecture == "x64"
|
|
guardItCommand = guardItCommand + "/bin64"
|
|
#else
|
|
# guardItCommand = guardItCommand + "/bin"
|
|
#end
|
|
|
|
|
|
|
|
extraParameters = ""
|
|
# HACKHACKHACKALERT
|
|
# We only want to add the disable section check for the game, and not the launcher.
|
|
#if parameters.release =~ /steam/i && parameters.guardscript =~ /gtav/i
|
|
# extraParameters = extraParameters + "/disablesectioncheck "
|
|
#end
|
|
|
|
# HACKHACKHACKALERT
|
|
# Supporting /arxmap for final builds
|
|
extraParameters = extraParameters + "/arxmap /arxinstmap /winfixedcalls "
|
|
|
|
if parameters.fastProtect == false
|
|
extraParameters = extraParameters + "/i6 "
|
|
end
|
|
|
|
guardItCommand = guardItCommand + "/guardit.exe /pm #{extraParameters}" + GENERATED_PATH + "/" + generatedGsFileName
|
|
# Get our input and output file names
|
|
inputFile = /input_file\>(.*)\<\/input_file/.match(populatedGsContents)
|
|
outputFile = /output_file\>(.*)\<\/output_file/.match(populatedGsContents)
|
|
|
|
# Ensure that our output file isn't currently running (saves me some time)
|
|
ProcTable.ps{ |proc_struct|
|
|
if outputFile == proc_struct.comm
|
|
log.error("The application you're trying to protect is currently running. #{proc_struct.comm}")
|
|
end
|
|
}
|
|
|
|
# Fixing a bug with GuardIT breaking
|
|
successfulProtection = false
|
|
|
|
log.info("Command to issue to console:\n#{guardItCommand}")
|
|
if parameters.debug == false
|
|
IO.popen guardItCommand do |fd|
|
|
until fd.eof?
|
|
myline = fd.readline
|
|
if parameters.verbose == true
|
|
log.info(myline, false)
|
|
else
|
|
log.debug(myline, false)
|
|
end
|
|
|
|
if myline =~ /Protection completed successfully/
|
|
successfulProtection = true
|
|
end
|
|
end
|
|
end
|
|
#
|
|
# The below commented code is used to test the scenario for error codes so that I can check when 0 is returned and when it's not
|
|
# The guardscript that's at that location is local to Amir Soofi, and should return 0 on success
|
|
#
|
|
#else
|
|
# guardItCommand = guardItCommand + "/guardit.exe /pm " + "\"C:/Users/asoofi/Documents/Visual Studio 2012/Projects/guardItTesting/guardItTesting.gsml\""
|
|
# #guardItCommand = guardItCommand + "/guardit.exe /pm " + "\"C:/asoofi/Documents/Visual Studio 2012/Projects/guardItTesting/guardItTesting.gsml\""
|
|
#
|
|
#
|
|
# if parameters.verbose == false
|
|
# guardItOutput = `#{guardItCommand}`
|
|
# else
|
|
# IO.popen guardItCommand do |fd|
|
|
# until fd.eof?
|
|
# log.info(fd.readline, false)
|
|
# end
|
|
# end
|
|
# end
|
|
end
|
|
|
|
log.info("Generated Guardscript: " + GENERATED_PATH + "/" + generatedGsFileName)
|
|
log.info("GuardIT Finished!")
|
|
|
|
if ENV['USERNAME'] == "asoofi"
|
|
#message_box("Protection Finished!", "Danger Will Robinson!",48)
|
|
end
|
|
|
|
|
|
|
|
|
|
# Handle the case where I'm protecting a DLL
|
|
binaryType = ".exe"
|
|
if inputFile[1].include? ".dll"
|
|
binaryType = ".dll"
|
|
end
|
|
|
|
errorCode = $?.to_i
|
|
if errorCode !=0 && successfulProtection == true
|
|
log.warn("No bueno: error code came back non-zero (#{errorCode}); forcing it")
|
|
errorCode = 0
|
|
end
|
|
if errorCode == 0 || successfulProtection == true
|
|
# Copy over artifacts, because it's just easier to do here.
|
|
inputFileBaseName = File.basename(inputFile[1], binaryType)
|
|
inputDir = File.dirname(inputFile[1])
|
|
artifactsICareAbout = [".map", ".pdb"]
|
|
artifactsICareAbout.push(binaryType)
|
|
|
|
# Build the files I care about
|
|
# Assume that it houses the contents of the files we care about
|
|
artifactsICareAbout.each {|artifact|
|
|
localPath = inputDir + "/" + inputFileBaseName + artifact
|
|
begin
|
|
destinationPath = GENERATED_PATH + "/" + CURR_TIME + "." + inputFileBaseName + ".unprotected" + artifact
|
|
FileUtils.cp(localPath, destinationPath)
|
|
rescue Exception =>e
|
|
log.warn(e.message)
|
|
end
|
|
}
|
|
|
|
if parameters.clobber == true
|
|
if binaryType == ".exe"
|
|
log.info("Overwriting original input file")
|
|
log.debug("Clobbering input ")
|
|
log.debug("\t" + inputFile[1])
|
|
log.debug("with")
|
|
log.debug("\t" + outputFile[1])
|
|
|
|
if parameters.debug == false
|
|
FileUtils.cp(inputFile[1], inputFile[1] + ".unprotected" + binaryType)
|
|
|
|
FileUtils.cp(outputFile[1],inputFile[1])
|
|
end
|
|
elsif binaryType == ".dll"
|
|
# Here we're handling Social Club DLL
|
|
# Bit of a dirty hack, but I need it for the distribution tonight
|
|
|
|
dllOutputDirectory = USER_DESKTOP + "/Rockstar Games"
|
|
if parameters.architecture == "x64"
|
|
if parameters.release.downcase == "debug"
|
|
dllOutputDirectory = dllOutputDirectory + "/x64/Social Club Debug"
|
|
elsif parameters.release.downcase == "release"
|
|
dllOutputDirectory = dllOutputDirectory + "/x64/Social Club"
|
|
end
|
|
elsif parameters.architecture == "x86"
|
|
if parameters.release.downcase == "debug"
|
|
dllOutputDirectory = dllOutputDirectory + "/x86/Social Club Debug"
|
|
elsif parameters.release.downcase == "release"
|
|
dllOutputDirectory = dllOutputDirectory + "/x86/Social Club"
|
|
end
|
|
end
|
|
|
|
if File.directory?(dllOutputDirectory) == false
|
|
begin
|
|
Dir.mkdir(dllOutputDirectory)
|
|
rescue Exception =>e
|
|
puts e.message
|
|
end
|
|
end
|
|
dllOutputFile = dllOutputDirectory + "/socialclub.dll"
|
|
|
|
log.info("Writing file to " + dllOutputFile)
|
|
if parameters.debug == false
|
|
FileUtils.cp(outputFile[1],dllOutputFile)
|
|
end
|
|
end
|
|
|
|
else
|
|
if parameters.debug == false
|
|
FileUtils.cp(outputFile[1],inputFile[1].gsub(binaryType, ".protected" + binaryType))
|
|
end
|
|
end
|
|
log.info("Protection Complete & Successful!")
|
|
else
|
|
begin
|
|
log.info("Protection Failed!")
|
|
FileUtils.cp(inputFile[1], outputFile[1].gsub(binaryType, ".failed" + binaryType))
|
|
rescue Exception => e
|
|
log.info("Unable to recover artifacts: " + e.message)
|
|
end
|
|
|
|
end
|
|
|
|
log.info("Return code: " + errorCode.to_s)
|
|
# Play a sound
|
|
print "\a"
|
|
exit errorCode
|