WinExec Return 0x21, but what exactly does this mean?

When WinExec is called to run .exe, I get a return value of 0x21.

According to MSDN, a return value greater than 31 (0x1F) means that the function completed successfully.

But what does this mean: 0x21, Why didn’t he give me another value?

+4
source share
3 answers

It’s not good for you to know what that means. This is an implementation detail. Even if you knew what this means for this version, the meaning may change in a future version. As a programmer, you are only interested in programming against the interface, not the basic implementation.

However, if you are really interested, I will take you through an approach that I would take to reverse engineer a function. On my WinExec system parsed:

 764F2C21 > 8BFF MOV EDI,EDI 764F2C23 55 PUSH EBP 764F2C24 8BEC MOV EBP,ESP 764F2C26 81EC 80000000 SUB ESP,80 764F2C2C 53 PUSH EBX 764F2C2D 8B5D 0C MOV EBX,DWORD PTR SS:[EBP+C] 764F2C30 56 PUSH ESI 764F2C31 57 PUSH EDI 764F2C32 33FF XOR EDI,EDI 764F2C34 47 INC EDI 764F2C35 33F6 XOR ESI,ESI 764F2C37 85DB TEST EBX,EBX 764F2C39 79 4F JNS SHORT kernel32.764F2C8A 764F2C3B 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4] 764F2C3E 50 PUSH EAX 764F2C3F 56 PUSH ESI 764F2C40 57 PUSH EDI 764F2C41 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] 764F2C44 50 PUSH EAX 764F2C45 C745 FC 20000000 MOV DWORD PTR SS:[EBP-4],20 764F2C4C E8 90BE0200 CALL <JMP.&API-MS-Win-Core-ProcessThread> 764F2C51 85C0 TEST EAX,EAX 764F2C53 0F84 D2000000 JE kernel32.764F2D2B 764F2C59 56 PUSH ESI 764F2C5A 56 PUSH ESI 764F2C5B 6A 04 PUSH 4 764F2C5D 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 764F2C60 50 PUSH EAX 764F2C61 68 01000600 PUSH 60001 764F2C66 56 PUSH ESI 764F2C67 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] 764F2C6A 50 PUSH EAX 764F2C6B C745 0C 00000800 MOV DWORD PTR SS:[EBP+C],80000 764F2C72 897D F8 MOV DWORD PTR SS:[EBP-8],EDI 764F2C75 E8 5CBE0200 CALL <JMP.&API-MS-Win-Core-ProcessThread> 764F2C7A 85C0 TEST EAX,EAX 764F2C7C 0F84 95000000 JE kernel32.764F2D17 764F2C82 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] 764F2C85 8945 C4 MOV DWORD PTR SS:[EBP-3C],EAX 764F2C88 EB 03 JMP SHORT kernel32.764F2C8D 764F2C8A 8975 0C MOV DWORD PTR SS:[EBP+C],ESI 764F2C8D 6A 44 PUSH 44 764F2C8F 8D45 80 LEA EAX,DWORD PTR SS:[EBP-80] 764F2C92 56 PUSH ESI 764F2C93 50 PUSH EAX 764F2C94 E8 B5E9F7FF CALL <JMP.&ntdll.memset> 764F2C99 83C4 0C ADD ESP,0C 764F2C9C 33C0 XOR EAX,EAX 764F2C9E 3975 0C CMP DWORD PTR SS:[EBP+C],ESI 764F2CA1 897D AC MOV DWORD PTR SS:[EBP-54],EDI 764F2CA4 0F95C0 SETNE AL 764F2CA7 66:895D B0 MOV WORD PTR SS:[EBP-50],BX 764F2CAB 8D0485 44000000 LEA EAX,DWORD PTR DS:[EAX*4+44] 764F2CB2 8945 80 MOV DWORD PTR SS:[EBP-80],EAX 764F2CB5 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18] 764F2CB8 50 PUSH EAX 764F2CB9 8D45 80 LEA EAX,DWORD PTR SS:[EBP-80] 764F2CBC 50 PUSH EAX 764F2CBD 56 PUSH ESI 764F2CBE 56 PUSH ESI 764F2CBF FF75 0C PUSH DWORD PTR SS:[EBP+C] 764F2CC2 56 PUSH ESI 764F2CC3 56 PUSH ESI 764F2CC4 56 PUSH ESI 764F2CC5 FF75 08 PUSH DWORD PTR SS:[EBP+8] 764F2CC8 56 PUSH ESI 764F2CC9 E8 A4E3F7FF CALL kernel32.CreateProcessA 764F2CCE 85C0 TEST EAX,EAX 764F2CD0 74 27 JE SHORT kernel32.764F2CF9 764F2CD2 A1 3C005476 MOV EAX,DWORD PTR DS:[7654003C] 764F2CD7 3BC6 CMP EAX,ESI 764F2CD9 74 0A JE SHORT kernel32.764F2CE5 764F2CDB 68 30750000 PUSH 7530 764F2CE0 FF75 E8 PUSH DWORD PTR SS:[EBP-18] 764F2CE3 FFD0 CALL EAX 764F2CE5 FF75 E8 PUSH DWORD PTR SS:[EBP-18] 764F2CE8 8B35 A0054776 MOV ESI,DWORD PTR DS:[<&ntdll.NtClose>] ; ntdll.ZwClose 764F2CEE FFD6 CALL ESI 764F2CF0 FF75 EC PUSH DWORD PTR SS:[EBP-14] 764F2CF3 FFD6 CALL ESI 764F2CF5 6A 21 PUSH 21 764F2CF7 EB 1D JMP SHORT kernel32.764F2D16 764F2CF9 E8 C9E4F7FF CALL <JMP.&API-MS-Win-Core-ErrorHandling> 764F2CFE 48 DEC EAX 764F2CFF 48 DEC EAX 764F2D00 74 12 JE SHORT kernel32.764F2D14 764F2D02 48 DEC EAX 764F2D03 74 0B JE SHORT kernel32.764F2D10 764F2D05 2D BE000000 SUB EAX,0BE 764F2D0A 75 0B JNZ SHORT kernel32.764F2D17 764F2D0C 6A 0B PUSH 0B 764F2D0E EB 06 JMP SHORT kernel32.764F2D16 764F2D10 6A 03 PUSH 3 764F2D12 EB 02 JMP SHORT kernel32.764F2D16 764F2D14 6A 02 PUSH 2 764F2D16 5E POP ESI 764F2D17 F745 0C 00000800 TEST DWORD PTR SS:[EBP+C],80000 764F2D1E 74 09 JE SHORT kernel32.764F2D29 764F2D20 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] 764F2D23 50 PUSH EAX 764F2D24 E8 A2BD0200 CALL <JMP.&API-MS-Win-Core-ProcessThread> 764F2D29 8BC6 MOV EAX,ESI 764F2D2B 5F POP EDI 764F2D2C 5E POP ESI 764F2D2D 5B POP EBX 764F2D2E C9 LEAVE 764F2D2F C2 0800 RETN 8 

The calling convention used in Win32 is stdcall , which returns the return values ​​in EAX . In case of WinExec there is only one way out of the function ( 0x764F2D2F ). By tracking from there, EAX is installed (at least when 0x21 returns):

 764F2D29 8BC6 MOV EAX,ESI 

Tracking further, ESI itself is installed from the POP ESI , which pulls the top of the stack into ESI . The meaning of this depends on what was previously pushed onto the stack. In the case of 0x21, this happens when:

 764F2CF5 6A 21 PUSH 21 

Immediately after that, JMP is produced on the POP ESI . As we got to PUSH 21 , it’s interesting only after calling CreateProcess .

 764F2CC9 E8 A4E3F7FF CALL kernel32.CreateProcessA 764F2CCE 85C0 TEST EAX,EAX 764F2CD0 74 27 JE SHORT kernel32.764F2CF9 764F2CD2 A1 3C005476 MOV EAX,DWORD PTR DS:[7654003C] 764F2CD7 3BC6 CMP EAX,ESI 764F2CD9 74 0A JE SHORT kernel32.764F2CE5 764F2CDB 68 30750000 PUSH 7530 764F2CE0 FF75 E8 PUSH DWORD PTR SS:[EBP-18] 764F2CE3 FFD0 CALL EAX 764F2CE5 FF75 E8 PUSH DWORD PTR SS:[EBP-18] 764F2CE8 8B35 A0054776 MOV ESI,DWORD PTR DS:[<&ntdll.NtClose>] ; ntdll.ZwClose 764F2CEE FFD6 CALL ESI 764F2CF0 FF75 EC PUSH DWORD PTR SS:[EBP-14] 764F2CF3 FFD6 CALL ESI 764F2CF5 6A 21 PUSH 21 

To see how the path leads you to PUSH 21 , look at the different branches. The first is like this:

 764F2CD0 74 27 JE SHORT kernel32.764F2CF9 

This means that if CreateProcess returns 0, call Win-Core-ErrorHandling . The return values ​​are then set in different ways (0x2, 0x3 and 0xB - all possible return values ​​if CreateProcess failed).

The following branch is much less obvious for reverse engineering:

 764F2CD9 74 0A JE SHORT kernel32.764F2CE5 

What he does is read the memory address, which probably contains a pointer to a function (we know this because the result of the reading is called later). This JE simply indicates whether to make this call at all. Regardless of whether the call is made, the next step is to call ZwClose (twice). Finally, 0x21 is returned.

So one easy way to look at this is when CreateProcess succeeds, returns 0x21, otherwise 0x2, 0x3, or 0xB is returned. This does not mean that these are the only return values. For example, 0x0 can also be returned from a branch in 0x764F2C53 (in this case, ESI is not used the same way). There are a few more possible return values, but I will leave them for you to look into yourself.

What I showed you is to do a very fine WinExec analysis specifically for returning 0x21. If you want to know more, you need to push more and try to understand from a higher level what is happening. You can learn a lot more by simply leaving the function and going through it (this way you can observe the data).


Another way is to look at the source of the wine , where someone has already done all the hard work for you:

 UINT WINAPI WinExec( LPCSTR lpCmdLine, UINT nCmdShow ) { PROCESS_INFORMATION info; STARTUPINFOA startup; char *cmdline; UINT ret; memset( &startup, 0, sizeof(startup) ); startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = nCmdShow; /* cmdline needs to be writable for CreateProcess */ if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(lpCmdLine)+1 ))) return 0; strcpy( cmdline, lpCmdLine ); if (CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info )) { /* Give 30 seconds to the app to come up */ if (wait_input_idle( info.hProcess, 30000 ) == WAIT_FAILED) WARN("WaitForInputIdle failed: Error %d\n", GetLastError() ); ret = 33; /* Close off the handles */ CloseHandle( info.hThread ); CloseHandle( info.hProcess ); } else if ((ret = GetLastError()) >= 32) { FIXME("Strange error set by CreateProcess: %d\n", ret ); ret = 11; } HeapFree( GetProcessHeap(), 0, cmdline ); return ret; } 

33d - 0x21, so this actually confirms the fruits of our early analysis.

Regarding the reason 0x21 is being returned, I assume that perhaps there is more internal documentation that makes it more useful in some way.

+9
source

In addition, this means success; the value of the return value is undefined. It may have been chosen so that legacy applications work well with that particular value. One thing is certain: what more to worry about!

http://msdn.microsoft.com/en-us/library/windows/desktop/ms687393(v=vs.85).aspx

+3
source

EDIT . This answer is incorrect because the OP result is not an error code. I mistakenly thought that it was said that this is an error code. I still think that the practical information below may be useful, plus what may be useful to see, which may lead to an incorrect assumption, so I give her an answer.


If you installed Visual Studio (full or express version), then you have a tool called errlook that uses the FormatMessage API FormatMessage to tell you what error code or HRESULT value means.

In this case

The process cannot access the file because another process has blocked part of the file.

You can do a lot of this manually by looking at the <winerror.h> file. For example, enter a #include in the C ++ source file in Visual Studio, then right-click and ask him to open the header. Where do you find that

 // // MessageId: ERROR_LOCK_VIOLATION // // MessageText: // // The process cannot access the file because another process has locked a portion of the file. // #define ERROR_LOCK_VIOLATION 33L 

By the way, WinExec is just an old compatibility feature. It is preferable to use ShellExecute or CreateProcess . ShellExecute can play more beautifully with Windows Vista and 7 User Access Control, and is easier to use, so it is usually preferable.

0
source

Source: https://habr.com/ru/post/1403420/


All Articles