/*## (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 GSecHandler extends IoctlWatch {
 public static final int IOC_GSEC_TYPE      = 0xff;

 public static final int gSecHAL_DecryptSCK = 0x06;

 private static boolean initialized;

 private static Mutex gsec_access;
 private static boolean fake_ecm;

 private static int key_num;

 static {
  gsec_access=new Mutex();
 }

 private boolean gsec_ioc_type(int cmd) {
  return ioc_type(cmd)==IOC_GSEC_TYPE;
 }

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

 public void pre_ioctl(int ptr,int fd,int cmd,int buf) {
  if (gsec_ioc_type(cmd)) {
   if (ioc_nr(cmd)==gSecHAL_DecryptSCK) {
    lock();

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

     int idx=Memory.read_byte(buf+0x10);

     fake_ecm=false;

     ApiMonitor.log("  --> gSecHAL_DecryptSCK idx "+Utils.hex_value(idx,2)+" "+Utils.hex_value(cw1,8)+" "+Utils.hex_value(cw2,8));

     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());
     }
    } catch(Throwable t) {
     ApiMonitor.log_stacktrace(t);
    }
   }
  }
 }

 public void post_ioctl(int ptr,int fd,int cmd,int buf,int res) {
  if (gsec_ioc_type(cmd)) {
   if (ioc_nr(cmd)==gSecHAL_DecryptSCK) {

    if (fake_ecm) {
     Memory.write_dword(buf+0x10,key_num);
     Memory.write_dword(buf+0x14,0);
     Memory.write_byte(buf+0x18,(key_num<<4)&0xff);
     Memory.write_byte(buf+0x17,(key_num>>4)&0xff);
     Memory.write_dword(buf+0x1c,0);

     change_res(ptr,0); 
    }

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

  if (gsec_ioc_type(cmd)) {
   if (ioc_nr(cmd)==gSecHAL_DecryptSCK) {
    if (!fake_ecm) return INVOKE_ORIGINAL;
     else return SKIP_ORIGINAL;
   }
  }

  return INVOKE_ORIGINAL;
 }

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

 public static void start() {
  if (!initialized) {
   ApiMonitor.add("GSec handler","ioctl","GSecHandler");
   initialized=true;
  }
 }

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

}
