首页
社区
课程
招聘
[原创]内存注册机的工作原理及masm源码
发表于: 2007-6-5 15:22 31110

[原创]内存注册机的工作原理及masm源码

2007-6-5 15:22
31110
收藏
免费 7
支持
分享
最新回复 (60)
雪    币: 314
活跃值: (15)
能力值: ( LV12,RANK:410 )
在线值:
发帖
回帖
粉丝
51
你说的没错,我的也没错,你看看这个:

CONTEXT STRUCT

ContextFlags dd ?
;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_DEBUG_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
iDr0 dd ?
iDr1 dd ?
iDr2 dd ?
iDr3 dd ?
iDr6 dd ?
iDr7 dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_FLOATING_POINT,返回本部分
;-----------------------------------------------------------------------------------------------------------

FloatSave FLOATING_SAVE_AREA <>

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_SEGMENTS,返回本部分
;-----------------------------------------------------------------------------------------------------------
regGs dd ?
regFs dd ?
regEs dd ?
regDs dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_INTEGER,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEdi dd ?
regEsi dd ?
regEbx dd ?
regEdx dd ?
regEcx dd ?
regEax dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_CONTROL,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEbp dd ?
regEip dd ?
regCs dd ?
regFlag dd ?
regEsp dd ?
regSs dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_EXTENDED_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) CONTEXT ENDS

你提出这样的看法说明你没用过RadAsm(当在结构型变量名后按 . 的时候它会自动提示你该结构有那些成员)。其实在定义数据结构时,成员的名字并不是太重要的,所以在VC++和RadAsm中就有了名字的不同(DWORD   Eip; 和 regEip dd ? ),但类型是相同的,这就行了。
2007-6-17 21:09
0
雪    币: 219
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
52
不错,但是多线程的问题好像没有解决。
2007-6-17 21:55
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
53
-->>invoke DialogBoxParam,eax,IDD_DLGMAIN,NULL,addr DlgProc,@ThreadConText.regEcx  

....

        .if eax==WM_INITDIALOG
                mov     ebx,lParam
                ;assume        ebx:ptr CONTEXT  ;ebx作为CONTEXT类型指针使用
                ;要显示什么数据,怎么显示,那你就去分析debugee的注册算法吧,我这里只是示例
                ;invoke ReadProcessMemory,PI.hProcess,RegKeyAddr,addr Buffer,sizeof Buffer,NULL   ;从特定内存地址中取注册码
               
                ;;ZWCAD2007
                ;invoke ReadProcessMemory,PI.hProcess,RegKeyAddr,addr Buffer,8,NULL   ;从特定内存地址中取注册码
                ;invoke wsprintf,addr Buffer,addr szFmt,ebx;[ebx].regEcx  ;从特定寄存器中取注册码
                invoke ReadProcessMemory,PI.hProcess,ebx,addr Buffer,8,NULL   ;从特定内存地址中取注册码
                ;invoke wsprintf,addr Buffer,addr szFmt,TimesBreak ;打印中断次数
                invoke SetDlgItemText,hWin,IDC_REGKEY,addr Buffer
2007-6-25 00:17
0
雪    币: 231
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
54
       恩  认真学习学习!!!
2007-6-26 13:47
0
雪    币: 8548
活跃值: (2797)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
55
认真学习,感谢分享
2007-6-30 12:25
0
雪    币: 266
活跃值: (332)
能力值: ( LV9,RANK:550 )
在线值:
发帖
回帖
粉丝
56
认真学习,感谢分享
2007-7-1 18:11
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
57
谢谢老大啊,我收藏先
2007-7-1 22:26
0
雪    币: 241
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
58
有些软件加了壳,不过我终于想出办法来对付它了

什么办法啊?
2007-8-17 10:05
0
雪    币: 277
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
59
不错 !学习中!
2007-8-18 13:35
0
雪    币: 66
活跃值: (15)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
60
偶然间在网上找到一篇同效的文章,其思路清晰代码漂亮,不敢独享,转贴上来给大家多个参考

来源:http://www.zhongts.net/MyEssay/EasyUnpack.htm


利用 Debug API 编写一个简单的脱壳机
作者: 一块三毛钱
邮箱: zhongts@163.com
日期: 2005.2.22

  脱壳的一般步骤是:查找入口点,中断在入口点,dump 进程,修复输入表。大家一般借助调试器来完成这几步。下面我就来介绍如何通过编程实现一个简单的脱壳机,自动完成上面的几个步骤。代码下载

1. 查找入口点

查找入口点可以利用现有的工具来完成,如 PEiD、PE-Scan 等。通过对 PEiD 中的 GenOEP 插件的逆向工程我们可以找到如下方法来查找入口点。这种方法的根据就是每个编译器编译出来的程序在入口点处的代码通常是一样的。比如说 VC6 编译的程序,入口点处的部分代码一般都是下面这个样子:

:00434E55 55 push ebp
:00434E56 8BEC mov ebp, esp
:00434E58 6AFF push FFFFFFFF
:00434E5A 68302E4500 push 00452E30
:00434E5F 68A83F4300 push 00433FA8
:00434E64 64A100000000 mov eax, dword ptr fs:[00000000]
:00434E6A 50 push eax
:00434E6B 64892500000000 mov dword ptr fs:[00000000], esp
其中几个被 push 的具体的值可能不同。根据这一点我们就可以在进程中查找上面这部分代码,找到的地方就是入口点。下面来看看具体的代码实现:

.data
g_Delphi_Signs db 55h, 8Bh, 0ECh, 83h, 0C4h, 0, 53h, 0B8h, 0, 0, 0, 0, 0E8h, 0, 0,
0, 0, 8Bh, 1Dh, 0, 0, 0, 0, 8Bh, 3h, 0E8h, 0, 0, 0, 0, 8Bh, 3h
g_VC6_Signs db 55h, 8Bh, 0ECh, 6Ah, 0FFh, 68h, 0, 0, 0, 0, 68h, 0, 0, 0, 0, 64h,
0A1h, 0, 0, 0, 0, 50h, 64h, 89h, 25h, 0, 0, 0, 0

.code
_GetOEP proc lpMem:DWORD, dwLen:DWORD
LOCAL dwOEP

pushad
invoke _InString, lpMem, dwLen, addr g_Delphi_Signs, 32
.if eax
jmp exit_1
.endif

invoke _InString, lpMem, dwLen, addr g_VC6_Signs, 29
.if eax
jmp exit_1
.endif

jmp exit_0

exit_1:
mov dwOEP, eax
popad
mov eax, dwOEP
ret
exit_0:
popad
xor eax, eax
ret
_GetOEP endp

_InString proc lpszStr:DWORD, dwStrLen:DWORD, lpszSubStr:DWORD, dwSubStrLen:DWORD
LOCAL dwPos

pushad
mov eax, dwStrLen
.if eax < dwSubStrLen
jmp exit_0
.endif
sub eax, dwSubStrLen
mov dwStrLen, eax

mov esi, lpszStr
mov edi, lpszSubStr
xor edx, edx

Loop1:
cmp edx, dwStrLen
jz exit_0
xor ecx, ecx
mov al, byte ptr [edi+ecx]
mov bl, byte ptr [esi+edx]
cmp al, bl
jz Loop2
inc edx
jmp Loop1

Loop2:
inc ecx
inc edx
cmp ecx, dwSubStrLen
jz exit_1
mov al, byte ptr [edi+ecx]
mov bl, byte ptr [esi+edx]
cmp al, bl
jz Loop2
test al, al
jz Loop2
sub edx, ecx
inc edx
jmp Loop1

exit_1:
sub edx, ecx
mov dwPos, edx
popad
mov eax, dwPos
ret

exit_0:
popad
xor eax, eax
ret
_InString endp
g_Delphi_Signs 和 g_VC6_Signs 分别对应 Delphi 和 VC6 编译的程序,其中的 0 代表可能不确定的字节。_GetOEP 函数就是具体获得入口点的函数,分别在进程空间中查找每一个特定的入口点特征代码,如果能找到就说明找到了入口点。查找特征代码又是由函数 _InString 来完成的,具体实现看看代码就清楚了。

2. 中断在入口点

找到了入口点后,需要中断在入口点处准备 dump 进程,通过 Windows 本身提供的 Debug API 可以实现这一点。

invoke CreateProcess, 0, addr szFile, 0, 0, 0, DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS,
0, 0, addr StartupInfo, addr ProcInfo2
.if !eax
invoke _OutputInfo, g_hOutputCtl, CTXT("不能创建进程!!!")
jmp l_exit
.endif
.while TRUE
invoke WaitForDebugEvent, addr DbgEvent, INFINITE
.if DbgEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
;下面这一行代码很重要,否则被调试进程不会完全退出
invoke ContinueDebugEvent, DbgEvent.dwProcessId, DbgEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.break
.elseif DbgEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DbgEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
inc dwCountBP
.if dwCountBP==1 ;第一次中断时在原始入口点处设置断点
invoke _OutputInfo, g_hOutputCtl, CTXT("在原始入口点设置断点...")
mov int3, 0CCh
invoke ReadProcessMemory, ProcInfo2.hProcess, dwOrgOEP, addr org_code, 1, 0
invoke WriteProcessMemory, ProcInfo2.hProcess, dwOrgOEP, addr int3, 1, 0

.elseif dwCountBP==2 ;第二次中断,这次是中断在原始入口点,在 OEP 处设置硬件断点
invoke _OutputInfo, g_hOutputCtl, CTXT("到达原始入口点")

mov g_context.ContextFlags, CONTEXT_CONTROL
invoke GetThreadContext, ProcInfo2.hThread, addr g_context
dec g_context.regEip
invoke WriteProcessMemory, ProcInfo2.hProcess, dwOrgOEP, addr org_code, 1, 0
invoke SetThreadContext, ProcInfo2.hThread, addr g_context

mov g_context.ContextFlags, CONTEXT_DEBUG_REGISTERS
invoke GetThreadContext, ProcInfo2.hThread, addr g_context
m2m g_context.iDr0, dwOEP
mov g_context.iDr7, 1
invoke SetThreadContext, ProcInfo2.hThread, addr g_context

invoke wsprintf, addr buf, CTXT("在 OEP: %08lXh 处设置硬件断点..."), dwOEP
invoke _OutputInfo, g_hOutputCtl, addr buf
.endif
invoke ContinueDebugEvent, DbgEvent.dwProcessId, DbgEvent.dwThreadId, DBG_CONTINUE
.continue
.elseif DbgEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP
;第三次中断,来到真正的入口点,抓取进程,然后终止进程
invoke wsprintf, addr buf, CTXT("中断在 OEP: %08lXh 处"), dwOEP
invoke _OutputInfo, g_hOutputCtl, addr buf
invoke _OutputInfo, g_hOutputCtl, CTXT("清除硬件断点...")

mov g_context.ContextFlags, CONTEXT_FULL
invoke GetThreadContext, ProcInfo2.hThread, addr g_context
mov g_context.iDr0, 0
mov g_context.iDr7, 0
invoke SetThreadContext, ProcInfo2.hThread, addr g_context

invoke _OutputInfo, g_hOutputCtl, CTXT("抓取进程...")
invoke _Dump, ProcInfo2.hProcess, dwImageBase, dwSizeOfImage, lpMem
invoke TerminateProcess, ProcInfo2.hProcess, 0
invoke ContinueDebugEvent, DbgEvent.dwProcessId, DbgEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
.endif
invoke ContinueDebugEvent, DbgEvent.dwProcessId, DbgEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
invoke CloseHandle, ProcInfo2.hThread
invoke CloseHandle, ProcInfo2.hProcess
mov ProcInfo2.hProcess, 0
关键技术就是要在入口点处设置一个硬件断点,从而中断在入口点处准备 dump 进程。这里要设置硬件断点而不能设置一个 int3 断点的原因是我们设置断点的时候外壳还没有解密程序代码。如果我们在入口点处写入一个 0CCh 字节来设置一个 int3 断点,当外壳把程序代码解密后,入口点处的 0CCh 字节又会被解密后的代码覆盖,所以 int3 断点不起作用。

3. dump 进程

中断在入口点处了就可以 dump 进程,这个代码很简单

_Dump proc hProcess:DWORD, lpBaseAddress:DWORD, dwSize:DWORD, lpBuffer:DWORD
pushad
invoke ReadProcessMemory, hProcess, lpBaseAddress, lpBuffer, dwSize, 0
popad
ret
_Dump endp

4. 修复输入表

修复输入表可以利用 ImpREC.dll 来完成,这个也很简单,只需调用一个 RebuildImport 函数就可以搞定。

mov g_lpRebuildImport, 0
invoke LoadLibrary, CTXT("ImpREC.dll")
.if eax
mov ebx, eax
invoke GetProcAddress, ebx, CTXT("RebuildImport")
.if eax
mov g_lpRebuildImport, eax
.else
invoke _OutputInfo, g_hOutputCtl, CTXT("不能从 ImpREC.dll 中引入 RebuildImport 函数")
invoke _OutputInfo, g_hOutputCtl, CTXT("脱壳后的文件不能重建输入表!!!")
.endif
.else
invoke _OutputInfo, g_hOutputCtl, CTXT("找不到 ImpREC.dll 文件")
invoke _OutputInfo, g_hOutputCtl, CTXT("脱壳后的文件不能重建输入表!!!")
.endif

invoke CreateProcess, NULL, addr szFile, NULL, NULL, NULL, NORMAL_PRIORITY_CLASS, \
NULL, NULL, addr StartupInfo, addr ProcInfo3
invoke WaitForInputIdle, ProcInfo3.hProcess, -1
invoke _OutputInfo, g_hOutputCtl, CTXT("重建输入表...")
mov ecx, dwOEP
sub ecx, dwImageBase
lea eax, g_buffer

push eax
push 5
push 0
push ecx
push ProcInfo3.dwProcessId
call g_lpRebuildImport ;调用 ImpREC.dll 中的 RebuildImport 函数重建输入表

.if eax==0
invoke _OutputInfo, g_hOutputCtl, CTXT("重建输入表失败!!!")
.else
invoke DeleteFile, addr g_buffer
lea esi, g_buffer
invoke lstrlen, esi
add esi, eax
sub esi, 4
invoke lstrcpy, esi, CTXT("_.exe")
.endif
invoke TerminateProcess, ProcInfo3.hProcess, 0
后记

除了上面介绍的几个步骤外,还有文件修正,文件结构优化等可以参考本文附件中给出的代码。这里实现的只不过是一个很简单的脱壳机,对付不了几个壳。在下是一个菜鸟,文章很简单可能还有很多错误,只是希望这篇文章能够对大家有一点点帮助。谢谢!

2007-8-21 12:34
0
雪    币: 157
活跃值: (416)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
61
学习了哦。不过有点太麻烦了。我不能在这个版发贴,有时间把我写的内存注册机。贴上来
2007-8-21 14:20
0
游客
登录 | 注册 方可回帖
返回
//