/*## (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 SubsFaker extends SCFilter {
 private static Vector fake_ents;

 private static SubsFaker subsfaker;

 private static boolean disable_req = false;
 private static boolean faking_subs = false;

 private static byte subs_resp[];
 private static byte new_subs_resp[];

 private int res1;
 private int res2;

 static {
  fake_ents=new Vector();
  subs_resp=null;
  new_subs_resp=null;
 }

 public SubsFaker() {
  super("Subscription Faker",ST_ALL);
 }

 private boolean faking_subs() {
  return faking_subs&&(subs_resp!=null);
 }

 public boolean match(int prefix,int state,int cmd,int ptr,int buf,int size) {
  if (cmd==Conax.CMD_GET_ENTITLEMENTS) {
   if (prefix==SCInterface.IST_PRECALL) {
    switch(state) {
     case SCInterface.IST_WRITE_REQ_HDR:
     case SCInterface.IST_WRITE_RESP_REQ:
      return true;
     default:
      return false;
    }
   } else {
    switch(state) {
     case SCInterface.IST_READ_RESP_CODE1:
     case SCInterface.IST_READ_RESP_CODE2:
     case SCInterface.IST_READ_RESP_BODY:
      return true;
     default:
      return false;
    }
   }

  } else return false;
 }

 public boolean action(int prefix,int state,int cmd,int ptr,int buf,int size) {
  ApiMonitor.log("Subscription faker action "+prefix+" "+state);

  switch(prefix) {
   case SCInterface.IST_PRECALL:
    if (state==SCInterface.IST_WRITE_REQ_HDR) {
     if (disable_req) {
      subsfaker.disable();
      disable_req=false;
     }

     if (new_subs_resp!=null) {
      subs_resp=new_subs_resp;
      new_subs_resp=null;
     }
    } else

    if (state==SCInterface.IST_WRITE_RESP_REQ) {
     Memory.write_byte(buf+4,res2);
    }
    break;

   case SCInterface.IST_POSTCALL:
    if (state==SCInterface.IST_READ_RESP_CODE1) {
     res1=Memory.read_byte(buf);
    } else

    if (state==SCInterface.IST_READ_RESP_CODE2) {
     res2=Memory.read_byte(buf);

     if (faking_subs()) {
      Memory.write_byte(buf,subs_resp.length);
     }
    } else

    if (state==SCInterface.IST_READ_RESP_BODY) {
     if (faking_subs()) {
      Utils.memcpy(subs_resp,buf,subs_resp.length);

      Memory.write_byte(buf+subs_resp.length,Conax.CMD_RET_OK1);
      Memory.write_byte(buf+subs_resp.length+1,Conax.CMD_RET_OK2);

      change_res(ptr,subs_resp.length+2);
     }
    }
    break;
  }

  return true;
 }

 public static int start() {
  if (faking_subs) return SubsFakerDefs.ALREADY_STARTED;

  if (subsfaker==null) {
   subsfaker=new SubsFaker();
   SCInterface.add_filter(subsfaker);
  }

  prepare_fake_subs();

  faking_subs=true;
  subsfaker.enable();

  return SubsFakerDefs.RES_OK;
 }

 public static int stop() {
  if (!faking_subs) return SubsFakerDefs.NOT_RUNNING;

  if (subsfaker!=null) {
   disable_req=true;
   faking_subs=false;
  }

  return SubsFakerDefs.RES_OK;
 }

 public static boolean status() {
  if (subsfaker!=null) {
   return subsfaker.enabled();
  } else return false;
 }

 public static Entitlement[] get_spoofed() {
  Entitlement res[]=null;

  try {
   Entitlement curent[]=Conax.get_entitlements();

   synchronized(fake_ents) {
    for(int i=0;i<fake_ents.size();i++) {
     Entitlement fake_ent=(Entitlement)fake_ents.elementAt(i);
     curent=Entitlement.merge(curent,fake_ent);
    }
   }

   res=curent;
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return res;
 }

 private static void prepare_fake_subs() {
  Entitlement enttab[]=get_spoofed();  

  if (enttab!=null) {
   new_subs_resp=Conax.ent2buf(enttab);
  } 
 }

 public static int add(String provider,int bitmask) {
  Entitlement newent=Entitlement.create(provider,bitmask);

  synchronized(fake_ents) {
   fake_ents.addElement(newent);
  }

  prepare_fake_subs();

  return SubsFakerDefs.RES_OK;
 }

 public static void clean() {
  synchronized(fake_ents) {
   fake_ents=new Vector();
  }

  prepare_fake_subs();
 }

 public static void print() {
  try {
   synchronized(fake_ents) {
    for(int i=0;i<fake_ents.size();i++) {
     Entitlement fake_ent=(Entitlement)fake_ents.elementAt(i);
     fake_ent.print();
    }
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

}
