diff --git "a/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/common.ts" "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/common.ts" new file mode 100644 index 0000000000000000000000000000000000000000..bbffafa17951df6d44136953e54fd9d777332f68 --- /dev/null +++ "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/common.ts" @@ -0,0 +1,16 @@ + +function logH1(title: String) { + console.log(` +================================================================= +================================ ${title} +================================================================= +`) +} + +function logH2(title: String) { + console.log(` +================================ ${title} +`) +} + +export { logH1, logH2 } // 导出函数 diff --git "a/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/index.ts" "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/index.ts" new file mode 100644 index 0000000000000000000000000000000000000000..38ac973330b9f75beddc16a4e2d7689b37cb7276 --- /dev/null +++ "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/index.ts" @@ -0,0 +1,234 @@ +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() diff --git "a/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/snippets.ts" "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/snippets.ts" new file mode 100644 index 0000000000000000000000000000000000000000..9dffec23ce46285cfdd24bcb5218093670951bf5 --- /dev/null +++ "b/course/frida/02_\345\270\270\350\247\201API\347\244\272\344\276\213\345\217\212\345\212\237\350\203\275\345\207\275\346\225\260\345\260\201\350\243\205\357\274\210snippets\357\274\211/snippets.ts" @@ -0,0 +1,137 @@ + +// Converts an integer (unicode value) to a char +function itoa(i: number) +{ + return String.fromCharCode(i); +} +// Converts a char into to an integer (unicode value) +function atoi(a: { charCodeAt: () => any; }) +{ + return a.charCodeAt(); +} +// i to hex +function itohex(i: { toString: (arg0: number) => any; }) +{ + var s = i.toString(16); + s = ('00000000' + s).slice(-8); + + return [s.slice(6, 8), s.slice(4, 6), s.slice(2, 4), s.slice(0, 2)].join(' '); +} +// itohex(0x1122334); +function str2hex(s: string | any[]) { // buffer is an ArrayBuffer + // for each element, we want to get its two-digit hexadecimal representation + const hexParts = []; + for(let i = 0; i < s.length; i++) { + // convert value to hexadecimal + const hex = atoi(s[i]).toString(16); + + // pad with zeros to length 2 + const paddedHex = ('00' + hex).slice(-2); + + // push to array + hexParts.push(paddedHex); + } + + // join all the hex values of the elements into a single string + return hexParts.join(' '); +} +// str2hex('ssssk') +function buf2hex(buffer: Iterable) { // buffer is an ArrayBuffer + // create a byte array (Uint8Array) that we can use to read the array buffer + const byteArray = new Uint8Array(buffer); + + // for each element, we want to get its two-digit hexadecimal representation + const hexParts = []; + for(let i = 0; i < byteArray.length; i++) { + // convert value to hexadecimal + const hex = byteArray[i].toString(16); + + // pad with zeros to length 2 + const paddedHex = ('00' + hex).slice(-2); + + // push to array + hexParts.push(paddedHex); + } + + // join all the hex values of the elements into a single string + return hexParts.join(' '); +} + + +// 根据类名获取虚表地址 +function GetVTableByClassName(dll_name: string, func: string) { + var module = Module.load(dll_name); + var s = ".?AV" + func + "@@"; + var results = Memory.scanSync(module.base, module.size, str2hex(s)); + + // class GxxLogin::LoginAccountPage `RTTI Type Descriptor' + // var nRTTITypeDescriptor = eval(results[0].address)-8; + var nRTTITypeDescriptor = results[0].address-0x08; + // MyLogD(results[0].address, itohex(nRTTITypeDescriptor)); + results = Memory.scanSync(module.base, module.size, itohex(nRTTITypeDescriptor)); + // MyLogD(JSON.stringify(results)); + + // RTTI Complete Object Locator + var nRTTI_COL = eval(results[0].address) - 0x0c; + // MyLogD(nRTTI_COL, itohex(nRTTI_COL)); + results = Memory.scanSync(module.base, module.size, itohex(nRTTI_COL)); + // MyLogD(JSON.stringify(results), eval(results[0].address), eval(results[0].address)-0+0x04); + return eval(results[0].address)-0+0x04; +} +// MyLogD(GetVTable('LoginAccountPage@GxxLogin')); + + +// 通过对象获取虚表 +// .?AVContextualErrorEdit@Phoenix@@ 战网的 accountNameEdit 、 passwordEdit 都是这个类实现的 +// .data:1164F34C ; public class Phoenix::ContextualErrorEdit /* mdisp:0 */ : +// .data:1164F34C ; public class QWidget /* mdisp:0 */ : +// .data:1164F34C ; public class QObject /* mdisp:0 */, +// .data:1164F34C ; public class QPaintDevice /* mdisp:8 */ +// .data:1164F34C ; class Phoenix::ContextualErrorEdit `RTTI Type Descriptor' +// .data:1164F34C ??_R0?AVContextualErrorEdit@Phoenix@@@8 dd offset ??_7type_info@@6B@ +function GetVTableNameByObj(obj: string | number | NativePointer) { + // TODO: + var pCol = ptr(obj).readPointer().sub(4).readPointer(); + var pTypeDescriptor = pCol.add(4*3).readPointer(); + return pTypeDescriptor.add(4*2).readCString() +} + +console.log('GetVTableNameByObj =', GetVTableNameByObj(ptr('0x821be68'))); + +// demo() { +// let address = Module.getExportByName("User32.dll", "MessageBoxW"); +// let m2 = Memory.alloc(Process.pageSize); +// let asm = new X86Writer(m2); +// asm.putPushU32(0x00000001); +// asm.putPushU32(2); +// asm.putPushU32(3); +// asm.putPushU32(4); +// asm.putCallAddress(address); +// asm.putRet(); +// asm.putPushReg("eax"); +// asm.flush(); + +// // this.show_asm(m2); +// this.show_asm(ptr(0x01003E21)); +// } + +function show_asm(start: NativePointer, length: number = 10) { + for (let index = 0; index < length; index++) { + let inst = Instruction.parse(start); + // console.log(JSON.stringify(inst)); + let byteArray = start.readByteArray(inst.size); + let byteCode = Array.prototype.slice.call(new Uint8Array(byteArray!)); + let mCode = byteCode.map(x => x.toString(16).padStart(2, "0")).join(" ").toUpperCase(); + console.log(inst.address.toString().toUpperCase().replace("0X", "0x"), mCode.padEnd(14, " "), "\t", inst.toString().toUpperCase().replace("0X", "0x")); + + start = inst.next; + if (start.readU32() == 0) break; + } +} + +function testSnippets() { + let address = Module.getExportByName("User32.dll", "MessageBoxW"); + show_asm(address); +} + +export { testSnippets }; \ No newline at end of file diff --git a/course/frida/common/qt5/Qt5Core.ts b/course/frida/common/qt5/Qt5Core.ts new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/course/frida/common/qt5/Qt5Widgets.ts b/course/frida/common/qt5/Qt5Widgets.ts new file mode 100644 index 0000000000000000000000000000000000000000..7cfa58887c7ea3bae0e6555bed7bc72953f8bcdc --- /dev/null +++ b/course/frida/common/qt5/Qt5Widgets.ts @@ -0,0 +1,235 @@ + +// using fnQApplication_topLevelWidgets = QListData * (_cdecl*)(void*); +var fnQApplication_topLevelWidgets = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?topLevelWidgets@QApplication@@SA?AV?$QList@PAVQWidget@@@@XZ'), + 'pointer', + ['pointer'], + 'mscdecl' +); + +// ?x@QWidget@@QBEHXZ +// 符号(已解码)=public: int __thiscall QWidget::x(void)const +var fnQWidget_x = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?x@QWidget@@QBEHXZ'), + 'int', + ['pointer'], + 'thiscall' +); +// ?y@QWidget@@QBEHXZ +// 符号(已解码)=public: int __thiscall QWidget::y(void)const +var fnQWidget_y = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?y@QWidget@@QBEHXZ'), + 'int', + ['pointer'], + 'thiscall' +); +// ?mapFromGlobal@QWidget@@QBE?AVQPoint@@ABV2@@Z +// 符号(已解码)=public: class QPoint __thiscall QWidget::mapFromGlobal(class QPoint const &)const +var fnQWidget_mapFromGlobal = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?mapFromGlobal@QWidget@@QBE?AVQPoint@@ABV2@@Z'), + 'pointer', + ['pointer', 'pointer', 'pointer'], + 'thiscall' +); +// ?mapToGlobal@QWidget@@QBE?AVQPoint@@ABV2@@Z +// 符号(已解码)=public: class QPoint __thiscall QWidget::mapToGlobal(class QPoint const &)const +var fnQWidget_mapToGlobal = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?mapToGlobal@QWidget@@QBE?AVQPoint@@ABV2@@Z'), + 'pointer', + ['pointer', 'pointer', 'pointer'], + 'thiscall' +); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 获取相对于顶层窗口的坐标 +function ez_fnQWidget_pos(obj) { + var qpoint_param = Memory.alloc(8); + try { + var ret = fnQWidget_pos(obj, qpoint_param); + var x = ret.add(0).readS32(); + var y = ret.add(4).readS32(); + // MyLogD('\t\t ez_fnQWidget_pos', qpoint_param.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_pos', error); + } + + return ret; +} +function ez_fnQWidget_mapFromGlobal(obj, point) { + var ret; + try { + var ret2 = Memory.alloc(8); + ret = fnQWidget_mapFromGlobal(obj, ret2, point); + var x = ret.add(0).readS32(); + var y = ret.add(4).readS32(); + MyLogD('\t\t ez_fnQWidget_mapFromGlobal', ret2.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapFromGlobal', error, ret); + } + + return ret; +} +function ez_fnQWidget_mapFromGlobal_x(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + if (pos2) { + return pos2.readU32(); + } + } catch (error) { + + } + return -1; +} +function ez_fnQWidget_mapFromGlobal_y(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + if (pos2) { + return pos2.add(4).readU32(); + } + } catch (error) { + + } + return -2; +} +function ez_fnQWidget_mapFromGlobal_xy(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + // MyLogD('【 111 】 ez_fnQWidget_mapFromGlobal_xy', pos); + if(pos) { + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + // MyLogD('【 222 】 ez_fnQWidget_mapFromGlobal_xy', pos2); + if (pos2) { + // MyLogD('【 333 】 ez_fnQWidget_mapFromGlobal_xy', pos2); + return `${pos2.readS32()},${pos2.add(4).readS32()}`; + } + } + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapFromGlobal_xy', error); + } + return `-1,-2`; +} +function ez_fnQWidget_mapToGlobal(obj, point) { + var ret2 = Memory.alloc(8); + try { + var ret = fnQWidget_mapToGlobal(obj, point, ret2); + // var x = ret.add(0).readS32(); + // var y = ret.add(4).readS32(); + // MyLogD('\t\t ez_fnQWidget_mapToGlobal', ret2.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapToGlobal', error); + } + return ret2; +} +function ez_fnQWidget_mapToGlobal_xy(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + // MyLogD('【 111 】 ez_fnQWidget_mapToGlobal_xy', pos); + if(pos) { + var pos2 = ez_fnQWidget_mapToGlobal(obj, pos); + // MyLogD('【 222 】 ez_fnQWidget_mapToGlobal_xy', pos2); + if (pos2) { + // MyLogD('【 333 】 ez_fnQWidget_mapToGlobal_xy', pos2); + return `${pos2.readS32()},${pos2.add(4).readS32()}`; + } + } + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapToGlobal_xy', error); + } + return `-1,-2`; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// ?isVisible@QWidget@@QBE_NXZ +// public: bool __thiscall QWidget::isVisible(void)const +var fnQWidget_isVisible = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?isVisible@QWidget@@QBE_NXZ'), + 'bool', + ['pointer'], + 'thiscall' +); +// public: class QPoint __thiscall QWidget::pos(void)const +var fnQWidget_pos = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?pos@QWidget@@QBE?AVQPoint@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'thiscall' +); +// public: class QSize __thiscall QWidget::size(void)const +var fnQWidget_size = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?size@QWidget@@QBE?AVQSize@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'thiscall' +); + + + +// public: void __thiscall QLineEdit::setText(class QString const &) +var fnQLineEdit_setText = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setText@QLineEdit@@QAEXABVQString@@@Z'), + 'void', + ['pointer', 'pointer'], + 'thiscall' +); +// public: void __thiscall QLineEdit::textChanged(class QString const &) +var fnQLineEdit_textChanged = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?textChanged@QLineEdit@@QAEXABVQString@@@Z'), + 'void', + ['pointer', 'pointer'], + 'thiscall' +); +// public: void __thiscall QCheckBox::setCheckState(enum Qt::CheckState) +var fnQCheckBox_setCheckState = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setCheckState@QCheckBox@@QAEXW4CheckState@Qt@@@Z'), + 'void', + ['pointer', 'int'], + 'thiscall' +); +// public: void __thiscall QAbstractButton::setChecked(bool) +var fnQAbstractButton_setChecked = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setChecked@QAbstractButton@@QAEX_N@Z'), + 'void', + ['pointer', 'bool'], + 'thiscall' +); +// 符号(已解码)=public: void __thiscall QAbstractButton::click(void) +var fnQAbstractButton_click = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?click@QAbstractButton@@QAEXXZ'), + 'void', + ['pointer'], + 'thiscall' +); +// fnQAbstractButton_click(ptr(my_read_u32(ptr(0xee7178).add(0x70)))) + +// 符号=?text@QAbstractButton@@QBE?AVQString@@XZ +// 符号(已解码)=public: class QString __thiscall QAbstractButton::text(void)const +var fnQAbstractButton_text = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?text@QAbstractButton@@QBE?AVQString@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'thiscall' +); +function ez_fnQAbstractButton_text(obj) { + var s = ez_fnQString_fromUtf8('') + fnQAbstractButton_text(obj, s); + return QString2JsString(s) +} + + + +// public: class QString __thiscall QLabel::text(void)const +var fnQLabel_text = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?text@QLabel@@QBE?AVQString@@XZ'), + 'void', + ['pointer','pointer'], + 'thiscall' +); +function ez_fnQLabel_text(obj) { + // var retQString = Memory.alloc(Process.pointerSize); + var retQString = ez_fnQString_fromUtf8(''); + fnQLabel_text(obj, ptr(retQString)); + MyLogD(retQString); + return my_read_u32_to_obj(retQString).add(0x10).readUtf16String(); +} diff --git a/course/frida/common/qt5/utils.ts b/course/frida/common/qt5/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba3ba05cdb841dfad5c62b084a88461af8bf3d38 --- /dev/null +++ b/course/frida/common/qt5/utils.ts @@ -0,0 +1,599 @@ + +function QString2JsString(qstr) { + return ptr('0x'+qstr.readU64().toString(16)).add(0x10).readUtf16String(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Qt5Core.dll +////////////////////////////////////////////////////////////////////////////////////////// +/* +地址=00007FFF68C67E60 +类型=导出 +序号=3828 +符号=?fromUtf8@QString@@SA?AV1@PEBDH@Z +符号(已解码)=public: static class QString __cdecl QString::fromUtf8(char const * __ptr64,int) + +地址=00007FFF68C64730 +类型=导出 +序号=1121 +符号=??4QString@@QEAAAEAV0@PEBD@Z +符号(已解码)=public: class QString & __ptr64 __cdecl QString::operator=(char const * __ptr64) __ptr64 + +地址=00007FFF68E65690 +类型=导出 +序号=5223 +符号=?objectName@QObject@@QEBA?AVQString@@XZ +符号(已解码)=public: class QString __cdecl QObject::objectName(void)const __ptr64 + +*/ +// console.log('fnQString_fromUtf8 = ', Module.findExportByName('Qt5Core.dll', '?fromUtf8@QString@@SA?AV1@PBDH@Z')); + +// 返回值大于4字节,当做第一个参数传递进去了!!! +// public: static class QString __cdecl QString::fromUtf8(char const *,int) +var fnQString_fromUtf8 = new NativeFunction( + Module.findExportByName('Qt5Core.dll', '?fromUtf8@QString@@SA?AV1@PEBDH@Z'), + 'void', + ['pointer','pointer', 'int'], + 'win64' +); +function ez_fnQString_fromUtf8(jsStr){ + var retQString = Memory.alloc(Process.pointerSize); + var cStrPointer = Memory.allocUtf8String(jsStr); + fnQString_fromUtf8(retQString, cStrPointer, -1); + return retQString; +} +var fn_QString_operator_asign_char = new NativeFunction( + Module.findExportByName('Qt5Core.dll', '??4QString@@QEAAAEAV0@PEBD@Z'), + 'void', + ['pointer','pointer'], + 'win64' +); +function ez_fn_QString_operator_asign_char(obj, jsStr) { + var cStrPointer = Memory.allocUtf8String(jsStr); + fn_QString_operator_asign_char(obj, cStrPointer); +} + +// using fnQObject_objectName = void* (__thiscall*)(void* pthis, void* qstr); +var fnQObject_objectName = new NativeFunction( + Module.findExportByName('Qt5Core.dll', '?objectName@QObject@@QEBA?AVQString@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'win64' +); +function ez_fnQObject_objectName(obj) { + var p = Memory.alloc(8); + p.writeU32(0); + fnQObject_objectName(obj, p); + console.log('ez_fnQObject_objectName >>> ', p.toString(16)) + console.log(hexdump(p, { + offset: 0, + length: 0x80, + header: true, + ansi: false + })); + return p; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// Qt5Widgets - QWidget +////////////////////////////////////////////////////////////////////////////////////////// +/* +地址=00007FFF60F7AEC0 +类型=导出 +序号=8298 +符号=?topLevelWidgets@QApplication@@SA?AV?$QList@PEAVQWidget@@@@XZ +符号(已解码)=public: static class QList __cdecl QApplication::topLevelWidgets(void) + +地址=00007FFF60FB2670 +类型=导出 +序号=9029 +符号=?x@QWidget@@QEBAHXZ +符号(已解码)=public: int __cdecl QWidget::x(void)const __ptr64 + +地址=00007FFF60FB26E0 +类型=导出 +序号=9037 +符号=?y@QWidget@@QEBAHXZ +符号(已解码)=public: int __cdecl QWidget::y(void)const __ptr64 + +地址=00007FFF60FA0B80 +类型=导出 +序号=4220 +符号=?mapFromGlobal@QWidget@@QEBA?AVQPoint@@AEBV2@@Z +符号(已解码)=public: class QPoint __cdecl QWidget::mapFromGlobal(class QPoint const & __ptr64)const __ptr64 + +地址=00007FFF60FA0CF0 +类型=导出 +序号=4264 +符号=?mapToGlobal@QWidget@@QEBA?AVQPoint@@AEBV2@@Z +符号(已解码)=public: class QPoint __cdecl QWidget::mapToGlobal(class QPoint const & __ptr64)const __ptr64 + +地址=00007FFF60F74AE0 +类型=导出 +序号=3953 +符号=?isVisible@QWidget@@QEBA_NXZ +符号(已解码)=public: bool __cdecl QWidget::isVisible(void)const __ptr64 + +地址=00007FFF60FA2DE0 +类型=导出 +序号=5050 +符号=?pos@QWidget@@QEBA?AVQPoint@@XZ +符号(已解码)=public: class QPoint __cdecl QWidget::pos(void)const __ptr64 + +地址=00007FFF60F84180 +类型=导出 +序号=7641 +符号=?size@QWidget@@QEBA?AVQSize@@XZ +符号(已解码)=public: class QSize __cdecl QWidget::size(void)const __ptr64 + + + + +*/ +// using fnQApplication_topLevelWidgets = QListData * (_cdecl*)(void*); +var fnQApplication_topLevelWidgets = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?topLevelWidgets@QApplication@@SA?AV?$QList@PEAVQWidget@@@@XZ'), + 'pointer', + ['pointer'], + 'win64' +); + +var fnQWidget_x = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?x@QWidget@@QEBAHXZ'), + 'int', + ['pointer'], + 'win64' +); +var fnQWidget_y = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?y@QWidget@@QEBAHXZ'), + 'int', + ['pointer'], + 'win64' +); +var fnQWidget_mapFromGlobal = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?mapFromGlobal@QWidget@@QEBA?AVQPoint@@AEBV2@@Z'), + 'pointer', + ['pointer', 'pointer', 'pointer'], + 'win64' +); +var fnQWidget_mapToGlobal = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?mapToGlobal@QWidget@@QEBA?AVQPoint@@AEBV2@@Z'), + 'pointer', + ['pointer', 'pointer', 'pointer'], + 'win64' +); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 获取相对于顶层窗口的坐标 +function ez_fnQWidget_pos(obj) { + var qpoint_param = Memory.alloc(8); + try { + var ret = fnQWidget_pos(obj, qpoint_param); + var x = ret.add(0).readS32(); + var y = ret.add(4).readS32(); + // MyLogD('\t\t ez_fnQWidget_pos', qpoint_param.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_pos', error); + } + + return ret; +} +function ez_fnQWidget_mapFromGlobal(obj, point) { + var ret; + try { + var ret2 = Memory.alloc(8); + ret = fnQWidget_mapFromGlobal(obj, ret2, point); + var x = ret.add(0).readS32(); + var y = ret.add(4).readS32(); + MyLogD('\t\t ez_fnQWidget_mapFromGlobal', ret2.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapFromGlobal', error, ret); + } + + return ret; +} +function ez_fnQWidget_mapFromGlobal_x(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + if (pos2) { + return pos2.readU64(); + } + } catch (error) { + + } + return -1; +} +function ez_fnQWidget_mapFromGlobal_y(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + if (pos2) { + return pos2.add(4).readU64(); + } + } catch (error) { + + } + return -2; +} +function ez_fnQWidget_mapFromGlobal_xy(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + // MyLogD('【 111 】 ez_fnQWidget_mapFromGlobal_xy', pos); + if(pos) { + var pos2 = ez_fnQWidget_mapFromGlobal(obj, pos); + // MyLogD('【 222 】 ez_fnQWidget_mapFromGlobal_xy', pos2); + if (pos2) { + // MyLogD('【 333 】 ez_fnQWidget_mapFromGlobal_xy', pos2); + return `${pos2.readS32()},${pos2.add(4).readS32()}`; + } + } + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapFromGlobal_xy', error); + } + return `-1,-2`; +} +function ez_fnQWidget_mapToGlobal(obj, point) { + var ret2 = Memory.alloc(8); + try { + var ret = fnQWidget_mapToGlobal(obj, point, ret2); + // var x = ret.add(0).readS32(); + // var y = ret.add(4).readS32(); + // MyLogD('\t\t ez_fnQWidget_mapToGlobal', ret2.readU64().toString(16), ',x=', x, ',y=', y); + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapToGlobal', error); + } + return ret2; +} +function ez_fnQWidget_mapToGlobal_xy(obj) { + try { + var pos = ez_fnQWidget_pos(obj); + // MyLogD('【 111 】 ez_fnQWidget_mapToGlobal_xy', pos); + if(pos) { + var pos2 = ez_fnQWidget_mapToGlobal(obj, pos); + // MyLogD('【 222 】 ez_fnQWidget_mapToGlobal_xy', pos2); + if (pos2) { + // MyLogD('【 333 】 ez_fnQWidget_mapToGlobal_xy', pos2); + return `${pos2.readS32()},${pos2.add(4).readS32()}`; + } + } + } catch (error) { + MyLogE('【catch】 ez_fnQWidget_mapToGlobal_xy', error); + } + return `-1,-2`; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +var fnQWidget_isVisible = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?isVisible@QWidget@@QEBA_NXZ'), + 'bool', + ['pointer'], + 'win64' +); +var fnQWidget_pos = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?pos@QWidget@@QEBA?AVQPoint@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'win64' +); +var fnQWidget_size = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?size@QWidget@@QEBA?AVQSize@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'win64' +); + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Qt5Widgets +// - QLineEdit +// - QCheckBox +// - QAbstractButton +// - QLabel +////////////////////////////////////////////////////////////////////////////////////////// +/* +地址=00007FFF6109A430 +类型=导出 +序号=7323 +符号=?setText@QLineEdit@@QEAAXAEBVQString@@@Z +符号(已解码)=public: void __cdecl QLineEdit::setText(class QString const & __ptr64) __ptr64 + +地址=00007FFF6109A840 +类型=导出 +序号=8169 +符号=?textChanged@QLineEdit@@QEAAXAEBVQString@@@Z +符号(已解码)=public: void __cdecl QLineEdit::textChanged(class QString const & __ptr64) __ptr64 + +地址=00007FFF61058010 +类型=导出 +序号=6254 +符号=?setCheckState@QCheckBox@@QEAAXW4CheckState@Qt@@@Z +符号(已解码)=public: void __cdecl QCheckBox::setCheckState(enum Qt::CheckState) __ptr64 + + +地址=00007FFF6104B140 +类型=导出 +序号=6261 +符号=?setChecked@QAbstractButton@@QEAAX_N@Z +符号(已解码)=public: void __cdecl QAbstractButton::setChecked(bool) __ptr64 + +地址=00007FFF61049580 +类型=导出 +序号=1850 +符号=?click@QAbstractButton@@QEAAXXZ +符号(已解码)=public: void __cdecl QAbstractButton::click(void) __ptr64 + +地址=00007FFF6104B780 +类型=导出 +序号=8145 +符号=?text@QAbstractButton@@QEBA?AVQString@@XZ +符号(已解码)=public: class QString __cdecl QAbstractButton::text(void)const __ptr64 + + +地址=00007FFF610606C0 +类型=导出 +序号=8150 +符号=?text@QLabel@@QEBA?AVQString@@XZ +符号(已解码)=public: class QString __cdecl QLabel::text(void)const __ptr64 + + +*/ +var fnQLineEdit_setText = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setText@QLineEdit@@QEAAXAEBVQString@@@Z'), + 'void', + ['pointer', 'pointer'], + 'win64' +); +var fnQLineEdit_textChanged = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?textChanged@QLineEdit@@QEAAXAEBVQString@@@Z'), + 'void', + ['pointer', 'pointer'], + 'win64' +); +var fnQCheckBox_setCheckState = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setCheckState@QCheckBox@@QEAAXW4CheckState@Qt@@@Z'), + 'void', + ['pointer', 'int'], + 'win64' +); + + +var fnQAbstractButton_setChecked = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?setChecked@QAbstractButton@@QEAAX_N@Z'), + 'void', + ['pointer', 'bool'], + 'win64' +); +var fnQAbstractButton_click = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?click@QAbstractButton@@QEAAXXZ'), + 'void', + ['pointer'], + 'win64' +); +// fnQAbstractButton_click(ptr(my_read_u64(ptr(0xee7178).add(0x70)))) +var fnQAbstractButton_text = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?text@QAbstractButton@@QEBA?AVQString@@XZ'), + 'pointer', + ['pointer', 'pointer'], + 'win64' +); +function ez_fnQAbstractButton_text(obj) { + var s = ez_fnQString_fromUtf8('') + fnQAbstractButton_text(obj, s); + return QString2JsString(s) +} + + + +var fnQLabel_text = new NativeFunction( + Module.findExportByName('Qt5Widgets.dll', '?text@QLabel@@QEBA?AVQString@@XZ'), + 'void', + ['pointer','pointer'], + 'win64' +); +function ez_fnQLabel_text(obj) { + // var retQString = Memory.alloc(Process.pointerSize); + var retQString = ez_fnQString_fromUtf8(''); + fnQLabel_text(obj, ptr(retQString)); + MyLogD(retQString); + return my_read_u64_to_obj(retQString).add(0x10).readUtf16String(); +} + + +var IsBadReadPtr = new NativeFunction( + Module.findExportByName('kernel32.dll', 'IsBadReadPtr'), + 'bool', + ['pointer', 'int'], + 'win64' +); + +function my_read_u32(addr) { + return ptr(addr.toString()).readU32() +} +function my_read_u64(addr) { + return ptr(addr.toString()).readU64() +} +function my_read_u64_to_obj(addr) { + return ptr(ptr(addr.toString()).readU64()); +} + + +function fnGetWidgetsName(obj) { + try { + if (IsBadReadPtr(obj, 4)) { + return ''; + } + if (IsBadReadPtr( + ptr( + '0x'+my_read_u64(obj.add(4)).toString(16) + ), 4 + ) + ) { + return ''; + } + if (IsBadReadPtr( + ptr(my_read_u64(obj.add(4))).add(0x1c), + 4) + ) { + return ''; + } + + var p = Memory.alloc(4); + p.writeU32(0); + fnQObject_objectName(obj, p); + // console.log(typeof p, p, p.readU64(), p.readU64().toString(16), ptr(p.readU64()).readU64().toString(16)) + // console.log(hexdump(ptr('0x'+p.readU64().toString(16)), { + // offset: 0, + // length: 0x20, + // header: true, + // ansi: false + // })); + return ptr('0x'+p.readU64().toString(16)).add(0x10).readUtf16String(); + } catch { + MyLogE('【catch】 fnGetWidgetsName'); + } + + return '' +} + +function getWidgetPosSize(obj) { + var qSzie_param = Memory.alloc(8); + try { + var qpoint_param = Memory.alloc(8); + var qpoint = fnQWidget_pos(obj, qpoint_param); + var x = qpoint.add(0).readU64(); + var y = qpoint.add(4).readU64(); + // MyLogD('\t\t', qpoint_param.readU64().toString(16), ',x=', x, ',y=', y); + + var qSzie = fnQWidget_size(obj, qSzie_param); + } catch { + MyLogE('【catch】 getWidgetPosSize'); + } + + return qSzie_param; +} + +function ez_QLineEdit_setText(obj, text) { + var qstr = ez_fnQString_fromUtf8(text); + fnQLineEdit_setText(obj, qstr); + // try { + // var qstr = ez_fnQString_fromUtf8('ez_QLineEdit_setText'); + // fnQLineEdit_setText(obj, qstr); + // } catch { + // console.log('【catch】 ez_QLineEdit_setText'); + // } +} +// ez_QLineEdit_setText(ptr(my_read_u64(ptr(0xe68a38).add(0x3c)))) + +function ez_fnQWidget_x(obj) { + try { + return fnQWidget_x(obj); + } catch (error) { + return -1; + } +} + +function ez_fnQWidget_y(obj) { + try { + return fnQWidget_y(obj); + } catch (error) { + return -1; + } +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// qt5webenginewidgets.dll / Qt5WebEngine.dll +// - QWebEnginePage +// - +////////////////////////////////////////////////////////////////////////////////////////// +/* + +地址=00007FFA9AA2CE80 +类型=导出 +序号=283 +符号=?runJavaScript@QWebEnginePage@@QEAAXAEBVQString@@@Z +符号(已解码)=public: void __cdecl QWebEnginePage::runJavaScript(class QString const & __ptr64) __ptr64 + +地址=00007FFA9AA2ADF0 +类型=导出 +序号=211 +符号=?loadFinished@QWebEnginePage@@QEAAX_N@Z +符号(已解码)=public: void __cdecl QWebEnginePage::loadFinished(bool) __ptr64 + + +WebEngineView QML Type +void runJavaScript(string script, variant callback) +地址=00007FFAAAEB43F0 +类型=导出 +序号=339 +符号=?loadFinished@QQuickWebEngineViewPrivate@@UEAAX_NAEBVQUrl@@0HAEBVQString@@@Z +符号(已解码)=public: virtual void __cdecl QQuickWebEngineViewPrivate::loadFinished(bool,class QUrl const & __ptr64,bool,int,class QString const & __ptr64) __ptr64 + +地址=00007FFAAAEB5280 +类型=导出 +序号=527 +符号=?runJavaScript@QQuickWebEngineView@@QEAAXAEBVQString@@AEBVQJSValue@@@Z +符号(已解码)=public: void __cdecl QQuickWebEngineView::runJavaScript(class QString const & __ptr64,class QJSValue const & __ptr64) __ptr64 + + + +class Q_QML_EXPORT QJSValue +{ +public: + enum SpecialValue { + NullValue, + UndefinedValue + }; +public: + QJSValue(SpecialValue value = UndefinedValue); + + ... + ... + ... +} +地址=00007FFAAAECBDD8 +类型=导入 +符号=qt5qml.??0QJSValue@@QEAA@W4SpecialValue@0@@Z +符号(已解码)=public: __cdecl QJSValue::QJSValue(enum QJSValue::SpecialValue) __ptr64 + + +地址=00007FFAA1E7C450 +类型=导入 +符号=qt5webenginecore.?runJavaScript@WebContentsAdapter@QtWebEngineCore@@QEAAXAEBVQString@@I@Z +符号(已解码)=public: void __cdecl QtWebEngineCore::WebContentsAdapter::runJavaScript(class QString const & __ptr64,unsigned int) __ptr64 + +*/ +var fnQWebEnginePage_runJavaScript = new NativeFunction( + Module.findExportByName('Qt5WebEngineWidgets.dll', '?runJavaScript@QWebEnginePage@@QEAAXAEBVQString@@@Z'), + 'void', + ['pointer', 'pointer'], + 'win64' +); +var fnQJSValue_QJSValue = new NativeFunction( + Module.findExportByName('Qt5Qml.dll', '??0QJSValue@@QEAA@W4SpecialValue@0@@Z'), + 'pointer', + ['pointer', 'int'], + 'win64' +); +function ez_gen_QJSValue_Undefined() { + var ret = Memory.alloc(Process.pointerSize); + fnQJSValue_QJSValue(ret, 1); + return ret; +} +var fnQQuickWebEngineView_runJavaScript = new NativeFunction( + Module.findExportByName('Qt5WebEngine.dll', '?runJavaScript@QQuickWebEngineView@@QEAAXAEBVQString@@AEBVQJSValue@@@Z'), + 'void', + ['pointer', 'pointer', 'pointer'], + 'win64' +); +var fnWebContentsAdapter_runJavaScript = new NativeFunction( + Module.findExportByName('Qt5WebEngineCore.dll', '?runJavaScript@WebContentsAdapter@QtWebEngineCore@@QEAAXAEBVQString@@I@Z'), + 'void', + ['pointer', 'pointer', 'int'], + 'win64' +); + diff --git a/course/frida/package.json b/course/frida/package.json index daff13a5c60f2f8620a3f9766416b4db780bbb94..5b120a7c44b185af363b8531003fe3b55cda6ff1 100644 --- a/course/frida/package.json +++ b/course/frida/package.json @@ -5,6 +5,7 @@ "main": "src/index.ts", "scripts": { "build": "frida-compile 04_frida_with_typescript/src/index.ts -o build/04.js -c", + "watch02": "frida-compile 02_常见API示例及功能函数封装(snippets)/index.ts -o build/02.js -w", "watch04": "frida-compile 04_frida_with_typescript/src/index.ts -o build/04.js -w", "watch05": "frida-compile ./05_读取棋盘数据/index.ts -o ./build/05.js -w", "watch06": "frida-compile ./06_分析扫雷游戏的数据,显示地雷位置/index.ts -o ./build/06.js -w", @@ -13,7 +14,8 @@ "watch09": "frida-compile ./09_获取软件窗口位置,设置鼠标指针位置/index.ts -o ./build/09.js -w", "watch10": "frida-compile ./10_设置鼠标位置,自动点击鼠标/index.ts -o ./build/10.js -w", "watch11": "frida-compile ./11_用鼠标自动标记棋盘上的雷区/index.ts -o ./build/11.js -w", - "runx": "D:/Python/Python371/Scripts/frida.exe -n winmine.exe -l ./build/11.js -q" + "runx": "D:/Python/Python371/Scripts/frida.exe -n winmine.exe -l ./build/02.js -q", + "runx-debug": "D:/Python/Python371/Scripts/frida.exe -n winmine.exe -l ./build/02.js --debug --runtime=v8" }, "keywords": [], "author": "",