How to solve "Error 13 while loading file" when using Qemu? - operating-system

I write the following bytes into a file named disk.img
FA 8D 36 1B 7C E8 01 00 F4 AC 3C 00 74 0C B4 0E
BB 07 00 B9 01 00 CD 10 EB EF C3 4D 61 79 20 74
68 65 20 66 6F 72 63 65 20 62 65 20 77 69 74 68
20 79 6F 75 21 0D 0A 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
..enough zero to make the size of file 512bytes.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
The above bytes are proper instructions and magic number that should work when loading into the boot sector. But after I executed "qemu-X86_64 disk.img", error happens.
Error -13 while loading disk.img
Does anyone know how to solve the problem or what is the reason that might lead to this error?
Thank you!

I don't know if you can fill an image with just anything and expect it to work just because you have 55 AA in the correct place. Since you seem to be writing a bootloader make sure your code thinks it is executing at the correct place. It should be in offset 0x7C00 (if I remember this correctly, double check that). You set it by writing the line [org 0x7C00] at the top of your assembly file.
Also I'm not sure you can have only a 512 byte file. Try to make the disk image bigger than that using something like dd if=/dev/zero of=disk.img bs=512 count=2000 and then just copy your bootloader to the first part of the disk using dd again.
Also, you should use the -hda or -fda tags, so it would be qemu -hda disk.img. -hda means hard drive image, and -fda means floppy disk image.

Related

Visual Studio Code inserting non printable control characters

I am using VS Code 1.24.0 on macOS to edit YAML files that are saved to an NFS share (published on a QNAP NAS) and used by an Ubuntu 18 linux system.
When saving the YAML file VS Code often inserts a bunch of non-printable control characters which causes an error parsing the YAML. To fix it I need to open the file with vim and remove them.
00000110 20 73 65 72 76 65 72 3a 20 4e 41 53 31 0a 20 20 | server: NAS1. |
00000120 70 65 72 73 69 73 74 65 6e 74 56 6f 6c 75 6d 65 |persistentVolume|
00000130 52 65 63 6c 61 69 6d 50 6f 6c 69 63 79 3a 20 52 |ReclaimPolicy: R|
00000140 65 74 61 69 6e 00 00 00 00 00 00 00 00 00 00 00 |etain...........|
00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
0000027d
Note 1: It never happens if I use VS Code on the linux system and edit the files locally; but I need to use this as a headless server so this is not how I want to work.
Note 2: This appears to be a similar issue to that raised here some time ago - but no solution is available.

View managed stack from a full memory dump

My managed process is suspected to have caused a BSOD at a client site. I received a full memory dump (i.e.: including kernel, physical pages only) - but still am not able to inspect my process' stacks.
After switching to my process context -
.process /p /r <MyProcAddress>
I see only -
1: kd> k
# ChildEBP RetAddr
00 b56e3b70 81f2aa5d nt!KeBugCheckEx+0x1e
01 b56e3b94 81e7b68d nt!PspCatchCriticalBreak+0x71
02 b56e3bc4 81e6dfd1 nt!PspTerminateAllThreads+0x2d
03 b56e3bf8 8d48159a nt!NtTerminateProcess+0xcd
WARNING: Stack unwind information not available. Following frames may be wrong.
04 b56e3c24 81c845e4 klif+0x7559a
05 b56e3c24 77da6bb4 nt!KiSystemServicePostCall
06 0262f34c 00220065 ntdll!KiFastSystemCallRet
07 0262f390 003e0022 0x220065
08 0262f394 0073003c 0x3e0022
09 0262f398 00730079 0x73003c
0a 0262f39c 006e003a 0x730079
0b 0262f3a0 006d0061 0x6e003a
0c 0262f3a4 00200065 0x6d0061
0d 0262f3a8 00610076 0x200065
0e 0262f3ac 0075006c 0x610076
0f 0262f3b0 003d0065 0x75006c
10 0262f3b4 00770022 0x3d0065
11 0262f3b8 006e0069 0x770022
12 0262f3bc 006f0077 0x6e0069
13 0262f3c0 00640072 0x6f0077
14 0262f3c4 00200022 0x640072
...
Which is natural for managed process. SOS extension does not work for kernel dumps.
Is there anything I can do to view the throwing managed stack? It was previously said to be 'much more difficult', but hopefully not impossible.
PS.
I'm aware of the presence of Kaspersky driver kilf.sys in the stack, and this is my personal suspect. But the question is more general - hopefully there's a way to understand what my process was doing at the time.
the stack as you posted is not correct
it appears to be overwritten or is a result of some other artefact
with such a stack details you will have a hard time deciphering
anything useful at all
the contents of stack converted to a printable range in english looks like this
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 00 3E 00 22 00 22 00 65 00 73 00 3C 00 3E 00 22 .>.".".e.s.<.>."
00000010 00 73 00 79 00 73 00 3C 00 6E 00 3A 00 73 00 79 .s.y.s.<.n.:.s.y
00000020 00 6D 00 61 00 6E 00 3A 00 20 00 65 00 6D 00 61 .m.a.n.:. .e.m.a
00000030 00 61 00 76 00 20 00 65 00 75 00 6C 00 61 00 76 .a.v. .e.u.l.a.v
00000040 00 3D 00 65 00 75 00 6C 00 77 00 22 00 3D 00 65 .=.e.u.l.w.".=.e
00000050 00 6E 00 69 00 77 00 22 00 6F 00 77 00 6E 00 69 .n.i.w.".o.w.n.i
00000060 00 64 00 72 00 6F 00 77 00 20 00 22 00 64 00 72 .d.r.o.w. .".d.r
try !analyze -v and see what is the bsod analysis results

How is this real number encoded?

Number in HEX is: 3EB8 EDFE 19FE
I know it means 16.131 in DEC, but I don't know how it's encoded.
I checked if it's BCD, IEE 754 or integer, but none of those worked.
EDIT: It's from Parkin Elmer Clarus 400 chromatograph. This data contains time in minutes.
EDIT2: Here is part of parsed data, I only have one example:
Peak Component Time Area ISTD Resp ISTD Component glycerol
# Name [min] [uV*sec] Ratio Name Amount
4 glicerol 3,823 52377,25 0,316 butanotriol 0,0159
5 butanotriol 5,267 165539,60 1,000 ----------
suma mono 16,131 2086652,93 0,489 mono C19 0,4887
And here are corespodning parts of raw data:
67 6C 69 63 65 72 6F 6C FF FF FF FF 00 00 00 04 glicerol˙˙˙˙....
00 00 00 00 00 00 00 00 00 00 00 00 86 95 E6 F4 ............†•ćô
3F 40 7E AD 2D CE E1 7C 3F 90 47 23 62 CD D1 20 ?#~.-Îá|?.G#bÍŃ
3F 24 AB 9A DF CC 8E 91 3F D4 3F F2 F7 93 40 52
3F C9 6F 26 00 00 00 00 00 00 00 00 74 CB ED 69
40 0A 5A BC 13 46 09 AC 41 54 1A F3 A6 C3 FE 05
C0 A1 AB 83 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 3B
00 00 0F 6D 00 00 0F D3 00 4A 28 80 00 7D BD 1A
00 4A 7E 4C 26 4A 16 A5 40 73 80 A7 00 00 00 00
40 73 C0 00 4B 83 8C 12 40 74 42 C7 CB 2C BB 1F
41 04 35 1C EA 62 D1 6A 40 DE 7D C4 00 00 00 00
00 00 00 07 00 00 00 80 00 00 00 67 00 00 00 81
00 00 00 00 00 00 00 78 00 00 00 00 00 00 00 00
00 00 00 0B 62 75 74 61 6E 6F 74 72 69 6F 6C 00 ....butanotriol.
FF FF FF FF FF FF FF FF 00 00 00 01 00 00 00 00 ˙˙˙˙˙˙˙˙........
00 00 00 00 8A 95 47 69 3F 5A 10 F0 00 00 00 00 ....Š•Gi?Z.đ....
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
3F F0 00 00 00 00 00 00 3F F0 00 00 00 00 00 00 ?đ......?đ......
00 00 00 00 A0 E8 FC 91 40 12 27 A5 BD 8C D6 B0
41 50 62 7A 34 5A FA AA 40 9C 49 2B 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 16 7C 00 00 16 E8 00 00 16 ED
00 4D 8C D8 03 B4 0E 0F 03 9D 5F AA C2 8F 5C 29
40 7C C8 F5 71 CB 11 FD 40 7D 53 58 56 D6 7F A3
40 7D 5A 4B EA 10 78 82 41 41 1C 1A 6D 3D 44 F7
41 1F E5 8C 00 00 00 01 00 00 00 02 00 00 00 6E
00 00 00 64 00 00 00 6F 00 00 00 00 00 00 00 72
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF
FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00
88 30 F9 69 3F 96 12 04 84 DC 1A D0 40 01 F0 DF
47 2F E1 08 3F 96 C8 38 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
CD CA D9 96 40 1A F5 01 D4 4E 32 45 C1 78 D2 E7
74 5D 17 46 40 F0 7E 09 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 16 ED 00 00 16 F6 00 00 16 FA 03 9D 5F AA
03 F4 EA 71 03 E0 F9 FB 56 D6 7F A3 40 7D 5A 4B
88 CF 98 C1 40 7D 65 32 7A 5F 43 F9 40 7D 6A EC
E1 92 F1 30 41 21 24 D6 52 75 CC 80 41 21 20 40
00 00 00 01 00 00 00 02 00 00 00 6E 00 00 00 64
00 00 00 6F 00 00 00 00 00 00 00 72 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
73 75 6D 61 20 6D 6F 6E 6F 00 00 00 00 00 00 02 suma mono.......
00 00 00 01 51 EB 85 1F 40 8E 3E B8 ED FE 19 FE ....Që….#Ž>¸íţ.ţ
41 3F D6 FC F1 41 FD 57 41 18 42 2A 4A C8 FD 16 A?ÖüńAýWA.B*JČý.
41 3B 96 32 3C 2C 44 95 41 13 F2 D1 FF FF FF FF A;–2<,D•A.ňŃ˙˙˙˙
00 00 00 1E 00 00 00 02 69 85 75 9B 3F 94 89 17 ........i…u›?”‰.
08 08 4F F0 3F DF 47 16 39 EC E5 8C 3F 73 DB DD ..Ođ?ßG.9ěĺŚ?sŰÝ
08 08 4F EF 3F DF 47 16 08 08 4F F0 3F DF 47 16 ..Oď?ßG...Ođ?ßG.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

What are the three 255's before the image in a monochrome .bmp?

I'm trying to figure out bitmap formatting for a project I'm working on, but there's one thing I don't really get. In this .bmp:
00000000 42 4d aa 00 00 00 00 00 00 00 82 00 00 00 6c 00 |BM............l.|
00000010 00 00 0a 00 00 00 0a 00 00 00 01 00 01 00 00 00 |................|
00000020 00 00 28 00 00 00 13 0b 00 00 13 0b 00 00 02 00 |..(.............|
00000030 00 00 02 00 00 00 42 47 52 73 00 00 00 00 00 00 |......BGRs......|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000060 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 |................|
00000080 00 00 ff c0 00 00 ff c0 00 00 ff c0 00 00 ff c0 |................|
*
000000a0 00 00 ff c0 00 00 aa 80 00 00 |..........|
000000aa
what are the three 256's at offset 122 (line 7). I assume they're some sort of color indicators, but I'm not sure.
This is the image I'm using.
The top part of your hex dump is a "BITMAPINFO structure" (https://msdn.microsoft.com/en-us/library/dd183375(v=vs.85).aspx). This is immediately followed by a color index array bmiColors (although its length may be 0, and you should check this in the BITMAPINFO data).
Although some say that
[t]hough seemingly a simple format, it is complicated by its many different versions, lack of an official specification, lack of any version control process, and ambiguities and contradictions in the documentation.
(http://fileformats.archiveteam.org/wiki/BMP)
you don't actually need to understand each single byte. The various structures either have a fixed size (the initial BITMAPFILEHEADER for example), or have their length as its first value.
A line by line annotation of most well-documented parts:
-------- BITMAPFILEHEADER
00000000 42 4d file type identifier
00000002 aa 00 00 00 size in bytes of the entire file
00000006 00 00 (reserved and must be 0)
00000008 00 00 (reserved and must be 0)
0000000A 82 00 00 00 offset from the beginning of the BITMAPFILEHEADER structure to the bitmap bits
-------- BITMAPINFOHEADER
0000000E 6c 00 00 00 BITMAPINFOHEADER structure size
00000012 0a 00 00 00 image width in pixels
00000016 0a 00 00 00 image height in pixels
0000001A 01 00 number of planes
0000001C 01 00 number of bits per pixel
0000001E 00 00 00 00 compression
00000022 28 00 00 00 size in bytes of image data
00000026 13 0b 00 00 horizontal resolution in pixels-per-meter
0000002A 13 0b 00 00 vertical resolution in pixels-per-meter
0000002E 02 00 00 00 number of colors in the color table that are actually used by the bitmap
00000032 02 00 00 00 number of color indexes that are required ("important")
........ badly documented stuff ........
00000036 42 47 52 73 00 00 00 00 00 00
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000060 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00
00000070 00 00 00 00 00 00 00 00 00 00
-------- end of BITMAPINFOHEADER
-------- bmiColors array
0000007A ff ff ff 00 color table entry #0
0000007E 00 00 00 00 color table entry #1
-------- Image data
00000082 ff c0 00 00 ff c0 00 00 ff c0 00 00 ff c0
000000a0 00 00 ff c0 00 00 aa 80 00 00
The "number of bits per pixel" at 0000001C is 1:
"1 = The bitmap is monochrome, and the bmiColors member of BITMAPINFO contains two entries. Each bit in the bitmap array represents a pixel. If the bit is clear, the pixel is displayed with the color of the first entry in the bmiColors table; if the bit is set, the pixel has the color of the second entry in the table."
(https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx)
and the number of colors in the array is reported to be 2. So the bmiColors array contains 2 elements in Microsoft's RGBQUAD format, with values in the odd order Blue, Green, Red, and Reserved.
In short: in your image, a pixel value of 0 (a 0 bit in a monochrome image) is FFFFFF: white, and a pixel value of 1 is 000000: black.

Can I trick libc (GLIBC_2.13) into loading a symbol it doesn't have (from GLIBC_2.15)?

In my attempt to get "Steam for Linux" working on Debian, I've run into an issue. libcef (Chromium Embedded Framework) works fine with GLIBC_2.13 (which eglibc on Debian testing can provide), but requires one pesky little extra function from GLIBC_2.15 (which eglibc can't provide):
$ readelf -s libcef.so | grep -E "#GLIBC_2\.1[4567]"
1037: 00000000 0 FUNC GLOBAL DEFAULT UND __fdelt_chk#GLIBC_2.15 (49)
2733: 00000000 0 FUNC GLOBAL DEFAULT UND __fdelt_chk##GLIBC_2.15
My plan of attack here was to LD_PRELOAD a shim library that provides just these functions. This doesn't seem to work. I really want to avoid installing GLIBC_2.17 (since it is in Debian experimental; even Debian sid still has GLIBC_2.13).
This is what I've tried.
fdelt_chk.c is basically stolen from the GNU C library:
#include <sys/select.h>
# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
# define _strong_alias(name, aliasname) \
extern __typeof (name) aliasname __attribute__ ((alias (#name)));
unsigned long int
__fdelt_chk (unsigned long int d)
{
if (d >= FD_SETSIZE)
__chk_fail ();
return d / __NFDBITS;
}
strong_alias (__fdelt_chk, __fdelt_warn)
My Versions script looks as follows:
GLIBC_2.15 {
__fdelt_chk; __fdelt_warn;
};
I then build the library as follows:
$ gcc -m32 -c -fPIC fdelt_chk.c -o fdelt_chk.o
$ gcc -m32 -shared -nostartfiles -Wl,-s -Wl,--version-script Versions -o fdelt_chk.so fdelt_chk.o
However, if I then run Steam (with a bunch of extra stuff to get it working in the first place), the loader still refuses to find the symbol:
% LD_LIBRARY_PATH="/home/tinctorius/.local/share/Steam/ubuntu12_32" LD_PRELOAD=./fdelt_chk.so:./steamui.so ./steam
./steam: /lib/i386-linux-gnu/i686/cmov/libc.so.6: version `GLIBC_2.15' not found (required by /home/tinctorius/.local/share/Steam/ubuntu12_32/libcef.so)
However, the version symbol is also provided by the .so I just built:
% readelf -s fdelt_chk.so
Symbol table '.dynsym' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __chk_fail#GLIBC_2.3.4 (3)
2: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS _edata
3: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS _end
4: 00000310 44 FUNC GLOBAL DEFAULT 11 __fdelt_warn##GLIBC_2.15
5: 00000310 44 FUNC GLOBAL DEFAULT 11 __fdelt_chk##GLIBC_2.15
6: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.15
7: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
At this point, I don't know what I can do to trick the loader (who?) into choosing my symbols. Am I going in the right direction at all?
I ran into this same problem, though not with Steam. What I was trying to run wanted 2.15 for fdelt_chk while my system had 2.14. I found a solution for simple cases like ours where we can easily provide our own implementation for the missing functionality.
I started out from your attempted solution of implementing the functionality and LD_PRELOADing it. Using LD_DEBUG=all (as suggested by osgx) showed that the linker was still looking for 2.15, so just having the right symbol wasn't enough and there was some other versioning mechanism somewhere. I noticed that objdump -p and readelf -V both showed references to 2.15, so I looked up documentation on ELF and found information on version requirements.
So my new goal was to transform references to 2.15 into references to something else. It seemed reasonable that I could just overwrite structures that referred to 2.15 with the structures that referred to some lower version, like 2.1. In the end, after some trial and error, I found just editing the right Elfxx_Vernaux(es?) in .gnu.version_r was sufficient, but caveat hacker, I guess.
The .gnu.version_r section is a list of 16-byte Elfxx_Verneeds and 16-byte Elfxx_Vernauxes. Each Elfxx_Verneed entry is followed by the associated Elfxx_Vernauxes. As far as I could tell, vn_file is actually how many associated Elfxx_Vernauxes there are, even though the docs say number of associated verneed array entries. It might just be a misunderstanding on my part, though.
So, to start off making the edits, let's look at some of the info from readelf -V. I snipped out parts we don't care about.
$ readelf -V mybinary
<snip stuff before .gnu.version_r>
Version needs section '.gnu.version_r' contains 5 entries:
Addr: 0x00000000000021ac Offset: 0x0021ac Link: 4 (.dynstr)
<snip libraries that don't refer to GLIBC_2.15>
0x00c0: Version: 1 File: libc.so.6 Cnt: 10
0x00d0: Name: GLIBC_2.3 Flags: none Version: 19
0x00e0: Name: GLIBC_2.7 Flags: none Version: 16
0x00f0: Name: GLIBC_2.2 Flags: none Version: 15
0x0100: Name: GLIBC_2.2.4 Flags: none Version: 14
0x0110: Name: GLIBC_2.1.3 Flags: none Version: 13
0x0120: Name: GLIBC_2.15 Flags: none Version: 12
0x0130: Name: GLIBC_2.4 Flags: none Version: 10
0x0140: Name: GLIBC_2.1 Flags: none Version: 9
0x0150: Name: GLIBC_2.3.4 Flags: none Version: 4
0x0160: Name: GLIBC_2.0 Flags: none Version: 2
From this we see that the section starts at 0x21ac. Each file listed will have a Elfxx_Verneed followed by an Elfxx_Vernaux for each of the subentries (like GLIBC_2.3). I assume the order of the info in the output will always match the order in the file since readelf is just dumping the structures. Here's my entire .gnu.version_r section.
000021A0 01 00 02 00
000021B0 A3 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
000021C0 00 00 11 00 32 0D 00 00 10 00 00 00 10 69 69 0D
000021D0 00 00 0B 00 3C 0D 00 00 00 00 00 00 01 00 02 00
000021E0 BE 0C 00 00 10 00 00 00 30 00 00 00 13 69 69 0D
000021F0 00 00 08 00 46 0D 00 00 10 00 00 00 10 69 69 0D
00002200 00 00 07 00 3C 0D 00 00 00 00 00 00 01 00 02 00
00002210 99 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
00002220 00 00 06 00 32 0D 00 00 10 00 00 00 10 69 69 0D
00002230 00 00 05 00 3C 0D 00 00 00 00 00 00 01 00 02 00
00002240 AE 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
00002250 00 00 12 00 32 0D 00 00 10 00 00 00 10 69 69 0D
00002260 00 00 03 00 3C 0D 00 00 00 00 00 00 01 00 0A 00
00002270 FF 0C 00 00 10 00 00 00 00 00 00 00 13 69 69 0D
00002280 00 00 13 00 46 0D 00 00 10 00 00 00 17 69 69 0D
00002290 00 00 10 00 50 0D 00 00 10 00 00 00 12 69 69 0D
000022A0 00 00 0F 00 5A 0D 00 00 10 00 00 00 74 1A 69 09
000022B0 00 00 0E 00 64 0D 00 00 10 00 00 00 73 1F 69 09
000022C0 00 00 0D 00 70 0D 00 00 10 00 00 00 95 91 96 06
000022D0 00 00 0C 00 7C 0D 00 00 10 00 00 00 14 69 69 0D
000022E0 00 00 0A 00 87 0D 00 00 10 00 00 00 11 69 69 0D
000022F0 00 00 09 00 32 0D 00 00 10 00 00 00 74 19 69 09
00002300 00 00 04 00 91 0D 00 00 10 00 00 00 10 69 69 0D
00002310 00 00 02 00 3C 0D 00 00 00 00 00 00
To briefly talk about the structure here, it starts out with an Elfxx_Verneed. As per the docs, we can see there will be 2 Elfxx_Vernauxes, one offset 16 bytes, and the next Elfxx_Verneed is offset 48 bytes. These offsets are from the start of the current structure. It looks like technically the associated Elfxx_Vernauxes might not be adjacent after the current Elfxx_Verneed but it was actually so in all the files I poked around in.
From this we can find the file we want (libc.so.6) in a few different ways. Cross reference the string (which I won't get into), find the Elfxx_Verneed with a count of 0A 00 (10, matching our readelf output above), or find the last Elfxx_Verneed since it's the last one readelf output. In any case, the right one for my file is at 0x226C. Its first Elfxx_Vernaux starts at 0x227C.
We want to find the Elfxx_Vernaux with a version of 0C 00 (12, again matching our readelf output above). We see the Elfxx_Vernaux that matches is at 0x22CC and the entire structure is 95 91 96 06 00 00 0C 00 7C 0D 00 00 10 00 00 00. We'll be overwriting the first 12 bytes so as to leave the offset alone. We're only modifying the data, not moving around the structures, after all.
To pick the data to overwrite with, we just copy it from a different Elfxx_Vernaux for a version of glibc we can satisfy. I picked one for 2.1, which is at 0x22EC in my file, with the data 11 69 69 0D 00 00 09 00 32 0D 00 00 10 00 00 00. So take the first 12 bytes from this and overwrite the first 12 bytes above, and that's it for the hex editing.
Of course, you might have multiple references to deal with. Your program might have multiple binaries to edit.
At this point, our program still won't run. But instead of being told something like GLIBC_2.15 not found it should complain about missing __fdelt_chk. Now we do the shim and LD_PRELOADing described in the question, except instead of versioning our implementation as 2.15, we use the version we picked while hex editing. At this point the program should run.
This method depends on being able to provide an implementation for whatever's missing. Our __fdelt_chk is extremely simple but I don't doubt that in some cases providing an implementation could be more difficult than just upgrading the system's libc instead.
For what it's worth, the __fdelt_chk function is related to the FORTIFY_SOURCE feature which was added in glibc 2.15. It enables compile-time and run-time checking for buffer overflows.
If you were able to recompile with the following CFLAGS added, it would build a backwards compatible binary without the extra checking:
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0