[SHCTF]easyLogin

附上源码,感兴趣的师傅可以看看https://github.com/SHangwendada/SHCTF-easyLogin/tree/main

https://github.com/SHangwendada/SHCTF-easyLogin/tree/main

方法1:

改机APP直接改,梭哈。

方法2:

直接看主体逻辑,其实就是发送了Username以及password还有passticket,username和password都给我们了,但是passticket并没有给我们,因此需要去逆向,首先根据下面Toast的代码告诉了我们这个可能和设备有关,这个时候需要考虑的验证是否新设备是否使用到设备id之类的。getPassticket注册于easyLogin,那么我们需要使用IDA直接分析easyLogin。这里我的设备为Arm64 因此我分析的为arm64-v8a,(注意不同设备接下来的操作可能不尽相同,但是原理逻辑基本一致)函数列表中没有发现Jni_Onload,可以确定为静态注册,静态注册直接搜索函数名字就好了:这里需要注意参数类型变换,可能需要手动更改:


可以发现这里有输出Device ID的字样。那么根据提示,我们的主要目标就是替换掉这个device id。查看一下数据包:可以发现passticket上传的并不是设备id,二十哈希之后的设备id,并且显然不是md5值。这个时候要么分析这个passticket 要么 复现这个哈希算法。看到这里基本也不会有想去逆向哈希算法的思路了。只有调试或者FridaHook了。但是程序存在hook检测主要看init Array段中的一些内容:创建了两个线程检测IDA的我们不用看,直接撸检测frida的:比较了本地和系统的libc中 signal 的前8个字符是否一致,不一致则认为被hook,这里hook点可以在v13==v12修改他们的比较结果即可,这里值的注意的是由于时机问题可以直接hook pthread_create来hook这个点位

function passAnti1() {
    Interceptor.attach(Module.getExportByName(null, "pthread_create"), {
        onEnter: function (args) {
            this.funAddr = args[2];
            var instruction = Instruction.parse(this.funAddr.add(0x2b4));
            if (instruction.mnemonic === "cmp") {
                Interceptor.attach(this.funAddr.add(0x2b4), {
                    onEnter: function (args) {
                            this.context.x25 = this.context.x24;
                    }
                });
            }
        },
        onLeave: function (retval) {
            
            console.log("pthread_create returned");
        }
    });
}

或者重定向fread:

function hook_memcmp_addr() {
    //hook反调试
    var memcmp_addr = Module.findExportByName("libc.so", "fread");
    if (memcmp_addr !== null) {
       // console.log("fread address: ", memcmp_addr);
        Interceptor.attach(memcmp_addr, {
            onEnter: function (args) {
                this.buffer = args[0];   // 保存 buffer 参数
                this.size = args[1];     // 保存 size 参数
                this.count = args[2];    // 保存 count 参数
                this.stream = args[3];   // 保存 FILE* 参数
            },
            onLeave: function (retval) {
              //  console.log(this.count.toInt32());
                if (this.count.toInt32() == 8) {
                    Memory.writeByteArray(this.buffer, [0x50, 0x00, 0x00, 0x58, 0x00, 0x02, 0x1f, 0xd6]);
                    retval.replace(8); // 填充前8字节
                  //  console.log(hexdump(this.buffer));
                }
            }
        });
    } else {
        console.log("Error: memcmp function not found in libc.so");
    }
}

最后我们的脚本还需要注意时机,这里给出完整的重定向的代码:

function hook_memcmp_addr() {
    var memcmp_addr = Module.findExportByName("libc.so", "fread");
    if (memcmp_addr !== null) {
        Interceptor.attach(memcmp_addr, {
            onEnter: function (args) {
                this.buffer = args[0];   
                this.size = args[1];     
                this.count = args[2];   
                this.stream = args[3];   
            },
            onLeave: function (retval) {
                if (this.count.toInt32() == 8) {
                    Memory.writeByteArray(this.buffer, [0x50, 0x00, 0x00, 0x58, 0x00, 0x02, 0x1f, 0xd6]);
                    retval.replace(8);
                }
            }
        });
    } else {
        console.log("Error: memcmp function not found in libc.so");
    }
}
 
function NativeHook() {
    var base = Module.getBaseAddress("libeasylogin.so");
    console.log("[Base]->", base);
 
    Interceptor.attach(base.add(0x1F250), {
        onEnter: function (args) {
            try {
                var originalStr = this.context.x0.readCString();
                console.log("Original x0:", originalStr);
                var newValue = "a24256ec5983b4a8";
 
                if (newValue.length <= originalStr.length) {
                    Memory.writeUtf8String(this.context.x0, newValue);
                 
                    console.log("Modified x0:", this.context.x0.readCString());
                } else {
                    console.warn("New string is longer than the original. Skipping write to avoid overflow.");
                }
            } catch (e) {
                console.error("Error modifying x0 content:", e);
            }
        }
    });
}
 
function Hookdlopenext() {
    hook_memcmp_addr();
    var dlopen = Module.findExportByName(null, "android_dlopen_ext");
    Interceptor.attach(dlopen, {
        onEnter: function (args) {
            var filePath = args[0].readCString();
            console.log("[android_dlopen_ext] -> ", filePath);
            if (filePath.indexOf("libeasylogin") != -1) {
                this.isCanHook = true;
            }
        }, onLeave: function (retValue) {
            if (this.isCanHook) {
                this.isCanHook = false;
                NativeHook();
            }
        }
    })
}
 
setImmediate(Hookdlopenext);

总结:

本题没有标准解,师傅们可以打开思维,需求更多的破解方式,体会Re的乐趣。