|
前言
随着软件逆向工程技术的快速发展,针对各种软件的分析破解技术层出不穷,动态调试技术也在不断的发展中,那么在这种环境下,就非常需要反调试技术了,反调试成了一种防止破解的一种手段,很好得使用反调试,可以保护软件。接下来,给大家分享一下我实现过的反调试技术及对应的反反调式技术。
BeingDebugged判断法
1.在PEB结构中,有BeingDebugged成员,当程序处于调式状态时,该成员的值会变为1,否则为0.根据这个特性可以判断程序是否处于调式状态,fs寄存器偏移0x18处指向TEB结构,TEB结构偏移0x30处指向PEB结构,PEB结构偏移0x2处即为BeingDebugged成员,TEB和PEB结构如下。
[Asm] 纯文本查看 复制代码typedef struct _NT_TEB{ ........ PVOID ThreadLocalStoragePointer; // 2Ch PPEB Peb; // 30h push dword ptr fs:[0] ; 添加SEH异常处理器00401078 |. 64:8925 00000>mov dword ptr fs:[0],esp0040107F |. 90 nop00401080 |. 90 nop00401081 |. 90 nop00401082 |. 90 nop00401083 |. 33C0 xor eax,eax00401085 |. C700 01000000 mov dword ptr ds:[eax],0x10040108B |. 90 nop0040108C |. 0000 add byte ptr ds:[eax],al0040108E |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL00401090 |. 6A 00 push 0x0 ; |Title = NULL00401092 |. 68 20304000 push seh_add1.00403020 ; |Text = "Debugging"00401097 |. 6A 00 push 0x0 ; |hOwner = NULL00401099 E8 C2FFFFFF call 0040109E |. 64:8F05 00000>pop dword ptr fs:[0] ; 删除SEH程序004010A5 |. 83C4 04 add esp,0x4004010A8 |. 6A 00 push 0x0 ; /ExitCode = 0x0004010AA E8 B7FFFFFF call 004010AF . C3 retn004010B0 90 nop004010B1 90 nop004010B2 90 nop004010B3 90 nop004010B4 90 nop004010B5 90 nop004010B6 90 nop004010B7 90 nop004010B8 /$ 8B7424 0C mov esi,dword ptr ss:[esp+0xC] ; 结构异常处理程序; Structured exception handler004010BC |. 64:A1 3000000>mov eax,dword ptr fs:[0x30]004010C2 |. 8078 02 01 cmp byte ptr ds:[eax+0x2],0x1 ; 判断BeingDebugged的值是否为1004010C6 |. 75 0C jnz short seh_add1.004010D4 ; 没有处于调式状态时,进行跳转004010C8 |. C786 B8000000>mov dword ptr ds:[esi+0xB8],seh_add1.004>; 把EIP修改为40108e,弹出消息提示框004010D2 |. EB 0A jmp short seh_add1.004010DE004010D4 |> C786 B8000000>mov dword ptr ds:[esi+0xB8],seh_add1.004>; 把EIP修改为原OEP004010DE |> 33C0 xor eax,eax004010E0 \. C3 retn
4.在内存窗口,可以看到该汇编代码对应的shellcode。通过手动的方式添加反调试代码必然麻烦,那么就需要开发一个工具来实现seh反调试的添加功能.该工具的实现思路是:加载PE文件到内存中->在代码段处查找空白代码区,记录该区的地址-》搜索空白代码区,添加debugging字符串->计算调用函数的汇编代码-》修改shellcode,导出PE文件到磁盘中。
5.工具实现的重要的代码如下,
[C++] 纯文本查看 复制代码LPVOID CAllToolDlg::PEFileToMemory(LPSTR lpszFile){ FILE *pFile = NULL; LPVOID pFileBuffer = NULL; errno_t err; if (!(err = fopen_s(&pFile, lpszFile, "wr+"))) { printf(" 无法打开 EXE 文件! "); return NULL; } //读取文件大小 fseek(pFile, 0, SEEK_END); fileSize = ftell(pFile); fseek(pFile, 0, SEEK_SET); //分配缓冲区 pFileBuffer = malloc(fileSize); //将文件数据读取到缓冲区 size_t n = fread(pFileBuffer, fileSize, 1, pFile); if (!n) { printf(" 读取数据失败! "); free(pFileBuffer); fclose(pFile); return NULL; } //关闭文件 fclose(pFile); return pFileBuffer;}//搜索代码段空白代码DWORD CAllToolDlg::FindEmptyCodeAndModifyShellCode(){ //把exe文件加载进内存 //pFileBuffer = PEFileToMemory(strpath); DWORD* DPointer = (DWORD*)pFileBuffer; BYTE* BPointer = (BYTE*)pFileBuffer; DWORD ImageBase = *(DPointer + 59); DWORD AddrEP = *(DPointer + 56); //修改OEP的值 ModifyShellCode(ImageBase + AddrEP, 0x6E); //记录空白代码区的起始地址 DWORD BeginAddr = 0; int i = 0; //找出空白代码区的起始地址 for (int j = 0x1000; j < 0x1000/4; j++) { if (*(DPointer + j) == 0x0) { i++; if (i >= 30) break; } else { i = 0; BeginAddr = j+1; } } //AddressOfEntryPoint修改后的值 DWORD AfterAddrEP = BeginAddr*4 + 0x1000; //修改AddressOfEntryPoint *(DPointer + 56) = AfterAddrEP; //获取MessageBoxA和EXitProcess的十六进制编码并修改 DWORD MessageBoxA = AddrMessageBoxA - (0x400000 + AfterAddrEP + 0x2D + 0x5); ModifyShellCode(MessageBoxA,0x2E); DWORD EXitProcess = AddrEXitProcess - (0x400000 + AfterAddrEP + 0x3E + 0x5); ModifyShellCode(EXitProcess, 0x3F); return AfterAddrEP;}//搜索数据段空白数据void CAllToolDlg::FindEmptyDataAndAddData(){ int i = 0; DWORD BeginAddr = 0; for (int j = 0x3000; j < 0x1000/4; j++) { if (*(DPointer + j) == 0x0) { i++; if (i >= 3) break; } else { i = 0; BeginAddr = j + 1; } } //修改内存数据 for (i = 0; i < strlen(s); i++) { *(BPointer + i + BeginAddr) = s; } DWORD AddrData = 0x3000 + BeginAddr * 4 + 0x400000; ModifyShellCode(AddrData, 0x28);}void CAllToolDlg::WriterToPEFileAndDisk(DWORD Addr){ for (int i = 0; i < len; i++) { *(BPointer + i + Addr) = ShellCode; } //写入磁盘中 ofstream out("seh_add.exe", ios::out | ios::trunc); if (!out.is_open()) { cout e_lfanew)) != IMAGE_NT_SIGNATURE) { printf("不是有效的PE标志\n"); free(pFileBuffer); return; } pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); //节表数量 NumberOfSection = pPEHeader->NumberOfSections; //最后节表位置 LastSectionAddr = PEOptionalAddress + pPEHeader->SizeOfOptionalHeader + (NumberOfSection - 1) * 0x28; DWORD* DPointer = (DWORD*)pFileBuffer; //PointerToRawData值 LastPointerToRawDataV = *(DPointer + LastSectionAddr+0xC);}//修改字段值VOID CAllToolDlg::ModifyMemoryValue(){ //修改TLS表中的RVA值 *(DPointer + PEOptionalAddress + 0xA8) = LastPointerToRawDataV + 0x200 + (NumberOfSection - 1) * 0x28; //修改TLS表中的Size值,_IMAGE_TLS_DIRECTORY32结构大小 *(DPointer + PEOptionalAddress + 0xAC) = 0x18; //SizeOfRawData值加0x200 *(DPointer + LastSectionAddr + 0x8) = *(DPointer + LastSectionAddr + 0x8) + 0x200; //PointerToRawData值加0x200 *(DPointer + LastSectionAddr + 0xC) = LastPointerToRawDataV + 0x200; //修改Characteristics值 *(BPointer + LastSectionAddr + 0x1C) = 0xE0;}//给TLS结构体赋值VOID CAllToolDlg::WriterToNewTls(){ DWORD Value = 0x400000 + 0x18 + *(DPointer + PEOptionalAddress + 0xA8); *(DPointer + fileSize + 0x200) = Value; *(DPointer + fileSize + 0x204) = Value+0x4; *(DPointer + fileSize + 0x208) = Value + 0x8; *(DPointer + fileSize + 0x20C) = Value + 0xC; *(DPointer + fileSize + 0x224) = 0x400000 + *(DPointer + PEOptionalAddress + 0xA8)+0x30;}//往PE文件中写入TLS回调函数,同WriterToPEFileAndDisk
解决方案:
删除TLS回调功能
TLS表中的 RVA处存放的是TLS结构体信息,Size字段记录的是TLS结构体的大小,把这两个字段的值设成0,就能删除TLS回调函数。实现的代码如下:
[Asm] 纯文本查看 复制代码//修改TLS表中的RVA值*(DPointer + PEOptionalAddress + 0xA8) = 0;//修改TLS表中的Size值*(DPointer + PEOptionalAddress + 0xAC) = 0;再写入磁盘中,就删除了。
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|