能力值:
( LV8,RANK:120 )
|
-
-
[原创]iOS inlinehook绕过反调试
楼主的方案和frida、dobbyhook的hook很像 . 但是有一个祖传的bug一直没有解决,就是页边界问题.每个页0x4000 hook一次最少使用0x10 也就是4行代码. 如果hook的函数正好处于 0x3ffc 的时候就会崩溃.我的解决方案就是 ``` typedef struct _CopyMemoryInfo { void* hook_address; int copy_size; addr_t page_align_address; int offset; void* remap_page; }CopyMemoryInfo; // 检查是否可以hOOK if (!isHooking((size_t)function_address)) { return kCantHook; } // // 先创建一页内存, 将被hOOK的函数当前页整个拷贝进来 // 因为后面调用内核函数mach_vm_remap只能复制整页内存回去 int page_size = OSMemory::PageSize(); static mach_port_t self_port = mach_task_self(); addr_t page_align_address = ALIGN_FLOOR(function_address, page_size); int offset = static_cast<int>((addr_t)function_address - page_align_address); void* remap_page = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, VM_MAKE_TAG(255), 0); if(remap_page == NULL) { return kMemoryOperationError; } // 第一页从哪开始 - 拷贝几个字节 int curr_size = page_size - offset; if (curr_size >= 0x10) { curr_size = 0x10; } CopyMemoryInfo copy_list[2] = {{function_address, curr_size, page_align_address, offset, remap_page}, {0}}; // 这种情况下,HOOK会崩溃.因为已经在页的边界了,我们最低需求是0x10个字节. if(curr_size < 0x10) { // 下一页从哪开始 - 拷贝几个字节 void * next_address = (void*)((size_t)function_address + curr_size); copy_list[1].hook_address = next_address; copy_list[1].copy_size = 0x10 - curr_size; addr_t next_page_align_address = ALIGN_FLOOR(next_address, page_size); copy_list[1].page_align_address = next_page_align_address; copy_list[1].offset = static_cast<int>((addr_t)next_address - next_page_align_address); void* remap_page = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, VM_MAKE_TAG(255), 0); if(remap_page == NULL) { return kMemoryOperationError; } copy_list[1].remap_page = remap_page; } // new一块内存保存 old头部的0x10代码 void* oldCall = OSMemory::NewMemory(0x20); if(oldCall == NULL) { return kMemoryOperationError; } // 跳转代码长度 size_t size = (size_t)func_JumpCallEndin - (size_t)func_JumpCallBegin; // 将原始代码先保存起来 int cpyLength = 0; for(int i = 0; i < 2; i++) { if(copy_list[i].hook_address == 0) { break; } kern_return_t kr = vm_copy( self_port,(vm_address_t)copy_list[i].page_align_address, page_size, (vm_address_t)copy_list[i].remap_page); if (kr != KERN_SUCCESS) { munmap((void *)copy_list[i].remap_page, (mach_vm_address_t)page_size); return kMemoryOperationError; } // 将原始代码先保存起来 memcpy((void*)((size_t)oldCall + cpyLength), (void*)((size_t)copy_list[i].remap_page + copy_list[i].offset), copy_list[i].copy_size); cpyLength += copy_list[i].copy_size; } // // 检查前4条指令是否可以HOOK,如果可以HOOK // 从新计算被HOOK的4条指令中的相对地址,并且生成新的字节码 // 例如 // 10643ca70 : adrp x8, #0x10872a000 的字节码是 68 17 01 d0 // 如果我们原封不动的将上面的字节码拷贝出来在运行的时候 // 由于oldCall的地址并不是10643ca70取值的指针错误导致崩溃或者其他错误 // 所以要根据oldCall的地址从新计算 if(!makeOrigin(oldCall, 0x10, (size_t)function_address)) { for(int i = 0; i < 2; i++) { if(copy_list[i].remap_page != 0) { munmap((void *)copy_list[i].remap_page, (mach_vm_address_t)page_size);; } } OSMemory::SubOffset(0x20); return kCantHook; } // // 制作一个跳转回去被HOOK函数的中继函数 // 上面创建oldCall的时候是0x20字节 // 前0x10字节保存hOOK函数的头部的前0x10 // 后0x10字节保存我们跳到hook函数+0x10的地址 memcpy((void*)((size_t)oldCall + 0x10), (const void*)func_JumpCallBegin, size); *(size_t*)((size_t)oldCall + 0x10 + size) = (size_t)function_address + 0x10; *origin_call = oldCall; // 这里注意,将oldCall这一整页一定设置成 只读、执行. 否则崩溃,这个错误我排了4个小时血泪记录 mprotect(OSMemory::GetAddress(), page_size, PROT_READ | PROT_EXEC); // 设置跳转代码 char* buffer = new char[size + 8]; memset(buffer, 0, size + 8); memcpy(buffer, (void*)func_JumpCallBegin, size); *(size_t*)(buffer + 8) = (size_t)replace_call; cpyLength = 0; for(int i = 0; i < 2; i++) { if(copy_list[i].hook_address == 0) { break; } memcpy((void*)((size_t)copy_list[i].remap_page + copy_list[i].offset), (void*)((size_t)buffer + cpyLength), copy_list[i].copy_size); cpyLength += copy_list[i].copy_size; } delete [] buffer; for(int i = 0; i < 2; i++) { if(copy_list[i].hook_address == 0) { break; } mprotect((void *)copy_list[i].remap_page, page_size, PROT_READ | PROT_EXEC); mach_vm_address_t dest_page_address_ = (mach_vm_address_t)copy_list[i].page_align_address; vm_prot_t curr_protection, max_protection; kern_return_t kr = mach_vm_remap(self_port, &dest_page_address_, page_size, 0, VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self_port, (mach_vm_address_t)copy_list[i].remap_page, TRUE, &curr_protection, &max_protection, VM_INHERIT_COPY); if (kr != KERN_SUCCESS) { return kMemoryOperationError; } // unmap the origin page int err = munmap((void *)copy_list[i].remap_page, (mach_vm_address_t)page_size); if (err == -1) { return kMemoryOperationError; } }
```
另外相对地址可以使用 Capstone + Keystone帮你完成
最后于 2021-1-16 16:07
被crazyearl编辑
,原因:
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
[讨论]关于国外某游戏保护的研究
刚才抽了一会烟,凭借模糊的回忆,告诉你怎么弄的当时
OD 可以使用SOD插件
CE 可以再沙盘中打开,但是一定是原版的。
在XT中可以发现.xem后缀的进程,2个在OD附加之前都可以给他们挂起,不是关闭。是挂起
关于调试我使用的是syser 有2个办法调试
第一个是bpload Xhunter1 这样在它加载驱动的时候就断了,后面的自己想办法
第二个是把syser 处于STOP状态,就是托盘里面的syser标志被禁用标志了,在那个图片出来以后,图片下面有个进度条读完的"时候"立即开启syser并按下ctrl+f12 然后设置断点,可以使用了
最后好像对国产的驱动加载软件InstDrv也有检测.无奈使用了monitor加载驱动
另外贴上keAttachProcess的解决办法,至此我想你的困惑应该没有了
以后的工作靠自己了
//////////////////
//////////////////////////////////////////////////////////////////////
// 名称: MyKeAttachProcess
// 功能: 中继函数
// 参数:
// 返回:
//////////////////////////////////////////////////////////////////////
static NAKED VOID MyKeAttachProcess()
{
//DbgPrint("MyKeAttachProcess() called");
__asm
{
mov edi,edi
push ebp
mov ebp,esp
push esi
jmp KeAttachProcessAddress
}
}
VOID Recovery_KeAttachProcess()
{
TOPCODE *topCode = NULL;
ULONG jad;
KIRQL Irql;
//获取函数地址
KeAttachProcessAddress = (BYTE*)MyGetFunAddress(L"KeAttachProcess");
if (KeAttachProcessAddress == NULL)
{
KdPrint(("KeAttachProcess地址为空"));
return;
}
//将指针指向AttachProcessAddress函数头部
topCode = (TOPCODE*)KeAttachProcessAddress;
jad = (ULONG)topCode->address;
//设定返回地址
KeAttachProcessAddress+=6;
WPOFF(); //清除CR0
//提升IRQL中断级
Irql=KeRaiseIrqlToDpcLevel();
//RtlCopyMemory((BYTE*)jmpAddress,JmpMyAttachProcess,5);
*(ULONG*)jad = (ULONG)MyKeAttachProcess;
//恢复Irql
KeLowerIrql(Irql);
WPON(); //恢复CR0
}
|
能力值:
( LV8,RANK:120 )
|
-
-
|
能力值:
( LV8,RANK:120 )
|
-
-
|