/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.util.*;

public class ELFUtils {

 public static final int ELF_MAGIC = 0x464c457f;

 public static final int ET_REL  = 1; 
 public static final int ET_EXEC = 2;
 public static final int ET_DYN  = 3;

 public static final int SHN_UNDEF     = 0;

 public static final int SHT_PROGBITS  = 1;
 public static final int SHT_SYMTAB    = 2;
 public static final int SHT_STRTAB    = 3;
 public static final int SHT_RELA      = 4;

 public static final int PT_LOAD       = 1;
 public static final int PT_DYNAMIC    = 2;

 public static final int DT_PLTRELSZ   = 2;
 public static final int DT_STRTAB     = 5;
 public static final int DT_SYMTAB     = 6;
 public static final int DT_STRSZ      = 10;
 public static final int DT_JMPREL     = 23;

 public static final int R_SH_JMP_SLOT = 0xa4;

 public static final int DEFAULT_BASE  = 0x400000;

 public static final int PAGESIZE      = 0x1000;

 int base;
 int type;
 int secnames;
 Hashtable syms;
 Hashtable got;
 boolean got_inited;

 int strtab;
 int strsize;
 int symtab;
 int symsize;
 int relatab;
 int relasize;

 String read_string(int addr,int max) {
  int len=0;

  for(int i=0;i<max;i++) {
   if (Memory.read_byte(addr+i)!=0) len++;
    else break;
  }  

  byte data[]=new byte[len];


  for(int i=0;i<len;i++) {
   data[i]=(byte)Memory.read_byte(addr+i);
  }

  return new String(data);
 }

 public ELFUtils() {
  this(DEFAULT_BASE);
 }

 private void parse_rel_image() {
  int e_shoff=Memory.read_dword(base+0x20);
  int e_shentsize=Memory.read_word(base+0x2e);
  int e_shnum=Memory.read_word(base+0x30);

  int shdr=base+e_shoff;

  int saddr[]=new int[e_shnum+1];

  symtab=strtab=0;

  int secaddr=0;

  for(int i=1;i<e_shnum;i++) {
   int sh_type=Memory.read_dword(shdr+i*e_shentsize+0x04);
   int sh_flags=Memory.read_dword(shdr+i*e_shentsize+0x08);
   int sh_size=Memory.read_dword(shdr+i*e_shentsize+0x14);

   if ((sh_type!=SHT_RELA)&&(sh_flags!=0)) {
    saddr[i]=secaddr;
   }

   if (sh_type==SHT_SYMTAB) {
    symtab=base+Memory.read_dword(shdr+i*e_shentsize+0x10)+0x10;
    symsize=sh_size;
   } else

   if (sh_type==SHT_STRTAB) {
    strtab=base+Memory.read_dword(shdr+i*e_shentsize+0x10);
    strsize=sh_size;
   }

   if ((sh_type!=SHT_RELA)&&(sh_flags!=0)) {
    int sh_addralign=Memory.read_dword(shdr+i*e_shentsize+0x20);
    secaddr+=(sh_size+(sh_addralign-1))&(~(sh_addralign-1));
   }
  }

  if ((strtab!=0)&&(symtab!=0)) {
   int symend=symtab+symsize;

   while(symtab<symend) {
    int st_name=Memory.read_dword(symtab);
    if ((st_name<0)||(st_name>symsize)) break;

    int addr=0;

    int st_shndx=Memory.read_word(symtab+0x0e);

    if (st_shndx==SHN_UNDEF) {
     addr=secaddr;
     secaddr+=4;   
    } else {
     int st_value=Memory.read_dword(symtab+0x04);
     addr=saddr[st_shndx]+st_value;
    }

    if (addr!=0) {
     String sym=read_string(strtab+st_name,0x40);
     syms.put(sym,new Integer(addr));
    }

    symtab+=0x10;
   }
  }
 }

 private void parse_exec_image(boolean islib) {
  int e_phoff=Memory.read_dword(base+0x1c);
  int e_phentsize=Memory.read_word(base+0x2a);
  int e_phnum=Memory.read_word(base+0x2c);

  int phdr=base+e_phoff;

  int dynamic=0;
  int dynamicsize=0;

  for(int i=0;i<e_phnum;i++) {
   int p_type=Memory.read_dword(phdr+i*e_phentsize);

   if (p_type==PT_DYNAMIC) {
    dynamic=Memory.read_dword(phdr+i*e_phentsize+0x08);
    dynamicsize=Memory.read_dword(phdr+i*e_phentsize+0x14);
   }
  }

  if (dynamic!=0) {
   if (islib) dynamic+=base;

   symtab=strtab=0;

   for(int i=0;i<(dynamicsize>>3);i++) {
    int d_tag=Memory.read_dword(dynamic+i*0x08);
    int d_val=Memory.read_dword(dynamic+i*0x08+0x04);

    if (d_tag==DT_STRTAB) {
     strtab=d_val;
    } else

    if (d_tag==DT_SYMTAB) {
     symtab=d_val;
    } else

    if (d_tag==DT_STRSZ) {
     symsize=d_val;
    } else

    if (d_tag==DT_JMPREL) {
     relatab=d_val;
    } else

    if (d_tag==DT_PLTRELSZ) {
     relasize=d_val;
    }

   }
  }

  if ((strtab!=0)&&(symtab!=0)) {
   int tab=symtab;

   while(true) {
    int st_name=Memory.read_dword(tab);
    if ((st_name<0)||(st_name>symsize)) break;

    int st_value=Memory.read_dword(tab+0x04);

    if (st_value!=0) {
     if (islib) st_value+=base;
     String sym=read_string(strtab+st_name,0x40);
     syms.put(sym,new Integer(st_value));
    }

    tab+=0x10;
   }

  }
 }

 public ELFUtils(int base) {
  this.base=base;
  syms=new Hashtable();
  got=new Hashtable();

  type=Memory.read_word(base+0x10);

  if (type==ET_REL) parse_rel_image();
   else
  if (type==ET_EXEC) parse_exec_image(false);
   else
  if (type==ET_DYN) parse_exec_image(true);
 } 

 public int get_sym_addr(String name) {
  Integer val=(Integer)syms.get(name);

  if (val!=null) {
   return val.intValue();
  }

  return 0;
 }

 public int get_got_addr(String name) {
  if (type==ET_EXEC) {
   Integer val=(Integer)syms.get(name);

   if (val!=null) {
    int addr=val.intValue();
    return Memory.read_dword(addr+0x14);
   }
  }

  return 0;
 }

 public static int elf_base(int addr) {
  addr&=~(PAGESIZE-1);

  while(Memory.read_dword(addr)!=ELF_MAGIC) {
   addr-=PAGESIZE; 
  }

  return addr;
 }

 public static int lib_base(String libsymbol) {
  NativeCode nc=NativeCode.getInstance();
  int addr=nc.get_got_addr(libsymbol);

  if (addr==0) {
   addr=nc.get_libc_got_addr(libsymbol);
  }

  if (addr==0) return 0;

  addr=Memory.read_dword(addr);

  return elf_base(addr);
 }

 private void init_got() {
  if (relatab!=0) {
   int pos=0;
 
   while(pos<relasize) {
    int r_offset=base+Memory.read_dword(relatab+pos);
    int r_info=Memory.read_dword(relatab+pos+0x04);
    int r_addend=Memory.read_dword(relatab+pos+0x08);

    if ((r_info&0xff)==R_SH_JMP_SLOT) {
     int sidx=r_info>>8;

     int st_name=Memory.read_dword(symtab+0x10*sidx);
     String sym=read_string(strtab+st_name,0x40);
      
     got.put(sym,new Integer(r_offset));
    }
  
    pos+=12;
   }

  }

  got_inited=true;
 }

 public int lookup_got_entry(String name) {
  if (!got_inited) init_got();

  Integer val=(Integer)got.get(name);

  if (val!=null) {
   return val.intValue();
  }

  return 0;
 }

 public static int[] get_segments(int base) {
  int e_phoff=Memory.read_dword(base+0x1c);
  int e_phentsize=Memory.read_word(base+0x2a);
  int e_phnum=Memory.read_word(base+0x2c);

  int phdr=base+e_phoff;

  int segnum=0;

  for(int i=0;i<e_phnum;i++) {
   int p_type=Memory.read_dword(phdr+i*e_phentsize);

   if (p_type==PT_LOAD) {
    segnum++;
   }
  }

  int segments[]=new int[2*segnum];

  int idx=0;

  for(int i=0;i<e_phnum;i++) {
   int p_type=Memory.read_dword(phdr+i*e_phentsize);

   if (p_type==PT_LOAD) {
    int addr=Memory.read_dword(phdr+i*e_phentsize+0x08);
    int size=Memory.read_dword(phdr+i*e_phentsize+0x14);

    segments[idx++]=base+addr;
    segments[idx++]=size;
   }
  }

  return segments;
 }
}
