import { logH1, logH2 } from "./common";
import { testSnippets } from "./snippets";

// Runtime information
function getRuntimeInfo() {
  logH1("Runtime information")
  // Frida
  logH2("Frida")
  console.log('Frida.version:', Frida.version)
  console.log('Frida.heapSize:', Frida.heapSize)

  // Script （QJS or V8）
  logH2("Script")
  console.log('Script.runtime:', Script.runtime)
}

// Process
function show_process() {
  logH1("Process")
  console.log("Process.id:\t\t", Process.id);
  console.log("Process.getCurrentThreadId():\t", Process.getCurrentThreadId());
  console.log("Process.arch:\t\t", Process.arch);
  console.log("Process.platform:\t", Process.platform);
  console.log("Process.pageSize:\t", Process.pageSize);
  console.log("Process.pointerSize:\t", Process.pointerSize);
  console.log("Process.codeSigningPolicy:\t", Process.codeSigningPolicy);
  // 下面三个接口， frida 15.0.18 还不支持；16.0.8 版本测试通过
  console.log("Process.getCurrentDir():\t", Process.getCurrentDir());
  console.log("Process.getHomeDir():\t", Process.getHomeDir());
  console.log("Process.getTmpDir():\t", Process.getTmpDir());

  logH2("Process.enumerateThreads")
  let threads = Process.enumerateThreads();
  for (const iterator of threads) {
    console.log(JSON.stringify(iterator));
  }

  logH2("Process.enumerateModules")
  let modules = Process.enumerateModules();
  for (const iterator of modules) {
    console.log(JSON.stringify(iterator));
  }

  logH2("Process.enumerateRanges")
  let ranges = Process.enumerateRanges("rwx");
  for (const iterator of ranges) {
    console.log(JSON.stringify(iterator));
  }

  // let mallocRanges = Process.enumerateMallocRanges();
  // for (const iterator of mallocRanges) {
  //   console.log(JSON.stringify(iterator));
  // }
}

// Thread
function show_thread() {
  logH1("Thread")
  // Thread.backtrace 有机会以后再写吧

  // Thread.sleep
  console.log("Thread.sleep(1000) start...");
  // 单位：秒、seconds
  Thread.sleep(1)
  console.log("Thread.sleep(1000) finish...");
}

// Module
function show_module() {
  logH1("Module")
  let module = Process.getModuleByName("winmine.exe");
  // let module = Process.getModuleByName("user32.dll");
  // module = Process.getModuleByName("Kernel32.dll");
  console.log("module", JSON.stringify(module, null, 4));

  logH2("Imports:");
  for (const iterator of module.enumerateImports()) {
    console.log(JSON.stringify(iterator));
  }

  logH2("Exports:");
  for (const iterator of module.enumerateExports()) {
    console.log(JSON.stringify(iterator));
  }

  logH2("Symbols:");
  for (const iterator of module.enumerateSymbols()) {
    console.log(JSON.stringify(iterator));
  }

  // enumerateRanges
  logH2("Ranges:");
  for (const iterator of module.enumerateRanges("r--")) {
    console.log(JSON.stringify(iterator));
  }

  // {"type":"function","name":"lstrlenW","address":"0x7630e0b0"}
  let p = module.findExportByName("lstrlenW");
  console.log(p);

  let p1 = Module.load("DBGHELP.DLL");
  console.log(JSON.stringify(p1));
  logH2("Exports:");
  for (const iterator of p1.enumerateExports()) {
    console.log(JSON.stringify(iterator));
  }

  logH2("Imports:");
  for (const iterator of p1.enumerateImports()) {
    console.log(JSON.stringify(iterator));
  }
}
// Memory
function show_memory() {
  logH1("Memory")
  // console.log(JSON.stringify(Memory));
  let module = Process.getModuleByName("winmine.exe");

  let p = ptr(0x00210604);
  let pattern = p.toMatchPattern();
  console.log("pattern", pattern);

  // Memory.scan(module.base, module.size, pattern, {
  Memory.scan(module.base, module.size, "04 ?? ?1 ?0", {
    onMatch: (address, size) => {
      console.log("onMatch", size, address, address.sub(module.base));
    },

    onError: (reason) => {
      console.log(reason);
    },

    onComplete: () => {
      console.log("Scan Complete!");
    }
  });
  // let matches = Memory.scanSync(module.base, module.size, pattern);
  let matches = Memory.scanSync(module.base, module.size, "04 ?? ?1 ?0");
  for (const iterator of matches) {
    console.log(JSON.stringify(iterator));
  }

  let m1 = Memory.alloc(Process.pageSize);
  console.log("protect", JSON.stringify(Process.getRangeByAddress(m1)));
  Memory.protect(m1, Process.pageSize, "r-x");
  console.log("protect", JSON.stringify(Process.getRangeByAddress(m1)));

  let lpText = Memory.allocUtf16String("This is a string！");
  let lpCaption = Memory.allocUtf16String("Caption");

  // WinApi.MessageBox(p, lpText, lpCaption, 0x00000001);

  let m2 = Memory.alloc(Process.pageSize);
  console.log("m2", m2);
  let address = Module.getExportByName("User32.dll", "MessageBoxW");

  Memory.patchCode(m2, Process.pageSize, (code) => {
    // console.log("code", code);
    let asm = new X86Writer(code);
    asm.putPushU32(0x00000001);
    asm.putPushU32(lpCaption.toUInt32());
    asm.putPushU32(lpText.toUInt32());
    // asm.putPushU32(p.toUInt32());
    asm.putPushU32(0);
    asm.putCallAddress(address);
    asm.putRet();
    asm.flush();
  });

  let func = new NativeFunction(m2, "void", []);
  func();

}

// Interceptor
function show_interceptor() {
  // DispatchMessageW
  let address = Module.getExportByName("User32.dll", "DispatchMessageW");
  // console.log(JSON.stringify(Interceptor));
  Interceptor.attach(address, {
    onEnter(this, args) {

      // console.log(this.context, this.depth, this.errno, this.lastError, this.returnAddress, this.threadId);
      console.log(JSON.stringify(this.context));

      // typedef struct tagMSG {
      //   HWND   hwnd;
      //   UINT   message;
      //   WPARAM wParam;
      //   LPARAM lParam;
      //   DWORD  time;
      //   POINT  pt;
      //   DWORD  lPrivate;
      // } MSG, *PMSG, *NPMSG, *LPMSG;                
      console.log('args[0]: ', args[0]);
      console.log(args[1]);
      console.log(args[2]);
      console.log(args[3]);
      console.log(args[4]);
      console.log('args[5]: ', args[5]);
      let msg = args[0];

      console.log("hwnd", msg.readPointer());
      console.log("message", msg.add(4).readPointer());
      console.log("wParam", msg.add(8).readPointer());
      console.log("lParam", msg.add(12).readPointer());
      console.log("pt", msg.add(20).readPointer());
      console.log("lPrivate", msg.add(24).readPointer());
    },
    onLeave(this, retval) {
      console.log(JSON.stringify(this.context));
      console.log(retval);
    },
  });
}

function main() {
  getRuntimeInfo()

  show_process()

  show_thread()

  show_module()

  // show_memory()

  // show_interceptor()

  logH1("Snippets")
  testSnippets()
}

main()
