def R5Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].RVA?", "What is the RVA that points at the bound import directory table?", "What is the value of IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size?", "What is the size of the bound import directory table?", "What is the value of IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].RVA?", "What is the RVA that points directly at the delay-load Import Address Table (IAT)?", "What is the value of IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size?", "What is the total size of the delay-load Import Address Table (IAT)?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" #TODO: want to be able to randomize entries/size of delay load IAT #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round5Q" + str(questionCounter) + suffix pe.write(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R5Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[11].VirtualAddress) elif q == 2 or q == 3: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[11].Size) elif q == 4 or q == 5: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[13].VirtualAddress) elif q == 6 or q == 7: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[13].Size)
def R1Q1(questionCounter): Qs = [ "How far into the binary is the IMAGE_NT_HEADERS structure?", "How far into the binary is the structure with the 'PE' signature?", "What is the IMAGE_DOS_HEADER.e_lfanew?", "What is the offset from the end of the IMAGE_DOS_HEADER to the IMAGE_NT_HEADERS?" ] #Open the binary and manipulate IMAGE_DOS_HEADER.e_lfanew x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #pe.write(filename=outFileName) #Made a new function in pefile which also writes the file #Create a random sized (but less than 0x200) byte list bytelist = ['\x41'] * 4 * random.randint(0, 128) #Insert the byte string wherever the DOS_HEADER.e_lfanew currently says it is #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round1Q" + str(questionCounter) + suffix pe.x_randomize_NT_HEADER_location(bytelist, pe.DOS_HEADER.e_lfanew, outFileName) pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R1Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") #Check the answer if q == 3: #When it's question [3] we're actually looking for a difference from the end #of the DOS header not just from the beginning of the file CheckAnswerNum(answer, (pe.DOS_HEADER.e_lfanew - list(pe.DOS_HEADER.__pack__()).__len__())) else: CheckAnswerNum(answer, pe.DOS_HEADER.e_lfanew)
def R2Q4(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.FileAlignment?", "What is the alignment needed to align section data *in the file*?", "What is the value of IMAGE_OPTIONAL_HEADER.SectionAlignment?", "What is the alignment needed to align section information *in memory*?", ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #TODO: randomize this a bit (just want to get the basics of the question for now) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round2Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) # if q == 0 or q == 1: # print "answer = %x" % pe.OPTIONAL_HEADER.FileAlignment # if q == 2 or q == 3: # print "answer = %x" % pe.OPTIONAL_HEADER.SectionAlignment print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.FileAlignment) if q == 2 or q == 3: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.SectionAlignment)
def R2Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint?", "What is the RVA of the first code which executes in this binary?", "What is the VA of the first code which executes in this binary?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #TODO: randomize this a bit (just want to get the basics of the question for now) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round2Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.AddressOfEntryPoint) elif q == 2: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.AddressOfEntryPoint + pe.OPTIONAL_HEADER.ImageBase)
def R2Q3(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.SizeOfImage?", "What is the total amount of memory this binary will reserve in memory?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 #Just reusing the capability from R1Q4 randomSectionNames = [ ".xeno", "xeno", ".kovah", "kovah", ".foo", ".bar", ".baz", "foobar", "foofus", "ONCE", "A_JOLLY", "SWAGMAN", "CAMPED", "BY_A", "BILABONG", "UNDER", "THE", "SHADE", "OF_A", "COOLIBAH", "TREE...", "AND_THEN", "A", "BASALISK", "GOT_HIM!" "<-eht", "<-taehc", "<-edoc", "<-si", "<-s" ] x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #Created new function to insert random sections and write the file out numExtraSections = random.randint(1, 5) totalNumSections = pe.FILE_HEADER.NumberOfSections + numExtraSections #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round2Q" + str(questionCounter) + suffix #Since we haven't actually modified pe.FILE_HEADER.NumberOfSections in the binary yet #this function will know whether to add new sections based on whether the first param #is greater than the existing header or not pe.x_modifySectionsAndWrite(totalNumSections, randomSectionNames, 1, outFileName) pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) # print "answer = %x" % pe.OPTIONAL_HEADER.SizeOfImage print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.SizeOfImage)
def R1Q0(questionCounter): Qs = [ "What is the IMAGE_DOS_HEADER.e_magic in ASCII?", "What is the numeric value of the IMAGE_DOS_HEADER.e_magic?" ] #Print the question q = random.randint(0, len(Qs) - 1) print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerString(answer, "MZ") else: CheckAnswerNum(answer, 0x5A4D)
def R2Q2(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.ImageBase?", "What is the preferred VA this binary would like to be loaded into memory at?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" newBase = 0x10000 * random.randint(0, 0x1000) pe.relocate_image(newBase) pe.OPTIONAL_HEADER.ImageBase = newBase #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round2Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) # print "answer = %x" % pe.OPTIONAL_HEADER.ImageBase print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, newBase)
def R5Q2(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 Qs = [ "How many ImgDelayDescr structures are in the delayed import directory table (excluding the null entry)?", "How many DLLs are referenced by the delay import directory table?", "What is the RVA of the delayed IAT for %s?", "What is the VA of the delayed IAT for %s?", "What is the RVA of the delayed INT for %s?", "What is the VA of the delayed INT for %s?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 possibleDLLNames = [ "ADVAPI32.dll", "GDI32.dll", "SHELL32.dll", "NTDLL.dll", "COMCTL32.dll", "MSI.dll", "OLE32.dll", "MSVCRT.dll", "SETUPAPI.dll", "CRYPT32.dll", "SECUR32.dll", "IMAGEHLP.dll" ] x = random.randint( 0, 1) #FIXME: change back to 0,3 when 64 bit delay load stuff is working if x == 0: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" elif x == 2: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" outFileName = "Round5Q" + str(questionCounter) + suffix numDelayLoadEntries = random.randint(1, 4) i = 0 randomDllList = [] randomFunctionsList = [] while i < numDelayLoadEntries: #meh, don't care if we get duplicates for now randomDllList.append(possibleDLLNames[random.randint( 0, len(possibleDLLNames) - 1)]) randomFunctionsList += [[]] randomFunctionsList[i] += GetExportsByName(randomDllList[i]) i += 1 #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #This will create a new section, ".dload", with a bunch of fake entries for delay load imports pe.x_CreateDelayLoadEntries(randomDllList, randomFunctionsList, outFileName) #reopen so we can pick a random delay load entry pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #select random existing DLL being imported from entry = pe.DIRECTORY_ENTRY_DELAY_IMPORT[random.randint( 0, len(pe.DIRECTORY_ENTRY_DELAY_IMPORT) - 1)] #Print the question q = random.randint(0, len(Qs) - 1) if q <= 1: interpolatedQuestion = Qs[q] else: interpolatedQuestion = Qs[q] % entry.dll print "For binary R5Bins/%s..." % outFileName print interpolatedQuestion answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, len(pe.DIRECTORY_ENTRY_DELAY_IMPORT)) if q == 2: CheckAnswerNum(answer, entry.struct.pIAT) if q == 3: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.ImageBase + entry.struct.pIAT) if q == 4: CheckAnswerNum(answer, entry.struct.pINT) if q == 5: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.ImageBase + entry.struct.pINT)
def R4Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR4 Qs = [ "How many DLLs does this binary import from?", "How many entries are there in the import directory table?", "Does this binary directly import functions from %s? (Y or N)" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR4 #These names should all be things that none of the template binaries actually import from randomDLLNames = [ "ADVAPI32.dll", "GDI32.dll", "SHELL32.dll", "NTDLL.dll", "COMCTL32.dll", "MSI.dll", "OLE32.dll", "MSVCRT.dll", "SETUPAPI.dll", "CRYPT32.dll", "SECUR32.dll", "IMAGEHLP.dll" ] x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #TODO: eventually I want to be able to add new IAT information #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round4Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R4Bins/%s..." % outFileName interpolatedQuestion = Qs[q] if q == 2: if random.randint(0, 1): #In this case we will ask about a fake DLL dllName = randomDLLNames[random.randint(0, len(randomDLLNames) - 1)] importsNamedDll = "N" else: #In this case we will ask about a real DLL dllName = pe.DIRECTORY_ENTRY_IMPORT[random.randint( 0, len(pe.DIRECTORY_ENTRY_IMPORT) - 1)].dll importsNamedDll = "Y" interpolatedQuestion = Qs[q] % dllName print interpolatedQuestion answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, len(pe.DIRECTORY_ENTRY_IMPORT)) elif q == 2: CheckAnswerString(answer, importsNamedDll)
def R4Q3(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR4 Qs = [ "How many functions does this import from %s?", "At what RVA do the IAT entries from %s start?", "At what VA do the IAT entries from %s start?", "At what RVA do the INT entries from %s start?", "At what VA do the INT entries from %s start?", "Does this import function %s!%s? (Y or N)" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR4 #These names should all be things that none of the template binaries actually import from the DLLs they do import from nonImportedFunctionNames = ["foo"] x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #TODO: eventually I want to be able to move around the IAT #For now it will always be in the same section, but with a randomized name RandomizeSectionNames(pe) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round4Q" + str(questionCounter) + suffix pe.write(filename=outFileName) pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #pick the question q = random.randint(0, len(Qs) - 1) #select random existing DLL being imported from entry = pe.DIRECTORY_ENTRY_IMPORT[random.randint( 0, len(pe.DIRECTORY_ENTRY_IMPORT) - 1)] if q < 5: #This is the simpler question, we just need the name interpolatedQuestion = Qs[q] % entry.dll else: #here we need to choose whether we will if random.randint(0, 1): realImport = "Y" importName = entry.imports[random.randint(0, len(entry.imports) - 1)].name else: realImport = "N" importedFunctions = [] for imp in entry.imports: importedFunctions.append(imp.name) nonImportedFunctionNames = GetExportsByName(entry.dll) #Now get only the functions that are NOT imported nonImportedFunctionNames = list( set(nonImportedFunctionNames).difference( set(importedFunctions))) importName = nonImportedFunctionNames[random.randint( 0, len(nonImportedFunctionNames) - 1)] interpolatedQuestion = Qs[q] % (entry.dll, importName) print "For binary R4Bins/%s..." % outFileName print interpolatedQuestion answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, len(entry.imports)) elif q == 1: CheckAnswerNum(answer, entry.struct.FirstThunk) elif q == 2: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.ImageBase + entry.struct.FirstThunk) elif q == 3: CheckAnswerNum(answer, entry.struct.OriginalFirstThunk) elif q == 4: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + entry.struct.OriginalFirstThunk) elif q == 5: CheckAnswerString(answer, realImport)
def R3Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 Qs = ["What is the value for this section's Characteristics?", "How many flags are set in the Characteristics?", "Does this section have the IMAGE_SCN_CTN_CODE characteristic set? (Y or N)", "Does this section contain code? (Y or N)", "Does this section have the IMAGE_SCN_CNT_INITIALIZED_DATA characteristic set? (Y or N)", "Does this section contain initialized data? (Y or N)", "Does this section have the IMAGE_SCN_CNT_UNINITIALIZED_DATA characteristic set? (Y or N)", "Does this section contain uninitialized data? (Y or N)", "Does this section have the IMAGE_SCN_MEM_NOT_CACHED characteristic set? (Y or N)", "Should this section not be cached? (Y or N)", "Does this section have the IMAGE_SCN_MEM_NOT_PAGED characteristic set? (Y or N)", "Should this section not be paged out to disk? (Y or N)", "Does this section have the IMAGE_SCN_MEM_DISCARDABLE characteristic set? (Y or N)", "Is this section discardable? (Y or N)", "Can this section be removed from memory when the loader is done with it? (Y or N)", "Does this section have the IMAGE_SCN_MEM_SHARED characteristic set? (Y or N)", "Can this section be shared between processes? (Y or N)", "Does this section have the IMAGE_SCN_MEM_EXECUTE characteristic set? (Y or N)", "Is this section executable? (Y or N)", "Does this section have the IMAGE_SCN_MEM_WRITE characteristic set? (Y or N)", "Is this section writable? (Y or N)"] # "Does this section have the IMAGE_SCN_MEM_READ characteristic set? (Y or N)", #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 x = random.randint(0,3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #pick a random section randSectIndex = random.randint(0,len(pe.sections)-1) numFlagsSet = 0 if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x00000020 IMAGE_SCN_CTN_CODE = "Y" numFlagsSet += 1 else: IMAGE_SCN_CTN_CODE = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x00000040 IMAGE_SCN_CNT_INITIALIZED_DATA = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x00000040 IMAGE_SCN_CNT_INITIALIZED_DATA = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x00000080 IMAGE_SCN_CNT_UNINITIALIZED_DATA = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x00000080 IMAGE_SCN_CNT_UNINITIALIZED_DATA = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x02000000 IMAGE_SCN_MEM_DISCARDABLE = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x02000000 IMAGE_SCN_MEM_DISCARDABLE = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x04000000 IMAGE_SCN_MEM_NOT_CACHED = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x04000000 IMAGE_SCN_MEM_NOT_CACHED = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x08000000 IMAGE_SCN_MEM_NOT_PAGED = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x08000000 IMAGE_SCN_MEM_NOT_PAGED = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x10000000 IMAGE_SCN_MEM_SHARED = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x10000000 IMAGE_SCN_MEM_SHARED = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x20000000 IMAGE_SCN_MEM_EXECUTE = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x20000000 IMAGE_SCN_MEM_EXECUTE = "N" if random.randint(0,1) == 1: pe.sections[randSectIndex].Characteristics |= 0x80000000 IMAGE_SCN_MEM_WRITE = "Y" numFlagsSet += 1 else: pe.sections[randSectIndex].Characteristics &= ~0x80000000 IMAGE_SCN_MEM_WRITE = "N" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round3Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter+=1 #Print the question q = random.randint(0,len(Qs)-1) print "For binary R3Bins/%s..." % outFileName print "For section '%s'..." % pe.sections[randSectIndex].Name print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer,pe.sections[randSectIndex].Characteristics) elif q == 1: CheckAnswerNum(answer,numFlagsSet) elif q == 2 or q == 3: CheckAnswerString(answer,IMAGE_SCN_CTN_CODE) elif q == 4 or q == 5: CheckAnswerString(answer,IMAGE_SCN_CNT_INITIALIZED_DATA) elif q == 6 or q == 7: CheckAnswerString(answer,IMAGE_SCN_CNT_UNINITIALIZED_DATA) elif q == 8 or q == 9: CheckAnswerString(answer,IMAGE_SCN_MEM_NOT_CACHED) elif q == 10 or q == 11: CheckAnswerString(answer,IMAGE_SCN_MEM_NOT_PAGED) elif q == 12 or q == 13 or q == 14: CheckAnswerString(answer,IMAGE_SCN_MEM_DISCARDABLE) elif q == 15 or q == 16: CheckAnswerString(answer,IMAGE_SCN_MEM_SHARED) elif q == 17 or q == 18: CheckAnswerString(answer,IMAGE_SCN_MEM_EXECUTE) elif q == 19 or q == 20: CheckAnswerString(answer,IMAGE_SCN_MEM_WRITE)
def R3Q3(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 Qs = ["Is the Optional Header SizeOfImage value correct? (Y or N)", "Does the IMAGE_OPTIONAL_HEADER.SizeOfImage match the expected value based on the number and size of sections? (Y or N)"] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 #Just reusing the capability from R1Q4 randomSectionNames = [".xeno", "xeno", ".kovah", "kovah", ".boring", ".section", ".names", ".are", ".the", ".new", ".normal", ".when", ".you're", ".just", ".trying", ".to", ".move", ".forward", ":P", ":D", ":O", "<-siht", "<-si", "<-ton", "<-a", "<-epip"] x = random.randint(0,3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #Created new function to insert random sections and write the file out numExtraSections = random.randint(1,5) totalNumSections = pe.FILE_HEADER.NumberOfSections + numExtraSections #Since we haven't actually modified pe.FILE_HEADER.NumberOfSections in the binary yet #this function will know whether to add new sections based on whether the first param #is greater than the existing header or not updateSizeOfImage = random.randint(0,1) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round3Q" + str(questionCounter) + suffix pe.x_modifySectionsAndWrite(totalNumSections, randomSectionNames, updateSizeOfImage, outFileName) #Reopen the file otherwise the pe.sections[] calculations below won't be accurate pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter+=1 #Print the question q = random.randint(0,len(Qs)-1) print "For binary R3Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: if updateSizeOfImage: CheckAnswerString(answer,"Y") else: CheckAnswerString(answer,"N") print "Bonus question: What should the value of SizeOfImage be?" answer = raw_input("Answer: ") CheckAnswerNum(answer, pe.sections[-1].Misc_VirtualSize + pe.sections[-1].VirtualAddress)
def R1Q2(questionCounter): Qs = [ "What year was this binary compiled according to the file header TimeDateStamp?", "What is the IMAGE_FILE_HEADER's TimeDateStamp?", "How many years old is this binary? (round down)" ] #Open the binary and manipulate FILE_HEADER.TimeDateStamp x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #pick the random question q = random.randint(0, len(Qs) - 1) currentTime = int(time()) #If we happen to get the [2]nd question, we need a binary #that's older than the current year if q == 2: pe.FILE_HEADER.TimeDateStamp = random.randint(0, currentTime) #otherwise it can just be from whenever else: pe.FILE_HEADER.TimeDateStamp = random.randint(0, 4294967296) #print "selected random TimeDateStamp = %u" % pe.FILE_HEADER.TimeDateStamp #31556926 seconds per year as far as epoc time is concerned #so divide by that many to get the year binaryYear = 1970 + int(pe.FILE_HEADER.TimeDateStamp / 31556926) #print binaryYear currentYear = 1970 + int(currentTime / 31556926) #write out the modified file #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round1Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question print "For binary R1Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, binaryYear) elif q == 1: CheckAnswerNum(answer, pe.FILE_HEADER.TimeDateStamp) elif q == 2: CheckAnswerNum(answer, int(currentYear - binaryYear))
def R6Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR6 Qs = ["What is the RVA of %s", "What is the ordinal of %s"] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR6 #Randomly chosen function names which will point at faux funcs funcs = [ "FunkyTown", "Func-yTown", "Karate", "Jujitsu", "TaeKwonDo", "DorKwonDo", "Judo", "Kickboxing", "DeleteHD", "PwnSauce", "JumpTheShark", "WakeUpNeo", "TheMatrixHasYou", "FollowTheWhiteRabbit", "KnockKnock", "Pro-rate", "Intimidate", "Inculcate", "Obviate", "Instantiate", "Devastate", "Infiltrate", "Obliviate", "Annihilate", "Eradicate", "KILLKILLKILL", "CheepSaram", "HousePersonLOL", "WonSoongEe", "EeShimNiDa", "Naaaay", "ChaDongCha", "NengJangGo", "PeeHenGee", "Add", "Sub", "Mul", "Div", "Mod", "AND", "OR", "XOR", "NOR", "NAND", "MakeMountainFromMolehill", "LookGiftHorseInTheMouth", "BurnCandleAtBothEnds", "SqeezeCharmin", "CountChickensBeforeTheyreHatched", "PlayWithFood", "CryOverSpilledMilk", "VoteWolverine", "VoteLobo", "BeginPhase2", "OpenThePortal", "GetAngry", "GetEven", "GetOdd", "GetShorty", "GetBusy", "GetGone" ] x = random.randint(0, 0) if x == 0: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" elif x == 1: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" # if x == 2: # pe = pefile.PE('../template64-bound.exe') # suffix = ".exe" # else: # pe = pefile.PE('../template64-bound.dll') # suffix = ".dll" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round6Q" + str(questionCounter) + suffix pe.x_CreateExports(5, funcs, outFileName) pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #pick a random export to ask questions about randomOrdinal = random.randint(0, len(pe.DIRECTORY_ENTRY_EXPORT.symbols) - 1) randomExport = pe.DIRECTORY_ENTRY_EXPORT.symbols[randomOrdinal] q = random.randint(0, len(Qs) - 1) print "For binary R6Bins/%s..." % outFileName print Qs[q] % randomExport.name answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, randomExport.address) elif q == 1: CheckAnswerNum(answer, randomOrdinal)
def R7Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR7 Qs = [ "According to the debugging information, what year was this file compiled?", "Does the debug information TimeDateStamp match the File Header TimeDateStamp?", "What is the RVA that would point at this file's path the the debugging information (.pdb file)?", "What is the full filename, including path, where this file's debugging information (.pdb file) was originally placed at compile time?", "What is the file offset that points at the .pdb file path?", "What is the RVA of the debug information?", "What is the VA of the debug information?", "What is the file offset to the debug information?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR7 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" elif x == 1: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" if x == 2: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" outFileName = "Round7Q" + str(questionCounter) + suffix if random.randint(0, 1): pe.DIRECTORY_ENTRY_DEBUG[0].struct.TimeDateStamp = random.randint( 0, 4294967296) if random.randint(0, 1): pe.FILE_HEADER.TimeDateStamp = pe.DIRECTORY_ENTRY_DEBUG[ 0].struct.TimeDateStamp #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round7Q" + str(questionCounter) + suffix #TODO: randomize some more of these elements pe.write(outFileName) error = 0 except IOError: questionCounter += 1 q = random.randint(0, len(Qs) - 1) print "For binary R7Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: debugYear = 1970 + int( pe.DIRECTORY_ENTRY_DEBUG[0].struct.TimeDateStamp / 31556926) CheckAnswerNum(answer, debugYear) elif q == 1: if pe.DIRECTORY_ENTRY_DEBUG[ 0].struct.TimeDateStamp == pe.FILE_HEADER.TimeDateStamp: CheckAnswerString(answer, "Y") else: CheckAnswerString(answer, "N") elif q == 2: #The 0x18 offset comes from the fact that all my template binaries currently are #using CV_INFO_PDB70 for the debug information #TODO: want to show both CV_INFO_PDB70 and CV_INFO_PDB20 types (in the original #class all the files were CV_INFO_PDB20 type) CheckAnswerNum( answer, pe.DIRECTORY_ENTRY_DEBUG[0].struct.AddressOfRawData + 0x18) elif q == 3: CheckAnswerString( answer, pe.get_string_at_rva( pe.DIRECTORY_ENTRY_DEBUG[0].struct.AddressOfRawData + 0x18)) elif q == 4: CheckAnswerNum( answer, pe.get_offset_from_rva( pe.DIRECTORY_ENTRY_DEBUG[0].struct.AddressOfRawData + 0x18)) elif q == 5: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[6].VirtualAddress) elif q == 6: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.DATA_DIRECTORY[6].VirtualAddress) elif q == 7: CheckAnswerNum( answer, pe.get_offset_from_rva( pe.OPTIONAL_HEADER.DATA_DIRECTORY[0].VirtualAddress))
def R5Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 Qs = [ "How many IMAGE_BOUND_IMPORT_DESCRIPTOR structures are in the bound import directory table (excluding the null entry)?", "How many DLLs are referenced by the bound import directory table?", "What is the value of IMAGE_BOUND_IMPORT_DESCRIPTOR.OffsetModuleName for %s?", "How far is it from the start of the bound import directory table to the string for %s?", "What is the value of IMAGE_BOUND_IMPORT_DESCRIPTOR.TimeDateStamp for %s?", "For the %s that bound imports were bound against, what year was it compiled?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" #TODO: want to be able to randomize entries/size of delay load IAT #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round5Q" + str(questionCounter) + suffix pe.write(outFileName) error = 0 except IOError: questionCounter += 1 #pick a random bound import entry entry = pe.DIRECTORY_ENTRY_BOUND_IMPORT[random.randint( 0, len(pe.DIRECTORY_ENTRY_BOUND_IMPORT) - 1)] #Print the question q = random.randint(0, len(Qs) - 1) if q <= 1: interpolatedQuestion = Qs[q] else: interpolatedQuestion = Qs[q] % entry.name print "For binary R5Bins/%s..." % outFileName print interpolatedQuestion answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, len(pe.DIRECTORY_ENTRY_BOUND_IMPORT)) elif q == 2 or q == 3: CheckAnswerNum(answer, entry.struct.OffsetModuleName) elif q == 4: CheckAnswerNum(answer, entry.struct.TimeDateStamp) elif q == 5: binaryYear = 1970 + int(entry.struct.TimeDateStamp / 31556926) CheckAnswerNum(answer, binaryYear)
def R1Q3(questionCounter): Qs = [ "Is this a 32 bit or 64 bit binary? (enter 32 or 64)", "Is this a PE32 or PE32+ binary? (enter PE32 or PE32+)", "What is the IMAGE_FILE_HEADER.Machine field?", "What IMAGE_FILE_HEADER.Machine value indicates a 64 bit binary?", "What IMAGE_FILE_HEADER.Machine value indicates a 32 bit binary?" ] #pick the random question q = random.randint(0, len(Qs) - 1) #For simplicity, rather than trying to go through the rigamarole of #changing the optional header, we just randomly pick whether we open #a 32 or 64 bit template x = random.randint(0, 1) if x: if random.randint(0, 1): pe = pefile.PE('../template32.exe') suffix = ".exe" else: pe = pefile.PE('../template32.dll') suffix = ".dll" binType = 32 binTypeStr = "PE32" else: if random.randint(0, 1): pe = pefile.PE('../template64.exe') suffix = ".exe" else: pe = pefile.PE('../template64.dll') suffix = ".dll" binType = 64 binTypeStr = "PE32+" #print "selected %s binary" % binTypeStr #In the future we might want to make modifications to the template binary in every case #but for now I don't think it significantly improves the learning, and it's just extra work ;) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round1Q" + str(questionCounter) + ".exe" pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question print "For binary R1Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, binType) elif q == 1: CheckAnswerString(answer, binTypeStr) elif q == 2: if binType == 32: CheckAnswerNum(answer, 0x014C) else: CheckAnswerNum(answer, 0x8664) elif q == 3: CheckAnswerNum(answer, 0x8664) elif q == 4: CheckAnswerNum(answer, 0x014C)
def R1Q4(questionCounter): Qs = [ "How many sections does this binary have?", "What is the IMAGE_FILE_HEADER.NumberOfSections field?", "Does this binary have %u sections? (Y or N)" ] #to point out that section names don't have to start with a . #and to have easter eggs for the student who becomes the reader :P randomSectionNames = [ ".xeno", "xeno", ".kovah", "kovah", ".foo", ".bar", ".baz", "foobar", "foofus", "UPX0", "UPX1", ".UPX2", "SCREEEE!", "HAHAHA", "FOOLISH", "MORTAL", "YOU_HAVE", "STEPPED", "INTO_THE", "DEN_OF", "THE...", "BASALISK", "<-eht", "<-taehc", "<-edoc", "<-si", "<-x" ] #pick the random question q = random.randint(0, len(Qs) - 1) correctNumSections = random.randint(0, 1) #print "correctNumSections = %u" % correctNumSections x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" #Created new function to insert random sections and write the file out numExtraSections = random.randint(1, 5) #print "numExtraSections = %u" % numExtraSections existingNumSections = pe.FILE_HEADER.NumberOfSections totalNumSections = existingNumSections + numExtraSections #If it picks 5 there is then a 25% chance you will get rickrolled by the #section names! (5% chance overall) Easter Egg! :D #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round1Q" + str(questionCounter) + ".exe" #Since we haven't actually modified pe.FILE_HEADER.NumberOfSections in the binary yet #this function will know whether to add new sections based on whether the first param #is greater than the existing header or not pe.x_modifySectionsAndWrite(totalNumSections, randomSectionNames, 1, outFileName) pe = pefile.PE(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question print "For binary R1Bins/%s..." % outFileName if q == 2: if correctNumSections: interpolatedQuestion = Qs[q] % totalNumSections correctStr = "Y" else: interpolatedQuestion = Qs[q] % (totalNumSections + random.randint(1, 2)) correctStr = "N" else: interpolatedQuestion = Qs[q] #ask question print interpolatedQuestion answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.FILE_HEADER.NumberOfSections) elif q == 2: CheckAnswerString(answer, correctStr)
def R1Q5(questionCounter): Qs = [ "Is the IMAGE_FILE_EXECUTABLE_IMAGE file header characteristic set? (Y or N)", "Is this file a regular executable (.exe)? (Y or N)", "Is the IMAGE_FILE_DLL file header characteristic set? (Y or N)", "Is this file a dynamic-link library (.dll)? (Y or N)", "Is the IMAGE_FILE_LARGE_ADDRESS_AWARE file header characteristic set? (Y or N)", "Does this file support being loaded at an address > 2GB? (Y or N)", "Is the IMAGE_FILE_32BIT_MACHINE file header characteristic set? (Y or N)", "What is the IMAGE_FILE_HEADER.Characteristics field? ", "How many characteristics are set in this file's file header?" ] #, # "According to the file header, is this a 32 or 64 bit binary? (32 or 64)"#Not a good question because it can be set on 64 bit executables just fine # "Is this file a .exe/.sys or .dll? (enter exe, sys, or dll)",#lazy, don't want to deal with the response # "Is this file an executable or dynamic library? (enter \"executable\" or \"library\")"]#lazy, don't want to deal with the response #I am not generating any questions about other characteristics, because the #point is not to make it a trivia game, but to reinforce information that I #think is useful: #pick the random question q = random.randint(0, len(Qs) - 1) currentTime = int(time()) #For simplicity, rather than trying to go through the rigamarole of #changing the optional header, we just randomly pick whether we open #a 32 or 64 bit template x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') binBits = 32 binTypeStr = "PE" isDll = "N" elif x == 1: pe = pefile.PE('../template64.exe') binBits = 64 binTypeStr = "PE+" isDll = "N" elif x == 2: pe = pefile.PE('../template32.dll') binBits = 32 binTypeStr = "PE" isDll = "Y" else: pe = pefile.PE('../template64.dll') binBits = 64 binTypeStr = "PE+" isDll = "Y" numFlagsSet = 1 if isDll == "Y": numFlagsSet += 1 #decide whether to twiddle the bits #Found out the hard way just doing something like #"pe.FILE_HEADER.IMAGE_FILE_LARGE_ADDRESS_AWARE = True" #doesn't effect the file for these characteristics if random.randint(0, 1) == 1: pe.FILE_HEADER.Characteristics |= 0x20 isLargeAware = "Y" numFlagsSet += 1 else: pe.FILE_HEADER.Characteristics &= ~0x20 isLargeAware = "N" if random.randint(0, 1) == 1: pe.FILE_HEADER.Characteristics |= 0x100 is32Characteristics = "Y" numFlagsSet += 1 else: pe.FILE_HEADER.Characteristics &= ~0x100 is32Characteristics = "N" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round1Q" + str(questionCounter) + ".txt" pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question print "For binary R1Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerString(answer, "Y") elif q == 2 or q == 3: CheckAnswerString(answer, isDll) elif q == 4 or q == 5: CheckAnswerString(answer, isLargeAware) elif q == 6: CheckAnswerString(answer, is32Characteristics) elif q == 7: CheckAnswerNum(answer, pe.FILE_HEADER.Characteristics) elif q == 8: CheckAnswerNum(answer, numFlagsSet)
def R5Q3(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 Qs = [ "Is this binary using \"normal\" or \"bound\" imports?", "How many IMAGE_BOUND_FORWARDER_REF structures are in the bound import directory table?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR5 totalBoundRefs = 0 q = random.randint(0, len(Qs) - 1) if q == 0: x = random.randint(0, 7) else: x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" elif x == 3: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" elif x == 4: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 5: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 6: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" if x <= 3: importType = "bound" for entry in pe.DIRECTORY_ENTRY_BOUND_IMPORT: totalBoundRefs += len(entry.entries) else: importType = "normal" #TODO: want to be able to randomize entries/size of delay load IAT #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round5Q" + str(questionCounter) + suffix pe.write(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question print "For binary R5Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerString(answer, importType) elif q == 1: CheckAnswerNum(answer, totalBoundRefs)
def R2Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the IMAGE_OPTIONAL_HEADER.Magic value?", "What value of the optional header 'Magic' field indicates a 32 bit (PE32) binary?", "According to the IMAGE_OPTIONAL_HEADER.Magic, is this a 32 bit (PE32) binary? (Y or N)", "What value of the optional header 'Magic' field indicates a 64 bit (PE32+) binary?", "According to the IMAGE_OPTIONAL_HEADER.Magic, is this a 64 bit (PE32+) binary? (Y or N)" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 #For simplicity, rather than trying to go through the rigamarole of #changing the optional header, we just randomly pick whether we open #a 32 or 64 bit template x = random.randint(0, 1) if x: if random.randint(0, 1): pe = pefile.PE('../template32.exe') suffix = ".exe" else: pe = pefile.PE('../template32.dll') suffix = ".dll" is32 = "Y" is64 = "N" else: if random.randint(0, 1): pe = pefile.PE('../template64.exe') suffix = ".exe" else: pe = pefile.PE('../template64.dll') suffix = ".dll" is32 = "N" is64 = "Y" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round2Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.Magic) elif q == 1: CheckAnswerNum(answer, 0x010B) elif q == 2: CheckAnswerString(answer, is32) elif q == 3: CheckAnswerNum(answer, 0x020B) elif q == 4: CheckAnswerString(answer, is64)
def R7Q1(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR7 Qs = [ "How many relocations are there from RVA %x to %x?", "Is 0x%x a relocation location? (Y or N)", "What is the RVA of the relocation information?", "What is the VA of the relocation information?", "What is the file offset to the relocation information?", "What section is the relocation information in?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR7 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" elif x == 1: pe = pefile.PE('../template32-bound.exe') suffix = ".exe" if x == 2: pe = pefile.PE('../template64-bound.exe') suffix = ".exe" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round7Q" + str(questionCounter) + suffix #TODO: randomize some more of these elements pe.write(outFileName) error = 0 except IOError: questionCounter += 1 q = random.randint(0, len(Qs) - 1) print "For binary R7Bins/%s..." % outFileName relocEntry = pe.DIRECTORY_ENTRY_BASERELOC[random.randint( 0, len(pe.DIRECTORY_ENTRY_BASERELOC) - 1)] if q == 0: #pick a random page to ask about interpolatedQuestion = Qs[q] % (relocEntry.struct.VirtualAddress, relocEntry.struct.VirtualAddress + 0x1000) elif q == 1: #pick a random relocation to ask about relocEntry2 = relocEntry.entries[random.randint( 0, len(relocEntry.entries) - 1)] relocRVA = relocEntry2.rva if random.randint(0, 1): realReloc = 1 else: realReloc = 0 relocRVA += 1 interpolatedQuestion = Qs[q] % relocRVA else: interpolatedQuestion = Qs[q] print interpolatedQuestion answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, len(relocEntry.entries)) elif q == 1: if realReloc: CheckAnswerString(answer, "Y") else: CheckAnswerString(answer, "N") elif q == 2: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[5].VirtualAddress) elif q == 3: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.DATA_DIRECTORY[5].VirtualAddress) elif q == 4: CheckAnswerNum( answer, pe.get_offset_from_rva( pe.OPTIONAL_HEADER.DATA_DIRECTORY[5].VirtualAddress)) elif q == 5: CheckAnswerString( answer, pe.get_section_by_rva( pe.OPTIONAL_HEADER.DATA_DIRECTORY[5].VirtualAddress).Name)
def R2Q5(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 Qs = [ "What is the value of IMAGE_OPTIONAL_HEADER.DLLCharacteristics?", "Is IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE set? (Y or N)", "Does this binary support ASLR? (Y or N)", "Is IMAGE_DLL_CHARACTERISTICS_NX_COMPAT set? (Y or N)", "Does this binary support hardware DEP? (Y or N)", "Does this binary support non-executable data? (Y or N)", "Is IMAGE_DLL_CHARACTERISTICS_NO_SEH set? (Y or N)", "Does this binary have the flag set for no support for SEH? (Y or N)", "Is IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE set? (Y or N)", "Does this binary support multiple users in terminal server (i.e. RDP) environments? (Y or N)", "How many flags are set in the IMAGE_OPTIONAL_HEADER.DLLCharacteristics?" ] # "Is IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY set? (Y or N)", # "Does this binary require checking its digital signature? (Y or N)", # "Is IMAGE_DLLCHARACTERISTICS_NO_ISOLATION set? (Y or N)", # "Is IMAGE_DLLCHARACTERISTICS_NO_BIND set? (Y or N)", # "Is IMAGE_DLLCHARACTERISTICS_WDM_DRIVER set? (Y or N)", #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR2 x = random.randint(0, 3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" numFlagsSet = 0 if random.randint(0, 1) == 1: #Found out the hard way just doing something like #"pe.OPTIONAL_HEADER.IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = True" #doesn't effect the file for these characteristics pe.OPTIONAL_HEADER.DllCharacteristics |= 0x40 # print "IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE" aslrCompat = "Y" numFlagsSet += 1 else: pe.OPTIONAL_HEADER.DllCharacteristics &= ~0x40 aslrCompat = "N" if random.randint(0, 1) == 1: pe.OPTIONAL_HEADER.DllCharacteristics |= 0x100 # print "IMAGE_DLL_CHARACTERISTICS_NX_COMPAT" numFlagsSet += 1 nxCompat = "Y" else: pe.OPTIONAL_HEADER.DllCharacteristics &= ~0x100 nxCompat = "N" if random.randint(0, 1) == 1: pe.OPTIONAL_HEADER.DllCharacteristics |= 0x400 # print "IMAGE_DLL_CHARACTERISTICS_NO_SEH" numFlagsSet += 1 noSEH = "Y" else: pe.OPTIONAL_HEADER.DllCharacteristics &= ~0x400 noSEH = "N" if random.randint(0, 1) == 1: pe.OPTIONAL_HEADER.DllCharacteristics |= 0x8000 # print "IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE" numFlagsSet += 1 termSrv = "Y" else: pe.OPTIONAL_HEADER.DllCharacteristics &= ~0x8000 termSrv = "N" #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round2Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) # print "characteristics = %x" % pe.OPTIONAL_HEADER.DllCharacteristics # print "numFlagsSet = %u" % numFlagsSet print "For binary R2Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DllCharacteristics) elif q == 1 or q == 2: CheckAnswerString(answer, aslrCompat) elif q == 3 or q == 4 or q == 5: CheckAnswerString(answer, nxCompat) elif q == 6 or q == 7: CheckAnswerString(answer, noSEH) elif q == 8 or q == 9: CheckAnswerString(answer, termSrv) elif q == 10: CheckAnswerNum(answer, numFlagsSet)
def R8Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR8 Qs = [ "What is the RVA of the TLS directory?", "What is the VA of the TLS directory?", "What is the file offset to the TLS directory?", "What is the RVA of the TLS callbacks array?", "What is the VA of the TLS callbacks array?", "What is the file offset to the TLS callbacks array?", "How many TLS callbacks does this binary contain?", "What is the RVA of callback index %u?", "What is the VA of callback index %u?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR8 #TODO: currently I just made one template file that has 5 functions that can possibly be called #and the code will just add or subtract pointers in the callback table to these functions x = random.randint(0, 0) if x == 0: pe = pefile.PE('../template32-tls.exe') suffix = ".exe" # elif x == 1: # pe = pefile.PE('../template32-bound.exe') # suffix = ".exe" # if x == 2: # pe = pefile.PE('../template64-bound.exe') # suffix = ".exe" # else: # pe = pefile.PE('../template64-bound.dll') # suffix = ".dll" numTlsEntries = random.randint(1, 5) #TODO: randomly and independently pick numTlsEntries entries from the binary's exports randomCallbackAddressesList = [0x401000] * numTlsEntries randomCallbackAddressesList += [ 0 ] #easier to just add the null terminator here #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round8Q" + str(questionCounter) + suffix pe.x_CreateTLS(randomCallbackAddressesList, outFileName) error = 0 except IOError: questionCounter += 1 pe = pefile.PE(outFileName) q = random.randint(0, len(Qs) - 1) print "For binary R8Bins/%s..." % outFileName if q >= 7: randIndex = random.randint(0, numTlsEntries - 1) interpolatedQuestion = Qs[q] % randIndex else: interpolatedQuestion = Qs[q] print interpolatedQuestion answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[9].VirtualAddress) elif q == 1: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.DATA_DIRECTORY[9].VirtualAddress) elif q == 2: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_TLS.struct.get_file_offset()) elif q == 3: CheckAnswerNum( answer, pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - pe.OPTIONAL_HEADER.ImageBase) elif q == 4: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks) elif q == 5: CheckAnswerNum( answer, pe.get_offset_from_rva( pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - pe.OPTIONAL_HEADER.ImageBase)) elif q == 6: CheckAnswerNum(answer, numTlsEntries) elif q == 7: callbackArray = pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - pe.OPTIONAL_HEADER.ImageBase CheckAnswerNum( answer, pe.get_dword_at_rva(callbackArray + randIndex * 4) - pe.OPTIONAL_HEADER.ImageBase) elif q == 8: callbackArray = DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks - pe.OPTIONAL_HEADER.ImageBase CheckAnswerNum(answer, pe.get_dword_at_rva(callbackArray + randIndex * 4))
def R3Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 Qs = ["What is the value of IMAGE_SECTION_HEADER.VirtualAddress?", "What is the RVA this section will be loaded at?", "What is the VA this section will be loaded at?", "What is the value of IMAGE_SECTION_HEADER.Misc.VirtualSize?", "How much virtual memory will this section occupy?", "What is the RVA of the first byte of memory after this section?", "What is the VA of the first byte of memory after this section?", "What is the value of IMAGE_SECTION_HEADER.PointerToRawData?", "How far into the file on disk is the data for this section?", "What is the file offset for this section's data?", "What is the value of IMAGE_SECTION_HEADER.SizeOfRawData?", "How much space does this section's data occupy on disk?", "What is the file offset for the first byte of data after this section?"] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR3 x = random.randint(0,3) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template64.exe') suffix = ".exe" elif x == 2: pe = pefile.PE('../template32.dll') suffix = ".dll" else: pe = pefile.PE('../template64.dll') suffix = ".dll" RandomizeSectionNames(pe) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the modified file outFileName = "Round3Q" + str(questionCounter) + suffix pe.write(filename=outFileName) error = 0 except IOError: questionCounter+=1 #pick a random section randSectIndex = random.randint(0,len(pe.sections)-1) #Print the question q = random.randint(0,len(Qs)-1) print "For binary R3Bins/%s..." % outFileName print "For section '%s'..." % pe.sections[randSectIndex].Name print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.sections[randSectIndex].VirtualAddress) if q == 2: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.ImageBase + pe.sections[randSectIndex].VirtualAddress) if q == 3 or q == 4: CheckAnswerNum(answer, pe.sections[randSectIndex].Misc_VirtualSize) if q == 5: CheckAnswerNum(answer, (pe.sections[randSectIndex].VirtualAddress + pe.sections[randSectIndex].Misc_VirtualSize)) if q == 6: CheckAnswerNum(answer, (pe.OPTIONAL_HEADER.ImageBase + pe.sections[randSectIndex].VirtualAddress + pe.sections[randSectIndex].Misc_VirtualSize)) if q == 7 or q == 8 or q == 9: CheckAnswerNum(answer, pe.sections[randSectIndex].PointerToRawData) if q == 10 or q == 11: CheckAnswerNum(answer, pe.sections[randSectIndex].SizeOfRawData) if q == 12: CheckAnswerNum(answer, (pe.sections[randSectIndex].PointerToRawData + pe.sections[randSectIndex].SizeOfRawData))
def R6Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR6 Qs = [ "What is the value of IMAGE_EXPORT_DIRECTORY.Base?", "What value should be subtracted from an ordinal to get its index into the AddressOfFunctions array?", "What is the value of IMAGE_EXPORT_DIRECTORY.NumberOfFunctions?", "How many functions does this binary export?", "What is the value of IMAGE_EXPORT_DIRECTORY.NumberOfNames?", "How many functions does this binary export by name?", "How many functions does this binary export by ordinal?", "Is NumberOfNames == NumberOfFunctions? (Y or N)", "What is the value of IMAGE_EXPORT_DIRECTORY.AddressOfFunctions?", "What is the RVA to the Export Address Table (EAT)?", "What is the VA to the Export Address Table (EAT)?", "What is the value of IMAGE_EXPORT_DIRECTORY.AddressOfNames?", "What is the RVA of the Export Names Table (ENT)?", "What is the VA of the Export Names Table (ENT)?", "What is the value of IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals?", "What is the value of IMAGE_EXPORT_DIRECTORY.TimeDateStamp?", "According to the exports information, what year was this binary compiled?", "Does the exports' TimeDateStamp match the File Header TimeDateStamp? (Y or N)", "Is the exports' TimeDateStamp what's checked against when the loader is checking if bound imports still have correct VAs? (Y or N)", "Is the File Header's TimeDateStamp what's checked against when the loader is checking if bound imports still have correct VAs? (Y or N)", "Which of the following points at the Export Address Table (EAT): AddressOfFunctions, AddressOfNames, or AddressOfNameOrdinals?", "Which of the following points at the Export Names Table (ENT): AddressOfFunctions, AddressOfNames, or AddressOfNameOrdinals?", ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR6 x = random.randint(2, 3) # if x == 0: # pe = pefile.PE('../template32-bound.exe') # suffix = ".exe" # elif x == 1: # pe = pefile.PE('../template64-bound.exe') # suffix = ".exe" if x == 2: pe = pefile.PE('../template32-bound.dll') suffix = ".dll" else: pe = pefile.PE('../template64-bound.dll') suffix = ".dll" #TODO: want to be able to randomize entries/size of delay load IAT #FIXME: what's the more graceful way of doing this? error = 1 while error: try: #write out the (actually un)modified file outFileName = "Round6Q" + str(questionCounter) + suffix pe.write(outFileName) error = 0 except IOError: questionCounter += 1 #Print the question q = random.randint(0, len(Qs) - 1) print "For binary R6Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0 or q == 1: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.Base) elif q == 2 or q == 3: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfFunctions) elif q == 4 or q == 5: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfNames) elif q == 6: CheckAnswerNum( answer, pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfFunctions - pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfNames) elif q == 7: if pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfFunctions == pe.DIRECTORY_ENTRY_EXPORT.struct.NumberOfNames: CheckAnswerString(answer, "Y") else: CheckAnswerString(answer, "N") elif q == 8 or q == 9: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.AddressOfFunctions) elif q == 10: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.DIRECTORY_ENTRY_EXPORT.struct.AddressOfFunctions) elif q == 11 or q == 12: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.AddressOfNames) elif q == 13: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.DIRECTORY_ENTRY_EXPORT.struct.AddressOfNames) elif q == 14: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.AddressOfNameOrdinals) elif q == 15: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_EXPORT.struct.TimeDateStamp) elif q == 16: exportYear = 1970 + int( pe.DIRECTORY_ENTRY_EXPORT.struct.TimeDateStamp / 31556926) CheckAnswerNum(answer, exportYear) elif q == 17: #50% chance to randomize file header timedatestamp if random.randint(0, 1): pe.FILE_HEADER.TimeDateStamp = random.randint(0, 4294967296) if pe.DIRECTORY_ENTRY_EXPORT.struct.TimeDateStamp == pe.FILE_HEADER.TimeDateStamp: CheckAnswerString(answer, "Y") else: CheckAnswerString(answer, "N") elif q == 18: CheckAnswerString(answer, "Y") elif q == 19: CheckAnswerString(answer, "N") elif q == 20: CheckAnswerString(answer, "AddressOfFunctions") elif q == 21: CheckAnswerString(answer, "AddressOfNames")
def R10Q0(questionCounter): #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR8 Qs = [ "What is the RVA of the load configuration directory?", "What is the VA of the load configuration directory?", "What is the file offset to the load configuration directory?", "What is the RVA of the security cookie used for buffer overflow protection?", "What is the RVA of the stack cookie (canary) added when the /GS compile option is used?", "What is the VA of the security cookie used for buffer overflow protection?", "What is the VA of the stack cookie (canary) added when the /GS compile option is used?", "What is the RVA of the structured exception handler table?", "What is the RVA of the table used when /SAFESEH linker option is used?", "What is the VA of the security cookie used for buffer overflow protection?", "What is the VA of the table used when /SAFESEH linker option is used?", "How many SEH handlers are available in this binary?" ] #NOTE: if you update the number of questions in this function, you need to update the boundaries in StartR8 #TODO: currently I just made one template file that has 5 functions that can possibly be called #and the code will just add or subtract pointers in the callback table to these functions x = random.randint(0, 1) if x == 0: pe = pefile.PE('../template32.exe') suffix = ".exe" elif x == 1: pe = pefile.PE('../template32.dll') suffix = ".dll" #Add some misc randomizations #TODO: make these actually consistent with the binaries pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SecurityCookie += random.randint( 0, 10) * 0x1000 pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerTable += random.randint( 0, 10) * 0x100 pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerCount = random.randint( 0, 10) #FIXME: what's the more graceful way of doing this? error = 1 while error: try: outFileName = "Round10Q" + str(questionCounter) + suffix #TODO: randomize some more of these elements pe.write(outFileName) error = 0 except IOError: questionCounter += 1 q = random.randint(0, len(Qs) - 1) print "For binary R10Bins/%s..." % outFileName print Qs[q] answer = raw_input("Answer: ") if q == 0: CheckAnswerNum(answer, pe.OPTIONAL_HEADER.DATA_DIRECTORY[10].VirtualAddress) elif q == 1: CheckAnswerNum( answer, pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.DATA_DIRECTORY[10].VirtualAddress) elif q == 2: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.get_file_offset()) elif q == 3 or q == 4: CheckAnswerNum( answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SecurityCookie - pe.OPTIONAL_HEADER.ImageBase) elif q == 5 or q == 6: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SecurityCookie) elif q == 7 or q == 8: CheckAnswerNum( answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerTable - pe.OPTIONAL_HEADER.ImageBase) elif q == 9 or q == 10: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerTable) elif q == 11: CheckAnswerNum(answer, pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SEHandlerCount)