2008年8月26日

本质理解脱壳之 添加节

代码含注释
流程如下:
1 将文件映射到内存得到基地址
2 检验PE标志
3 添加新节
3.1 增加节个数
3.2 得到文件对齐和节对齐值
3.3 通过节个数得到最后一节节头的地址和新节节头的地址
3.4 构建新节节头,包括name,Characteristics,misc.VirtualSize(实际大小),
通过VirtualSize文件对齐得到SizeOfRawData
3.5 通过最后一节节头VirtualAddress+VirtualSize节对齐后得到新节的VirtualAddress
通过最后一节节头PointerToRawData+SizeOfRawData文件对齐后得到新节PointerToRawData
3.6 修改SizeOfImage大小为新节的VirtualAddress+VirtualSize节对齐值
3.7 通过新节的PointerOfRawData(文件中的新节位置),将新节清0
4 修改 AddressOfEntryPoint, 旧的保存到新节最后, 新的指向新节首地址
5 将新节数据填充到新节中




.386
.model flat, stdcall
option casemap:none

include AddSection.Inc

APPEND_SIZE equ 2000h ;;添加到文件尾的大小

.const

g_szTargetFile db 'target.exe', 0
g_szNewSectionName db ".cjay", 0
g_szOpenFileFailed db "打不开文件", 0
g_szErr db "错误", 0
g_szDone db "成功", 0
g_szDoneCap db "Good Job", 0
g_szGetFileSizeFailed db "获取文件大小失败", 0
g_szCreateMapFailed db "创建文件映射失败", 0
g_szMapFileFailed db "映射文件失败", 0
g_szInvalidPE db "无效PE文件", 0

.data
g_bError db 0 ;错误代码
g_dwNewSectionSize dd 0 ;新节长度


;; 函数定义
CryptFile proto szFname : LPSTR ;;加密文件
AddSection proto pMem : LPVOID, pSectionName : LPVOID, dwSectionSize : DWORD ;; 添加新节
PEAlign proto dwTarNum : DWORD, dwAlignTo : DWORD ;;对齐

.code
Start:
invoke CryptFile, offset g_szTargetFile
invoke ExitProcess, 1

CryptFile proc szFname : LPSTR
LOCAL hFile : HANDLE
LOCAL hMap : HANDLE
LOCAL pMem : LPVOID
LOCAL dwOrigFileSize : DWORD ;;存放原始文件大小
LOCAL dwNTHeaderAddr : DWORD ;;存放NT头地址

xor eax, eax
mov hFile, eax
mov hMap, eax
mov pMem, eax

mov g_bError, al
mov eax, offset EndNewSection - offset NewSection
mov g_dwNewSectionSize, eax

;; open File
invoke CreateFile,szFname, \
GENERIC_READ+GENERIC_WRITE,\
FILE_SHARE_READ+FILE_SHARE_WRITE,\
NULL,\
OPEN_EXISTING,\
FILE_ATTRIBUTE_NORMAL,\
0
.IF eax == INVALID_HANDLE_VALUE
jmp OpenFileFailed
.endif
mov hFile, eax

invoke GetFileSize, hFile, NULL
.IF eax == 0
jmp GetFileSizeFailed
.endif

mov dwOrigFileSize, eax
add eax, APPEND_SIZE
xchg eax, ecx

;; create memory map
xor ebx, ebx
invoke CreateFileMapping, hFile, ebx, PAGE_READWRITE, ebx, ecx, ebx
.IF eax == 0
jmp CreateMapFailed
.endif
mov hMap, eax

;; map file to memory
invoke MapViewOfFile, hMap, FILE_MAP_WRITE+FILE_MAP_READ+FILE_MAP_COPY,ebx,ebx,ebx
.IF eax == 0
jmp MapFileFailed
.endif
mov pMem, eax

;; check PE
xchg eax, esi
assume esi : ptr IMAGE_DOS_HEADER
.IF [esi].e_magic != 'ZM'
jmp InvalidPE
.endif
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
.IF word ptr [esi].Signature != 'EP'
jmp InvalidPE
.endif
mov dwNTHeaderAddr, esi

;; add section
invoke AddSection,pMem,offset g_szNewSectionName,g_dwNewSectionSize
;; return New Section Addr(VA)
push eax
mov esi, dwNTHeaderAddr
assume esi : ptr IMAGE_NT_HEADERS
;;AddressOfEntryPoint
mov ebx, dword ptr [esi].OptionalHeader.AddressOfEntryPoint
add ebx, dword ptr [esi].OptionalHeader.ImageBase
;; Save the old entry
mov eax, offset OrigAddressOfEntry
mov dword ptr [eax], ebx
;; New Rntry is at New Section start
pop eax
assume eax : ptr IMAGE_SECTION_HEADER
push dword ptr [eax].VirtualAddress
pop dword ptr [esi].OptionalHeader.AddressOfEntryPoint

mov esi, offset NewSection
mov edi, dword ptr [eax].PointerToRawData
add edi, pMem
mov ecx, g_dwNewSectionSize
cld
rep movsb

LogicShellExit:
;; close handle & write it
.IF pMem != NULL
invoke UnmapViewOfFile, pMem
.endif
.IF hMap != NULL
invoke CloseHandle, hMap
.endif
.IF hFile != NULL
invoke CloseHandle, hFile
.endif
.IF g_bError == 0
;; show success message
invoke MessageBox, NULL, offset g_szDone, offset g_szDoneCap, MB_ICONINFORMATION
.ENDIF
ret
;; ----- Show error message -----
OpenFileFailed:
lea eax, g_szOpenFileFailed
jmp ShowErr
GetFileSizeFailed:
lea eax, g_szGetFileSizeFailed
jmp ShowErr
CreateMapFailed:
lea eax, g_szCreateMapFailed
jmp ShowErr
MapFileFailed:
lea eax, g_szMapFileFailed
jmp ShowErr
InvalidPE:
lea eax, g_szInvalidPE
jmp ShowErr
ShowErr:
invoke MessageBox, NULL, eax, offset g_szErr, MB_ICONERROR
mov al, 1
mov g_bError, al
jmp LogicShellExit
;; ----- NEW SCETION-----
NewSection:
call GetEip
GetEip:
pop eax
add eax, offset OrigAddressOfEntry - offset GetEip
mov eax, dword ptr [eax]
jmp eax
OrigAddressOfEntry dd ?
EndNewSection:

CryptFile endp

AddSection proc uses ebx ecx edx esi edi, pMem:LPVOID, pSectionName:LPVOID, dwSectionSize:DWORD
LOCAL dwNTHeader : LPVOID
LOCAL dwFileAlign : DWORD ;文件对齐
LOCAL dwSecAlig : DWORD ;节对齐
LOCAL dwLastSecTbl : DWORD ;最后一节地址(VA)
mov esi, pMem
add esi, dword ptr [esi+03ch]
mov dwNTHeader, esi
assume esi : ptr IMAGE_NT_HEADERS
mov cx, word ptr [esi].FileHeader.NumberOfSections
movzx ecx, cx
inc word ptr [esi].FileHeader.NumberOfSections
push dword ptr [esi].OptionalHeader.FileAlignment
pop dwFileAlign
push dword ptr [esi].OptionalHeader.SectionAlignment
pop dwSecAlig

add esi, sizeof IMAGE_NT_HEADERS ;Section Header Base
mov eax, sizeof IMAGE_SECTION_HEADER
mov ebx, ecx
imul ebx
add esi, eax

push esi
sub esi, sizeof IMAGE_SECTION_HEADER
mov dwLastSecTbl, esi
pop esi ;; esi保存了新节地址
;; 设置新节
assume esi : ptr IMAGE_SECTION_HEADER
push esi
lea edi, [esi].Name1
mov esi, pSectionName
CopySectionNameLoop:
lodsb
test al, al
jz EndCopySectionNameLoop
stosb
jmp CopySectionNameLoop
EndCopySectionNameLoop:
pop esi
;;属性
push 0E00000E0h
pop dword ptr [esi].Characteristics
;;VirtualSize
push dwSectionSize
pop dword ptr [esi].Misc.VirtualSize
;;SizeOfRawdata
invoke PEAlign, dwSectionSize, dwFileAlign
mov dword ptr [esi].SizeOfRawData, eax
;;VirtualAddress, PointerToRawData
mov eax, dwLastSecTbl
assume eax : ptr IMAGE_SECTION_HEADER
mov ecx, dword ptr [eax].VirtualAddress
add ecx, dword ptr [eax].Misc.VirtualSize
mov ebx, dword ptr [eax].PointerToRawData
add ebx, dword ptr [eax].SizeOfRawData

invoke PEAlign,ecx,dwSecAlig
mov dword ptr [esi].VirtualAddress, eax
invoke PEAlign,ebx,dwFileAlign
mov dword ptr [esi].PointerToRawData, eax
;; SizeOfImage
mov eax, dword ptr [esi].VirtualAddress
add eax, dword ptr [esi].Misc.VirtualSize
invoke PEAlign,eax,dwSecAlig
mov edx, dwNTHeader
assume edx : ptr IMAGE_NT_HEADERS
mov dword ptr [edx].OptionalHeader.SizeOfImage, eax

push dword ptr [esi].PointerToRawData
pop edi
add edi, pMem
;; clear new Section
mov ecx, dwSectionSize
xor eax, eax
cld
rep stosb

;; return New Section Address(VA)
mov eax, esi
assume esi : nothing
assume eax : nothing
assume edx : nothing
ret
AddSection endp

PEAlign proc uses ecx edx, dwTarNum : DWORD, dwAlignTo : DWORD
mov ecx, dwAlignTo
mov eax, dwTarNum
xor edx, edx
div ecx
cmp edx, 0 ;; edx保存余数
jz AlreadyAligned
inc eax
AlreadyAligned:
mul ecx
ret
PEAlign endp

end Start


展开 Read More...

2008年8月17日

OLLYDBG消息断点+控件ID 准确下断

这篇文章是CCDEBUGER的OD教程五的补充,很好


作 者: hklzt
时 间: 2007-06-06,16:50
联 系: QQ:87784858

看完了《OllyDBG 入门系统(五)-消息断点及 RUN 跟踪》之后感觉如何?会否有如下问题:
1、 是否觉得不知道在哪下断?
2、 为什么要这样子下断?
3、 如何确定断下来后的位置就是正确的?

好,就本着这几个问题来分析一遍。

首先,先回顾下Windows的消息机制。要点:所有要处理的消息必然会由程序自己处理,不处理的消息都交由Windows处理。Windows的消息处理函数的格式,如下:

LRESULT CALLBACK WindowProc(
HWND hwnd, // handle of window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);

其中uMsg就是关键,它代表消息的类型,如:WM_COMMAND,WM_GETTEXT等。记好哦。

下面,我们来用实例讲解。









这个CrackMe是用对话框做的(你是怎么知道的?)。
1、 用OllyDbg加载,Crtl + N,找到函数:USER32.DialogBoxParamA,右击->“在每个参考上设置断点”。
2、 F9,运行。看,被拦了下来,如图:






3、 其中DlgProc的内容,就是我们需要找的东东。这个地址是消息处理函数的入口点。现在来解释为什么要在窗口那才有消息断点,看图,













要下消息断点,首先得找到具有Windows消息处理函数格式的函数,然后,再根据栈的数据来判断消息,如果符合下断的消息,那么,OllyDbg就会拦下来(还会觉得不知道在哪下消息断点了吗?知道该如何下消息断点了吧?)。很明显有一点,这个消息断点的功能是有限的,比如,要拦主窗口中的菜单消息呢?所以,消息断点的功能还是有限的。如何扩展?扯远了,下面再讲。
4、 Ctrl + G来到cyle.0041029,我们来到了消息处理函数了,但是,OllyDbg并未识别出这个函数是消息处理函数。所以,在cyle.0041029处,右击->“分析”->“假定参数”,如图:









弹出一个对话框,选择“WinProc(hWnd,msg,wParam,lParam)”。











点击“应用”后,如图。




5、在cyle.0041029处,右击->“断点”->“在WinProc上的消息断点”(平时是不是没见过这个菜单呀?呵呵)。
6、 在“消息”列表框中,选择你要下断的消息(Alt+B,删除以前的断点,以免影响心情)。










7、 F9,运行,程序运行起来了。这时没什么事情发生,当你在里面点了一个文本框之后(点它,是想让它获得Focus(焦点),以便能够输入数据),事情就发生了。现在没办法在文本框里输入注册码,也没办法点击按钮。这是怎么回事呢?仔细看一下Stack(栈)的那个窗口,噢!原来被文本框的通知 EN_SETFOUCE搞坏了!停留在EN_SETFOCUS和EN_KILLFOCUS两个消息之间了。
8、 好,现在来扩展消息断点(消息断点是否是条件断点的特殊例子?),即使用条件断点(卖弄了一下,呵呵)。
















看你需求,条件和条件记录,在这里是没什么区别的,因为不需要记录的内容。
8、 按Shitf+F2,输入 MSG ==WM_COMMAND && [ESP+C]==66(这个66是怎么来的?这个就是那个Check按钮的ID,意思就是“当收到WM_COMMAND,且是由ID为0x66发出的时候中断”),确定,F9,运行。
9、现在,输入Name和Serial之后,点”Check”按钮。
10、这次中断,位置上似乎没有变化,但是,明白了消息处理机制,应该知道这次中断的不同吧?(这次会流程会流到注册算法那哦)


00401029 >/. >enter 0, 0 ; 解码为
0040102D |. >push ebx
0040102E |. >push edi
0040102F |. >push esi
00401030 |. >cmp [arg.2], 110
00401037 |. >je short cycle.0040105E
00401039 |. >cmp [arg.2], 111 //111=WM_COMMAND
00401040 |. >je short cycle.00401082 //肯定在这里跳(为什么?)
00401042 |. >cmp [arg.2], 10
00401046 |. >je short cycle.00401057
00401048 |. >cmp [arg.2], 2
0040104C |. >je short cycle.00401057
0040104E |. >xor eax, eax

11、来到 cycle.00401082,
00401082 |> \>cmp [arg.3], 67
00401086 |. >jnz short cycle.0040108D //这里肯定会跳
00401088 |. >call cycle.00401151
0040108D |> >cmp [arg.3], 66
00401091 |. >jnz short cycle.00401098 //这里肯定不会跳(又是为什么呢?)
00401093 |. >call cycle.0040109C //关键,
00401098 |> >xor eax, eax

12、蓝色部分,已经在CCDebuger那篇文章分析过了,这里就不分析了,我用红色字体来标明重点。
0040109C /$ >mov dword ptr ds:[402182], FEDCBA98
004010A6 |. >push 11 ; /Count = 11 (17.)
004010A8 |. >push cycle.00402171 ; |Buffer = cycle.00402171
004010AD |. >push 3E9 ; |ControlID = 3E9 (1001.)
004010B2 |. >push [arg.1] ; |hWnd
004010B5 |. >call ; \GetDlgItemTextA(Serial)
004010BA |. >or eax, eax
004010BC |. >je short cycle.0040111F
004010BE |. >push 11 ; /Count = 11 (17.)
004010C0 |. >push cycle.00402160 ; |Buffer = cycle.00402160
004010C5 |. >push 3E8 ; |ControlID = 3E8 (1000.)
004010CA |. >push [arg.1] ; |hWnd
004010CD |. >call ; \GetDlgItemTextA(name)
004010D2 |. >or eax, eax
004010D4 |. >je short cycle.0040111F
004010D6 |. >mov ecx, 10
004010DB |. >sub ecx, eax
004010DD |. >mov esi, cycle.00402160
004010E2 |. >mov edi, esi
004010E4 |. >add edi, eax
004010E6 |. >cld
004010E7 |. >rep movs byte ptr es:[edi], byte ptr>
004010E9 |. >xor ecx, ecx
004010EB |. >mov esi, cycle.00402171
004010F0 |> >/inc ecx
004010F1 |. >|lods byte ptr ds:[esi]
004010F2 |. >|or al, al
004010F4 |. >|je short cycle.00401100
004010F6 |. >|cmp al, 7E
004010F8 |. >|jg short cycle.00401100
004010FA |. >|cmp al, 30
004010FC |. >|jb short cycle.00401100
004010FE |.^ >\jmp short cycle.004010F0
00401100 |> >cmp ecx, 11
00401103 |. >jnz short cycle.0040111F //判断长度是否为16个有效字符,即16个字节,不是则跳
00401105 |. >call cycle.004011F1 //算法
0040110A |. >mov ecx, 0FF01
0040110F |. >push ecx
00401110 |. >call cycle.00401190 //算法
00401115 |. >cmp ecx, 1
00401118 |. >je short cycle.00401120 //需要跳
0040111A |> >call cycle.00401166
0040111F |> >retn
00401120 |> >mov eax, dword ptr ds:[402168]
00401125 |. >mov ebx, dword ptr ds:[40216C]
0040112B |. >xor eax, ebx
0040112D |. >xor eax, dword ptr ds:[402182]
00401133 |. >or eax, 40404040
00401138 |. >and eax, 77777777
0040113D |. >xor eax, dword ptr ds:[402179]
00401143 |. >xor eax, dword ptr ds:[40217D]
00401149 |.^ >jnz short cycle.0040111A //不可以跳
0040114B |. >call cycle.0040117B //提示注册成功!
00401150 \. >retn

终于写完了,现在来回顾下刚开始的问题:
1、 是否觉得不知道在哪下断?
答:在Windows消息处理函数的入口处下消息断点。
2、 为什么要这样子下断?
答:可能OllyDbg是根据栈的数据和函数原型来匹配,所以,一般来说,匹配条件都会是[Esp + XX] ==XXXXX
3、 如何确定断下来后的位置就是正确的?
答:这里是根据编程的思路以及Windows的消息处理机制来定位的,理论与实战相结合。

最后,总结下,由于windows是消息驱动的,很大一部分都是通过消息来完成的,所以,有很大一部分可以通过对消息下断来达到目的,但是,如何下消息断点?从大体上讲,是这样子的:1、找出消息循环处理的函数 2、在消息循环处理函数的入口处设断

写到这里,废话一下。赞扬CCDebuger的太多了,但是,在赞扬的同时,不知道大家有没仔细消化人家的成果?呵呵,其实,我也没有,因为我看不太懂,所以,还是照着自己的思路走。写这篇文章的目的是为了帮一位朋友,他想下消息断点,但是,不知道如何下,我就把CCDebuger的这那篇消息断点给他,可是,还是没解决,后来,自己也动了一下手,确实,对于WM_COMMAND消息来说,OD肯定会不停的拦下来的,所以,单纯的消息断点就行不通了,所以,再结合Run跟踪来记录下,刚好能解决问题,也就产生了CCDebuger的那篇文章(猜的,呵呵)。
最后,帮忙纠正下错误:
引用:
写到这准备跟踪算法时,才发现这个 crackme 还是挺复杂的,具体算法我就不写了,实在没那么多时间详细跟踪。有兴趣的可以跟一下,注册码是17 位,用户名采用复制的方式扩展到 16 位,如我输入“CCDebuger”,扩展后就是“CCDebugerCCDebug”。大致是先取扩展后用户名的前 8 位和注册码的前 8 位,把用户名的前四位和后四位分别与注册码的前四位和后四位进行运算,算完后再把扩展后用户名的后 8 位和注册码的后 8 位分两部分,再与前面用户名和注册码的前 8 位计算后的值进行异或计算,最后结果等于 0 就成功。注册码的第 17 位我尚未发现有何用处。对于新手来说,可能这个 crackme 的难度大了一点。没关系,我们主要是学习 OllyDBG 的使用,方法掌握就可以了。

关于“位”的概念,“位”,是指二进制位,在这里,一个字节等于8位,一个字符等于一个字节。“注册码是17位”,应改成“注册码是17个字节”,如果你跟踪分析过,你可以发现,这样子还是不对的,最后应该是“注册码是16个字节”,
cmp ecx, 11,这里的11是16进制,即十进制数:17。从代码中可以看出这个十进制数17,还得减1才是字符串的真实长度,所以,应该改成“注册码是16个字节”,后面的“位”,需要改成“字节”。第一次看的时候,没注意看,都给蒙了。

展开 Read More...

2008年4月20日

SEH原理追踪

附件
我也来分析看雪的seh实例程序。当然第一步是ollydbg载入啦

入口

00401000 > $ 8D4424 F8 lea eax, dword ptr [esp-8] ;;把esp-8后的地址传给 eax,lea的 ;;用法,别把[]当成取值啦,这里这样用是提前把esp指向
;;_EXCEPTION_REGISTRATION结构地址,当然下面就是在堆栈中去构造那个结构,参考图1,2

00401004 . 64:8705 00000>xchg dword ptr fs:[0], eax ;;交换指令,eax存放旧的fs[0], 现在fs:[0]被替换,为我们提前准备的地址
0040100B . BB 2E104000 mov ebx, 0040102E ;;开始构造,放我们的异常处理函数
00401010 . 53 push ebx ;;push一下, [esp-4] : handler
00401011 . 50 push eax ;;push两下, [esp-8] : pre
00401012 . BE 00000000 mov esi, 0 ;;构造异常,esi放入0地址
00401017 . 8B06 mov eax, dword ptr [esi] ;;取0地址的值,当然出错啦,相当于在 ;;c语言里这样用: int a = *0;
;;正常情况不会到这里了,直接到我们定义的异常处理函数地址
00401019 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
0040101B . 68 00304000 push 00403000 ; |Title = "OK"
00401020 . 68 10304000 push 00403010 ; |Text = "SEH Fail"
00401025 . 6A 00 push 0 ; |hOwner = NULL
00401027 . E8 1C000000 call ; \MessageBoxA
0040102C . EB 13 jmp short 00401041
0040102E . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401030 . 68 00304000 push 00403000 ; |Title = "OK"
00401035 . 68 03304000 push 00403003 ; |Text = "SEH Succeed "
0040103A . 6A 00 push 0 ; |hOwner = NULL
0040103C . E8 07000000 call ; \MessageBoxA
00401041 > 6A 00 push 0 ; /ExitCode = 0
00401043 . E8 06000000 call ; \ExitProcess
00401048 $- FF25 08204000 jmp dword ptr [<&USER32.MessageBoxA>>; USER32.MessageBoxA
0040104E .- FF25 00204000 jmp dword ptr [<&KERNEL32.ExitProces>; kernel32.ExitProcess

很明显是masm的,注释在代码中



图1, 先指向esp-8















图 2 push两下放好数据









OK,异常产生了,单步过这一句,od提示访问[00000000]异常,在跟壳的时候貌似经常出现这些访问异常


00401017 . 8B06 mov eax, dword ptr [esi]

shift+f7

到了ntdll区域,看od提示

7C92EAEC > 8B4C24 04 mov ecx, dword ptr [esp+4]
7C92EAF0 8B1C24 mov ebx, dword ptr [esp] ;;停在这里,可见F7的作用,当然就是step了一下
7C92EAF3 51 push ecx
7C92EAF4 53 push ebx
7C92EAF5 E8 C78C0200 call 7C9577C1
7C92EAFA 0AC0 or al, al
7C92EAFC 74 0C je short 7C92EB0A
7C92EAFE 5B pop ebx


这个时候看堆栈,换堆栈了,这个堆栈地址应该是内核堆栈,
OD ALT+M可以看到主线程的堆栈是

0012D000 - 00130000

看看有些什么东西,下面就很枯燥了,一堆结构指针以及数据,不过用来做坏事相当有用哦(@_@)

0012FCCC 0012FCD4 ;; 指向EXCEPTION_RECORD,不就在下面吗
0012FCD0 0012FCF0 ;; 指向EXCEPTION_CONTEXT,EXCEPTION_RECORD结构之后
0012FCD4 C0000005 ;; 异常代码,windows.inc 定义的STATUS_开头的类型, 这里就是读取内存 ;; 异常,还有很多,暂时不管,你脱壳时迟早会遇到,到时有兴趣再查一样
0012FCD8 00000000 ;; 异常标识,0--可修复, 1-不可修复, 2-正在展开,不要试图修复什 ;; 么,需要的话,释放必要的资源
0012FCDC 00000000 ;; 指向另一个EXCEPTION_RECORD结构
0012FCE0 00401017 Seh.00401017 ;; 异常发生的地址,被抓住了吧
0012FCE4 00000002 ;; ExceptionInformation所含有的dword数目
0012FCE8 00000000 ;; ExceptionInformation 1
0012FCEC 00000000 ;; ExceptionInformation 2
0012FCF0 0001003F ;; EXCEPTION_CONTEXT结构, ContextFlags
0012FCF4 00000000 ;; iDr0
0012FCF8 00000000 ;; iDr1
0012FCFC 00000000 ;; iDr2
0012FD00 00000000 ;; iDr3
0012FD04 00000000 ;; iDr6
0012FD08 00000000 ;; iDr7
0012FD0C FFFF027F ;; FloatSave 1C - 88h, 这里就是 0012FD0C - 0012FD78
;;不重要,略去
0012FD7C 00000000 ;; Gs
0012FD80 0000003B ;; Fs
0012FD84 00000023 ;; Es
0012FD88 00000023 ;; Ds
0012FD8C 7C930738 ntdll.7C930738 ;; Edi
0012FD90 00000000 ;; Esi
0012FD94 0040102E Seh.0040102E ;; Ebx
0012FD98 7C92EB94 ntdll.KiFastSystemCallRet ;; Edx
0012FD9C 0012FFB0 ;; Ecx
0012FDA0 0012FFE0 ;; Eax
0012FDA4 0012FFF0 ;; Ebp
0012FDA8 00401017 Seh.00401017 ;; Eip,指向的是发生异常的地址
0012FDAC 0000001B ;; Cs
0012FDB0 00010383 ;; Flag
0012FDB4 0012FFBC ;; Esp
0012FDB8 00000023 ;; Ss
0012FDBC 0000027F
0012FDC0 00000000

结构分析完了, 现在继续走, 就是处理这些数据了

7C92EAF3 51 push ecx ;;0012FCF0, 看上面的堆栈,EXCEPTION_CONTEXT结构地址
7C92EAF4 53 push ebx ;;0012FCD4, EXCEPTION_RECORD结构指针
7C92EAF5 E8 C78C0200 call 7C9577C1 ;;如果f8过,会出现SEH succeed 提示窗口,即执行了程序 ;;自己的异常代码,F7跟进追踪
7C92EAFA 0AC0 or al, al
7C92EAFC 74 0C je short 7C92EB0A
7C92EAFE 5B pop ebx
7C92EAFF 59 pop ecx

;; 到这里
7C9577C1 8BFF mov edi, edi ; ntdll.7C930738
7C9577C3 55 push ebp
7C9577C4 8BEC mov ebp, esp
7C9577C6 83EC 64 sub esp, 64
7C9577C9 56 push esi
7C9577CA FF75 0C push dword ptr [ebp+C] ;;ebp+4返回地址,+8参数1地址。+C参数2地址
7C9577CD 8B75 08 mov esi, dword ptr [ebp+8]
7C9577D0 56 push esi
7C9577D1 C645 FF 00 mov byte ptr [ebp-1], 0
7C9577D5 E8 C2FFFFFF call 7C95779C ;; 跟进, 没干什么, 检测 7C99C320 地址是否指向 7C99C320??
7C9577DA 84C0 test al, al
7C9577DC 0F85 84720100 jnz 7C96EA66
7C9577E2 53 push ebx ;; 0012FCD4, EXCEPTION_RECORD结构指针
7C9577E3 8D45 F4 lea eax, dword ptr [ebp-C] ;; 局部变量3 ,ebp = 0012FCBC ,存放线程堆栈地址上限
7C9577E6 50 push eax
7C9577E7 8D45 F8 lea eax, dword ptr [ebp-8] ;; local2, 存放下限
7C9577EA 50 push eax
7C9577EB E8 1CC1FCFF call 7C92390C ;; 取线程堆栈地址范围, 小技巧, dd跟随ebp地址, dd跟随fs指向的地址, 寄存器窗口上有
7C9577F0 E8 38C1FCFF call 7C92392D ;; mov eax, dword ptr fs:[0]
7C9577F5 8365 08 00 and dword ptr [ebp+8], 0 ;; 参数1清零
7C9577F9 8BD8 mov ebx, eax ;; 后面是一系列检测地址是否合法,可以略看,大概是检测堆栈地址,SEH异常处理函数地址
7C9577FB 83FB FF cmp ebx, -1
7C9577FE 0F84 8F000000 je 7C957893
7C957804 57 push edi
7C957805 3B5D F8 cmp ebx, dword ptr [ebp-8]
7C957808 ^ 0F82 1D32FFFF jb 7C94AA2B
7C95780E 8D43 08 lea eax, dword ptr [ebx+8]
7C957811 3B45 F4 cmp eax, dword ptr [ebp-C]
7C957814 ^ 0F87 1132FFFF ja 7C94AA2B
7C95781A F6C3 03 test bl, 3
7C95781D ^ 0F85 0832FFFF jnz 7C94AA2B
7C957823 8B43 04 mov eax, dword ptr [ebx+4]
7C957826 3B45 F8 cmp eax, dword ptr [ebp-8]
7C957829 72 09 jb short 7C957834
7C95782B 3B45 F4 cmp eax, dword ptr [ebp-C]
7C95782E ^ 0F82 F731FFFF jb 7C94AA2B
7C957834 50 push eax ;; 异常处理函数地址
7C957835 E8 67000000 call 7C9578A1 ;; 可以看见利用fs一系列指针,fs:[18]即为fs指向的地址
;; fs:[30],貌似指向PEB,PEB:[C]又是什么,继续偏移+18,找到ImageBase,再取入口地址,来检测异常处理函数地址^_^!!,真不嫌麻烦
下一个call继续跟进,得到NTHEADER地址

7C9579D1 E8 738EFDFF call RtlImageNtHeader

;;后面不太清楚在干啥,反正是系统API就直接F8,是call xxxxxxxx这种就F7进, 否则跑飞了又要重来了,还是很痛苦滴

7C95783A 84C0 test al, al
7C95783C ^ 0F84 E931FFFF je 7C94AA2B


当到了这里时

7C923799 55 push ebp
7C92379A 8BEC mov ebp, esp
7C92379C FF75 0C push dword ptr [ebp+C]
7C92379F 52 push edx
7C9237A0 64:FF35 0000000>push dword ptr fs:[0]
7C9237A7 64:8925 0000000>mov dword ptr fs:[0], esp ; 替换fs:[0]了,指向我们定义的地址
7C9237AE FF75 14 push dword ptr [ebp+14] ; 参数4 _lpDispatchrContext ?,大概放了线程堆栈范围
7C9237B1 FF75 10 push dword ptr [ebp+10] ; 参数3 _lpDContext,指向Context结构
7C9237B4 FF75 0C push dword ptr [ebp+C] ; 参数2 _lpSEH ,指向ERR结构
7C9237B7 FF75 08 push dword ptr [ebp+8] ; 参数1 _lpExceptionRecord ,指向ExceptionRecord结构
7C9237BA 8B4D 18 mov ecx, dword ptr [ebp+18] ;; 取我们定义的地址,到这里可以看下堆栈
7C9237BD FFD1 call ecx ;; 直接去执行了
7C9237BF 64:8B25 0000000>mov esp, dword ptr fs:[0]
7C9237C6 64:8F05 0000000>pop dword ptr fs:[0]
7C9237CD 8BE5 mov esp, ebp
7C9237CF 5D pop ebp
7C9237D0 C2 1400 retn 14


相关数据结构

;//================================以下是两个成员的详细结构

EXCEPTION_RECORD STRUCT
ExceptionCode DWORD ? ;//异常码
ExceptionFlags DWORD ? ;//异常标志
pExceptionRecord DWORD ? ;//指向另外一个EXCEPTION_RECORD的指针
ExceptionAddress DWORD ? ;//异常发生的地址
NumberParameters DWORD ? ;//下面ExceptionInformation所含有的dword数目
ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORD ENDS ;//EXCEPTION_MAXIMUM_PARAMETERS ==15

;//================================具体解释========================================

ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多
的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:

C0000005h----读写内存冲突
C0000094h----非法除0
C00000FDh----堆栈溢出或者说越界
80000001h----由Virtual Alloc建立起来的属性页冲突
C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常
C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,
如果RtlUnwind时没有Exception Record参数也同样会填入这个代码
80000003h----调试时因代码中int3中断
80000004h----处于被单步调试状态

注:也可以自己定义异常代码,遵循如下规则:
_____________________________________________________________________+

位: 31~30 29~28 27~16 15~0
_____________________________________________________________________+
含义: 严重程度 29位 功能代码 异常代码
0==成功 0==Mcrosoft MICROSOFT定义 用户定义
1==通知 1==客户
2==警告 28位
3==错误 被保留必须为0
ExceptionFlags 异常标志
0----可修复异常
1----不可修复异常
2----正在展开,不要试图修复什么,需要的话,释放必要的资源
pExceptionRecord 如果程序本身导致异常,指向那个异常结构
ExceptionAddress 发生异常的eip地址
ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下
第一个dword 0==读冲突 1==写冲突
第二个dword 读写冲突地址
;//================================解释结束========================================
off.
CONTEXT STRUCT ; _
ContextFlags DWORD ? ; | +0
iDr0 DWORD ? ; | +4
iDr1 DWORD ? ; | +8
iDr2 DWORD ? ; >调试寄存器 +C
iDr3 DWORD ? ; | +10
iDr6 DWORD ? ; | +14
iDr7 DWORD ? ; _| +18
FloatSave FLOATING_SAVE_AREA <> ;浮点寄存器区 +1C~~~88h
regGs DWORD ? ;--| +8C
regFs DWORD ? ; |\段寄存器 +90
regEs DWORD ? ; |/ +94
regDs DWORD ? ;--| +98
regEdi DWORD ? ;____________ +9C
regEsi DWORD ? ; | 通用 +A0
regEbx DWORD ? ; | 寄 +A4
regEdx DWORD ? ; | 存 +A8
regEcx DWORD ? ; | 器 +AC
regEax DWORD ? ;_______|___组_ +B0
regEbp DWORD ? ;++++++++++++++++ +B4
regEip DWORD ? ; |控制 +B8
regCs DWORD ? ; |寄存 +BC
regFlag DWORD ? ; |器组 +C0
regEsp DWORD ? ; | +C4
regSs DWORD ? ;++++++++++++++++ +C8
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
;//================================以上是两个成员的详细结构



展开 Read More...

2008年4月19日

visio2003注册码

WFDWY-XQXJF-RHRYG-BG7RQ-BBDHM

隐藏

展开 Read More...

SEH学习

据我所知,SEH在很多加壳程序里经常出现。
貌似就是try{} catch{} finally{}的本质体现

很多介绍SEH的教程前面都有这么一句

push 00401037 ; SE handler installation
push dword ptr fs:[0]
mov dword ptr fs:[0], esp

什么意思呢?

fs指向一个TIB结构 (THREAD INFORMATION BLOCK)
第一个元素fs:[0]指向一个_EXCEPTION_REGISTRATION结构

fs:[0]->
_EXCEPTION_REGISTRATION struc
prev dd ? ;前一个_EXCEPTION_REGISTRATION结构
handler dd ? ;异常处理例程入口
_EXCEPTION_REGISTRATION ends


这里

push 00401037 ; SE handler installation
push dword ptr fs:[0]
mov dword ptr fs:[0], esp


第一步放我们的异常处理函数地址

第二步放fs:[0]指向的异常处理函数地址

第三步用esp地址覆盖fs:[0]地址

结果就是这样,














图中栈顶数据为旧的fs:[0]地址,现在新的fs:[0]还是指向他,
改变的是第二个结构,即
handler dd ? ;异常处理例程入口

这样做,当发生异常就可以跳到我们自己定义的异常处理函数去了。

可以用od数据窗口跟踪最初的结构,比如dd fs:[0]

可以看出是这样的链式结构



展开 Read More...

RadAsm 制表符修改

小技巧,很多编辑软件,IDE也有这项功能,
目的是为了格式统一,美观

第一步:
















第二步:修改制表符大小, 勾上转换制表符为空格,应用,确定。



展开 Read More...

汇编指令技巧

不断更新吧,脑袋太笨,容量不够,特作笔记,方便查阅


cdq ; 使用eax寄存器的符号位填充edx寄存器

例:eax为正数,edx为0; eax为负数,edx为0XFFFFFFFF,即全1

展开 Read More...

2007年1月20日

简单汇编指令

N年前写的,呵呵。第一贴,还是不删了。


MOV EBP,ESP
MOV [XXX],ESP //XXX 为具体内存地址数据

一定要记住没加括号只是访问的寄存器中的值
加了括号的,比如[10]是内存地址10指向的值,

比如内存是这样的

00000010: 00 (我乱写的)

对[10]操作就相当于对00操作.

我比较笨,有时候简单的问题都要卡住,还是记下来警告自己吧...

展开 Read More...