Author Topic: how to debug  (Read 24807 times)

Offline applefarm

  • Jr. Member
  • *
  • Posts: 4
how to debug
« on: April 10, 2010, 02:22:04 PM »
On windows XP i created file "hw.nasm":
Code: [Select]
  org 100h
    mov dx,msg
    mov ah,9
    int 21h
    mov ah,4Ch
    int 21h
    msg db 'Hello, World!',0Dh,0Ah,'$'

I compiled and runned on Console liek this:

Code: [Select]
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\Charles.roos>cd C:\charles\ise

C:\charles\ise>nasm hw.nasm -o hw.exe

C:\charles\ise>hw
One or more CON code pages invalid for given keyboard code
Hello, World!

C:\charles\ise>

Then i runned OllyDbg v2.00.
I choosed "File-->Open-->hw.exe".
I got error message:
"Unable to open or read file C:\charles\ise\hw.exe".
So i was unable to debug my program.
What went wrong?
Which IDE will enable me to debug the file? Or did i compiled wrongly my source file?

Thanks.


Offline jeesus

  • Jr. Member
  • *
  • Posts: 6
Re: how to debug
« Reply #1 on: April 10, 2010, 04:30:13 PM »
You suppose to write nasm hw.nasm -o hw.com. To make exe files you should use linker or write exe header somehow. Dos comes with debug program called debug. http://mirror.href.com/thestarman/asm/debug/debug.htm I dont know any other debugging things.
« Last Edit: April 10, 2010, 04:32:29 PM by jeesus »

Offline applefarm

  • Jr. Member
  • *
  • Posts: 4
Re: how to debug
« Reply #2 on: April 10, 2010, 04:58:22 PM »
But i don't want to create .com file, i want to create .exe file and open it with OllyDbg.

I did now following way:
Code: [Select]

C:\charles\ise>nasm hw.nasm -o hw.exe

C:\charles\ise>nasm -fobj hw.nasm
hw.nasm:1: error: parser: instruction expected

C:\charles\ise>alink -oPE hw
ALINK v1.6 (C) Copyright 1998-9 Anthony A.J. Williams.
All Rights Reserved

Error opening file hw.obj

C:\charles\ise>hw.exe
Hello, World!

C:\charles\ise>

As you see i manage to create some kind of .exe file which executes and print "Hello world".
Now i want to open in OllyDbg this executable and analyze it there.
As you see i failed to create .obj file. Should i change my source code or do something elseway?
My goal is to create with simplest code HelloWorld program and compile it into .exe file and run it step by step in OllyDbg.
So, what i should do?

--

I managed to create .com file as you suggested and i can debug it with debug.exe.
Can you show how i could create true "Hello world" .exe file now which i could open with OllyDbg? I have installed only Nasm, and Alink. I don't have any c-libraries or dll-s downloaded. Also i want the source code to be as clean and short as possible, for example i don't want to include some kind of GUI-libraries into source code, if possible let the source code stay as simple as it is at the moment.
« Last Edit: April 10, 2010, 05:05:12 PM by applefarm »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: how to debug
« Reply #3 on: April 10, 2010, 05:12:38 PM »
Well... you didn't compile it "wrong", but you didn't compile it exactly "right", either.

There are different executable formats. The simplest one is a .com file, which has no "header" - the file just starts with executable code, and is loaded at 100h offset into some segment chosen by the OS. This is what your "hw.nasm" has code for - the "org 100h" is the tipoff.

You'd want to use Nasm's "-f bin" format to assemble it. "-f bin" is Nasm's default, so by not specifying an "-f" option, that's what you've got. So far, so good. Nasm's "-o" switch determines the name of the output file - it does *not* determine the output format. So you've got a .com format file, named .exe. This "shouldn't" make any difference. When your OS loads a file named either .com or .exe, it determines which it is by the existance of the "MZ header" (the literal letters "MZ" are the first thing in the file - the initials of the guy who wrote the code - Mark ...something Polish, IIRC), not by the name. We don't have that header, so it runs as a .com file, as it should.

I don't know what the error about invalid code pages is about - that's an "OS question", I don't think anything in your code will fix it. (anyone know how to fix this?) It does go on to print your "Hello, World!", so it's not a fatal error, apparently.

I'm not familiar with Ollydbg, but apparently it doesn't do 16-bit code. I'm surprised it didn't complain about "not a valid executable format" rather than "can't open", but I don't think it's going to work, in any case.

You probably already have "DEBUG" - a rather rudimentary debugger, but it'll work for this. I much preferred David Lindauer's GRDB - like DEBUG with a brain:

http://ladsoft.tripod.com/grdb.htm

You'll have to give either debug or grdb the full file name - "debug hw.com", not just "debug hw". Then use "t" to step through it ("?" for help, "q" to quit). When you get to the "int 21h", use "p" to "proceed" past it. My experience is that stepping through dos with "t" generally causes the debugger to crash before learning anything useful - try it if you like...

If I were you, I'd change the command line to "nasm hw.nasm -o hw.com", just to get the "expected" name... I'd probably also use "-f bin" for clarity, but it shouldn't matter. You're doing pretty good - at least it says "Hello, World!" instead of "this program has performed an illegal operation and will be terminated"! :)

Best,
Frank

P.S. Just saw your newer post... Yes, you will need to change the source code. We'll try to dig up an example for that. Requires substantial changes - it's a different OS!


Offline jeesus

  • Jr. Member
  • *
  • Posts: 6
Re: how to debug
« Reply #4 on: April 10, 2010, 05:28:57 PM »
Even working exes make OllyDbg say Unable to open or read file.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: how to debug
« Reply #5 on: April 10, 2010, 06:18:44 PM »
Okay, start with something so simple it's wrong. :) This used to "work", as I recall, but it isn't "right"...

Code: [Select]
; nasm -f obj hwcons.asm

    extern  WriteFile
    import WriteFile kernel32.dll

    extern  ExitProcess
    import ExitProcess kernel32.dll

section _TEXT use32 class=CODE
..start

    push    dword 0
    push    dword num_chars
    push    dword MSGLEN
    push    dword msg
    push    dword -11
    call    [WriteFile]

exit_ok:
    push    dword 0
exit:
    call    [ExitProcess]

section _DATA use32 class=DATA
    num_chars       dd      0
    msg             db      'Hello, Console.', 13, 10
    MSGLEN          equ     $ - msg

We aren't supposed to be using "-11" as the handle. I suspect that any "invalid handle" writes to stderr - just a guess. We're supposed to give that "-11" as a parameter to "GetStdHandle", and that'll return our actual handle in eax. This is an untested(!) attempt to correct it.

Code: [Select]
; nasm -f obj hwcons.asm
; alink -oPE -subsys console hwcons.obj

    extern GetStdHandle
    import GetStdHandle kernel32.dll

    extern  WriteFile
    import WriteFile kernel32.dll

    extern  ExitProcess
    import ExitProcess kernel32.dll

section _TEXT use32 class=CODE
..start
    push -11
    call [GetStdHandle]

    push    dword 0
    push    dword num_chars
    push    dword MSGLEN
    push    dword msg
    push    dword eax  ; our handle
    call    [WriteFile]

    push    dword 0
    call    [ExitProcess]

section _DATA use32 class=DATA
    num_chars       dd      0
    msg             db      'Hello, Console.', 13, 10
    MSGLEN          equ     $ - msg

Note that "import" allows us to operate without any libraries, as you asked. It is specific to Nasm's "-f obj" output format, and won't work in "-f win32" (which is the more "modern" format for Windows). I would suggest that you *do* use libraries - probably download the Nasmx package:

http://www.asmcommunity.net/projects/nasmx

Pity I didn't work out some more "correct" examples for Windows before I "graduated" to Linux, but this is about the simplest thing I've got...

(the error "parser: instruction expected" is from "org 100h" - "org" is specific to "-f bin" - different source code for different purposes, I'm afraid)

Best,
Frank


Offline applefarm

  • Jr. Member
  • *
  • Posts: 4
Re: how to debug
« Reply #6 on: April 11, 2010, 09:45:37 AM »
Hi, thx,
with you code i managed to compile and open the program in OllyDbg:

Code: [Select]
C:\charles\ise>nasm -f obj hwcons.asm
hwcons.asm:10: warning: label alone on a line without a colon might be in error

C:\charles\ise>dir hwcons*
 Volume in drive C has no label.
 Volume Serial Number is 5807-705D

 Directory of C:\charles\ise

04/11/2010  12:32               564 hwcons.asm
04/11/2010  12:33               298 hwcons.obj
               2 File(s)            862 bytes
               0 Dir(s)  216 538 595 328 bytes free

C:\charles\ise> alink -oPE -subsys console hwcons.obj
ALINK v1.6 (C) Copyright 1998-9 Anthony A.J. Williams.
All Rights Reserved

Loading file hwcons.obj
matched Externs
matched ComDefs
Generating PE file hwcons.exe

C:\charles\ise>hwcons.exe
Hello, Console.

C:\charles\ise>

But this code has calls to some kind of resources like "kernel32.dll". I understand that BIOS or something contains interrupts that does all i need for my Helloworld program. So what i want to achieve is:

1) Write true Win32 executable which prints "Hello" to console. I don't want old .com file, or old 16bit exe file, i want modern win32 executable.
2) Use as simple code as possible, dont use any additional complex dll-s or libraries, let BIOS do the output printing, or interrupt of operating system (DOS).
3) After compilation i want to study and debug those few simple code lines in OllyDbg.

Are those 3 doable?

--


I looked win32 Helloworld examples fro mthere too now:
http://stackoverflow.com/questions/1023593/how-to-write-hello-world-in-assembler-under-windows

Those all has in code referencing to some external resource. Either they use "Windows API" or "C Standard Library" to print something out. Can i call BIOS function to print out my message to console? I understand that BIOS is available for my win32 executable right?

--
I think the code should be like this:

Code: [Select]
   global      _main
    section .text
_main:    
; load message address into SI register:
LEA SI,[msg]
; screen function:
MOV AH,0Eh
print:  MOV AL,[SI]        
CMP AL,0        
JZ done ; zero byte at end of string
INT 10h ; write character to screen.    
     INC SI        
JMP print

; wait for 'any key':
done:   MOV AH,0      
     INT 16h ; waits for key press
; AL is ASCII code or zero
; AH is keyboard code


    ret

msg DB  'Hello',13,10

But i can't manage to put it work, it contains logical error, so it crashes when runned. Can you look where the code crashes?

--

now i googled some boot loader source codes and seems those codes use BIOS to output message. So, why i can't manage to call BIOS function in win32 executable? Maybe in code i should enable access to BIOS functions somehow? Maybe there is some kind of special mode i have to turn on in my code firstly thenafter the Bios wil lbe available?
« Last Edit: April 11, 2010, 11:39:09 AM by applefarm »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: how to debug
« Reply #7 on: April 11, 2010, 04:32:02 PM »
No.

Best,
Frank


Offline sapero

  • Jr. Member
  • *
  • Posts: 9
Re: how to debug
« Reply #8 on: April 11, 2010, 08:52:09 PM »
applefarm, if you really want to use bios code, you'll need to:
1. Save the entire RAM or at least all reserved and commited pages. Save all CPU level registers including MSR, memory tables, interrupt tables (acpi too), fpu, mmx, xmm, hardware state (timers) ...
2. Switch to real mode, restore CPU to the state, that is valid for your bios (including memory, interrupt vectors and system space remapping) to the state before current OS booted.
3. call bios code
4. revert step 2 and 1, assuming that bios did not reconfigured hardware settings.

Step 1 and 4 is already implemented somewhere in the kernel - when you suspend the machine, the current state is saved, and restored after next boot.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: how to debug
« Reply #9 on: April 11, 2010, 10:10:35 PM »
Okay, I lied. Sapero's right, it can be done (it can be done in Linux, anyway, which faces the same problem). Is Ollydbg going to follow all this? I suspect it's still "no". And asking on another forum isn't going to get you a different answer! :)

Over on AsmCommunity...

http://www.asmcommunity.net/board/index.php?topic=29914.0

...you seem to come to the conclusion that printf is what you want to use. I don't think I agree that this is the "cleanest" interface (a black box calling another black box), but it can be arranged...

Just for comparison, here's a simple "hello world" for Linux:

Code: [Select]
global _start

section .data
msg db 'Hello, world',0Ah
msg_len equ $-msg

section .text
_start:

nop
bp1:

mov eax,4
mov ebx,1
mov ecx, msg
mov edx,msg_len
int 80h
mov eax,1
int 80h

Int 80h isn't a BIOS interrupt, it's "the" Linux interrupt. Unlike the BIOS interrupts, it's 32-bit code, so it'll work. And you can step through it in a debugger (although gdb ain't Ollydbg!) This might appear to be a "cleaner" interface. This is somewhat illusory. If you follow through what the "int 80h" does (source code is available), it pushes your parameters on the stack - very much like the call to WriteFile in Windows...

I can show you code that puts our string "directly into screen memory" without using even a BIOS int. But the act of putting 01000001 into "screen memory" (regeneration buffer, if you want the right name) doesn't magically turn the cathode ray gun (or however LEDs do it) on and off at the right time to make the letter 'A' appear on the screen. There's always a "black box" at the end. Frustrating!

Your desire for an executable for a "modern OS", but "simple code" that doesn't rely on "outside resources" is pretty much mutually exclusive. You might actually be happier going back to the .com file, although I agree that it's a "waste of time". A modern executable just isn't simple!

You mention - on the AsmCommunity board - downloading Cygwin... I found Cygwin painfully slow (on an old clunker, to be sure), and not easy to install/use - I never did figure out where they had the libraries stashed... I wish you luck with it, but I don't think you'll find it helpful... That thread seems to end up with "Nasmx"... which I think I already mentioned. :)

Best,
Frank




Offline applefarm

  • Jr. Member
  • *
  • Posts: 4
Re: how to debug
« Reply #10 on: April 12, 2010, 06:44:15 AM »
Thx.
I still want to stay on Windows OS for now, so i'm not going to write linux code that calls Linuxe's interrupt number 80. but i agree the Linux coe is the cleanest, looks to me too the cleanest, and 32 bit, which DOS-code isn't.
Using C-runtime i can move with my knowledge later to linux, because printf is implemented on linux too. So i still want to stay on printf-solution. But it doesn't matter much, which boat i choose. Seems like nasm+Cruntime is not bad choise, let it be then, i will try to work with it now, but thx again.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: how to debug
« Reply #11 on: April 12, 2010, 07:23:50 AM »
Okay. The C interface is the way we're "supposed" to interface with Linux, too. We have the option of using the int 80h interface, but "the Unix interface *is* the C interface". So you won't go too far wrong with that. Dr. Carter's tut uses C to "bridge the gap" so his examples will work on dos (32-bit extended dos) Windows or Linux, BSD, or whatever. (might be some examples there for you?)

I don't know the details of getting hooked up with msvcrt.dll - in Linux, libc is "just there" - different "tricks" required - so I'll let someone else help you with that part. After that, "code is code", so I'll probably see you around. :)

Best,
Frank


Offline sapero

  • Jr. Member
  • *
  • Posts: 9
Re: how to debug
« Reply #12 on: April 13, 2010, 09:52:24 PM »
On Windows and other platforms, you can always simulate interrupts by writing a wrapper in exception handler:
Code: [Select]
%define WIN32_LEAN_AND_MEAN
%include "windows.inc" ; CONTEXT structure, exception constants

section .data
$szHeloText: db "hello world\r\n",0

section .text
global _main

_main:
; setup structured exception handler
; EXCEPTION_REGISTRATION_RECORD struct {next, handler, optional userdata...}, ntddk.inc
; on winXP the EXCEPTION_REGISTRATION_RECORD node MUST be allocated on the stack.
push dword seHandler
push dword[fs:0] ; save old EXCEPTION_REGISTRATION_RECORD pointer
mov  dword[fs:0],esp

; display a string using custom implementation for int 16
mov edx,$szHeloText
int 10h ; 00401118 cd10 int 10h access violation

; remove exception handler
pop dword[fs:0]
pop edx ; handler

mov eax,0
ret

seHandler: ; EXCEPTION_DISPOSITION __cdecl _exception_handler(EXCEPTION_RECORD *,EXCEPTION_REGISTRATION_RECORD *,CONTEXT *,void *)
; prototype in excpt.inc
mov eax,ExceptionContinueSearch ; assume unhandled exception
; check if we have access violation
mov edx,[esp+4]
cmp dword[edx+EXCEPTION_RECORD.ExceptionCode],STATUS_ACCESS_VIOLATION
jne .q

; check the instruction, int 16 -> CDh 10h
mov ecx,[edx+EXCEPTION_RECORD.ExceptionAddress]
; TODO: check if memory at ecx is readable
; invoke IsBadCodePtr,ecx; return ExceptionContinueSearch if eax!=0
cmp word[ecx],10CDh ; int 16 ?
jne .q
; yup! Skip it
mov edx,[esp+12]
add dword[edx+CONTEXT.Eip],2

; output the string
extern __imp_puts
push dword[edx+CONTEXT.Edx]
call [__imp_puts]
pop  edx ; puts is cdecl

mov eax,ExceptionContinueExecution ; the exception was handled
.q:
ret

Or using a wrapper function:
Code: [Select]
seHandler:
mov eax,ExceptionContinueSearch ; assume unhandled exception
; check if we have access violation
mov edx,[esp+4]
cmp dword[edx+EXCEPTION_RECORD.ExceptionCode],STATUS_ACCESS_VIOLATION
jne .q

; check the failing instruction
mov ecx,[edx+EXCEPTION_RECORD.ExceptionAddress]
; TODO: check if memory at ecx is executable
cmp word[ecx],10CDh ; int 16 ?
jne .q

mov edx,[esp+12] ; load CONTEXT* to edx
; redirect execution to int16wrapper function
mov eax,[edx+CONTEXT.Eip]
add eax,2 ; skip int 16
mov dword[edx+CONTEXT.Eip],int16wrapper
; put ExceptionAddress+2 and CONTEXT.Edx onto stack
mov ecx,[edx+CONTEXT.Esp]
mov [ecx-8],eax ; return address = ExceptionAddress+2
mov eax,[edx+CONTEXT.Edx]
mov [ecx-4],eax ; char* = CONTEXT.Edx
sub dword[edx+CONTEXT.Esp],8

mov eax,ExceptionContinueExecution
.q:
ret


align 4
int16wrapper: ; void __stdcall int16wrapper(char*)
extern __imp_puts
push dword[esp+4]
call [__imp_puts]
pop  eax
ret 4
« Last Edit: April 13, 2010, 10:24:05 PM by sapero »