Access violation while requesting memory in getmem.inc - debugging

I'm trying to understand an access violation (c0000005) in my C++ program build with "Codegear C++ Builder 2009"
I have catched the access violation with procdump and analysed it with WinDbg.
Here is the information i gathered with WinDbg.:
Callstack:
# ChildEBP RetAddr Args to Child
>00 0d98f960 004b3728 0001e000 108c6af0 0001e000 MMIServer!SystemSysGetMem$qqri+0x316 [GETMEM.INC # 2015]
01 0d98f978 004b443f 0d98f988 004890b8 108d5b20 MMIServer!SysReallocMem+0x2dc [GETMEM.INC # 3404]
02 0d98f998 0048910c 0d98f9f4 108c6af0 00482730 MMIServer!ReallocMem+0x13 [System.pas # 3521]
03 0d98f9ac 00488a40 09b60b58 108c6af0 0000f000 MMIServer!TMemoryStreamWrite+0x30 [Classes.pas # 6181]
04 0d98f9bc 00488b0e 0d98fa04 00488b3c 0d98f9f4 MMIServer!ClassesTStreamWriteBuffer+0x18 [Classes.pas # 5789]
05 0d98f9f4 0046d910 0000fc40 00000000 0d98fa10 MMIServer!ClassesTStreamCopyFrom+0xae [Classes.pas # 5814]
06 0d98fa38 0046dc85 0d98faec 0046dcec 0d98fad8 MMIServer!TOPToSoapDomConvertDOMToStream+0x80 [..\..\Patches\BDS2009\TByteDynArrayThroughSOAP\OPToSOAPDomConv.pas # 895]
07 0d98fad8 0052c272 01ca3930 10680b90 10d4daa8 MMIServer!TOPToSoapDomConvertMakeResponse+0x31d [..\..\Patches\BDS2009\TByteDynArrayThroughSOAP\OPToSOAPDomConv.pas # 987]
08 0d98fb94 0052eff9 0d98fcfb 10680b90 10680870 MMIServer!TSoapPascalInvokerInvoke+0x34e [SOAPPasInv.pas # 230]
09 0d98fc24 00526edb 0d98fcfb 10680b90 10680870 MMIServer!THTTPSoapPascalInvokerDispatchSOAP+0x20d [soaphttppasinv.pas # 82]
0a 0d98fd2c 00566d5b 10d1fee0 0d98fd6c 00566d83 MMIServer!THTTPSoapDispatcherDispatchRequest+0x28b [WebBrokerSOAP.pas # 223]
0b 0d98fd5c 00566e88 00000000 10d1fee0 0d98fda4 MMIServer!DispatchHandler+0x8b [HTTPApp.pas # 1511]
0c 0d98fd98 00567063 0046b308 0d98fdb0 0046b321 MMIServer!TCustomWebDispatcherDispatchAction+0xf0 [HTTPApp.pas # 1546]
0d 0d98fe10 0046b205 08dae450 02e3f360 09b21b70 MMIServer!TCustomWebDispatcherHandleRequest+0xb [HTTPApp.pas # 1594]
0e 0d98fe28 005b6e1f 08dae450 0d98fe40 005b6e43 MMIServer!TIdHTTPWebBrokerBridgeDoCommandGet+0x2d [..\..\Patches\Indy10\IdHTTPWebBrokerBridge.pas # 964]
0f 0d98fefc 005d86be 01c23370 005c7939 005c7783 MMIServer!IdcustomhttpserverTIdCustomHTTPServerDoExecute$qqrp20IdcontextTIdContext+0x683
10 0d98ff70 0048da71 0d98ff84 0048da7b 0d98ffa0 MMIServer!IdcontextTIdContextRun$qqrv+0x12
11 0d98ffa0 004b63f2 0d98ffdc 004b5e74 0d98ffb4 MMIServer!ThreadProc+0x45 [Classes.pas # 10892]
12 0d98ffb4 7c80b729 08b3e090 030afabc 00000000 MMIServer!ThreadWrapper+0x2a [System.pas # 13819]
CodeGear\RAD Studio\6.0\source\Win32\rtl\sys\getmem.inc:
2004 #GotBinAndGroup:
2005 {ebx = block size, ecx = bin number, edx = group number}
2006 push esi
2007 push edi
2008 {Get a pointer to the bin in edi}
2009 lea edi, [MediumBlockBins + ecx * 8]
2010 {Get the free block in esi}
2011 mov esi, TMediumFreeBlock[edi].NextFreeBlock
2012 {Remove the first block from the linked list (LIFO)}
2013 mov eax, TMediumFreeBlock[esi].NextFreeBlock
2014 mov TMediumFreeBlock[edi].NextFreeBlock, eax
>2015 mov TMediumFreeBlock[eax].PreviousFreeBlock, edi
2016 {Is this bin now empty?}
2017 cmp edi, eax
2018 jne #MediumBinNotEmptyForMedium
2019 {eax = bin group number, ecx = bin number, edi = #bin, esi = free block, ebx = block size}
2020 {Flag this bin as empty}
2021 mov eax, -2
2022 rol eax, cl
2023 and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
2024 jnz #MediumBinNotEmptyForMedium
2025 {Flag the group as empty}
2026 btr MediumBlockBinGroupBitmap, edx
2027 #MediumBinNotEmptyForMedium:
Disassembly:
004b31d4 56 push esi
004b31d5 57 push edi
004b31d6 8d3ccd00be3201 lea edi,MMIServer!NeverSleepOnMMThreadContention+0x1ef (0132be00)[ecx*8]
004b31dd 8b7704 mov esi,dword ptr [edi+4]
004b31e0 8b4604 mov eax,dword ptr [esi+4]
004b31e3 894704 mov dword ptr [edi+4],eax
>004b31e6 8938 mov dword ptr [eax],edi ds:0023:00000000=????????
004b31e8 39c7 cmp edi,eax
004b31ea 7517 jne MMIServer!SystemSysGetMem$qqri+0x333 (004b3203)
004b31ec b8feffffff mov eax,0FFFFFFFEh
004b31f1 d3c0 rol eax,cl
004b31f3 21049580bd3201 and dword ptr MMIServer!NeverSleepOnMMThreadContention+0x16f (0132bd80)[edx*4],eax
004b31fa 7507 jne MMIServer!SystemSysGetMem$qqri+0x333 (004b3203)
004b31fc 0fb3157cbd3201 btr dword ptr [MMIServer!NeverSleepOnMMThreadContention+0x16b (0132bd7c)],edx
My understanding of this piece of code of getmem.inc is:
There is a double-linked list of free memory blocks.
One block is taken from this list.
The Double Linked List is reconnected.
In drawing:
|----------|--------------------->|----------|--------------------->|----------|
| | NextFreeBlock | | NextFreeBlock | |
| 132d408 | | 11290132 | | 00000000 |
| |<---------------------| |<---------------------| |
|----------| PreviousFreeBlock |----------| PreviousFreeBlock |----------|
^ ^ ^
| | |
edi esi eax
|----------|--------------------->|----------| |----------|
| | NextFreeBlock | | | |
| 132d408 | | 00000000 | | 11290132 |
| |<---------------------| | | |
|----------| PreviousFreeBlock |----------| |----------|
^ ^ ^
| | |
edi eax esi
Some registers:
eax 0
ebx 1e030
ecx 2c1
edx 16
edi 132d408
esi 11290132
ebp 1e000
eip 4b31e6
esp d98f958
There is a NULL pointer in the Double Linked Lists of the free memory blocks.
When writing the PreviousFreeBlock to this address the access violation occurred.
How can there be a NULL pointer in the Double Linked Lists of the free memory blocks?
Was the memory already corrupted?
Have anyone experienced the same problem in getmem.inc?
What can i do to investigate this crash further?

Related

Gcc -g What happens?

The assembly file is obtained by using gcc -g -S, and the part of .s file is as follows:
.L3:
.loc 1 22 11
mov eax, DWORD PTR -12[rbp]
mov edx, eax
mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
call _ZNSolsEi
.loc 1 22 18
mov rdx, QWORD PTR .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_[rip]
mov rcx, rax
call _ZNSolsEPFRSoS_E
.loc 1 23 7
mov DWORD PTR -12[rbp], 0
.loc 1 12 2
add DWORD PTR -4[rbp], 1
jmp .L6
What does .loc 1 22 11 stand for?
When the -g flag is added to gcc it directs the compiler to add debugging information. .loc appears only when the compiler generates debugging information with -g flag:
https://sourceware.org/binutils/docs-2.38/as/Loc.html#Loc

How can I set a breakpoint to target this emulated location when it's at a different offset in WinDbg?

I'm using the qiling framework to emulate a snake game which runs fine in my x86 64 Windows environment, but it's failing in the emulated environment. It works fine run normally, but I'm having trouble getting a breakpoint to work in WinDbg at the place it's failing. My question is more about understanding my problem in WinDbg, but I'll provide the emulator logs for context:
[=] Initiate stack address at 0xfffdd000
[=] Loading snake.exe to 0x400000
[=] PE entry point at 0x4033ae
[=] TEB addr is 0x6000
[=] PEB addr is 0x6044
[=] Loading ../examples/rootfs/x8664_windows\Windows\System32\ntdll.dll ...
[!] Warnings while loading ../examples/rootfs/x8664_windows\Windows\System32\ntdll.dll:
[!] - SizeOfHeaders is smaller than AddressOfEntryPoint: this file cannot run under Windows 8.
[!] - AddressOfEntryPoint lies outside the sections' boundaries. AddressOfEntryPoint: 0x0
[=] Done with loading ../examples/rootfs/x8664_windows\Windows\System32\ntdll.dll
[=] Loading ../examples/rootfs/x8664_windows\Windows\System32\kernel32.dll ...
[=] Done with loading ../examples/rootfs/x8664_windows\Windows\System32\kernel32.dll
[=] Loading ../examples/rootfs/x8664_windows\Windows\System32\mscoree.dll ...
[=] Done with loading ../examples/rootfs/x8664_windows\Windows\System32\mscoree.dll
0x4033ae: jmp qword ptr [rip + 0x402000]
[!] api _CorExeMain is not implemented
This seems like a likely culprit, so I try to set a breakpoint at 0x4033ae in WinDbg with the command bu 0x4033ae. I also tried bp.
0x102bdbd1: push rbx
0x102bdbd3: sub esp, 0x20
0x102bdbd7: and dword ptr [rsp + 0x30], 0
0x102bdbdd: lea ecx, [rsp + 0x30]
0x102bdbe1: call 0x102b4548
0x102b4549: push rbx
0x102b454b: sub esp, 0x20
0x102b454e: mov eax, dword ptr [rip + 0x5b4dc]
[x] CPU Context:
[x] ah : 0xff
... snip ...
[x] gs : 0x78
[x] Hexdump:
[x] 8b 05 dc b4 05 00 48 8b
[x] Disassembly:
[=] 102b454e [mscoree.dll + 0x00154e] 8b 05 dc b4 05 00 48 8b d9 85 c0 75 05 e8 c4 fc ff ff 8b 05 ca b4 05 00 83 f8 02 75 0f 48 85 db 74 0a 48 8b 05 c9 b4 05 00 48 89 03 8b 05 b0 b4 05 00 48 83 c4 20 5b c3 cc cc cc cc cc cc cc ccmov eax, dword ptr [0x5b4dc]
> dec eax
> mov ebx, ecx
> test eax, eax
> jne 0x102b4560
> call 0x102b4224
> mov eax, dword ptr [0x5b4ca]
> cmp eax, 2
> jne 0x102b457a
> dec eax
> test ebx, ebx
> je 0x102b457a
> dec eax
> mov eax, dword ptr [0x5b4c9]
> dec eax
> mov dword ptr [ebx], eax
> mov eax, dword ptr [0x5b4b0]
> dec eax
> add esp, 0x20
> pop ebx
> ret
> int3
> int3
> int3
> int3
> int3
> int3
> int3
> int3
[x] PC = 0x102b454e (../examples/rootfs/x8664_windows\Windows\System32\mscoree.dll + 0x154e)
[=] Memory map:
[=] Start End Perm Label Image
[=] 00006000 - 0000c000 rwx [FS/GS]
[=] 00030000 - 00031000 rwx [GDT]
[=] 00400000 - 00408000 rwx [PE] snake.exe
[=] 05000000 - 05001000 rwx [heap]
[=] 06000000 - 0c000000 rwx [FS/GS]
[=] 10000000 - 101f5000 rwx ntdll.dll ../examples/rootfs/x8664_windows\Windows\System32\ntdll.dll
[=] 101f5000 - 102b3000 rwx kernel32.dll ../examples/rootfs/x8664_windows\Windows\System32\kernel32.dll
[=] 102b3000 - 10318000 rwx mscoree.dll ../examples/rootfs/x8664_windows\Windows\System32\mscoree.dll
[=] fffdd000 - ffffe000 rwx [stack]
Traceback (most recent call last):
... snip ...
File "C:\Users\jonat\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\unicorn\unicorn.py", line 465, in emu_start
raise UcError(status)
unicorn.unicorn.UcError: Invalid memory mapping (UC_ERR_MAP)
In WinDbg, I get:
CommandLine: C:\Users\jonat\Documents\GitHub\synthesis\obfu\snake.exe
************* Path validation summary **************
Response Time (ms) Location
Deferred srv*
Symbol search path is: srv*
Executable search path is:
ModLoad: 00e60000 00e68000 ConsoleGraphics.exe
ModLoad: 770f0000 77293000 ntdll.dll
ModLoad: 74810000 74862000 C:\WINDOWS\SysWOW64\MSCOREE.DLL
ModLoad: 74fb0000 750a0000 C:\WINDOWS\SysWOW64\KERNEL32.dll
ModLoad: 75fa0000 761b5000 C:\WINDOWS\SysWOW64\KERNELBASE.dll
(9b8.7854): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=3c560000 edx=00000000 esi=77102054 edi=7710261c
eip=771a1ba2 esp=00fff9cc ebp=00fff9f8 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2b:
771a1ba2 cc int 3
Which seems to be a standard breakpoint triggered by ntdll but by the time this is triggered, we have already passed the address 0x4033ae where I was trying to apply it. I realized that this is probably because the addressing scheme of the process seems to be mapped differently by the execution context within my OS / WinDbg and the qiling emulation. How can I begin debugging this problem, or at least finding the relevant breakpoint in WinDbg.
the query isn't exactly related to windbg
as I commented Qiling Framework hasn't yet implemented dotnet and needs someone to contribute the implementation
since this query has a windbg tag and a debugging tag too and
I have been wanting to test the Qiling framework for sometime now on a windows machine
I took this query as an opportunity to do so
Qiling is built upon unicorn emulation framework
I have dabbled with unicorn and found it quiet useful
installed Qiling [pip3 install Qiling] on a x64 windows10 machine
windows documentation is quiet scarce and the one example disasm_x886_windows.py indicated in github repo is missing in the repo
had to scratch around to get a working setup
after installing Qiling it needs a virtual filesystem to operate upon with pertient windows dlls and registry hives
this is done by using the dllcollector.bat provided by in the repo
basically the collector.bat xcopies relevent 32 bit and 64 bit dlls and reg saves registry hives
f:\>md QILING
f:\>cd QILING
f:\QILING>ls
f:\QILING>f:\wget\wget.exe -c https://raw.githubusercontent.com/qilingframework/qiling/master/examples/scripts/dllscollector.bat
2021-11-14 03:03:05 (1.28 MB/s) - 'dllscollector.bat' saved [10085/10085]
f:\QILING>ls
dllscollector.bat
f:\QILING>file dllscollector.bat
dllscollector.bat: DOS batch file, ASCII text, with very long lines
f:\QILING>dllscollector.bat
Does F:\QILING\examples\rootfs\x8664_windows\Windows\registry\NTUSER.DAT specify a file name
or directory name on the target
(F = file, D = directory)? f
C:\Users\Default\NTUSER.DAT -> F:\QILING\examples\rootfs\x8664_windows\Windows\registry\NTUSER.DAT
1 File(s) copied
The operation completed successfully.
snip all copy and save operations
f:\QILING>ls
dllscollector.bat examples
now that we have collected the dlls lets copy two test binaries
one an x64 console app and
another a .net console binary and
write a python script to emulate them using QILING framework
f:\QILING>ls
dllscollector.bat examples
f:\QILING>md testqiling
f:\QILING>xcopy ..\tbins .\testqiling\
..\tbins\mcall.exe
..\tbins\printxcode.exe
..\tbins\qiliwin.py
3 File(s) copied
f:\QILING>cd testqiling
f:\QILING\testqiling>file *
mcall.exe: PE32+ executable (GUI) x86-64, for MS Windows
printxcode.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows
qiliwin.py: Python script, ASCII text executable, with CRLF line terminators
f:\QILING\testqiling>printxcode.exe |head -n 2
HResult is 80070057 xcode is E0434352 Value does not fall within the expected range.
HResult is 80004003 xcode is E0434352 Value cannot be null.
f:\QILING\testqiling>start /wait mcall.exe
f:\QILING\testqiling>echo %errorlevel%
1677
the script as follows
the stop_on_exit_trap is added to avoid crashing of mcall.exe when it returns to crt from main() due to unreachable PC (0x0 as rip)
trace traces and prints all executed instructions
verbose provides some additional logs
f:\QILING\testqiling>cat qiliwin.py
import os
from qiling import *
from qiling.const import QL_VERBOSE
from qiling.extensions import trace
os.system('') #bug explotation to make ansi colors
rootfs = r"F:\QILING\examples\rootfs\x8664_windows"
bin2exec = [
r"F:\QILING\testqiling\mcall.exe",
r"F:\QILING\testqiling\printxcode.exe"
]
for binary in bin2exec:
print("executing binary\n=====================\n%s\n=====================\n" % binary);
ql = Qiling([binary],rootfs,verbose=QL_VERBOSE.DEBUG,stop_on_exit_trap=True)
trace.enable_full_trace(ql)
ql.run()
executing the script we get
qiling.exception.QlErrorFileNotFound: Cannot find dll in F:\QILING\examples\rootfs\x8664_windows\Windows\System32\mscoree.dll
copying the mscoree.dll from system32 to rootfs/system2 and checking it crashes again with unmapped error as pointed in query
lets open the .net binary in an x64 windbg and check
F:\QILING\testqiling>cdb -c "sxe ld:mscoree;g;q" printxcode.exe | awk /Reading/,/quit/
0:000> cdb: Reading initial command 'sxe ld:mscoree;g;q'
ModLoad: 00000000`77e30000 00000000`77e39000 C:\WINDOWS\System32\wow64cpu.dll
ModLoad: 00000000`73f90000 00000000`73fe2000 C:\WINDOWS\SysWOW64\MSCOREE.DLL
quit:
so this binary needs the mscoree from syswow
f:\QILING\testqiling>copy c:\Windows\SysWOW64\mscoree.dll F:\QILING\examples\rootfs\x8664_windows\Windows\System32\.
Overwrite F:\QILING\examples\rootfs\x8664_windows\Windows\System32\.\mscoree.dll? (Yes/No/All): y
1 file(s) copied.
Execution now doesnt crash
F:\QILING\testqiling>python qiliwin.py
executing binary
=====================
F:\QILING\testqiling\mcall.exe
=====================
[+] Profile: Default
[+] Windows Registry PATH: F:\QILING\examples\rootfs\x8664_windows\Windows\registry
[=] Initiate stack address at 0x7ffffffde000
[=] Loading F:\QILING\testqiling\mcall.exe to 0x140000000
[=] PE entry point at 0x140001030
[=] TEB addr is 0x6000030
[=] PEB addr is 0x60000b8
[=] Loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll ...
[!] Warnings while loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll:
[!] - SizeOfHeaders is smaller than AddressOfEntryPoint: this file cannot run under Windows 8.
[!] - AddressOfEntryPoint lies outside the sections' boundaries. AddressOfEntryPoint: 0x0
[+] DLL preferred base address: 0x180000000
[=] Done with loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll
[=] Loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\kernel32.dll ...
[+] DLL preferred base address: 0x180000000
[+] DLL preferred base address is taken, loading to: 0x1801f0000
[=] Done with loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\kernel32.dll
[+] Done with loading F:\QILING\testqiling\mcall.exe
[+] Setting up exit trap at 0x0x140004000
[+] 140001030 | 4883ec48 sub rsp, 0x48 | rsp = 0x0
[+] 140001034 | 41b803000000 mov r8d, 0x3 |
[+] 14000103a | ba02000000 mov edx, 0x2 |
[+] 14000103f | b901000000 mov ecx, 0x1 |
[+] 140001044 | e8b7ffffff call 0x140001000 | rsp = 0x0, rip = 0x0
[+] 140001000 | 4489442418 mov dword ptr [0x18], r8d | rsp = 0x0, r8d = 0x0
[+] 140001005 | 89542410 mov dword ptr [0x10], edx | rsp = 0x0, edx = 0x2
[+] 140001009 | 894c2408 mov dword ptr [0x8], ecx | rsp = 0x0, ecx = 0x1
[+] 14000100d | 8b442410 mov eax, dword ptr [0x10] | rsp = 0x0
[+] 140001011 | 8b4c2408 mov ecx, dword ptr [0x8] | rsp = 0x0
[+] 140001015 | 03c8 add ecx, eax | ecx = 0x1, eax = 0x2
[+] 140001017 | 8bc1 mov eax, ecx | ecx = 0x3
[+] 140001019 | 03442418 add eax, dword ptr [0x18] | eax = 0x3, rsp = 0x0
[+] 14000101d | c3 ret | rsp = 0x0
[+] 140001049 | 89442428 mov dword ptr [0x28], eax | rsp = 0x0, eax = 0x6
[+] 14000104d | 41b806000000 mov r8d, 0x6 |
[+] 140001053 | ba07000000 mov edx, 0x7 |
[+] 140001058 | b908000000 mov ecx, 0x8 |
[+] 14000105d | e89effffff call 0x140001000 | rsp = 0x0, rip = 0x0
snipoff
[+] 140004000 | 90 nop |
[=] Process returned from entrypoint (exit_trap)!
[+] Syscalls called:
[+] Registries accessed:
[+] Strings:
executing binary
=====================
F:\QILING\testqiling\printxcode.exe
=====================
[+] Profile: Default
[+] Map GDT at 0x30000 with GDT_LIMIT=4096
[+] Write to 0x30018 for new entry b'\x00\xf0\x00\x00\x00\xfeO\x00'
[+] Write to 0x30028 for new entry b'\x00\xf0\x00\x00\x00\x96O\x00'
[+] Write to 0x30070 for new entry b'\x00`\x00`\x00\xf6#\x00'
[+] Write to 0x30078 for new entry b'\x00\x00\x00\x00\x00\xf6#\x06'
[+] Windows Registry PATH: F:\QILING\examples\rootfs\x8664_windows\Windows\registry
[=] Initiate stack address at 0xfffdd000
[=] Loading F:\QILING\testqiling\printxcode.exe to 0x400000
[=] PE entry point at 0x402eda
[=] TEB addr is 0x6000
[=] PEB addr is 0x6044
[=] Loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll ...
[!] Warnings while loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll:
[!] - SizeOfHeaders is smaller than AddressOfEntryPoint: this file cannot run under Windows 8.
[!] - AddressOfEntryPoint lies outside the sections' boundaries. AddressOfEntryPoint: 0x0
[+] DLL preferred base address: 0x180000000
[+] DLL preferred base address exceeds memory upper bound, loading to: 0x10000000
[=] Done with loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\ntdll.dll
[=] Loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\kernel32.dll ...
[+] DLL preferred base address: 0x180000000
[+] DLL preferred base address exceeds memory upper bound, loading to: 0x101f0000
[=] Done with loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\kernel32.dll
[=] Loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\mscoree.dll ...
[+] DLL preferred base address: 0x10000000
[+] DLL preferred base address is taken, loading to: 0x102b0000
[=] Done with loading F:\QILING\examples\rootfs\x8664_windows\Windows\System32\mscoree.dll
[+] Done with loading F:\QILING\testqiling\printxcode.exe
[+] Setting up exit trap at 0x0xc000000
[+] 00402eda | ff2500204000 jmp dword ptr [0x402000] |
[!] api _CorExeMain is not implemented
[+] 102c4330 | 8bff mov edi, edi | edi = 0x0
[+] 102c4332 | 56 push esi | esp = 0x0, esi = 0xffffd000
snipoff
[+] 0c000000 | 90 nop |
[=] Process returned from entrypoint (exit_trap)!
[+] Syscalls called:
[+] Registries accessed:
[+] Strings:

GDB is off by a line when debugging a nasm application

I just encountered a weird issue with gdb, though I'm not sure, if I'm missing something.
Let's suppose I have these two files:
somefile.asm:
01 section .text
02
03 funca:
04 mov eax, 5
05 mov ebx, 5
06 cmp eax, ebx
07 je aisequal
08 mov ecx, 13
09 mov edx, 19
10 ret
11
12 aisequal:
13 mov ecx, 17
14 mov edx, 21
15 ret
and
somefile_test.asm:
01 %include "somefile.asm"
02
03 section .text
04 global _start
05
06 _start:
07 xor eax, eax
08 xor ebx, ebx
09 xor ecx, ecx
10 xor edx, edx
11 call funca
12
13 mov eax, 1
14 mov ebx, 0
15 int 0x80
I compile and link it using
nasm -f elf -g -F dwarf somefile_test.asm
ld -m elf_i386 -o somefile_test.out somefile_test.o
And then debug my application using gdb:
gdb somefile_test.out
I now set a breakpoint in the imported file:
GNU gdb (GDB) 10.1
(gdb) b somefile.asm:5
Breakpoint 1 at 0x8049000: file somefile.asm, line 5.
(gdb) r
Starting program: /<bla>/somefile_test.out
Breakpoint , funca () at somefile.asm:5
5 mov ebx, 5
Now appearantly, the execution stopped at the correct position. The next line to be executed would be 5, which is mov ebx, 5.
However, the last line should've been mov eax, 5 which should have already been executed. It was not:
(gdb) i r eax
eax 0x0 0
It gets even weirder:
(gdb) si
6 cmp eax, ebx
(gdb) i r eax ebx
eax 0x5 5
ebx 0x0 0
Now, eax is set, but ebx is not (yet).
If I execute the next line, it is set:
(gdb) si
7 je aisequal
(gdb) i r eax ebx
eax 0x5 5
ebx 0x5 5
However, I'd expect the program to jump to line 12 (aisequal) now, but it doesn't:
(gdb) si
8 mov ecx, 13
On the next instruction, it suddenly goes to the right line:
(gdb) si
14 mov edx, 21
(gdb) i r eax ebx edx
eax 0x5 5
ebx 0x5 5
edx 0x0 0
And so on:
(gdb) si
15 ret
(gdb) i r eax ebx ecx edx
eax 0x5 5
ebx 0x5 5
ecx 0x11 17
edx 0x0 0
If I put all my code in a single file, everything works as expected:
01 section .text
02 global _start
03
04 _start:
05 xor eax, eax
06 xor ebx, ebx
07 xor ecx, ecx
08 xor edx, edx
09 call funca
10
11 mov eax, 1
12 mov ebx, 0
13 int 0x80
14
15 funca:
16 mov eax, 5
17 mov ebx, 5
18 cmp eax, ebx
19 je aisequal
20 mov ecx, 13
21 mov edx, 19
22 ret
23
24 aisequal:
25 mov ecx, 17
26 mov edx, 21
27 ret
GBU gdb (GDB) 10.1
(gdb) b 16
Breakpoint 1 at 0x8049019: file singlefile.asm, line 16.
(gdb) r
Starting program: /<bla>/singlefile.out
Breakpoint 1, funca () at singlefile.asm:16
16 mov eax, 5
(gdb) i r eax ebx ecx edx
eax 0x0 0
ebx 0x0 0
ecx 0x0 0
edx 0x0 0
(gdb) si
17 mov ebx, 5
(gdb) i r eax ebx ecx edx
eax 0x5 5
ebx 0x0 0
ecx 0x0 0
edx 0x0 0
(gdb) si
18 cmp eax, ebx
(gdb) si
19 je aisequal
(gdb) si
25 mov ecx, 17
(gdb) si
26 mov edx, 21
(gdb) i r eax ebx ecx edx
eax 0x5 5
ebx 0x5 5
ecx 0x11 17
edx 0x0 0
(gdb) si
aisequal () at singlefile.asm:27
27 ret
(gdb) i r eax ebx ecx edx
eax 0x5 5
ebx 0x5 5
ecx 0x11 17
edx 0x15 21
(gdb) si
_start () at singlefile.asm:11
11 mov eax, 1
Now I've only picked up gdb two days ago, so I'm not that familiar with it.
Can someone explain to me what's happening?
Is this a bug or am I missing something?
I am using
nasm 2.15.05-1
binutils 2.35.1-1
gdb 10.1-4
gcc 10.2.0-4
on Linux 5.9.14-arch1-1 #1 SMP PREEMPT Sat, 12 Dec 2020 14:37:12 +0000 x86_64 GNU/Linux
This looks like a bug in nasm. It didn't reproduce for me using nasm-2.14.
GDB will only display source info that the compiler/assembler tells it. If the assembler puts out incorrect info, then GDB will display that incorrect info and can't do anything about it.
To verify that the problem is in nasm, run objdump -dS somefile_test.o and compare the assembly and source listing. If they are also off by one, it's a bug in nasm.
Here is what I see:
somefile_test.o: file format elf32-i386
Disassembly of section .text:
00000000 <funca>:
section .text
funca:
mov eax, 5
0: b8 05 00 00 00 mov $0x5,%eax
mov ebx, 5
5: bb 05 00 00 00 mov $0x5,%ebx
cmp eax, ebx
a: 39 d8 cmp %ebx,%eax
je aisequal
c: 74 0b je 19 <aisequal>
Note how instructions and source perfectly line up.
Apparently, this is a regression in nasm.
Reported it to them.

read a character from a string via inline assembler in visual studio

I'm having a problem with the visual studio inline assembler and it doesn't seem to be loading the right values. I declare a C-string like so:
const char* str = "1235";
Then I access the string like so in asm:
movzx ebx, byte ptr str[esi]
the problem is that instead of loading 49 into ebx (ascii code for '1') it loads 0. I'm sure that esi is the right index because of extensive debugging. am I loading the string wrong?
The instruction
movzx ebx, byte ptr str[esi]
says to read the byte that is esi bytes after the start of str. It is the assembly language equivalent of
ebx = *(unsigned char*)(&str) + esi);
In your case, str is a pointer variable, so str[0] is the low 8 bits of the pointer, str[1] is bits 8-15, str[2] is bits 16-23, and str[3] is bits 24-31. Any value larger than 3 is a buffer overflow because you are reading memory that is not part of str.
What you actually want to do to load the str variable into a register, and then do indexed addressing from that register.
mov ebx, str
movzx ebx, byte ptr [ebx+esi]
In pictures:
+----+
1003 | 12 |
+----+
1002 | 34 |
+----+
1001 | 56 |
+----+
str 1000 | 78 |
+----+
+----+
1234567D | 00 |
+----+
1234567C | 45 |
+----+
1234567B | 44 |
+----+
1234567A | 43 |
+----+
12345679 | 42 |
+----+
12345678 | 41 |
+----+
You are trying to read byte str+esi, which reads part of the variable str. If esi is greater than 3, you are reading past the end of str.
What you actually want to do is read the thing that str points to. This means that you need to load str into a register (I chose ebx), then add esi to the result, then access the byte there.
Try:
movzx ebx, byte ptr str[esi]

Debugging assembly to find a static pointer for reference to a value in a game

I previously asked a question on here, but I was unregistered and wasn't able to edit my entry (not sure if you can) or add any information about the issue. I'm going to attempt to be more thorough this time so I can hopefully get an answer...
I'm trying to find a static pointer and a list of offsets so that I can easily find information in a game every time it's restarted. I've been successful with every piece of information but one...
Currently, I'm using CheatEngine to help me debug and find the pointer paths needed.
The address of the value I want (which changes with each game start) is currently: 849576A For reference, this is the first inventory slot of my first character. I know that each slot is offset by 20h and each character by 550h. So character two's first inventory slot is 849576A+550h. Again, these addresses change each restart, but the offsets do not.
Using CE, I can see what access this address... it returns the following opcodes:
These two are returned before doing anything in-game:
004b7ef9 - 0f bf 08 - movsx ecx,word ptr [eax]
004b542b - 0f bf 04 0a - movsx eax,word ptr [edx+ecx]
Then when shifting items in my inventory I get these:
74be5008 - 72 2a - jb memcpy+84
004bfc3a - 0f bf 4c 02 60 - movsx ecx,word ptr [edx+eax+60]
004bf43f - 8d 7d 9c - lea edi,[ebp-64]
I'm unsure of which to use, so I just pick one and set a breakpoint on one of them, I chose 004b542b, here's the complete code section:
004B53F0 | 55 | PUSH EBP |
004B53F1 | 8BEC | MOV EBP, ESP |
004B53F3 | 83EC 0C | SUB ESP, C |
004B53F6 | 894D F4 | MOV DWORD PTR [EBP-C], ECX |
004B53F9 | C745 FC 00000000 | MOV DWORD PTR [EBP-4], 0 |
004B5400 | 837D 08 00 | CMP DWORD PTR [EBP+8], 0 |
004B5404 | 7F 04 | JG 004B540A |
004B5406 | 33C0 | XOR EAX, EAX |
004B5408 | EB 43 | JMP 004B544D |
004B540A | C745 F8 0F000000 | MOV DWORD PTR [EBP-8], F |
004B5411 | EB 09 | JMP 004B541C |
004B5413 | 8B45 F8 | MOV EAX, DWORD PTR [EBP-8] |
004B5416 | 83C0 01 | ADD EAX, 1 |
004B5419 | 8945 F8 | MOV DWORD PTR [EBP-8], EAX |
004B541C | 837D F8 19 | CMP DWORD PTR [EBP-8], 19 |
004B5420 | 7D 28 | JGE 004B544A |
004B5422 | 8B4D F8 | MOV ECX, DWORD PTR [EBP-8] |
004B5425 | C1E1 05 | SHL ECX, 5 |
004B5428 | 8B55 F4 | MOV EDX, DWORD PTR [EBP-C] |
004B542B | 0FBF040A | MOVSX EAX, WORD PTR [EDX+ECX] |
004B542F | 3B45 08 | CMP EAX, DWORD PTR [EBP+8] |
004B5432 | 75 14 | JNZ 004B5448 |
004B5434 | 8B4D F8 | MOV ECX, DWORD PTR [EBP-8] |
004B5437 | C1E1 05 | SHL ECX, 5 |
004B543A | 8B55 F4 | MOV EDX, DWORD PTR [EBP-C] |
004B543D | 0FBF440A 02 | MOVSX EAX, WORD PTR [EDX+ECX+2] |
004B5442 | 0345 FC | ADD EAX, DWORD PTR [EBP-4] |
004B5445 | 8945 FC | MOV DWORD PTR [EBP-4], EAX |
004B5448 | EB C9 | JMP 004B5413 |
004B544A | 8B45 FC | MOV EAX, DWORD PTR [EBP-4] |
004B544D | 8BE5 | MOV ESP, EBP |
004B544F | 5D | POP EBP |
004B5450 | C2 0400 | RETN 4 |
I decide to set a breakpoint so I can see the register values before and after the line that supposedly accesses my value (004B542B | 0FBF040A | MOVSX EAX, WORD PTR [EDX+ECX]).
BEFORE:
EAX: 00000000
EBX: 00000000
ECX: 000001E0
EDX: 0849558C
ESI: 000000D0
EDI: 013A38A8
EBP: 00189CE0
ESP: 00189CD4
EIP: 004B542B
AFTER:
EAX: 00000DAD
EBX: 00000000
ECX: 000001E0
EDX: 0849558C
ESI: 000000D0
EDI: 013A38A8
EBP: 00189CE0
ESP: 00189CD4
EIP: 004B542F
To me, this means EDX 0849558C should be the value I'm looking to find and then apply an offset of 1E0. However. When searching memory for hex values matching EDX, I get no results which means there aren't any pointers to that address.
I've used the same methods I'm attempting to use here, to successfully gather each static address then apply the offsets. For example, here's the static address + offsets to find my health: 01263FC8 +284 +C +30 +90
I've finally figured it out. Unfortunately, the debugging was leading me nowhere so I started looking at the pointers I had found previously for my characters. Particularly health and mana as these were closest to the addresses I was getting for my inventory. I did some math based on the address I was trying to find and pointer closest to my health and I found an offset. Using that offset and the same static pointer I had found for my health I was able to find my inventory each time.

Resources