一份Reverseme解答
ReversveMe v0.1答案
作者:nbwwww.vxer.com
ReversveMe v0.1是一份逆向练习作业,点击bitmap菜单便可以显示出软件中定义好的一幅位图。我把自己的练习过程写下来,供大家斧正。多说无益,因此一些简单的操作我就不写了。
位图ID:
用exeScope获取位图句柄为106(即6A) 菜单消息处理:
:004029E9 2D419C0000 sub eax, 00009C41 ;9c41-->bitmap句柄
:004029EE 742D je 00402A1D 剩余空间:
用俺写的"剩余空间分析器"分析,部分结果如下:
.text 0000529e 0000529e 3426 否
就是说在.text段的末尾从0000529e开始有3426字节的剩余空间.后面的否是说本区段不可写,这样运行的时候就无法向本区段写数据。故此,用PeEdit把这个.text区段改成可写可读。 修改菜单消息处理:
将
:004029E9 2D419C0000 sub eax, 00009C41 ;9c41-->bitmap句柄
:004029EE 742D je 00402A1D
:004029F0 48 dec eax
:004029F1 741D je 00402A10
:004029F3 48 dec eax
:004029F4 7527 jne 00402A1D
改为:
:004029E9 2D419C0000 sub eax, 00009C41 ;9c41-->bitmap句柄
:004029EE jmp 00405320
:004029F3 48 dec eax
:004029F4 7527 jne 00402A1D
中间少了:
:004029F0 48 dec eax
:004029F1 741D je 00402A10 记录下来,以后补上 填写显示位图代码:
首先把原来的补上:
:00405320 jz @F
dec eax
:00405323 je 00402a10 ;填补上面缺少的代码
jmp 000029F3 ;跳回
@@: pushad
;显示位图的代码
popad
jmp 000029F3 ;跳回
这里需要注意的是,在hiew中处理:00405323 je 00402a10,如果直接以asm形式输入je 2a10,虽然形式上正确,但实际上并不对,因为这样修改以后会导致退出菜单非法操作。正确的做法应该是直接计算出je 00402a10的代码:0F84E7D6ffff,并把这些代码写入文件。看来hiew还有需要改进的地方。 下面看显示位图的代码:
local hBmp,hDc,memDc,hOld
invoke LoadBitmap,hInstance,IDB ;载入位图,IDB是位图ID,hInstance是进程句柄
mov hBmp,eax
;如果在paint消息中,可以不用GetDC,而是用BeginPaint,代替GetDC,不过最后要用EndPaint
invoke GetDC,hWinMain ;获取窗体设备环境,hWinMain是窗口句柄
mov hDc,eax
nop
nop
nop
invoke CreateCompatibleDC,hDc ;建立设备环境
mov memDc,eax
nop
nop
nop
invoke SelectObject,memDc,hBmp
mov hOld,eax
invoke BitBlt, hDc, 0,0,CLOCK_SIZE,CLOCK_SIZE, memDc,0,0,SRCAND ;把memDc设备环境内容拷贝到hDc
nop
nop
nop
invoke SelectObject, hDc , hOld
invoke DeleteDC,memDc
需要说明的是,hInstance是进程的句柄在,程序载入的时候GetModuleHandleA函数返回的便是这个值,所以可以从这个获取这个函数的返回值,我这里图方便,用400000代替,因为我发现这个返回值一直是400000对于win32写的程序和VC程序好象都是这样,我并不知道在xp和2000下如何,不管怎么说,我这种做法不是推荐的。如果不闲麻烦,应该从GetModuleHandleA函数获取这个值。
hWinMain是窗体句柄.由于"关于"对话框用的是MessageBoxA函数:
* Reference To: USER32.MessageBoxA, Ord:01BEh
|
:00402A07 FF151C614000 Call dword ptr [0040611C]
这个函数最后一个参数可以是窗口句柄,当然也可以不是。老大这里很给面子,用了窗体句柄,所以我们就可以省事一些,跟进去看看,在我们的显示位图代码开始:pushad的地方,[esp+20]处便是窗口句柄。
代码中的nop一来可以用于分段,便于写代码时候方便查看,二来如果写完代码后中间需要添加代码。可以用这些nop占用的空间补充,不至于因为空间不足而重新写所有的代码。 变量组织:
上面说过,从文件地址0000529e处开始的多余代码便可以为我们所用,但是代码之所以从00005320开始。除了考虑为以后方便在代码前面再添加代码之外,更多的是我们可以利用从0000529e开始到00005320的空间作为放置变量的地方。下面是我们变量组织形式:
0040529e --àhBmp 4052A2 --àhDc
4052A6--àmemDc 4052AA --àhOld 4052AE --àhWinMain
需要用的函数地址:
LoadBitmap : user32库
GetDC : user32库
CreateCompatibleDC : [406034]
SelectObject : [406014]
BitBlt : [40600c]
DeleteDC : [406030]
LoadLibraryA : [406060]
GetProcAddress : [406064]
其中前两个函数没有被载入,需要动一些脑筋。可以修改程序引入表来手工引入,我没仔细研究过这个,还是用原始办法:GetProcAddress,惭愧啊。L
这样我们就需要再添加一些变量:
004052B2:user32的ASCII码 004052Ba:LoadBitmap(ASCII) 004052c6:LoadBitmap地址
004052CA:GetDC的ASCII码 004052d1:GetDC的地址 004052d5:LoadLibrary地址
现在可以进入具体实施阶段了,对pushad和popad中间的代码组织如下:
0040532e: pushad
mov eax,[esp+24]
mov dword [4052AE],eax
nop
nop
nop
nop
push 4052B2
call LoadLibrary ;[406060]
mov [004052d5],eax ;保存LoadLibrary地址
nop
push 004052Ba ;LoadBitmap(ASCII)
push eax
call GetProcAddress ;[406064]
mov [004052c6],eax ;保存LoadBitmap地址
nop
push 004052CA ;GetDC(ASCII)
push [004052d5]
call GetProcAddress ;[406064]
mov [004052d1],eax ;GetDC的地址
nop
nop
nop
nop
;下面是显示位图的代码
push 6A ;位图ID
push 400000
call dword [004052c6] ;invoke LoadBitmap,hInstance,IDB
mov [0040529e],eax ;mov hBmp,eax
push [4052AE] ;invoke GetDC,hWinMain
call dword [004052d1]
mov [4052A2],eax ;mov hDc,eax
push eax ;invoke CreateCompatibleDC,hDc
call dword [406034]
mov [4052A6],eax
nop :004052a9 push [0040529e] ;invoke SelectObject,memDc,hBmp
push [4052A6]
call dword [406014]
mov [4052AA],eax ;mov hOld,eax
; invoke BitBlt, hDc, 0,0,CLOCK_SIZE,CLOCK_SIZE, memDc,0,0,SRCAND
push 00CC0020
push 0
push 0
push [4052A6] ;push memDc
push 96 ;Height
push 96 ;Width
push 0 ;top
push 0 ;Left
push [4052A2] ;push hDc
:004053e2 call dword[40600c] ;call BitBlt
nop
nop
push [4052AA] ;invoke SelectObject, hDc , hOld
push [4052A2]
call dword [406014]
nop
push [4052A6]
call dword [406030] ;invoke DeleteDC,memDc
popad
jmp 000029F3 ;跳回 运行时候发现最后的DeleteDC处的代码无法被载入执行,所以可以考虑从:004053e2处跳转到别的地方执行,这里采用从:004052d5开始的空间。
修改:004053e2为:
:004053e2 jmp 52d5
添加:
:004052d5 push [4052AA] ;invoke SelectObject, hDc , hOld
push [4052A2]
call dword [406014]
nop
push [4052A6]
call dword [406030] ;invoke DeleteDC,memDc
popad
jmp 000029F3 ;跳回原来的程序
看一下结果,图片的宽度没有完全显示出来,就是说宽度不够。我用的是7F,无法再加大了,因为到了80就显示不出来图片了。我在MSDN中查了一下 BitBlt 的说明,也没有太多解释。看来还需要高手指点L
代码看起来比较凌乱,这是因为代码空间没有组织好事实上这是不可避免的。为了防止不断对代码进行修改,我在代码中插入了很多nop指令,为的只是可以对局部代码进行修改而不用重新写如全部代码。这样做是和我们的任务性质有关的,因为没有编译器来帮我们重新组织文件。当然了,如果看着不顺眼,作完毕后可以重新写一便.我懒得动弹。也就算了J
总结:
总体看来这是一份不错的练习作业,但是细节方面还有待商榷。比如我感觉可以适当添加一些难度,比如源程序可以采用Win32编写,我稍微写了一个,才26K,比这一份小了一半,这样在程序中很难找到多余的空间添加代码而让练习者必须添加一个区段。另外还可以适当加壳增加难度(不过我脱壳水平血菜,以后不要来考我就好J)。 附件:Reverseme.rar
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)