LoadImage加载到系统OEM图片的BUG解决方案

简单说解决方法就是LoadImage第一个参数不要传递NULL,而使用GetModuleHandle(NULL)代替。

 

以下是邮件原文:

http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.gdi/2007-01/msg00145.html

Hi,

There is a bug that I get with LoadImage once in a while where it does not 
load the image from the file that I specify but instead loads up a system 
graphic. This only happens very rarely but is annoying when it does. To the 
point where this issue has been annoying me on and off for probably the last 
5 years. I use LoadImage as follows:

hbm = (HBITMAP)LoadImage(NULL, filename, IMAGE_BITMAP, m_ddsd.dwWidth, 
m_ddsd.dwHeight, LR_LOADFROMFILE|LR_CREATEDIBSECTION);

Luckily, with the current build of my software is it is finally occurring 
every time giving me a chance to debug this. Below is what I have discovered 
is going wrong with LoadImage and a workaround at the end.

Stepping into the disassembly LoadImage calls the function _LoadBmp@20, with 
the first parameter as NULL for the handle to the instance of the module. 
The second parameter is the pointer to the string for the bmp file to open, 
where my string is at address 0x00197fe8, and staying at 0x00197fe8 each time 
at the moment which is why I could debug this.

Below is the disassembly with some of my own comments noting the important 
parts:

_LoadBmp@20:
77D55E95 mov edi,edi 
77D55E97 push ebp 
77D55E98 mov ebp,esp 
77D55E9A sub esp,24h 
77D55E9D mov ecx,dword ptr [ebp+0Ch] ; Copies address of filename 
into ecx
77D55EA0 push ebx 
77D55EA1 push esi 
77D55EA2 mov esi,dword ptr [ebp+8] ; Copies paramater handle to 
instance of module to esi
77D55EA5 push edi 
77D55EA6 xor edi,edi ; Sets edi to zero
77D55EA8 cmp esi,edi ; Checks if parameter handle to instance of 
module is NULL
77D55EAA mov dword ptr [ebp-4],edi 
77D55EAD mov dword ptr [ebp-8],edi 
77D55EB0 je _LoadBmp@20+21h (77D57C6Eh) ; If esi was NULL jumps 
to code at 77D57C6E
77D55EB6 push dword ptr [ebp+18h] 
77D55EB9 push dword ptr [ebp+14h] 
77D55EBC push dword ptr [ebp+10h] 
77D55EBF push 2 
77D55EC1 push ecx 
77D55EC2 push esi 
77D55EC3 call _ObjectFromDIBResource@24 (77D5298Dh) ; This is what 
we want it to call 


The two important parts above are:
1. The code at 77D55E9D. Which copies the address of the bmp file string 
into ecx.

2. The code at 77D55EA8. This checks if the hinst parameter passed into 
LoadImage is NULL. If we jump to the code at address 77D57C6E becuase of this 
line:

77D55EB0 je _LoadBmp@20+21h (77D57C6Eh) ; If esi was NULL jumps 
to code at 77D57C6E


This takes us to this assembly:

77D57C6E mov esi,dword ptr [_hmodUser (77DA0264h)] 
77D57C74 movzx eax,cx 
77D57C77 xor ebx,ebx 
77D57C79 sub eax,7FDCh 
77D57C7E mov dword ptr [ebp+0Ch],edi 
77D57C81 mov dword ptr [ebp+8],edi 
77D57C84 je 77D745B6 
77D57C8A sub eax,16h 
77D57C8D je _LoadBmp@20+141h (77D74471h) 
77D57C93 sub eax,0Dh 
77D57C96 je _LoadBmp@20+128h (77D74458h) 
77D57C9C xor eax,eax 
77D57C9E cmp cx,7FE2h 
77D57CA3 sete al 
77D57CA6 mov edx,77D41A98h ;edx is set here to address 77D41A98h 
77D57CAB mov dword ptr [ebp-0Ch],eax 
77D57CAE xor eax,eax 
77D57CB0 cmp cx,7FF7h 
77D57CB5 sete al 
77D57CB8 mov dword ptr [ebp-10h],eax 
77D57CBB xor eax,eax ; sets eax to zero
77D57CBD cmp word ptr [edx],cx ; cx is the lower 16 bits of the 
filename addesss
77D57CC0 je _LoadBmp@20+81h (77D57CCEh) ; jumps if cx equals 
address in edx
77D57CC2 add edx,6 ; edx += 6
77D57CC5 inc eax ; eax++
77D57CC6 cmp edx,offset _gwszShellFont2 (77D41B58h) ; checks if end 
loop
77D57CCC jl _LoadBmp@20+70h (77D57CBDh) 
77D57CCE cmp eax,20h ; if we looped around 0x20 
77D57CD1 je 77D55EB6 ; times then jumps back to 77D55EB6


The loop at the end is important part, and is what causes the issue. I will 
step through the important parts:

The register edx is set to the value 77D41A98. 

77D57CA6 mov edx,77D41A98h 

Here is the loop:
77D57CC6 cmp edx,offset _gwszShellFont2 (77D41B58h) ;
77D57CCC jl _LoadBmp@20+70h (77D57CBDh) ; 

This keeps looping around back to 77D57CBD while edx is less than the value 
77D41B58. Incrementing edx by 6 each time. So the following calculation:
0x77D41B58 - 0x77D41A98 = 0xC0 / 6 = 0x20

Means the max eax can equal is 0x20. This is important because the next two 
lines after the loop are:
77D57CCE cmp eax,20h ; if we looped around 0x20 
77D57CD1 je 77D55EB6 ; times then jumps back to 77D55EB6

This is what we want as that way it jumps back to the code at 77D55EB6 which 
is the first lot of assembly code, and then continues down to the line: 
77D55EC3 call _ObjectFromDIBResource@24 (77D5298Dh)

Which loads the bmp from the file, and all is good.

Now the issue is at the the line the loop keeps jumping to:
77D57CBD cmp word ptr [edx],cx 

ecx is where the address of the bmps filename is stored shown earlier. 
However, this line uses the register cx which is the lower 16 bits of the 
address. And it is checking to see if it matches a value at the address of 
edx which is being incremented each time through the loop. As my string is 
at address 0x00197fe8 it is therefore checking if 0x7fe8 is in the list in 
memory between 0x77D41A98 and 0x77D41B58, this is the list of OEM images. In 
my case it gets a match when eax equals 16, and leaves the loop. It then 
continues down through the code to:
77D57D56 call _CreateScreenBitmap@24 (77D52723h) 

Which then creates the OEM image instead of giving loading the bitmap I 
specified. 

The above loop shows why it is rare to get the wrong image. As the bottom 
16 bits of the filenames address has to match an OEM's ID for it to occur.

The following code will replicate this issue:
HBITMAP hbm = 0;
hbm = (HBITMAP)LoadImage(NULL, (TCHAR*)0x00197fe8, IMAGE_BITMAP, 0, 0, 
LR_LOADFROMFILE|LR_CREATEDIBSECTION);

If you use this code you will find you get a handle to a bitmap even though 
you have given an address which will have random data in it. This is because 
it will never try use access this address, but will just use the 0x7fe8 part 
of it as it happens to match and OEM image ID.


The fix for this when loading from a file is to always pass 
'GetModuleHandle(0)' as the first parameter to LoadImage. Even though the 
documentation states:
hinst - [in] Handle to an instance of the module that contains the image to 
be loaded.

This is because of the following in the first lot of assembly I posted is:
77D55EA2 mov esi,dword ptr [ebp+8] 

This copies the handle to instance of module into esi. 

It then checks if this is zero with the following code:
77D55EA6 xor edi,edi ; Sets edi to zero
77D55EA8 cmp esi,edi ; Checks if parameter handle to instance of 
module NULL

If it is not NULL then it does not do the next lot of assembly which checks 
if the lower 16 bits of your address matches an OEM image.

So the the verdict is if you are loading a bmp from a file you should always 
pass the first parameter to LoadImage, till this issue is fixed ( if ever ).

My problem is the case where I am currently experiencing this bug I am not 
actually calling LoadImage directly, but calling the DirectX function 
'D3DXLoadTextureFromFile' which ends up calling LoadImage internally, passing 
NULL as the first parameter.

I guess I will have to go back to LoadImage for loading bmps into DirectX, 
and replicate the extra couple of features 'D3DXLoadTextureFromFile' provides 
that I use. 

Adam Dokter

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值