
{% extends "base/base_layout.html" %}
{% load static %}
{% block sidebar_option %}
    sidebar-collapse
{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static "others/css/terminal.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "others/css/devices.min.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "codemirror/codemirror.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "enlighterjs/enlighterjs.min.css" %}" />
<link rel="stylesheet" href="{% static "codemirror/lint.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "others/css/spinner.css" %}">
<link href="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.css" %}" rel="stylesheet">
<style>
     #stat {
        width: 100%;
        height: 170px;
        -moz-border-bottom-colors: none;
        -moz-border-left-colors: none;
        -moz-border-right-colors: none;
        -moz-border-top-colors: none;
        background: none repeat scroll 0 0 rgba(0, 0, 0, 0.07);
        border-color: -moz-use-text-color #FFFFFF #FFFFFF -moz-use-text-color;
        border-image: none;
        border-radius: 6px 6px 6px 6px;
        border-style: none solid solid none;
        border-width: medium 1px 1px medium;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12) inset;
        color: #555555;
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 1em;
        line-height: 1.4em;
        padding: 5px 8px;
        transition: background-color 0.2s ease 0s;
    }
    #stat:focus {
        background: none repeat scroll 0 0 #FFFFFF;
        outline-width: 0;
    }
    .highlight { border: solid 2px #1e6dff;}
    #code-editor{
      transform: scale(1.25) !important;
      transform-origin: top left;
      width: 80%;
      height: auto;
      overflow: auto;
      max-height: 600px;
      border: 1px solid #ddd;
      border-radius: 4px;
      padding: 10px;
      background: #f8f9fa;
      margin-bottom: 150px;
      position: relative;
      z-index: 1;
      scrollbar-width: none; /* Firefox */
      -ms-overflow-style: none; /* Internet Explorer 10+ */
    }

    #code-editor::-webkit-scrollbar {
      display: none; /* Chrome, Safari, Opera */
    }

    .CodeMirror {
        height: 610px;
        font-size: 11px;
        border: 1px solid #eee;
      }
    .form-group {
        position: relative;
        z-index: 2;
        margin-top: 20px;
    }
    .frida-pre {
      font-family: "courier new", courier, monospace;
      font-size: 11px;
    }
    #start_activity{
        margin-top: 5px;
    }
    #start_activity_form{
        margin-top: 10px;
    }

</style>

{% endblock %}
{% block content %}

<script src="{% static "adminlte/plugins/jquery.min.js" %}"></script>

<div class="content-wrapper">
  <div class="content-header">
    <div class="container-fluid">
      <div class="row mb-2">
        <div id="mobsf_header" align="center" style="padding-left: 20px;">
            <h2><strong>Dynamic Analyzer </strong>- {{ bundle_id }}</h2>
        </div>
      </div>
    </div>
  </div>
   <div class="container-fluid">

   
   <div class="row">
          <div class="col-md-12">
            <div class="card card-default">
              <!-- /.card-header -->
              <div class="card-body">
                 <div id="but" align="center">
                  <a href="#" id="screen" class="btn btn-primary" role="button"><i class="fa fa-mobile-alt"></i> Show Screen</a>
                  <a href="#" id="network" class="btn btn-primary" role="button"><i class="fa fa-certificate"></i> Start Network Capture</a>
                  <a href="{% url 'ios_live_pcap_download' %}?instance_id={{instance_id}}" id="pcap" class="btn btn-primary" role="button"><i class="fas fa-globe"></i> Download Live Packet Capture</a>
                  <a href="#" id="ss" class="btn btn-primary" role="button"><i class="fa fa-camera"></i> Take a Screenshot</a>
                  <a href="{% url 'ios_system_logs' %}?instance_id={{instance_id}}" target="_blank" id="system_logs" class="btn btn-primary"><i class="fa fa-water"></i> System Logs</a>
                  <a href="{% url 'ios_api_monitor' %}?checksum={{ checksum }}&bundle_id={{ bundle_id }}" id="apimon" target="_blank" class="btn btn-primary" style='display: none'><i class="fa fa-search"></i> Live API Monitor</a>
                  <a href="#" id="report" class="btn btn-info disabled" role="button"><i class="fa fa-file-alt"></i> Generate Report</a>
                </div>
              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->


          <div class="col-md-3">
            <div class="card card-default">
              <!-- /.card-header -->
              <div class="card-body">
                
                <div align="center">
                  <div class="marvel-device iphone5s">
                    <div class="top-bar"></div>
                    <div class="sleep"></div>
                    <div class="volume"></div>
                    <div class="camera"></div>
                    <div class="sensor"></div>
                    <div class="speaker"></div>
                  <div class="screen" id="container" tabindex="0" style="outline: none;">
                  <img width="320" height="570" src="../../download/screen/screen.png" id="my" onerror="$('#my').attr('src','/static/img/loading.jpg')" style="background-color:black;">
                  </div>
                  <div class="home" id="ios_home"></div>
                  <div class="bottom-bar"></div>
                  </div>
                </div>

                <div class="card-body">
                  <p>
                  <div class="input-group input-group-sm">
                    <input class="form-control form-control-navbar" id="ios_text" type="text" placeholder="Text Input" >
                    <div class="input-group-append">
                      <button id="ios_text_btn" class="btn btn-primary">
                        <i class="far fa-paper-plane"></i>
                      </button>
                    </div>
                  </div>
                </p>
              </div>


              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->

           <div class="col-md-4">
            <div class="card card-default">
              <!-- /.card-header -->
              <div class="card-body">

            <div class="card card-primary card-outline card-outline-tabs">
              <div class="card-header p-0 border-bottom-0">
                <ul class="nav nav-tabs" id="tabnav" role="tablist">
                  <li class="nav-item">
                    <a class="nav-link active" id="dm-tab" data-toggle="pill" href="#dm" role="tab" aria-controls="dm" aria-selected="true">Dynamic Analyzer</a>
                  </li>
                  <li class="nav-item">
                    <a class="nav-link" id="errors-tab" data-toggle="pill" href="#errors" role="tab" aria-controls="errors" aria-selected="false">Errors</a>
                  </li>
                </ul>
              </div>
              <div class="card-body">
                <div class="tab-content" id="tabs">
                  <div class="tab-pane fade active show" id="dm" role="tabpanel" aria-labelledby="dm-tab">
                  <textarea id="stat"></textarea>
                <!--box-->
                <div id="frida_options" class="box box-primary">
                    <!-- /.box-header -->
                    <!-- form start -->
                    <form role="form">
                      <div class="box-body">
                        <div class="form-group">
                            <hr/>
                        <h4> Default Frida Scripts</h4>
                             <label>
                             <input name="default_hooks" type="checkbox" value="jailbreak_bypass" checked>
                              Jailbreak Detection Bypass 
                            </label>
                            <label>
                              <input name="default_hooks" type="checkbox" value="ssl_pinning" checked>
                              SSL Pinning Bypass
                            </label>
                        </div>
                        <hr/>
                          <div class="form-group">
                      <h4> Trace Frida Scripts</h4>
                        <label>
                          <input name="dump_hooks" type="checkbox" value="network" checked>
                            Network
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="crypto" checked>
                            Crypto
                          </label>
                          <label>
                            <input name="dump_hooks" type="checkbox" value="cookies" checked>
                            Cookies 
                          </label>
                            <label>
                            <input name="dump_hooks" type="checkbox" value="file-access" checked>
                            File Access 
                          </label>
                          <label>
                            <input name="dump_hooks" type="checkbox" value="json" checked>
                            Json Data
                          </label>
                          <label>
                            <input name="dump_hooks" type="checkbox" value="sqlite" checked>
                            Sqlite Query
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="data-dir" checked>
                            Data Directory
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="keychain" checked>
                            Keychain
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="nslog" checked>
                            NSLog
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="text-inputs" checked>
                            Text Inputs
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="nsurlcredentialstorage" checked>
                            URL Credential Storage
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="nsuserdefaults" checked>
                            User Defaults
                          </label>
                          <label>
                          <input name="dump_hooks" type="checkbox" value="pasteboard" checked>
                            Pasteboard
                          </label>
                      </div>
                     <hr/>
                        <div class="form-group">
                        <h4> Auxiliary Frida Scripts</h4>
                            <label>
                            <input name="auxiliary" type="checkbox" value="enum_class" id="enum_class">
                              Enumerate Classes
                            </label> 
                            <label>
                              <input name="auxiliary" type="checkbox" value="enum_class_methods" id="enum_class_methods">
                                Enumerate Classes &amp; Methods
                              </label> 
                             <label>
                            <input name="auxiliary" type="checkbox" value="string_capture" id="string_capture">
                               Capture Strings
                            </label>
                             <label>
                            <input name="auxiliary" type="checkbox" value="string_compare" id="string_compare">
                                Capture String Comparisons
                            </label>
                            <br />
                             <label>
                              <input onclick="aux_click(this)" name="auxiliary" type="checkbox" value="enum_methods" id="enum_methods">
                              Enumerate Class Methods
                            </label>
                             <input type="text" class="form-control" id="class_name" placeholder="NSData">
                             <label>
                              <input onclick="aux_click(this)" name="auxiliary" type="checkbox" value="search_class" id="search_class">
                              Search Class Pattern
                            </label>
                             <input type="text" class="form-control" id="class_search" placeholder="Jailbreak*">
                             <label>
                              <input onclick="aux_click(this)" name="auxiliary" type="checkbox" value="search_method" id="search_method">
                              Find all classes with Specific Method
                            </label>
                             <input type="text" class="form-control" id="method_search" placeholder="MethodName">
                             <label>
                             <input onclick="aux_click(this)" name="auxiliary" type="checkbox"  value="trace_class" id="trace_class">
                              Trace Class Methods
                            </label>
                             <input type="text" class="form-control" id="class_trace" placeholder="NSURLConnection">
                        </div>

                      </div>
                      <!-- /.box-body -->
                      <hr/>
                      <div class="form-group">
                        <h4> Instrumentation </h4>
                      </div>
                      <div class="box-footer">
                      <button id="run" type="submit" class="btn btn-success btn-sm"><i class="fas fa-play"></i> Run</button>
                      <button id="frida_spawn" type="submit" class="btn btn-success btn-sm"><i class="far fa-play-circle"></i> Spawn & Inject</button>
                      <button id="frida_session" type="submit" class="btn btn-success btn-sm" disabled><i class="fas fa-redo"></i> Inject </button>
                      <button id="frida_attach" type="submit" class="btn btn-success btn-sm"><i class="fas fa-bug"></i> Attach </button>
                      <button id="frida_get" class="btn btn-primary btn-sm" hidden><i class="fas fa-code"></i> Injected Code</button>
                    </div>
                    </form>
                  </div>

              <!--tab end-->

                  </div>
                  <div class="tab-pane fade" id="errors" role="tabpanel" aria-labelledby="errors-tab">
                    <iframe sandbox frameborder="0" width="100%" height="650px" id="er"></iframe>
                  </div>
                </div>
              </div>
              <!-- /.card -->
            </div>


              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->

           <div class="col-md-5" id="codeedit">
            <div class="card card-default">
              <div class="card-header">
                <h3 class="card-title"><i class="fa fa-code"></i> Frida Code Editor </h3>
                <!-- /.card-tools -->
              </div>
              <!-- /.card-header -->
              <div class="card-body">
                <div id="code-editor">
<textarea id="code-js" rows="100">
  // Use send() for logging
</textarea>
                </div>
                <br/>
                <div class="form-group">
                  <span class="box-title">Available Scripts (Use CTRL to choose multiple)  </span>
                  <button id="loadscript" type="submit" class="btn btn-primary btn-sm">Load</button>
                </div>
                <div class="form-group">
                  <select id="fd_scs" size="10" multiple="" class="form-control">
                  </select>
                </div>
              
              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->

            <div class="col-md-12">
            <div class="card card-default">
              <!-- /.card-header -->
              <div class="card-body">
                <form style="display: flex; justify-content: flex-end">
                  <label for="id_file" class="btn btn-primary btn-sm"><i class="fas fa-file-upload"></i> Upload to Device</label>
                  <input type="file" style="visibility:hidden; width: 0%;" name="file" required="" id="id_file" onchange="uploadFile()">
                </form>
                   <div id="shell">
                    <output></output>
                    <div id="input-line" class="input-line">
                      <div class="prompt"></div><div><input id="cmdinp" class="cmdline" autofocus /></div>
                    </div>
                  </div>
              
              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->
        


          <div class="col-md-12">
            <div class="card card-default">
              <!-- /.card-header -->
              <div class="card-body">
                   <div>
                    <h4 class="page-header"><strong>Frida Logs </strong></h4>
                    <div id="messages"></br>Data refreshed in every 3 seconds.</div>
                    <pre class="frida-pre" id="frida-logs" style="font-size:15pt"></pre>
                  </div>
              </div>
              <!-- /.card-body -->
            </div>
            <!-- /.card -->
          </div>
          <!-- /.col -->


        </div>

        </div>
       </div>
     </div>
    </div>
</div>

<!--Loader-->
<div class="hidden loading">
  <div class='uil-ring-css' style='transform:scale(0.79);'>
    <div></div>
  </div>
  </div>


  <!-- Modal -->
 <div class="modal fade" id="get_code_modal" tabindex="-1" role="dialog">
  <div class="modal-dialog modal-xl">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Injected Frida Script</h4>
        <button type="button" class="close" data-dismiss="modal">&times;</button>
      </div>
      <div class="modal-body" id="injected_content">
      </div>
    </div>
  </div>
</div>



<!-- Modal -->
<div class="modal fade" id="frida_attach_mdl" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Attach to a Running Process</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <form role="form">
          <div class="form-group">
            <select id="process_select" class="form-select" aria-label="Select a process">
            </select>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button id="attach_to_pid" type="button" class="btn btn-primary"><i class="fas fa-bug"></i> Attach</button>
      </div>

    </div>
  </div>
</div>



    <!-- /.modal-content -->
  </div>
  <!-- /.modal-dialog -->
</div>
<!-- End Modal -->

{% endblock %}
{% block extra_scripts %}
<script src="{% static "codemirror/codemirror.js" %}"></script>
<script src="{% static "codemirror/javascript.js" %}"></script>
<script src="{% static "codemirror/jshint.js" %}"></script>
<script src="{% static "codemirror/lint.js" %}"></script>
<script src="{% static "codemirror/javascript-lint.js" %}"></script>
<script src="{% static "others/js/swipe.js" %}"></script>
<script src="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.js" %}"></script>
<script src="{% static "enlighterjs/enlighterjs.min.js" %}"></script>
<script>

var appContainerPath;
// Loading Spinner
function dynamic_loader(){
    var loadingOverlay = document.querySelector('.loading');
    loadingOverlay.classList.remove('hidden');
}
function stop_loader(){
    var loadingOverlay = document.querySelector('.loading');
    loadingOverlay.classList.add('hidden');
}
// Prevent screen image drag
document.getElementById('my').ondragstart = function() { return false; };

// Hide Frida Logs on page load
document.getElementById('frida-logs').style.display = 'none';

// Frida Auxiliary actions
function aux_click(cb) {
  if (cb.checked && cb.value==='search_class')
    $('#cls_search').addClass('highlight')
  else if (!cb.checked && cb.value==='search_class')
    $('#cls_search').removeClass('highlight')
  else if(cb.checked && cb.value==='search_method')
    $('#cls_method').addClass('highlight')
  else if (!cb.checked && cb.value==='search_method')
    $('#cls_method').removeClass('highlight')
  else if (cb.checked && cb.value==='enum_methods')
    $('#cls_name').addClass('highlight')
  else if (!cb.checked && cb.value==='enum_methods')
    $('#cls_name').removeClass('highlight')
  else if (cb.checked && cb.value==='trace_class')
    $('#cls_trace').addClass('highlight')
  else if (!cb.checked && cb.value==='trace_class')
    $('#cls_trace').removeClass('highlight')
}

//For All Ajax
function action(url, data, on_success){
  //Add CSRF
  data.csrfmiddlewaretoken ='{{ csrf_token }}';
  $.ajax({
    url : url, 
    type : "POST",
    dataType: "json", 
    data : data,
    success : function(json){ on_success(json) },
    error : function(xhr, ajaxOptions, thrownError) {
      document.getElementById("er").srcdoc = xhr.responseText;
    }
  });
}

// Escape
function escape(data_string) {
    var encodedStr = data_string.replace(/[\u00A0-\u9999<>\&]/gim, function (i) {
        return '&#' + i.charCodeAt(0) + ';';
    });
    return encodedStr;
}

// Escape HTML
function escapeHtml(unsafe){
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

// Frida load other scripts
function load_frida_others(){
  action('{% url 'list_frida_scripts' %}', {device: 'ios'}, function(json) {
    if (json.status == 'ok'){
        json.files.forEach(function(script) {
          $('<option>').val(script).text(script).appendTo('#fd_scs');
        });
      }
  });
}

// Code Editor
var editor = CodeMirror.fromTextArea(document.getElementById("code-js"), {
    lineNumbers: true,
    mode: "javascript",
    gutters: ["CodeMirror-lint-markers"],
    lint: true,
    lineWrapping: true
});

// Restore saved content
restore();

//Restore Code
function restore(){
  var custom_code = window.sessionStorage.getItem("code");
  if (custom_code !== null) {
    setTimeout(function(){ editor.getDoc().setValue(custom_code); }, 2000);
  }
}

// Terminal
var util = util || {};
util.toArray = function (list) {
    return Array.prototype.slice.call(list || [], 0);
};

var Terminal = Terminal || function (cmdLineContainer, outputContainer) {
    window.URL = window.URL || window.webkitURL;
    window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;

    var cmdLine_ = document.querySelector(cmdLineContainer);
    var output_ = document.querySelector(outputContainer);

    const CMDS_ = [
        'clear', 'date', 'help', 'app_path'
    ];

    var history_ = [];
    var histpos_ = 0;
    var histtemp_ = 0;
    var intro = '<h3 style="letter-spacing: 2px">Shell Access</h3><p>' + new Date() + '</p>';
    var shell = document.getElementById('shell');
    shell.addEventListener('click', function (e) {
        cmdLine_.focus();
    }, false);

    cmdLine_.addEventListener('click', inputTextClick_, false);
    cmdLine_.addEventListener('keydown', historyHandler_, false);
    cmdLine_.addEventListener('keydown', processNewCommand_, false);

    function inputTextClick_(e) {
        this.value = this.value;
    }

    function historyHandler_(e) {
        if (history_.length) {
            if (e.keyCode == 38 || e.keyCode == 40) {
                if (history_[histpos_]) {
                    history_[histpos_] = this.value;
                } else {
                    histtemp_ = this.value;
                }
            }

            if (e.keyCode == 38) { // up
                histpos_--;
                if (histpos_ < 0) {
                    histpos_ = 0;
                }
            } else if (e.keyCode == 40) { // down
                histpos_++;
                if (histpos_ > history_.length) {
                    histpos_ = history_.length;
                }
            }

            if (e.keyCode == 38 || e.keyCode == 40) {
                this.value = history_[histpos_] ? history_[histpos_] : histtemp_;
                setTimeout(() => {
                    this.setSelectionRange(this.value.length, this.value.length);
                }, 0); // Sets cursor to end of input.
            }
        }
    }

    function output(html, escape_=false, pre=false, error=false) {
        if (escape_)
            html = escape(html);
        if (pre)
         output_.insertAdjacentHTML('beforeEnd', '<pre class="clean-text">' + html + '</pre>');
        else if (error)
          output_.insertAdjacentHTML('beforeEnd', '<pre>' + html + '</pre>');
        else
         output_.insertAdjacentHTML('beforeEnd', '<p>' + html + '</p>');
    }

    function scrollToShellBottom(){
      // Scroll to shell bottom and ensure shell is completely visible
      var myElement = document.getElementById('shell');
      
      // Scroll the shell content to the bottom
      myElement.scrollTop = myElement.scrollHeight;
      
      // Ensure the shell element is visible in the viewport
      myElement.scrollIntoView({ 
        block: 'end', 
        behavior: 'smooth',
        inline: 'nearest'
      });
      
      // For dynamic content, we need to continuously monitor and scroll
      // Set up a MutationObserver to watch for content changes
      if (!window.shellObserver) {
        window.shellObserver = new MutationObserver(function(mutations) {
          // When content changes, scroll to keep shell visible
          myElement.scrollTop = myElement.scrollHeight;
          myElement.scrollIntoView({ 
            block: 'end', 
            behavior: 'smooth',
            inline: 'nearest'
          });
        });
        
        // Start observing the shell for content changes
        window.shellObserver.observe(myElement, {
          childList: true,
          subtree: true,
          characterData: true
        });
      }
    }

  
    var pwd = '/private/var/root';
    var cd = '';
    var cwd_string = '';
    const cd_regex = /cd ([^\s;&]+)/gm;
    function ssh_execute(exec_cmd){
      // CWD logic
      let m;
      while ((m = cd_regex.exec(exec_cmd)) !== null) {
          // This is necessary to avoid infinite loops with zero-width matches
          if (m.index === cd_regex.lastIndex) {
            cd_regex.lastIndex++;
          }
          
          // The result can be accessed through the `m`-variable.
          m.forEach((match, groupIndex) => {
              if (groupIndex === 1) {
                cwd_string = match;
              }
          });
      }
      new_cmd = `${cd}${exec_cmd}`;
      console.log('New Command: ' + new_cmd);
      action('{% url 'ssh_execute' %}', {instance_id: '{{ instance_id }}', cmd: new_cmd }, function(json) {
          if (json.status==="ok")
            output(json.message, true, true);

          // Logic to handle change directory
          if (exec_cmd.startsWith('cd')){
            if (!json.message.includes('No such file or directory') && !json.message.includes('Not a directory')) {
              console.log('Match: ' + cwd_string)
              if (cwd_string.startsWith('/') || cwd_string.startsWith('~')){
                // Absolute path
                pwd = cwd_string;
                cd = `cd ${cwd_string};`;
              } else if(cwd_string ==='~' || cwd_string === '-'){
                return;
              } else {
                // Relative path
                var _cd;
                if (pwd.endsWith('/')){
                  _cd = `${pwd}${cwd_string}`;
                } else {
                  _cd = `${pwd}/${cwd_string}`;
                }
                pwd = _cd;
                cd = `cd ${_cd};`;
              }
              console.log('CD: ' + cd);
              console.log('PWD: ' + pwd);
            }
          }
      });
    }

    function processNewCommand_(e) {
        if (e.keyCode == 9) { // tab
            e.preventDefault();
            // Implement tab suggest.
        } else if (e.keyCode == 13) { // enter
            // Save shell history.
            if (this.value) {
                history_[history_.length] = this.value;
                histpos_ = history_.length;
            }

            // Duplicate current input and append to output section.
            var line = this.parentNode.parentNode.cloneNode(true);
            line.removeAttribute('id')
            line.classList.add('line');
            var input = line.querySelector('input.cmdline');
            input.autofocus = false;
            input.readOnly = true;
            output_.appendChild(line);

            if (this.value && this.value.trim()) {
                var args = this.value.split(' ').filter(function (val, i) {
                    return val;
                });
                var cmd = args[0].toLowerCase();
                var exec_cmd = args.join(' ');
                args = args.splice(1); // Remove cmd from arg list.
            }

            switch (cmd) {
                case 'clear':
                    output_.innerHTML = '';
                    this.value = '';
                    output(intro);
                    return;
                case 'date':
                    output(new Date());
                    break;
                case 'help':
                    output('<div class="ls-files">' + CMDS_.join('<br>') + '</div>');
                    break;    
                case 'app_path':
                    if (appContainerPath)
                      output(appContainerPath);
                    break;                  
                default:
                   if (exec_cmd.length > 0)
                        ssh_execute(exec_cmd)
            };
            scrollToShellBottom();
            this.value = ''; // Clear/setup line for next input.
        }
    }
    return {
        init: function () {
            output(intro);
        },
        output: output
    }
};

$(document).ready(function() {

//Terminal
$('.prompt').html('[root@iPhone] # ');
var term = new Terminal('#input-line .cmdline', '#shell output');
term.init();

// Start up
load_frida_others();


print_status("Setting up MobSF Dynamic Analysis environment...");
print_status("Environment is ready for Dynamic Analysis.");

// Frida Logs Tab
setInterval( function () {
  $.ajax({
      url : '{% url 'frida_logs' %}?hash={{ checksum }}&stream=1', 
      type : "GET",
      dataType: "json", 
      success : function(json){ 
         if (json.status==="ok"){
            $('#frida-logs').text(json.message);
          } else {
            $('#frida-logs').text('')
          }
      },
      error : function(xhr, ajaxOptions, thrownError) {
        console.log(xhr.responseText);
      }
    });
}, 3000);

// Get App Container path
function getContainerPath(){
  action('{% url 'get_container_path' %}', {bundle_id: '{{ bundle_id }}'}, function(json) {
    if (json.status==="ok"){
        appContainerPath = json.message;
    }
  });
}

// Screenshot
$("#ss").click(function() {
  print_status("Taking a screenshot.");
  action('{% url 'take_screenshot' %}', {instance_id: '{{ instance_id }}', save: '1', checksum: '{{ checksum }}'}, function(json) {
    if (json.status==="ok"){
        print_status("Screenshot taken!");
        console.log(json.message)
    } else {
        print_status("Failed to take screenshot!");
        print_status(json.message);
    }
  });
  return false;  
});

// View Screen
var screen_width, screen_height;
var screenfunc;
$("#screen").click(function() {
      var stext = $("#screen").text();
      if (stext.includes("Show Screen")){
          $("#screen").html("<i class='fa fa-mobile'></i> Stop Screen");
          print_status('Screen streaming started.')
          touch = "on";
          screenfunc = setInterval(function(){
              // Capture Screen
              action('{% url 'take_screenshot' %}', {instance_id: '{{ instance_id }}'}, function(json) {
                if (json.status==="ok"){
                  if (screen_width === undefined || screen_height === undefined){
                    // Get Screen Resolution from Image
                    var i = new Image();
                    i.onload = function(){
                      screen_width = i.width;
                      screen_height = i.height;
                      console.log('Screen Resolution: ' + screen_width + 'x' + screen_height)
                      i = null;
                    };
                    i.src = json.message;
                  }
                  $('#my').attr('src', json.message);
                } else {
                  console.log(json.message)
                }
              });
          },2000);
      } else {
          $("#screen").html("<i class='fa fa-mobile-alt'></i> Show Screen");
          print_status("Screen streaming stopped.");
          $('#my').attr('src', '/static/img/loading.jpg');
          clearInterval(screenfunc);
          touch = "off";
      }
      return false;  
}); 


// Touch Events
var touch = "off";
function ios_input(event, x, y){
  if ( touch === "on"){
    action('{% url 'ios_touch' %}', { event, instance_id: '{{ instance_id }}', x, y, max_x: screen_width, max_y: screen_height}, function(json) {
        if (json.status==='ok')
          console.log('Event: ' + event);
        else
          console.log(json.message)
    });
  }
}

var xaxis, yaxis = 0;

function getXY(e, t){
    // X-Y axis for clicks
    var offset = $(t).offset();
    var x = e.pageX - offset.left;
    var y = e.pageY - offset.top;
    xaxis = Math.trunc(x * (screen_width/320)); 
    yaxis = Math.trunc(y * (screen_height/570));
    console.log('X Axis: '+ xaxis + " Y Axis: "+ yaxis);
}

// Touch Screen Click Events
var pressedTime;
var releaseTime;
$("#my").on( "mousedown", function() {
  pressedTime = new Date().getTime();
});

$("#my").on( "mouseup", function(e) {
  releaseTime = new Date().getTime();
  getXY(e, this);
  if (releaseTime - pressedTime < 300 && touch === "on") {
      ios_input('touch', xaxis, yaxis);
  }
});

// Touch Show Home screen
$('#ios_home').click(function(e) {
    ios_input('home', 0, 0);
});

// Touch Text Input
$('#ios_text_btn').click(function(e) {
  if ($('#ios_text').val()){
    // Send the text to device
    ios_input('text', $('#ios_text').val(), 0);
    $('#ios_text').val('');
  } else {
    // send the enter event
    ios_input('enter', 0, 0);
  }
});

// Keyboard Events
$('#container').on('keypress', function(event) {
    // Detect printable characters
    if (touch === "on"){
        switch(event.keyCode){
          case 13:
            // Enter
            // Send text to VM
            $('#ios_text_btn').click();
            break;
          case 32:
            // Space
            // Prevent space propagation to page scroll
            $('#ios_text').val($('#ios_text').val() + ' ');
            event.preventDefault();
            break;
          default:
          $('#ios_text').val($('#ios_text').val() + String.fromCharCode(event.keyCode));
        }
    }
 });
 $('#container').on('keydown', function(event) {
    // Detect Backspace etc.
    if (touch === "on")
    {
        switch(event.keyCode){
          case 8:
            // Backspace
            ios_input('backspace', 0, 0)
            break;
          case 37:
            // Left
            ios_input('left', 0, 0)
            break;
          case 39:
            // Left
            ios_input('right', 0, 0)
            break;
        }
    }
 });
 // Send text on enter from text box
 $('#ios_text').keypress(function(event){
  if (touch === "on"){
    var keycode = (event.keyCode ? event.keyCode : event.which);
    if(keycode === 13){
      $('#ios_text_btn').click();
    }
	event.stopPropagation();
  }
});

// Touch Swipe Events
const box = document.getElementById("my");
const classes = Swipe.directions();
const swipe = new Swipe(box, {
  corners: true,
  minDistance: 100
});

swipe.addEventListener("after", direction => {
  if (touch === "on") {
    switch(direction){
      case 'up': case 'leftup': case 'rightup':
        ios_input('swipe_down', xaxis, yaxis)
        break;
      case 'down': case 'leftdown': case 'rightdown':
        ios_input('swipe_up', xaxis, yaxis)
        break;
      case 'left':
        ios_input('swipe_right', xaxis, yaxis)
        break;
      case 'right':
        ios_input('swipe_left', xaxis, yaxis)
        break;
    }
}
});

function start_network_capture(){
  // Start Network Capture
  action('{% url 'network_capture' %}', {instance_id: '{{ instance_id }}', state: 'on'}, function(json) {
      $("#network").html("<i class='fa fa-certificate'></i> Stop Network Capture");
      print_status(json.message);
  });
}

function stop_network_capture(){
  // Stop Network Capture
  action('{% url 'network_capture' %}', {instance_id: '{{ instance_id }}', state: 'off'}, function(json) {
      $("#network").html("<i class='fa fa-certificate'></i> Start Network Capture");
      print_status(json.message);
  });
}

//Network Capture
$("#network").click(function() {
  var stext = $("#network").text();
  if (stext.includes("Start")){
    start_network_capture();
  } else {
    stop_network_capture();
  }
  return false;  
}); 

// App Run

$("#run").click(function() {
  action('{% url "run_app" %}', {instance_id: '{{ instance_id }}', bundle_id: '{{ bundle_id }}',}, function(json) {
    if (json.status==="ok"){
        print_status('Runnning application');
    } else {
        print_status('Failed to run the application');
        Swal.fire('Failed!', escape(json.message), 'error')
    }
  });
  return false;  
}); 

function enableReportBtn(seconds){
  // Enable Report button after instrumentation only
  setTimeout(function() {
    $('#report').removeClass('disabled');
  }, seconds * 1000);
}

// Load Frida Scripts
$("#loadscript").click(function() {
   var scripts = $('#fd_scs').val();
   action('{% url 'get_script' %}', {scripts: scripts, device: 'ios'}, function(json) {
    if (json.status==="ok"){
        editor.getDoc().setValue(json.content);
    }
  });
  return false;  
});

// Frida Spawn, Session and Ps
function frida_instrument(frida_action, pid=null, new_bundle_id=null){
    // Save Frida Code to Session Storage
    var code = editor.getDoc().getValue();
    window.sessionStorage.setItem('code', code);
    // Show Frida logs
    document.getElementById('frida-logs').style.display = 'block';
    // Show Frida inject button
    document.getElementById('frida_get').hidden = false;
    var default_hooks = [];
    var dump_hooks = [];

    $.each($("input[name='default_hooks']:checked"), function(){            
        default_hooks.push($(this).val());
    });
    $.each($("input[name='dump_hooks']:checked"), function(){            
        dump_hooks.push($(this).val());
    });
    if(dump_hooks.length > 0)
      $('#apimon').show();
    else
      $('#apimon').hide();
    var auxiliary_hooks = [];
    $.each($("input[name='auxiliary']:checked"), function(){            
        auxiliary_hooks.push($(this).val());
    });

    if (frida_action === 'spawn'){
      document.getElementById('frida_session').disabled = false;
      print_status("Instrumenting the application with Frida.");
    } else if (frida_action === 'session' && pid === null){
      print_status("Injecting Frida script to existing session.");
    } else if (pid){
      print_status("Attaching to PID: " + parseInt(pid));
    } else if (frida_action == 'ps'){
      print_status("Enumerating running processes.");
    } else if (frida_action == 'get'){
      print_status("Getting injected frida script.");
    }
    action('{% url "ios_instrument" %}', {
      frida_action,
      pid, // attach pid
      new_bundle_id, // attach bundle id
      hash: '{{ checksum }}',
      bundle_id: '{{ bundle_id }}',
      instance_id: '{{ instance_id}}',
      default_hooks: default_hooks.join(","),
      dump_hooks: dump_hooks.join(","),
      auxiliary_hooks: auxiliary_hooks.join(","),
      class_name: $('#class_name').val(),
      class_search:  $('#class_search').val(),
      method_search: $('#method_search').val(),
      class_trace:  $('#class_trace').val(),
      frida_code: editor.getDoc().getValue(),
    }, function(json) {
      if (json.status !== "ok")
          print_status(json.message);
      else {
        if (!appContainerPath)
          setTimeout(function(){ getContainerPath(); }, 5000);
        // Attach to a running process
        if (frida_action == 'ps' && json.message && json.message.length > 0 && Array.isArray(json.message)){
          $("#process_select").empty();
          json.message.forEach(function(p) {
            $('<option>').val(p.pid.toString() + ',' + p.identifier.toString()).text(p.pid.toString() + ': ' + p.name.toString() + ' - ' + p.identifier.toString()).appendTo('#process_select');
          });
          $('#frida_attach_mdl').modal('toggle');
        } else if (frida_action =='get'){
          var injected = document.getElementById("injected_content");
          injected.innerHTML = '<pre id="injected-pre"></pre>';
          document.getElementById("injected-pre").textContent = json.message;
          EnlighterJS.enlight( document.getElementById('injected-pre'), {
            theme: 'enlighter',
            linehover: false,
            textOverflow: 'scroll',
            language: 'javascript',
          });
          $('#get_code_modal').modal('toggle');
        }
      }
  });
}

// Frida Spawn
$("#frida_spawn").click(function() {
  start_network_capture();
  frida_instrument('spawn');
 enableReportBtn(5);
  return false;  
}); 

// Frida Session
$("#frida_session").click(function() {
  var default_hooks = "input[name='default_hooks']";
  var dump_hooks = "input[name='dump_hooks']";

  if ($(default_hooks + ":checked").length == $(default_hooks).length && $(dump_hooks + ":checked").length == $(dump_hooks).length){
    // If all default hooks and dump hooks are selected, uncheck all during injection
    $( default_hooks + ":checkbox" ).prop( "checked", false );
    $( dump_hooks + ":checkbox" ).prop( "checked", false );
  }
  

  start_network_capture();
  frida_instrument('session');
  enableReportBtn(2);
  return false;  
}); 


// Frida Get Processes to attach
$("#frida_attach").click(function() {
  frida_instrument('ps');
  return false;  
});

// Frida Attach to Pid
$("#attach_to_pid").click(function() {
  var values = $('#process_select').val();
  var pid = values.split(',')[0];
  var bundle_id = values.split(',')[1];
  start_network_capture();
  frida_instrument('session', pid, bundle_id);
  $('#frida_attach_mdl').modal('hide');
  enableReportBtn(2);
  return false;  
});

// Frida Get Injected Script
$("#frida_get").click(function() {
  frida_instrument('get');
  return false;  
});

//Finish & Generate Report
$("#report").one( "click", function() {
  dynamic_loader();
  // Stop Screen Casting if it's running
  var stext = $("#screen").text();
  if (stext.includes("Stop Screen"))
    $("#screen").click();
  // Stop packet capture
  stop_network_capture();
  // Download app data
  print_status("Downloading application data.");
  action('{% url 'ios_download_data' bundle_id=bundle_id %}', {instance_id: '{{ instance_id }}'}, function(json) {
    stop_loader();
    if (json.status==="ok"){
      // Generate Report
      print_status("Generating report");
      location.href = '{% url 'ios_view_report' bundle_id=bundle_id %}?instance_id={{ instance_id }}';
    } else {
      print_status("Failed to download application data");
    }
  });
});

//end document.ready
});


// Upload file to device
function uploadFile(){
  try {
    var file = id_file.files[0];
    if (!file){
      return;
    }
    var formdata = new FormData();
    formdata.append("file", id_file.files[0]);
    formdata.append("instance_id", '{{ instance_id }}');
    dynamic_loader();
    $.ajax({
        url: '{% url 'upload_file' %}', 
        type: "POST",
        data: formdata,
        contentType: false,
        cache: false,
        processData: false,
        beforeSend: function(request) {
          request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          request.setRequestHeader('X-CSRFToken', '{{ csrf_token }}');
        },
        success : function(json){
          stop_loader();
          if (json.status==="ok"){
            print_status("File uploaded successfully.");
            Swal.fire(
                      'Uploaded!',
                      'File uploaded to: <b>/tmp/' + escape(id_file.files[0].name) + '</b>',
                      'success')
          } else {
            print_status("Failed to upload file.");
          }
        },
        error : function(xhr, ajaxOptions, thrownError) {
          stop_loader();
          document.getElementById("er").srcdoc = xhr.responseText;
        }
      });
  } catch (e) {
      stop_loader();
      print_status("Failed to upload file: " + e);
  }
}

//Print Status
function print_status(message){
  $('#stat').append(message + '\n');
  $('#stat').scrollTop($('#stat')[0].scrollHeight);
}

</script>
{% endblock %}
