/*## (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.*;

public class TkdHandler extends ApiHandler { 
 public static final int SET_PROTECTED_DESCRAMBLINGKEY = 0x02;
 public static final int CW_BASE_PTR = 0x0600;

 private static boolean initialized;

 private static Mutex tkd_access;
 private static boolean fake_ecm;

 private static int key_num;


 static {
  tkd_access=new Mutex();
 }

 public static int key_num() {
  return key_num;
 }

 public TkdHandler(String name,String function,int mid,int addr) {
  super(name,function,mid,addr);
 }

 private int reverse_bytes(int val) {
  int res=0;

  int b4=(val>>24)&0xff;
  int b3=(val>>16)&0xff;
  int b2=(val>>8)&0xff;
  int b1=(val)&0xff;

  res=(b1<<24)|(b2<<16)|(b3<<8)|(b4);

  return res;
 }

 public void pre_handler(int ptr,int cmd,int buf,int idx,int outbuf) {
  lock();

  int cw1=Memory.read_dword(buf+4);
  int cw2=Memory.read_dword(buf);

  cw2=reverse_bytes(cw2);

  ApiMonitor.log("TkdHandler cw "+Utils.hex_value(cw1,8)+" idx "+idx);

  fake_ecm=false;

  if (cw1==CWFaker.CW_FAKE_VALUE) {
   key_num=CW.map_key_idx(idx);

   fake_ecm=true;

   if (CW.cur_word()==cw2) {
    CW.cur_key_num(key_num);
   } else {
    int last_nxt_key_num=CW.nxt_key_num();

    if (last_nxt_key_num!=-1) {
     CW.cur_key_num(last_nxt_key_num);
    }

    CW.nxt_key_num(key_num);
   }

   CW.set_initialized(cw2);
   CW.set_chip_info(cw2,key_num);

   CW.decrypt_time(System.currentTimeMillis());
  }

 }

 public void post_handler(int ptr,int cmd,int buf,int idx,int outbuf,int res) {
  if (fake_ecm) {
   int off=CW_BASE_PTR+(key_num<<4);

   Memory.write_byte(outbuf+0x06,(off>>8)&0xff);
   Memory.write_byte(outbuf+0x07,(off)&0xff);

   Memory.write_byte(outbuf+0x0e,(off>>8)&0xff);
   Memory.write_byte(outbuf+0x0f,(off)&0xff);

   change_res(ptr,0); 
  }

  unlock();
 }
 
 public int pre_call(int ptr,int[] args) {
  pre_handler(ptr,args[0],args[1],args[2],args[3]);
  
  int cmd=args[0];

  if (cmd==SET_PROTECTED_DESCRAMBLINGKEY) {
   if (!fake_ecm) return INVOKE_ORIGINAL;
    else return SKIP_ORIGINAL;
  }

  return INVOKE_ORIGINAL; 
 }

 public void post_call(int ptr,int[] args,int[] res) {
  post_handler(ptr,args[0],args[1],args[2],args[3],res[0]);
 }

 public static void start() {
  if (!initialized) {
   int handle=Dl.open_flags("libstd_drv_scdc.so",Dl.RTLD_NOW|Dl.RTLD_NODELETE);
   int sym=Dl.getsym(handle,"SCDC_SetCWPK");

   int base=ELFUtils.elf_base(sym);  

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

   ELFUtils eu=new ELFUtils(base);

   int pgot=eu.lookup_got_entry("STTKDMA_DecryptKey");
   ApiMonitor.log("pgot "+Utils.hex_value(pgot,8));
  
   ApiMonitor.add("Tkd handler",pgot,"TkdHandler");

   initialized=true;
  }
 }

 public static void lock() {
  tkd_access.lock();
 }
 
 public static void unlock() {
  tkd_access.unlock();
 }

}
