I was watching some InfinityExists videos yesterday talking about bypassing AV detection by modifying the executable itself. I thought of writing an automated file chopper which will zero-in on the part which AV is detecting to be malicious. That way I can directly analyze the offset and do not have to manually find it ( for the most part ).
Script has 2 phases:
In Phase 1 it recursively chops up the files into smaller chunks divisible by 2 and figures out minimal set of bytes required to trigger AV.
For example:
Original File Size: 600
Derived File Sizes: 600->300->150....
Every chunk is scanned with ClamaV via Ruby library and the part that is "clean" is discarded.
Phase 2 is taking the last successful AV detected file chunk and makes it even smaller by shrinking with predefined increment.
File Size ( phase 1 ): 150
Increment: 10
Derivative File Sizes: 150->140->130 ...
This way we can get closer to the actual piece triggering the AV.
Sample Run with original file size of 37888 bytes and phase 2 increment of 100 bytes.
dimas@appdev1:~/Code/vira$ ./chopfile.rb -f /tmp/msf3/data/templates/template.exe
Positive: /tmp/X_0-18944: Worm.Palevo-1
Positive: /tmp/X_0-9472: Worm.Palevo-1
Positive: /tmp/X_0-4736: Worm.Palevo-1
Positive: /tmp/X_0-2368: Worm.Palevo-1
======== G R A P H ===========
|X_0-18944
|-X_0-9472
|--X_0-4736
|---X_0-2368
Positive: /tmp/XA_0-2268: Worm.Palevo-1
Positive: /tmp/XA_0-2168: Worm.Palevo-1
Positive: /tmp/XA_0-2068: Worm.Palevo-1
Positive: /tmp/XA_0-1968: Worm.Palevo-1
Positive: /tmp/XA_0-1868: Worm.Palevo-1
Positive: /tmp/XA_0-1768: Worm.Palevo-1
Positive: /tmp/XA_0-1668: Worm.Palevo-1
Positive: /tmp/XA_0-1568: Worm.Palevo-1
End of 100 chain
======== G R A P H ===========
|XA_0-2268
|-XA_0-2168
|--XA_0-2068
|---XA_0-1968
|----XA_0-1868
|-----XA_0-1768
|------XA_0-1668
|-------XA_0-1568
Some quick and dirty Ruby code.
#!/usr/bin/env ruby
require 'optparse'
require 'clamav'
def bin_chop(pos_A,pos_Z,fp)
payload=""
splitmark=(pos_Z-pos_A)/2
if splitmark <= 1
return
end
fi=File.open(fp,"rb:binary")
fi.sysseek(pos_A, IO::SEEK_SET)
fi.sysread(splitmark,payload)
fc1="/tmp/X_" + pos_A.to_s + "-" + splitmark.to_s
fo=File.open(fc1, "wb:binary")
fo.syswrite(payload)
fo.close
cv=ClamAV.new(ClamAV::CL_SCAN_STDOPT)
result=cv.scanfile(fc1)
if ( result != 0 )
puts "Positive: #{fc1}: #{result}"
cv=nil
fi.close
bin_chop(0,File.size(fc1),fc1);
else
# Negative - Delete!
File.delete(fc1)
payload=""
fi.sysread((pos_Z-splitmark),payload)
fi.close
fc2="/tmp/X_" + splitmark.to_s + "-" + pos_Z.to_s
fo=File.open(fc2, "wb:binary")
fo.syswrite(payload)
fo.close
cv=ClamAV.new(ClamAV::CL_SCAN_STDOPT)
result=cv.scanfile(fc2)
if ( result != 0 )
puts "Positive: #{fc2}: #{result}"
cv=nil
bin_chop(0,File.size(fc2),fc2);
else
# Negative - Delete!
File.delete(fc2)
end
end
end
def incr_chop(pos_A,pos_Z,fp,increment)
payload=""
splitmark=(pos_Z-increment)
fi=File.open(fp,"rb:binary")
fi.sysseek(pos_A, IO::SEEK_SET)
fi.sysread(splitmark,payload)
fc1="/tmp/XA_" + pos_A.to_s + "-" + splitmark.to_s
fo=File.open(fc1, "wb:binary")
fo.syswrite(payload)
fo.close
cv=ClamAV.new(ClamAV::CL_SCAN_STDOPT)
result=cv.scanfile(fc1)
if ( result != 0 )
puts "Positive: #{fc1}: #{result}"
cv=nil
fi.close
incr_chop(0,File.size(fc1),fc1,increment);
else
# Negative - Delete!
puts "End of #{increment} chain"
cv=nil
fi.close
File.delete(fc1)
end
end
meta = Hash.new;
opts=OptionParser.new
opts.on("-f", "--filepath VAL", String) {|val| meta[:fp]=val}
rest=opts.parse(ARGV)
if ( !meta[:fp].nil? and ( File.exists?(meta[:fp]) and File.readable?(meta[:fp]) ))
# Phase I
bin_chop(0,File.size(meta[:fp]),meta[:fp])
puts "\n\t======== G R A P H ===========\n"
Dir.chdir("/tmp")
files = Dir["X_*"]
sorted = files.sort {|a,b| File.size(a) <=> File.size(b)}
sorted.reverse.each_with_index do |fl,i|
puts "\t\t|" + "-"*i + fl
end
# Phase II
incr_chop(0,File.size(sorted.shift),sorted.shift,100)
puts "\n\t======== G R A P H ===========\n"
Dir.chdir("/tmp")
files = Dir["XA_*"]
sorted = files.sort {|a,b| File.size(a) <=> File.size(b)}
sorted.reverse.each_with_index do |fl,i|
puts "\t\t|" + "-"*i + fl
end
else
puts "invalid"
puts opts.to_s
raise Exception
end