![redis 深度历险](https://i-blog.csdnimg.cn/blog_migrate/daec5263c7b73a73d766cfde59d784b2.png)
redis 深度历险
Ah, yes crazy bugs, they are my life. Here's today's saga. We did this from 9:30am until lunch, so we were able to figure it out in about two and half hours.
啊,是的,疯狂的虫子,它们是我的生命。 这是今天的传奇。 我们从上午9:30到午餐一直这样做,因此我们能够在大约两个半小时内弄清楚。
One of our systems retrieves Check Images (pictures of cleared checks). The Checks move through the system as Base64'ed strings and are eventually the separate front and back checks are displayed in the user's browser as a single image using a dynamic compositing technique I mentioned a while back.
我们的系统之一检索支票图像(已清除支票的图片)。 Checks作为Base64的字符串在系统中移动,最终使用我前面提到的动态合成技术在用户浏览器中将正面和背面的单独支票显示为单个图像。
However, it seemed that when we took the decoded from BASE64 schmutz and did basically this to convert the GIF to a JPEG:
但是,似乎当我们从BASE64 schmutz中获取解码后,基本上这样做是将GIF转换为JPEG:
using(MemoryStream m = new MemoryStream(bytes))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(m))
{
Response.ContentType = "image/jpeg";
image.Save(Response.OutputStream,ImageFormat.Jpeg);
}
}
We'd get an error from the bowels of System.Drawing that there was an "invalid parameter." Reflectoring showed that Image.FromStream is managed spackle over a GDI+ method.
我们将从System。的肠子中得到一个错误。画出一个“无效的参数”。 Reflectoring表明Image.FromStream是通过GDI +方法管理的。
|
I tried loading it into a number of picture viewers, most of which said nope. Surprisingly, IE didn't have a problem with it. This is odd to me because I thought the GDI+ security fixes would apply to IE, but not so.
我尝试将其加载到许多图片查看器中,其中大多数都说不。 令人惊讶的是,IE没问题。 这对我来说很奇怪,因为我认为GDI +安全修复程序将适用于IE,但并非如此。
To review - I've got a weird GIF that shows up in IE, but that .NET and GDI+ refuse to recognize. I could look for other image libraries that would "clean" the GIFs but that's reaching. The mainframe/host system that generates and holds these GIF isn't likely to change, and even if it did it wouldn't be fast enough for this implementation.
回顾一下-我在IE中显示了一个奇怪的GIF,但是.NET和GDI +拒绝识别。 我可能会寻找其他可以“清除” GIF的图像库,但是已经实现了。 生成并保存这些GIF的大型机/主机系统不太可能更改,即使这样做,对于该实现来说也不够快。
We could just pass it all the way through the system unmolested as the GIF that it is. This would WORK but only until browsers like IE became more security aware and started slapping down invalid GIFs like this one.
我们可以将其一直通过GIF完整地通过系统。 这将起作用,但是直到IE之类的浏览器变得更加安全,并开始像这样的无效GIF拍打。
So, these GIFs are invalid. But how? As with all things for me, I begin with Notepad2. I opened a bad example check image into Notepad2:
因此,这些GIF无效。 但是如何? 就像对我来说所有事情一样,我从Notepad2开始。 我在记事本2中打开了一个不好的示例检查图像:
First I notice that it's a GIF87a. Noteworthy only like an old piece of gray paper from Kindergarten is noteworthy. Then we (Patrick and I - at this point I've drafted him) notice that the alphabet and numbers appears a hundred bytes in. We figured that's the color table as they are triplets and this is a grayscale gif of 128 colors. But, without getting all 0xHex-y this early on, what else can I do to determine if this is a valid GIF or not? Well, I got it to display in IE before. I'll copy it (now a bitmap) to the clipboard and save it as a GIF. It'll likely save as a GIF89 because, hey, it's like 2 better, right?
首先,我注意到它是GIF87a。 值得一提的是,就像幼稚园里的旧灰纸一样。 然后,我们(帕特里克和我-至此,我已经起草了他)注意到字母和数字出现了一百个字节。我们认为这是色表,因为它们是三胞胎,并且是128种颜色的灰度gif。 但是,如果没有尽早获得所有0xHex-y,我该怎么办才能确定这是否是有效的GIF? 好吧,我之前可以在IE中显示它。 我将其(现在是位图)复制到剪贴板,并将其另存为GIF。 它可能会另存为GIF89,因为,嘿,它好2左右,对吧?
Here's the same graphic saved again. Ya, it looks totally different, so you assume my copy/paste was an invalid thing to do (in the scientific method sense). Well, hang in there, it gets worse. It's clearly a GIF89a and it clearly has a different color table. Otherwise, nothing here jumps out when comparing them with our eyes.
这是再次保存的同一图形。 是的,它看起来完全不同,因此您认为我的复制/粘贴是无效的操作(从科学方法的角度而言)。 好吧,挂在那里,情况变得更糟。 它显然是GIF89a,并且显然具有不同的色表。 否则,将它们与我们的眼睛进行比较时,这里什么都不会跳出来。
At this point, it's time to bite the bullet and decode the GIF header. We figure a GIF can be corrupt in two ways, either the header is bogus or the image data is. We'll do the easy one first. Time to pull out the June 15th, 1987 GIF spec from Compuserve.
现在,该该咬一下子弹并解码GIF标头了。 我们认为GIF可以通过两种方式损坏,即标头是伪造的,还是图像数据是伪造的。 我们先做一个简单的。 是时候从Compuserve中撤出1987年6月15日的GIF规范了。
Working structure by structure we produced this little nugget of uselessness:
我们逐个工作地构造了这个无用的小块:
using (FileStream f = File.Open(@"C:\Documents and Settings\shanselm\Desktop\bad.gif",FileMode.Open))
//using (FileStream f = File.Open(@"C:\Documents and Settings\shanselm\Desktop\good.gif",FileMode.Open))
{
using(BinaryReader reader = new BinaryReader(f))
{
string sigversion = new string(reader.ReadChars(6));
if (sigversion.StartsWith("GIF"))
{
ushort width = reader.ReadUInt16();
ushort height = reader.ReadUInt16();
byte someshit = reader.ReadByte();
int colortable = someshit & 0x7;
byte bgcolor = reader.ReadByte();
byte apsectratio = reader.ReadByte();
int logicallength = (int)Math.Pow(2,colortable+1);
int colortablelength = (int)(3 * logicallength);
RGB[] RGBs = new RGB[logicallength];
for (int i = 0; i < logicallength; i++)
{
RGB rgb = RGBs[i];
rgb.R = reader.ReadByte();
rgb.G = reader.ReadByte();
rgb.B = reader.ReadByte();
RGBs[i] = rgb;
}
//Image Descriptor
byte imageseparator = reader.ReadByte();
uint leftpos = reader.ReadUInt16();
uint toppos = reader.ReadUInt16();
uint widthagain = reader.ReadUInt16();
uint heightagain = reader.ReadUInt16();
byte localcolortableflags = reader.ReadByte();
int localcolortablepresent = localcolortableflags & 0x80;
int interlace = localcolortableflags & 0x40;
int sortbit = localcolortableflags & 0x20;
int localcolortable = localcolortableflags & 0x07;
//We figured if the header was bad we'd mess with it in this process somewhere
// then if we fixed it in the byte[], we'd fall through to the code below
}
}
using (MemoryStream m = new MemoryStream(bytes))
{
using (Image image = Image.FromStream(m))
{
image.Save("foo.jpg",ImageFormat.Jpeg);
}
}
Well, crap. We made it all this way and there didn't appear to be ANYTHING (per spec) wrong with the GIF header. We checked everything out in the Watch Window line by line. Nothing.
好吧,废话我们以这种方式完成了工作,GIF标头似乎没有任何错误(按照规范)。 我们在监视窗口中逐行检查了所有内容。 没有。
Ok, back to differences. How about checking them out in Beyond Compare?
好,回到差异。 如何在“超越比较”中检出它们?
Zoom in on that baby. Look real close. Notice in the upper left corner, there's not many differences. Remember that the old GIF87 is on the left, and the new one that I made via COPY/PASTE is on the right. The basic image data is the same, cool. So, really the only differences are the header, a byte or two in the middle, and what? What's that at the VERY BOTTOM RIGHT CORNER? A semicolon? In the valid image? WTF is that?
放大那个婴儿。 看起来真的很近。 请注意,在左上角没有太多区别。 请记住,旧的GIF87在左侧,而我通过COPY / PASTE制作的新GIF87在右侧。 基本图像数据是一样的,很酷。 那么,实际上唯一的区别是标头,中间的一两个字节,又是什么? 最右下角是什么? 分号? 在有效图片中? WTF是吗?
Hm...back to the spec. Since we've just decoded the header, perhaps there's a footer/trailer/terminator.
嗯...回到规格。 由于我们刚刚解码了标头,因此也许有一个页脚/尾随/终止符。
June 15, 1987
(c) CompuServe Incorporated, 1987
1987年6月15日(c)CompuServe Incorporated,1987年
GIF TERMINATOR
In order to provide a synchronization for the termination of a GIF
image file, a GIF decoder will process the end of GIF mode when the
character 0x3B hex or ';' is found after an image has been processed.
By convention the decoding software will pause and wait for an action
GIF终止符为了为GIF的终止提供同步图片文件时,GIF解码器将在GIF模式结束时处理字符0x3B十六进制或';' 在处理图像后找到。 按照惯例,解码软件将暂停并等待操作
Lovely. Do we have an off-by-one? Are we dropping the last byte as we go through the system?
可爱。 我们有一对一吗? 我们通过系统时是否要丢弃最后一个字节?
We go back to the system that sites just ahead of the mainframe check imager and request an image. We look at the byte array returned, and notice that the LAST BYTE IS MISSING. The images are trasmitted on a secure internal network using HTTP. The Content/Type is image/gif and the Content-Length HTTP Header, in this case, was 20814. That was exactly how many bytes were received.
我们回到位于大型机前面的系统检查成像器并请求图像。 我们查看返回的字节数组,并注意到LAST BYTE正在丢失。 使用HTTP在安全的内部网络上传输图像。 “内容/类型”为image / gif,“内容长度” HTTP标头在这种情况下为20814。这恰好是接收到的字节数。
So here's the question (that hasn't been answered):
所以这是问题(尚未得到回答):
Is it more likely that the host system has or is generating bogus/bad/invalid GIFs or that the Content-Length HTTP Header being returned by their unknown kind of Web Server is off by one and System.Net.HttpWebRequest is trusting what it's being told? I vote bogus GIFs, Patrick thinks bad Content-Length. Not sure if we'll ever know.
主机系统是否更有可能正在生成或正在生成虚假/错误/无效的GIF,或者由未知类型的Web服务器返回的Content-Length HTTP标头被一个关闭并且System.Net.HttpWebRequest信任它是什么告诉? 我投了假的GIF,Patrick认为Content-Length不好。 不知道我们是否会知道。
The fix, of course, was to check if the byte array representing this kind of GIF is terminated with 0x3b or not, and if not, append it. Once 0x3b was appended, System.Drawing and GDI+ had NO problem with the bytes.
当然,解决方法是检查代表这种GIF的字节数组是否以0x3b结尾,如果没有,则附加它。 一旦附加了0x3b,System.Drawing和GDI +的字节就没有问题了。
Crisis averted. Chao continues.
避免危机。 潮继续。
翻译自: https://www.hanselman.com/blog/adventures-in-debugging-expensive-semicolons-and-invalid-gifs
redis 深度历险