I'm on MAC OSX and I'm trying to call through assembly the execve syscall..
His opcode is 59 .
In linux I have to set opcode into eax, then parameters into the others registers, but here I have to put the opcode into eax and push parameters into the stack from right to left.
So I need execve("/bin/sh",NULL,NULL), I found somewhere that with assembly null=0, so I put null into 2nd and 3rd parameters.
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
pop ebx ;stringa
push 0x0 ;3rd param
push 0x0 ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
int 0x80
string:
call main
db '/bin/sh',0
When I try to execute it say:
Bad system call: 12
32-bit programs on BSD (on which OS/X is based) requires you to push an extra 4 bytes onto the stack if you intend to call int 0x80 directly. From the FreeBSD documentation you will find this:
By default, the FreeBSD kernel uses the C calling convention. Further, although the kernel is accessed using int 80h, it is assumed the program will call a function that issues int 80h, rather than issuing int 80h directly.
[snip]
But assembly language programmers like to shave off cycles. The above example requires a call/ret combination. We can eliminate it by pushing an extra dword:
open:
push dword mode
push dword flags
push dword path
mov eax, 5
push eax ; Or any other dword
int 80h
add esp, byte 16
When calling int 0x80 you need to adjust the stack pointer by 4. Pushing any value will achieve this. In the example they just do a push eax. Before your calls to int 0x80 push 4 bytes onto the stack.
Your other problem is that add eax,0x3b for example requires EAX to already be zero which is almost likely not the case. To fix that add an xor eax, eax to the code.
The fixes could look something like:
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
xor eax, eax ;zero EAX
pop ebx ;stringa
push 0x0 ;3rd param
push 0x0 ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
push eax ;Push a 4 byte value after parameters per calling convention
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
push eax ;Push a 4 byte value after parameters per calling convention
; in this case though it won't matter since the system call
; won't be returning
int 0x80
string:
call main
db '/bin/sh',0
Shellcode
Your code is actually called the JMP/CALL/POP method and is used for writing exploits. Are you writing an exploit or did you just find this code online? If it is intended to be used as shell code you would need to avoid putting a 0x00 byte in the output string. push 0x00 will encode 0x00 bytes in the generated code. To avoid this we can use EAX which we are now zeroing out and push it on the stack. As well you won't be able to NUL terminate the string so you'd have to move a NUL(0) character into the string. One way after zeroing EAX and popping EBX is to move zero to the end of the string manually with something like mov [ebx+7], al. Seven is the index after the end of the string /bin/sh. Your code would then look like this:
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
xor eax, eax ;Zero EAX
pop ebx ;stringa
mov [ebx+7], al ;append a zero onto the end of the string '/bin/sh'
push eax ;3rd param
push eax ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
push eax
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
push eax
int 0x80
string:
call main
db '/bin/sh',1
You are using a 64 bit syscall numbers and a 32 bit instruction to jump to the syscall. That is not going to work.
For 32 bit users:
opcode for Linux/MacOS execve: 11
instruction to call syscall: int 0x80
For 64 bit users:
opcode for Linux execve: 59 (MacOS 64-bit system calls also have a high bit set).
instruction to call syscall: syscall
The method for passing args to system calls is also different: 32-bit uses the stack, 64-bit uses similar registers to the function-calling convention.
Related
I have been trying to create an ISR handler following this
tutorial by James Molloy but I got stuck. Whenever I throw a software interrupt, general purpose registers and the data segment register is pushed onto the stack with the variables automatically pushed by the CPU. Then the data segment is changed to the value of 0x10 (Kernel Data Segment Descriptor) so the privilege levels are changed. Then after the handler returns those values are poped. But whenever the value in ds is changed a GPE is thrown with the error code 0x2544 and after a few seconds the VM restarts. (linker and compiler i386-elf-gcc , assembler nasm)
I tried placing hlt instructions in between instructions to locate which instruction was throwing the GPE. After that I was able to find out that the the `mov ds,ax' instruction. I tried various things like removing the stack which was initialized by the bootstrap code to deleting the privilege changing parts of the code. The only way I can return from the common stub is to remove the parts of my code which change the privilege levels but as I want to move towards user mode I still want them to stay.
Here is my common stub:
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
xor eax,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
xor eax,eax
pop eax
mov ds, ax ; This is the instruction everything fails;
mov es, ax
mov fs, ax
mov gs, ax
popa
iret
My ISR handler macros:
extern isr_handler
%macro ISR_NOERRCODE 1
global isr%1 ; %1 accesses the first parameter.
isr%1:
cli
push byte 0
push %1
jmp isr_common_stub
%endmacro
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli
push byte %1
jmp isr_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
...
My C handler which results in "Received interrupt: 0xD err. code 0x2544"
#include <stdio.h>
#include <isr.h>
#include <tty.h>
void isr_handler(registers_t regs) {
printf("ds: %x \n" ,regs.ds);
printf("Received interrupt: %x with err. code: %x \n", regs.int_no, regs.err_code);
}
And my main function:
void kmain(struct multiboot *mboot_ptr) {
descinit(); // Sets up IDT and GDT
ttyinit(TTY0); // Sets up the VGA Framebuffer
asm volatile ("int $0x1"); // Triggers a software interrupt
printf("Wow"); // After that its supposed to print this
}
As you can see the code was supposed to output,
ds: 0x10
Received interrupt: 0x1 with err. code: 0
but results in,
...
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
...
Which goes on until the VM restarts itself.
What am I doing wrong?
The code isn't complete but I'm going to guess what you are seeing is a result of a well known bug in James Molloy's OSDev tutorial. The OSDev community has compiled a list of known bugs in an errata list. I recommend reviewing and fixing all the bugs mentioned there. Specifically in this case I believe the bug that is causing problems is this one:
Problem: Interrupt handlers corrupt interrupted state
This article previously told you to know the ABI. If you do you will
see a huge problem in the interrupt.s suggested by the tutorial: It
breaks the ABI for structure passing! It creates an instance of the
struct registers on the stack and then passes it by value to the
isr_handler function and then assumes the structure is intact
afterwards. However, the function parameters on the stack belongs to
the function and it is allowed to trash these values as it sees fit
(if you need to know whether the compiler actually does this, you are
thinking the wrong way, but it actually does). There are two ways
around this. The most practical method is to pass the structure as a
pointer instead, which allows you to explicitly edit the register
state when needed - very useful for system calls, without having the
compiler randomly doing it for you. The compiler can still edit the
pointer on the stack when it's not specifically needed. The second
option is to make another copy the structure and pass that
The problem is that the 32-bit System V ABI doesn't guarantee that data passed by value will be unmodified on the stack! The compiler is free to reuse that memory for whatever purposes it chooses. The compiler probably generated code that trashed the area on the stack where DS is stored. When DS was set with the bogus value it crashed. What you should be doing is passing by reference rather than value. I'd recommend these code changes in the assembly code:
irq_common_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10 ;0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; At this point ESP is a pointer to where GS (and the rest
; of the interrupt handler state resides)
; Push ESP as 1st parameter as it's a
; pointer to a registers_t
call irq_handler
pop ebx ; Remove the saved ESP on the stack. Efficient to just pop it
; into any register. You could have done: add esp, 4 as well
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8
sti
iret
And then modify irq_handler to use registers_t *regs instead of registers_t regs :
void irq_handler(registers_t *regs) {
if (regs->int_no >= 40) port_byte_out(0xA0, 0x20);
port_byte_out(0x20, 0x20);
if (interrupt_handlers[regs->int_no] != 0) {
interrupt_handlers[regs->int_no](*regs);
}
else
{
klog("ISR: Unhandled IRQ%u!\n", regs->int_no);
}
}
I'd actually recommend each interrupt handler take a pointer to registers_t to avoid unnecessary copying. If your interrupt handlers and the interrupt_handlers array used function that took registers_t * as the parameter (instead of registers_t) then you'd modify the code:
interrupt_handlers[r->int_no](*regs);
to be:
interrupt_handlers[r->int_no](regs);
Important: You have to make these same type of changes for your ISR handlers as well. Both the IRQ and ISR handlers and associated code have this same problem.
Supposing we have got a text file sample.txt:
one
two
...
Now we want to remove the first line:
two
...
A quick way to do that is to use input redirection, set /P and findstr1 (I know there are other ways using more or for /F, but let us forget about them for now):
#echo off
< "sample.txt" (
set /P =""
findstr "^"
)
The output is going to be as expected.
However, why is the output empty when I replace the input redirection < by type and a pipe | :
#echo off
type "sample.txt" | (
set /P =""
findstr "^"
)
When I replace set /P ="" by pause > nul, the output is what I expect -- the input file is output but with the first character of the first line missing (as it is consumed by pause). But why does set /P seem to consume everything instead of only the first line like it does with the redirection < approach? Is that a bug?
To me it looks like set /P fails to adequately initialise the reading pointer to the piped data.
I watched that strange behaviour on Windows 7 and on Windows 10.
It becomes even more weird: when calling the script containing the pipe multiple times, for instance by a loop like for /L %I in (1,1,1000) do #pipe.bat, and the input file contains about fifteen lines or more, sometimes (a few times out of thousand) a fragment of the input file is returned; that fragment is exactly the same each time; it seems that there are always 80 bytes missing at the beginning.
1) findstr hangs in case the last line is not terminated by a line-break, so let us assume such is there.
When retrieving data, the set /p tries to fill a 1023 character buffer (if they are available) with data from stdin. Once this read operation has ended, the first end of line is searched and once it has been found (or the end of the buffer has been reached), the SetFilePointer API is called to reposition the input stream pointer after the end of the read line. This way the next read operation will start to retreive data after the read line.
This works flawlessly when a disk file is associated with the input stream, but as Microsoft states in the SetFilePointer documentation
The hFile parameter must refer to a file stored on a seeking device;
for example, a disk volume. Calling the SetFilePointer function with a
handle to a non-seeking device such as a pipe or a communications
device is not supported, even though the SetFilePointer function may
not return an error. The behavior of the SetFilePointer function in
this case is undefined.
What is happening is that, while not generating any error, the call to reposition the read pointer fails when stdin is associated with a pipe, the pointer is not moved back and the 1023 bytes (or the number of available read bytes) keep read.
edited in response to Aacini request
The set command is processed by the eSet function, who calls SetWork to determine which type of set command will be executed.
As it is a set /p the SetPromptUser function is called and from this function the ReadBufFromInput function is called
add esp, 0Ch
lea eax, [ebp+var_80C]
push eax ; int
push 3FFh ; int
lea eax, [ebp+Value]
push eax ; int
xor esi, esi
push 0FFFFFFF6h ; nStdHandle
mov word ptr [ebp+Value], si
call edi ; GetStdHandle(x) ; GetStdHandle(x)
push eax ; hFile
call _ReadBufFromInput#16 ; ReadBufFromInput(x,x,x,x)
it requests 3FFh (1023) characters from standard input handle (0FFFFFFF6h = -10 = STD_INPUT_HANDLE)
ReadBufFromInput uses the GetFileType API to determine if it should read from the console or from a file
; Attributes: bp-based frame
; int __stdcall ReadBufFromInput(HANDLE hFile, int, int, int)
_ReadBufFromInput#16 proc near
hFile= dword ptr 8
; FUNCTION CHUNK AT .text:4AD10D3D SIZE 00000006 BYTES
mov edi, edi
push ebp
mov ebp, esp
push [ebp+hFile] ; hFile
call ds:__imp__GetFileType#4 ; GetFileType(x)
and eax, 0FFFF7FFFh
cmp eax, 2
jz loc_4AD10D3D
and, as in this case it is a pipe (GetFileType returns 3) the code jumps to the ReadBufFromFile function
; Attributes: bp-based frame
; int __stdcall ReadBufFromFile(HANDLE hFile, LPWSTR lpWideCharStr, DWORD cchWideChar, LPDWORD lpNumberOfBytesRead)
_ReadBufFromFile#16 proc near
var_C= dword ptr -0Ch
cchMultiByte= dword ptr -8
NumberOfBytesRead= dword ptr -4
hFile= dword ptr 8
lpWideCharStr= dword ptr 0Ch
cchWideChar= dword ptr 10h
lpNumberOfBytesRead= dword ptr 14h
This function will call the ReadFile API function to retrive the indicated number of characters
push ebx ; lpOverlapped
push [ebp+lpNumberOfBytesRead] ; lpNumberOfBytesRead
mov [ebp+var_C], eax
push [ebp+cchWideChar] ; nNumberOfBytesToRead
push edi ; lpBuffer
push [ebp+hFile] ; hFile
call ds:__imp__ReadFile#20 ; ReadFile(x,x,x,x,x)
The returned buffer is iterated in search of an end of line, and once it is found, the pointer in the input stream is moved after the found poisition
.text:4AD06A15 loc_4AD06A15:
.text:4AD06A15 cmp [ebp+NumberOfBytesRead], 3
.text:4AD06A19 jl short loc_4AD06A2D
.text:4AD06A1B mov al, [esi]
.text:4AD06A1D cmp al, 0Ah
.text:4AD06A1F jz loc_4AD06BCF
.text:4AD06A25
.text:4AD06A25 loc_4AD06A25:
.text:4AD06A25 cmp al, 0Dh
.text:4AD06A27 jz loc_4AD06D14
.text:4AD06A2D
.text:4AD06A2D loc_4AD06A2D:
.text:4AD06A2D movzx eax, byte ptr [esi]
.text:4AD06A30 cmp byte ptr _DbcsLeadCharTable[eax], bl
.text:4AD06A36 jnz loc_4AD12018
.text:4AD06A3C dec [ebp+NumberOfBytesRead]
.text:4AD06A3F inc esi
.text:4AD06A40
.text:4AD06A40 loc_4AD06A40:
.text:4AD06A40 cmp [ebp+NumberOfBytesRead], ebx
.text:4AD06A43 jg short loc_4AD06A15
.text:4AD06BCF loc_4AD06BCF:
.text:4AD06BCF cmp byte ptr [esi+1], 0Dh
.text:4AD06BD3 jnz loc_4AD06A25
.text:4AD06BD9 jmp loc_4AD06D1E
.text:4AD06D14 loc_4AD06D14:
.text:4AD06D14 cmp byte ptr [esi+1], 0Ah
.text:4AD06D18 jnz loc_4AD06A2D
.text:4AD06D1E
.text:4AD06D1E loc_4AD06D1E:
.text:4AD06D1E mov eax, [ebp+var_C]
.text:4AD06D21 mov [esi+2], bl
.text:4AD06D24 sub esi, edi
.text:4AD06D26 inc esi
.text:4AD06D27 inc esi
.text:4AD06D28 push ebx ; dwMoveMethod
.text:4AD06D29 push ebx ; lpDistanceToMoveHigh
.text:4AD06D2A mov [ebp+cchMultiByte], esi
.text:4AD06D2D add esi, eax
.text:4AD06D2F push esi ; lDistanceToMove
.text:4AD06D30 push [ebp+hFile] ; hFile
.text:4AD06D33 call ds:__imp__SetFilePointer#16 ; SetFilePointer(x,x,x,x)
The short summarize of a long discussion at dostips (mentioned already by Aacini
set /p problems with pipes).
Reading with set /p from a redirect, reads always to the end of the line and removes the \r\n characters.
Reading with set /p from a pipe, reads up to 1023 bytes from the pipe buffer.
It doesn't stop at any \r or \n characters but it drops all content after a \n.
After closing the pipe on the left side, a set /p on the right side will read empty lines.
[SOLVED]
I'm trying to do my own assembly code to do what similar C code will do:
main()
{
scanf("%d",&integer_var); // here must be the address of the integer_var
printf("Your Value is:%d",integer_var);
}
Well this is in C, so I'm doing with NASM under linux with extern functions. scanf and printf and compile first with nasm and then with gcc.
Here's my code (is not right :D)
SECTION .text
argstr: db "%d",10,0
str: db "Your value is:%d",10,0
extern printf
extern scanf
SECTION .data
global main
main:
push ebp
mov esp,ebp
sub esp, 0x10 ;ok integer right?
mov [ebp-0x4],0x0 ;just put 0 number on our integer variable
mov eax,(ebp-0x4) ;here i don't know how to push the address of ebp-0x4
push ecx ;first push is last argument so here's our address to scanf
push argstr ;just the string format
call scanf ;call that to input something
;I have no idea how to do this
;but if i don't do this i get an error
;because the scanf won't clear the arguments on stack
;and what scanf can't return
pop edx ;maybe help here? but it works fine
pop edx
push [-0x4(ebp)] ;i want the value of our var :D
push str
call printf
pop edx ;clear the stack to avoid "segment fault" or something similar
pop edx
mov esp,ebp
pop ebp
ret ;the end :(
Compiler error:
a.asm:18: error: invalid operand type
a.asm:28: error: parser: expecting ]
Another thing: Do I need to align the stack on this case, by the way?
thanks guys ! :)
EDIT solved whole program!
well at least, I can print the variable with printf. scanf i will do later and then I will share here the last result:
SECTION .text
str: db "Value is:%d",10,0
extern printf
SECTION .data
global main
main:
push ebp ;the main function starts here.
mov ebp,esp
;
sub esp,4 ;we need 4bytes of space for the integer
and esp,0xfffffff0 ;align the stack
mov [esp-4], dword 0xff ;move the value 0xff to our var
mov eax,[esp-4] ;move our variable value to the eax
push eax ;second argument of printf
push str ;first argument of printf
call printf ;printf
;
add esp,16 ;this add to the stack pointer what we pushed basicly
mov ebp,esp ;if we don't do add 16 to esp it shows us
pop ebp ;a segment fault cuz ret doesnt pop saved ebp
ret ;of who whatever called this program :)
To load the address EBP-4 into EAX, use lea eax, [ebp-4]. (this is NOT the same as pushing the address.)
In order to push the value at memory location EBP-4, push dword [ebp-4] should work.
Then you need to specify operand size for one of your movs, too: mov [ebp-4], dword 0x0.
These will fix your current assembler errors, and make your program compile, but there are a few other errors in there that will probably prevent it from running.
Here's a working attempt that is close to yours:
;note the sections, the string literals are better in .rodata
;all code goes in .text
SECTION .rodata
;no newline after scanf string
argstr: db "%d",0
str: db "Your value is: %d",10,0
SECTION .text
extern printf
extern scanf
global main
main:
push ebp
mov ebp,esp ;move esp to ebp, NOT other way round!
sub esp, 4 ;4 bytes are enough for the local variable
;there are NO alignment requirements for this program
lea eax,[ebp-4]
push eax
push dword argstr
call scanf
add esp, 8 ;since we don't actually need the popped values
;we can increment esp instead of two pop edx
push dword [ebp-4]
push dword str
call printf
add esp, 8
mov esp,ebp
pop ebp
ret
I am currently trying to append a null terminator to an(a?) user inputted string:
.386
.model flat, stdcall
WriteFile PROTO STDCALL:DWORD, :PTR, :DWORD, :PTR DWORD, :PTR OVERLAPPED
ReadFile PROTO STDCALL:DWORD, :PTR, :DWORD, :PTR DWORD, :PTR OVERLAPPED
GetStdHandle PROTO STDCALL:DWORD
.data
buff DB 100h DUP(?)
stdInHandle DWORD 0
bytesRead DWORD ?
.code
start:
;read string from stdin
INVOKE GetStdHandle, -10
MOV stdInHandle, eax
INVOKE ReadFile, stdInHandle, BYTE PTR[buff], 100, ADDR bytesRead, 0
;append null terminator on CR,LF
MOV eax, bytesRead
MOV edx, BYTE PTR[buff]
SUB eax, 2
AND BYTE PTR [eax+edx], 0
RET
END start
It refuses to assemble at MOV edx, BYTE PTR[buff] and gives me an error:
error: Invalid combination of opcode and operands (or wrong CPU setting).
So I'm assuming I cannot MOV the value of BYTE PTR[buff] into register edx. So I can't even begin to test if this method of trying to apply a NULL terminator to a string will even work.
My question is, what is wrong with the above code (should I use a different register instead of edx?)
What is the best way to apply a NULL terminator to the string?
You can't move a byte value into a dword sized register. You either need to use a byte sized register such as dl, or zero-extend it with movzx. As you are working with bytes, I suggest you go with the first option.
When I had to create methods for strings without using anything from good ole Irvine, I got the length of the string, incremented what the length returned as (you need to include an extra +1 for the null-terminator) by 1, and then added 0h to the end of the string where the pointer was where the counter is.
MOV EAX, SIZEOF lpSourceString + 1 ; Get the string length of string, add 1 to include null-terminator
INVOKE allocMem, EAX ; Allocate memory for a target to copy to
LEA ESI, [lpSourceString] ; put source address in ESI
MOV EDI, EAX ; copy the dest address to another register we can increment
MOV ECX, SIZEOF lpSourceString ; Set up loop counter
We have the size of the string. Now we can add the null-terminate to it. To do that, we need to make sure that we have a pointer looking at the end of the string. So if we have a method that returns a string in EAX, EAX needs to point to the start of the string (so we leave the allocMem unmodified, instead incrementing a copy in EDI). Let's say that we are putting characters in a string:
nextByte: ; Jump label, get the next byte in the string until ECX is 0
MOV DL, [ESI] ; Get the next character in the string
MOV [EDI], DL ; Store the byte at the position of ESI
INC ESI ; Move to next char in source
INC EDI ; INCrement EDI by 1
loop nextByte ; Re-loop to get next byte
MOV byte ptr[EDI], 0h ; Add null-terminator to end of string
; EAX holds a pointer to the start of the dynamically-allocated
; 0-terminated copy of lpSourceString
MOV requires the byte ptr size specifier because neither the [EDI] memory operand nor the 0 immediate operand would imply a size for the operation. The assembler wouldn't know if you meant a byte, word, or dword store.
I have this in my MASM, but I use a String_length stdcall method I had written due to a class requirement.
This is so common that the MASM32 runtime supplies this functionality as part of its runtime. All you need to do is include the relevant code:
include \masm32\include\masm32rt.inc
Then use the StripLF function as so:
invoke StripLF, addr buff
To fix your current problem (if you want to do it manually) , you need to move the address of buff to edx instead.
mov edx, offset buff
I am trying just to print out the value of argc using the CommandLineToArgvW Window's API function in NASM. The following is what I have:
extern _ExitProcess#4
extern _GetCommandLineA#0
extern _CommandLineToArgvW#8
extern printf
global _start
section .code
Format:
db "%d",10,0
FormatS:
db "%s",10,0
_start:
push ebp
mov ebp, esp
sub esp, 4 ; Create empty space for ArgC
call _GetCommandLineA#0
push eax; Push value beneath ArgC
mov ebx, ebp ; Set ebx to ebp
sub ebx, 4
push dword ebx ; pushes ArgC address onto stack
push dword [ebp - 8] ; pushes pointer to Command Line String
call _CommandLineToArgvW#8
push dword [ebp - 4]
push Format
call printf
push dword 0
call _ExitProcess#4
No matter what I do, the value for argc is 1. What am I doing wrong?
I assemble and link with these commands:
nasm -fwin32 FunctionTests.asm
golink FunctionTests.obj kernel32.dll msvcrt.dll shell32.dll /console /entry _start
FunctionTests.exe hi asdf asdf asdf asdf
As you can see, from the last line, argc should be 6.
Change _GetCommandLineA to _GetCommandLine*W* CommandLineToArgv expects a pointer to a UNICODE string.
MASM but just about the same:
start:
push ebp
mov ebp, esp
sub esp, 4
call GetCommandLineW
lea ecx, dword ptr[ebp - 4] ; need the address of local
push ecx ; address of local
push eax ; pointer to unicode string
call CommandLineToArgvW
push dword ptr [ebp - 4] ; value of local
push offset Format
call crt_printf
add esp, 8
; this is all? Then we don't have to restore stack.
push 0
call ExitProcess
end start
and this is the output:
D:\Projects>ConTest.exe boo boo I see you
6
D:\Projects>
Set a breakpoint just before you call CommandLineToArgvW and inspect the parameters you're about to pass. Observe that the first parameter you are passing is not a pointer to a string. It is a pointer to a pointer to a string.
You need to use GetCommandLineW rather than GetCommandLineA.
You are using CommandLineToArgvW with an ANSI string, use GetCommandLineW to get the command line in unicode.
You are also not dereferencing the pointer to the command line:
push ebx ; pushes ArgC address onto stack