Difference between revisions of "Gits2012teaser"

From Fixme.ch
Jump to: navigation, search
(#2 AL's Revenge)
Line 34: Line 34:
 
if (name & a)
 
if (name & a)
 
name ^= b;
 
name ^= b;
}
+
}H
  
 
return (result == 1);
 
return (result == 1);
 
}
 
}
 +
</pre>
 +
 +
* it looks like a multiplication over a galois field, with the irreducible polynomial 0x1a348fccd93aea5a7 (note leading bit not in C), but it actually isn't, because the high bit gets checked after the shift, not before.
 +
* lacking math knowledge and math package fu, decided to treat the problem as a linear equation system:
 +
* The shifting and xoring produces a set of integers, call them name_i.  If the serial bit s_i is set, name_i gets added to the result.  So you can roughly say r = N * s, with r being the result vector, s the serial vector and N the names matrix.
 +
* lacking math package fu, implement a gaussian elimination manually:
 +
 +
<pre>
 +
class Numeric
 +
  def bits64
 +
    sprintf("%064b", self).each_char.map{|c| c == "0" ? 0 : 1}
 +
  end
 +
end
 +
 +
def names(name)
 +
  p = 0xa348fccd93aea5a7
 +
 +
  if (name >> 63) & 1 == 1
 +
    name ^= p
 +
  end
 +
 +
  ([name] + 63.times.map do |i|
 +
    name <<= 1
 +
    if (name >> 63) & 1 == 1
 +
      name ^= p
 +
    end
 +
    name
 +
  end).map(&:bits64)
 +
end
 +
 +
class Array
 +
  def pivot
 +
    res = []
 +
    self[0].size.times.map do |i|
 +
      self.size.times.map do |j|
 +
        self[j][i]
 +
      end
 +
    end
 +
  end
 +
 +
  def bitary_to_int
 +
    self.join.to_i(2)
 +
  end
 +
end
 +
 +
class Eq < Array
 +
  attr_accessor :res
 +
 +
  def initialize(ary, res)
 +
    self.replace(ary)
 +
    @res = res
 +
  end
 +
 +
  def lead_zero
 +
    self.take_while{|e| e == 0}
 +
  end
 +
 +
  def first_pos
 +
    self.index(1)
 +
  end
 +
 +
  def subtract(o)
 +
    Eq.new(self.zip(o).map{|aa, bb| aa ^ bb}, @res ^ o.res)
 +
  end
 +
end
 +
 +
class EqSys < Array
 +
  def initialize(ary, res=nil)
 +
    if !(Eq === ary[0]) && !ary.empty?
 +
      ary = ary.zip(res).map{|a, r| Eq.new(a, r)}
 +
    end
 +
    self.replace(ary)
 +
  end
 +
 +
  def sort_by_zeros
 +
    self.sort do |a, b|
 +
      a.lead_zero <=> b.lead_zero
 +
    end
 +
  end
 +
 +
  def subtract_eq(eq)
 +
    pos = eq.first_pos
 +
    return self if not pos
 +
    EqSys.new(self.map{|e| e[pos] == 1 ? e.subtract(eq) : e})
 +
  end
 +
 +
  def elim
 +
    front = EqSys.new([])
 +
    cur = nil
 +
    rest = self.sort_by_zeros
 +
    while not rest.empty?
 +
      front << cur if cur
 +
      cur = rest.shift
 +
 +
      front = front.subtract_eq(cur)
 +
      rest = rest.subtract_eq(cur)
 +
      rest = rest.sort_by_zeros
 +
    end
 +
    front << cur
 +
 +
    front
 +
  end
 +
 +
  def to_s
 +
    self.map{|e| e.map(&:to_s).join + " = #{e.res}"}.join("\n")
 +
  end
 +
end
 +
 +
require 'pp'
 +
if __FILE__ == $0
 +
  ns = names(0x6638623261336134)
 +
 +
  pv = ns.pivot
 +
  es = EqSys.new(pv, 1.bits64)
 +
 +
  puts es.to_s
 +
  puts
 +
 +
  r = es.elim
 +
  puts r.to_s
 +
 +
  res = r.map{|e| e.res}.reverse
 +
  puts res.bitary_to_int.to_s(16)
 +
 +
  s0 = r.pivot.last.reverse
 +
  puts s0.bitary_to_int.to_s(16)
 +
  puts (res.bitary_to_int^s0.bitary_to_int).to_s(16)
 +
end
 
</pre>
 
</pre>
  
 
== #3 Hackquest ==
 
== #3 Hackquest ==

Revision as of 15:46, 8 January 2012

#1 TelAviv

What is the password? (File:7139a4ea239dcac655f7c38ca6a77b61.bin)
Hint: TeLaViv+ is a packet forensics challenge.

#2 AL's Revenge

  • file 49dd327824d5afe9cdf931ea4b13719f.bin says xz compressed file -> xzcat > f
  • file f says LLVM bitcode -> llvm-dis > f.s (only works with LLVM 2.8, not with 3.0)
  • analyze disassembly, extract C representation:
int
VerifySerial(uint64_t name, uint64_t serial)
{
	uint64_t a = 0x8000000000000000LL;
        uint64_t b = 0xa348fccd93aea5a7LL;
	uint64_t result = 0;

	/* high order bit set? */
	if (name & a)
		a ^= b;

	if (serial & a)
		serial ^= b;

	while (serial != 0) {
		if (serial & 1)
			result ^= name;

		serial >>= 1;
		name <<= 1;

		if (name & a)
			name ^= b;
	}H

	return (result == 1);
}
  • it looks like a multiplication over a galois field, with the irreducible polynomial 0x1a348fccd93aea5a7 (note leading bit not in C), but it actually isn't, because the high bit gets checked after the shift, not before.
  • lacking math knowledge and math package fu, decided to treat the problem as a linear equation system:
  • The shifting and xoring produces a set of integers, call them name_i. If the serial bit s_i is set, name_i gets added to the result. So you can roughly say r = N * s, with r being the result vector, s the serial vector and N the names matrix.
  • lacking math package fu, implement a gaussian elimination manually:
class Numeric
  def bits64
    sprintf("%064b", self).each_char.map{|c| c == "0" ? 0 : 1}
  end
end

def names(name)
  p = 0xa348fccd93aea5a7

  if (name >> 63) & 1 == 1
    name ^= p
  end

  ([name] + 63.times.map do |i|
    name <<= 1
    if (name >> 63) & 1 == 1
      name ^= p
    end
    name
  end).map(&:bits64)
end

class Array
  def pivot
    res = []
    self[0].size.times.map do |i|
      self.size.times.map do |j|
        self[j][i]
      end
    end
  end

  def bitary_to_int
    self.join.to_i(2)
  end
end

class Eq < Array
  attr_accessor :res

  def initialize(ary, res)
    self.replace(ary)
    @res = res
  end

  def lead_zero
    self.take_while{|e| e == 0}
  end

  def first_pos
    self.index(1)
  end

  def subtract(o)
    Eq.new(self.zip(o).map{|aa, bb| aa ^ bb}, @res ^ o.res)
  end
end

class EqSys < Array
  def initialize(ary, res=nil)
    if !(Eq === ary[0]) && !ary.empty?
      ary = ary.zip(res).map{|a, r| Eq.new(a, r)}
    end
    self.replace(ary)
  end

  def sort_by_zeros
    self.sort do |a, b|
      a.lead_zero <=> b.lead_zero
    end
  end

  def subtract_eq(eq)
    pos = eq.first_pos
    return self if not pos
    EqSys.new(self.map{|e| e[pos] == 1 ? e.subtract(eq) : e})
  end

  def elim
    front = EqSys.new([])
    cur = nil
    rest = self.sort_by_zeros
    while not rest.empty?
      front << cur if cur
      cur = rest.shift

      front = front.subtract_eq(cur)
      rest = rest.subtract_eq(cur)
      rest = rest.sort_by_zeros
    end
    front << cur

    front
  end

  def to_s
    self.map{|e| e.map(&:to_s).join + " = #{e.res}"}.join("\n")
  end
end

require 'pp'
if __FILE__ == $0
  ns = names(0x6638623261336134)

  pv = ns.pivot
  es = EqSys.new(pv, 1.bits64)

  puts es.to_s
  puts

  r = es.elim
  puts r.to_s

  res = r.map{|e| e.res}.reverse
  puts res.bitary_to_int.to_s(16)

  s0 = r.pivot.last.reverse
  puts s0.bitary_to_int.to_s(16)
  puts (res.bitary_to_int^s0.bitary_to_int).to_s(16)
end

#3 Hackquest