I'm very new to communication with Windows Device Drivers.
A) I need to communicate with a third-party driver. I see that CreateFile() accept both the device name (such as \\\\.\\DeviceName) and also I can call the full file name (such as \\\\.\\C:\\MyPath\\DriverName.sys). What is the best option? Why? Both works on the same way?
B) I see that many device drivers has two names, for example:
SymbolicLink "\GLOBAL??\VirtualSerial"
Destination "\Device\VrSerialrs232"
If I try open for example open VrSerialrs232 with CreateFile() it fails. So, why is used the VrSerialrs232 if I always have to call the SymbolicLink(VirtualSerial)?
C) I installed a DeviceIOControl monitor to check why my code is failing with Error 50 (The request is not supported) and I can't figure why.
The output of the DeviceIOControl monitor is here
The ones from test.exe are my code, the other (protected) is the original application calling the same device.
My code is like this:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
void ErrorExit(LPTSTR lpszFunction){
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(dw);
}
BOOL OpenDevice(PWSTR DriverName, HANDLE *lphDevice){
WCHAR DeviceName[MAX_PATH]; HANDLE hDevice;
if ((GetVersion() & 0xFF) >= 5) {
wcscpy(DeviceName, L"\\\\.\\Global\\");
} else {
wcscpy(DeviceName, L"\\\\.\\"); }
wcscat(DeviceName, DriverName); printf("Opening.. %S\n", DeviceName);
hDevice = CreateFileW(DeviceName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("CreateFile() ERROR %d\n", GetLastError()); return FALSE;
}
*lphDevice = hDevice; return TRUE;
}
int _tmain(int argc, _TCHAR* argv[]){
HANDLE hDevice = NULL;
DWORD cb = 0;
int ret = 0;
char tcode[] = "\x8a\xb3\x39\x9d"; /* Copied from original request seen on Monitor) */
if(!OpenDevice(L"MyDeviceName",&hDevice)) {
printf("Error: Error opening device\n");
return(0);
} else {
printf("Device succesfully opened!\n");
}
char *Buff = (char *)VirtualAlloc(NULL, 0x330, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (Buff){
ret = DeviceIoControl(hDevice, 0xa028442f, tcode, 0x04, 0, NULL, &cb, (LPOVERLAPPED)NULL);
if (ret == 0) {
printf("Error: Bytes returned %#x\n",cb);
ErrorExit(TEXT("DeviceIoControl: "));
}
}
CloseHandle(hDevice);
return(0);
}
I always get this error:
C:>Test.exe
Opening.. \\.\Global\MyDeviceName
Device succesfully opened!
Error: Bytes returned 0
DeviceIOControl: Error 50 - The request is not supported
Why?
I don't know the name of the IOCTL commands, but I know the numbers. There is a way to translate a IOCTL number to a name?
This is a valid and real request that I captured with a IOCTL monitor.
Log started...
'C:\PathToApplication\OriginalAppName.exe' (PID: 2896)
'\Device\VSFilterbpd' (0x86b83c40) [\??\C:\LocalPath\DeviceDriverName.sys]
SymbolicLink "\GLOBAL??\VSFFilter"
IOCTL Code: 0xa028442f, Method: METHOD_NEITHER
InBuff: 0x004883a4, InSize: 0x00000004
--------------------------------------------------------------------
9c 84 e2 86 | ....
OutBuff: 0x004b4f68, OutSize: 0x00001b20
--------------------------------------------------------------------
03 00 00 00 1c 03 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 e4 0c 00 00 00 00 00 00 00 00 00 00 | ................
A lot of data.
What I'm missing to reproduce / clone / replicate the exact same message to the same driver by from my own application?
Thanks
Typically, Windows device drivers present themselves to user mode applications by creating a symbolic link to themselves in the global namespace via IoCreateSymbolicLink().
So, it woud appear that the author of the driver in question has decided to expose this device to user mode applications, using the name "GLOBAL??\VirtualSerial". So, "\\.\VirtualSerial" (escaped) is the name your application should be using to obtain handles to the device.
User mode services do not have access to the "\Device" namespace for security reasons. This is why your user mode applications cannot open "\Device\VrSerialrs232".
Now, the return code from DeviceIOControl() indicates that the command that you're sending the device is not supported. IOCTL commands are DWORD values whose bits describe the format of the command (buffered/unbuffered, access rights required, etc.). Your best bet would be to obtain a list of the IOCTL commands for the device in question before you start working with this device. If this is a serial port type device, this would be a good place to start.
Good luck!
Related
I'm trying to examine the token and security descriptor in an access check. For learning purposes I wrote the following program that I could use to test:
#include <iostream>
#include <windows.h>
int wmain( int argc, wchar_t *argv[], wchar_t *envp[] )
{
if (argc < 3)
{
std::cerr << "Usage: OpenWithAccess <file> <read/write> [ -d ]" << std::endl;
return ERROR_INVALID_PARAMETER;
}
if (argc == 4 && !_wcsicmp(argv[3], L"-d"))
{
__debugbreak();
}
HANDLE hFile = ::CreateFile(argv[1], !_wcsicmp(argv[2], L"write") ? FILE_ALL_ACCESS : GENERIC_READ, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
std::wcout << L"Successfully opened " << argv[1] << L" with " << argv[2] << L" access" << std::endl;
}
else
{
std::wcout << L"Failed to open " << argv[1] << L" with " << argv[2] << L" access, error = " << ::GetLastError() << std::endl;
}
return ERROR_SUCCESS;
}
When I run this program with OpenWithAccess.exe c:\Windows\system32\drivers\etc\hosts write -d as a standard user, I know the call passes through NtAccessCheck (which fails with an access denied).
In the kernel debugger I can set a breakpoint in nt!NtCreateFile like so:
bp /p <myprocessaddress> nt!NtCreateFile
This breaks fine. However:
bp /p <myprocessaddress> nt!NtAccessCheck
Does not break. However, if I break in nt!NtCreateFile and then simply keep running t to trace through the API I do eventually end up in nt!NtAccessCheck. So why does one breakpoint work but not the other?
I should note that if I run this without my current process like:
bp nt!NtAccessCheck
It does break, but not on my current thread, that is skipped even though I know it ran and some other access check is caught. The one I expect is never caught. What am I missing?
try setting a breakpoint on nt!SeAccessCheck and check if it provides you enough information
i just compiled the code and wrung it through it hits nt!SeAccessCheck
0: kd> !process #$proc 3f
PROCESS ffffd10fc1503080
SessionId: 1 Cid: 17b8 Peb: 55e8cf000 ParentCid: 0bd4
DirBase: 0ef40002 ObjectTable: ffffaa883bdb1240 HandleCount: 32.
Image: fufu.exe
hit bp3
0: kd> bp /p ffffd10fc1503080 nt!NtCreateFile
0: kd> bp /p ffffd10fc1503080 nt!IopCreateFile
0: kd> bp /p ffffd10fc1503080 nt!NtAccessCheck
0: kd> bp /p ffffd10fc1503080 nt!SeAccessCheck
0: kd> g
Breakpoint 0 hit
nt!NtCreateFile:
fffff802`4c8974e0 4881ec88000000 sub rsp,88h
1: kd> g
Breakpoint 1 hit
nt!IopCreateFile:
fffff802`4c897570 4c894c2420 mov qword ptr [rsp+20h],r9
1: kd> g
Breakpoint 3 hit
nt!SeAccessCheck:
fffff802`4c3bd730 48895c2410 mov qword ptr [rsp+10h],rbx
1: kd> kb
# RetAddr Call Site
00 fffff802`4c942c28 nt!SeAccessCheck
01 fffff802`4c9417bf nt!ObpLookupObjectName+0x188
02 fffff802`4c897974 nt!ObOpenObjectByNameEx+0x1df
03 fffff802`4c897559 nt!IopCreateFile+0x404
04 fffff802`4c46d785 nt!NtCreateFile+0x79
05 00007fff`2c6e0114 nt!KiSystemServiceCopyEnd+0x25
06 00007fff`295ee5d6 ntdll!NtCreateFile+0x14
07 00007fff`295ee2c6 KERNELBASE!CreateFileInternal+0x2f6
08 00007ff7`afaa12ed KERNELBASE!CreateFileW+0x66
09 ffffffff`fffffffe fufu!wmain+0xed [c:\users\xxx\desktop\fufu\fufu.cpp # 17]
0a 00007ff7`00000002 0xffffffff`fffffffe
0b 00007ff7`afb39358 0x00007ff7`00000002
0c 00007ff7`afaa1032 fufu!std::classic_locale$initializer$
0d 00007ff7`00000004 fufu!`dynamic initializer for 'std::numpunct<wchar_t>::id''+0x12
0e 00000000`00000080 0x00007ff7`00000004
0f 00000000`00000000 0x80
1: kd> dt nt!_SECURITY_DESCRIPTOR #rcx
+0x000 Revision : 0x1 ''
+0x001 Sbz1 : 0 ''
+0x002 Control : 0x10
+0x008 Owner : 0xffffd10f`bb148fb0 Void
+0x010 Group : 0xffffd10f`bb148fb0 Void
+0x018 Sacl : 0xffffaa88`36e05c10 _ACL
+0x020 Dacl : (null)
I want to use SymGetSourceFile to get a source file from source server using info from a dump file. But the first param is a handle to process but during postmortem we dont have a process, so is it meant to be used only for live debugging tools? How can I use it from a postmortem debugging tool?
BOOL IMAGEAPI SymGetSourceFile(
HANDLE hProcess,
ULONG64 Base,
PCSTR Params,
PCSTR FileSpec,
PSTR FilePath,
DWORD Size
);
https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symgetsourcefile
Update:
I have tried using IDebugAdvanced3 interface for same but get HR = 0x80004002 for GetSourceFileInformation call.
char buf[1000] = { 0 };
HRESULT hr = g_ExtAdvanced->GetSourceFileInformation(DEBUG_SRCFILE_SYMBOL_TOKEN,
"Application.cs",
0x000000dd6f5f1000, 0, buf, 1000, 0);
if (SUCCEEDED(hr))
{
dprintf("GetSourceFileInformation = %s", buf);
char buftok[5000] = { 0 };
hr = g_ExtAdvanced->FindSourceFileAndToken(0, 0x000000dd6f5f1000,
"Application.cs", DEBUG_FIND_SOURCE_TOKEN_LOOKUP,
buf, 1000, 0, buftok, 5000, 0);
if (SUCCEEDED(hr))
{
dprintf("FindSourceFileAndToken = %s", buf);
}
else
dprintf("FindSourceFileAndToken HR = %x", hr);
}
else
dprintf("GetSourceFileInformation HR = %x", hr);
I have dump that has this module and pdb loaded. and pass an address within the module - 0x000000dd6f5f1000, to GetSourceFileInformation
this was a comment but grew up so addingas answer
GetSourceFileINformation iirc checks the source servers those that start with srv or %srcsrv%
this returns a token for use with findsourcefileandtoken
if you have a known offset (0x1070 == main() in case below )
use GetLineByOffset this has the added advantage of reloading all the modules
hope you have your private pdb for the dump file you open.
this is engext syntax
Hr = m_Client->OpenDumpFile("criloc.dmp");
Hr = m_Control->WaitForEvent(0,INFINITE);
unsigned char Buff[BUFFERSIZE] = {0};
ULONG Buffused = 0;
DEBUG_READ_USER_MINIDUMP_STREAM MiniStream ={ModuleListStream,0,0,Buff,BUFFERSIZE,Buffused};
Hr = m_Advanced2->Request(DEBUG_REQUEST_READ_USER_MINIDUMP_STREAM,&MiniStream,sizeof(
DEBUG_READ_USER_MINIDUMP_STREAM),NULL,NULL,NULL);
MINIDUMP_MODULE_LIST *modlist = (MINIDUMP_MODULE_LIST *)&Buff;
Hr = m_Symbols->GetLineByOffset(modlist->Modules[0].BaseOfImage+0x1070,&Line,
FileBuffer,0x300,&Filesize,&Displacement);
Out("getlinebyoff returned %x\nsourcefile is at %s line number is %d\n",Hr,FileBuffer,Line);
this is part src adapt it to your needs.
the result of the extension command is pasted below
0:000> .load .\mydt.dll
0:000> !mydt
Loading Dump File [C:\Users\xxxx\Desktop\srcfile\criloc.dmp]
User Mini Dump File with Full Memory: Only application data is available
OpenDumpFile Returned 0
WaitForEvent Returned 0
Request Returned 0
Ministream Buffer Used 28c
06 00 00 00 00 00 8d 00 00 00 00 00 00 e0 04 00
f0 9a 05 00 2d 2e a8 5f ba 14 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 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
43 00 00 00 4a 38 00 00 00 00 00 00 00 00 00 00
40 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00
No of Modules =6
Module[0]
Base = 8d0000
Size = 4e000
getlinebyoff returned 0
sourcefile is at c:\users\xxx\desktop\misc\criloc\criloc.cpp line number is 21 <<<<<<<<<
||1:1:010> lm
start end module name
008d0000 0091e000 CRILOC (private pdb symbols) C:\Users\xxxx\Desktop\misc\CRILOC\CRILOC.pdb
||1:1:010>
and the actual source file contents on path
:\>grep -i -n main CRILOC.CPP
20:int main(void) << the curly braces is on line 21
UPDATE:
yes if the src file is not source indexed (cvs,perforce,... ) GetSourceFileInformation () will not return a token
it checks for a token using the Which parameter
and the returned info can be used in FindSourceFileAndToken();
if your source is not source indexed and you only have a source path
use FindSourceFileandToken() with DEBUG_FIND_SOURCE_FULL_PATH Flag
be aware you need to either use SetSourcePath() or issue .srcpath command or use _NT_SOURCE_PATH environment variable or use -srcpath commandline switch prior to invoking FindSourceFileAndToken()
see below for a walkthrough
sourcefile and contents
:\>ls *.cpp
mydt.cpp
:\>cat mydt.cpp
#include <engextcpp.cpp>
#define BSIZE 0x1000
class EXT_CLASS : public ExtExtension {
public:
EXT_COMMAND_METHOD(mydt);
};
EXT_DECLARE_GLOBALS();
EXT_COMMAND( mydt, "mydt", "{;e,o,d=0;!mydt;}" ){
HRESULT Hr = m_Client->OpenDumpFile("criloc.dmp");
Hr = m_Control->WaitForEvent(0,INFINITE);
char Buff[BSIZE] = {0};
ULONG Buffused = 0;
DEBUG_READ_USER_MINIDUMP_STREAM MiniStream ={ModuleListStream,0,0,
Buff,BSIZE,Buffused};
Hr = m_Advanced2->Request(DEBUG_REQUEST_READ_USER_MINIDUMP_STREAM,&MiniStream,
sizeof(DEBUG_READ_USER_MINIDUMP_STREAM),NULL,NULL,NULL);
MINIDUMP_MODULE_LIST *modlist = (MINIDUMP_MODULE_LIST *)&Buff;
//m_Symbols->SetSourcePath("C:\\Users\\xxx\\Desktop\\misc\\CRILOC");
char srcfilename[BSIZE] ={0};
ULONG foundsize =0 ;
Hr = m_Advanced3->FindSourceFileAndToken(0,modlist->Modules[0].BaseOfImage,"criloc.cpp",
DEBUG_FIND_SOURCE_FULL_PATH,NULL,0,NULL,srcfilename,0x300,&foundsize);
Out("gsfi returned %x\n" , Hr);
Out("srcfilename is %s\n",srcfilename);
}
compiled and linked with
:\>cat bld.bat
#echo off
set "INCLUDE= %INCLUDE%;E:\windjs\windbg_18362\inc"
set "LIB=%LIB%;E:\windjs\windbg_18362\lib\x86"
set "LINKLIBS=user32.lib kernel32.lib dbgeng.lib dbghelp.lib"
cl /LD /nologo /W4 /Od /Zi /EHsc mydt.cpp /link /nologo /EXPORT:DebugExtensionInitialize /Export:mydt /Export:help /RELEASE %linklibs%
:\>bld.bat
mydt.cpp
E:\windjs\windbg_18362\inc\engextcpp.cpp(1849): warning C4245: 'argument': conversion from 'int' to 'ULONG64', signed/unsigned mismatch
Creating library mydt.lib and object mydt.exp
:\>file mydt.dll
mydt.dll; PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit
executing
:\>cdb cdb
Microsoft (R) Windows Debugger Version 10.0.18362.1 X86
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ntdll!LdrpDoDebuggerBreak+0x2c:
77d805a6 cc int 3
0:000> .load .\mydt.dll
0:000> .chain
Extension DLL chain:
.\mydt.dll: API 1.0.0, built Thu Mar 18 20:40:04 2021
[path: C:\Users\xxxx\Desktop\srcfile\New folder\mydt.dll]
0:000> !mydt
Loading Dump File [C:\Users\xxxx\Desktop\srcfile\New folder\criloc.dmp]
User Mini Dump File with Full Memory: Only application data is available
gsfi returned 80004002
srcfilename is
||1:1:010> .srcpath "c:\\users\\xxxx\\desktop\\misc\\criloc\\"
Source search path is: c:\\users\\xxxx\\desktop\\misc\\criloc\\
************* Path validation summary **************
Response Time (ms) Location
OK c:\\users\\xxxx\\desktop\\misc\\criloc\\
||1:1:010> !mydt
Loading Dump File [C:\Users\xxxx\Desktop\srcfile\New folder\criloc.dmp]
gsfi returned 0
srcfilename is c:\\users\\xxxx\\desktop\\misc\\criloc\\criloc.cpp
||2:2:021>
I am trying to get the temperature data of LM75A which is connected to atmega8 microcontroller using i2c, and display the data to docklight using serial communication. I have written the code and the output I am getting is
FF 7F 0F
According to the datasheet, if I ignore FF then 7F 0F will lead to +125 C temperature. But i dont know if its right or wrong(and why to ignore FF). So i am confused in cracking the output I am getting. The code which I think is correct but if it is wrong please correct it.
CODE:
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include<util/delay.h>
#include <stdio.h>
//Serial tansmit
void serial_avr(char *str)
{
UCSRB=(1<<TXEN);
UCSRC=(1<<UCSZ1)|(1<<UCSZ0)|(1<<URSEL);
UBRRL=51;
for (unsigned int i=0;str[i]!=0;i++)
{
UDR=str[i];
while(!(UCSRA&(1<<UDRE)));
}
_delay_ms(500);
}
void i2c_init(void)
{
TWSR=0x00;
TWBR=0x47;
TWCR=0x04;
}
void i2c_start(void)
{
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while ((TWCR & (1 << TWINT)) == 0);
}
void i2c_write(unsigned char data)
{
TWDR = data ;
TWCR = (1<< TWINT)|(1<<TWEN);
while ((TWCR & (1 <<TWINT)) == 0);
}
unsigned char i2c_read(unsigned char ackVal)
{
TWCR = (1<< TWINT) | (1<<TWEN) | (ackVal<<TWEA);
while (!(TWCR & (1 <<TWINT)));
return TWDR ;
}
void i2c_stop()
{
TWCR = (1<< TWINT)|(1<<TWEN)|(1<<TWSTO);
}
void main(void)
{
int i =23;
unsigned char temp[20];
i2c_init();
i2c_start();
i2c_write(0b10010001); //slave address for LM75A
i2c_stop();
i2c_init();
i2c_start();
i2c_write(0b00000000); //pointer register address of LM75A
i2c_stop();
i2c_init();
i2c_start();
temp[20] = i2c_read(1);
i2c_stop();
while(1)
{
serial_avr(temp);
_delay_ms(2000);
}
}
I am reading the temperature in array and I am getting the output as FF 7F 0F and when I initialise it as a normal char variable then I am getting the output as C4. I am confused, i dont know where I am missing the point. If there is any error in the code then please tell me and how to crack the output.??
Please help, thanks.!
The first obvious error is how you treat the array temp[20].
You only read one byte from the sensor, but then write the value off the end of the array. (The only valid spots in the array are temp[0] to temp[19]. temp[20] is past the memory allocated.) You should be reading 3 bytes from the sensor and storing them at temp[0] to temp[2].
The next error with temp is how you write it out over the serial. You should be writing all the bytes of the array, not all the bytes until a 0. You don't know the last byte of the array is 0, because it never had a string in it. A convenient thing to do would be to #define a value for the length of the array so you could refer to it in the declaration of the array and in the write function.
Until you fix these problems, it is hard to tell if the rest is working. I don't see how you can even know that the values form the sensor are FF 7F 0F.
I have a JNI DLL, that crashes when using GetFieldID() on a class object, that was passed into a function. The library is working fine on Linux with 32-bit and 64-bit JVMs and only crashes when using 32-bit under Windows - 64-bit is fine.
The original DLL was cross-compiled on ubuntu 13.10 x86_64 using MinGW-w64 GCC 4.6.3, but I also compiled it natively under Windows using MinGW-w64 GCC 4.6.3 and I still got the same crash. Using ubuntu 14.04 with MinGW-w64 4.8.2 still produces the same error.
It appears there is some memory corruption going on since when I use an unoptimized DLL the crash doesn't happen an the first call on GetFieldID(), but on a later one (the original DLL has way more code than the stripped down example below) or even after the function finished somewhere in the JVM garbage collection.
The JVM I am using is Java 7u60, but I also tried it with 8u5 and got the same results. I tested it with the 32-bit JVM on a 64-bit and 32-bit systems as I came across an article, that said, that a 32-bit JVM might not be reliable on 64-bit Windows operating systems (sounded a bit bogus to me, but just to be sure).
Also there are other JNI DLLs, that don't utilize GetFieldID() at all and they are working just fine with 32-bit.
The crash data from the hs_err_pid.log
Current thread (0x00d5e000): JavaThread "main" [_thread_in_native, id=1104, stack(0x00dd0000,0x00e20000)]
siginfo: ExceptionCode=0xc0000005, ExceptionInformation=0x00000008 0x3462c9e8
Registers:
EAX=0x00000000, EBX=0x00e1f1fc, ECX=0x97254d7c, EDX=0x00d5eac4
ESP=0x00e1f1dc, EBP=0x00e1f1ec, ESI=0x3462c6e8, EDI=0x00d5e000
EIP=0x3462c9e8, EFLAGS=0x00010246
Top of Stack: (sp=0x00e1f1dc)
0x00e1f1dc: 00000000 3462c6e8 00000000 00e1f1fc
0x00e1f1ec: 00e1f224 025f334f 246970c0 025f88c9
0x00e1f1fc: 24695668 2460b700 00e1f204 34628d1b
0x00e1f20c: 00e1f22c 34628ee8 00000000 34628d40
0x00e1f21c: 00e1f1fc 00e1f22c 00e1f25c 025f3207
0x00e1f22c: 24693760 24693760 00000001 24693758
0x00e1f23c: 00e1f234 34628c56 00e1f264 34628ee8
0x00e1f24c: 00000000 34628c88 00e1f22c 00e1f268
Instructions: (pc=0x3462c9e8)
0x3462c9c8: 78 bc 62 34 50 bb 62 34 c0 bd 62 34 30 bd 62 34
0x3462c9d8: 00 00 00 00 00 00 00 00 0c 00 00 00 02 00 00 00
0x3462c9e8: 01 00 00 00 60 f9 5f 39 02 00 00 00 a0 b9 62 34
0x3462c9f8: 0a 00 b8 00 10 d6 00 39 00 00 00 00 01 00 40 80
Register to memory mapping:
EAX=0x00000000 is an unknown value
EBX=0x00e1f1fc is pointing into the stack for thread: 0x00d5e000
ECX=0x97254d7c is an unknown value
EDX=0x00d5eac4 is an unknown value
ESP=0x00e1f1dc is pointing into the stack for thread: 0x00d5e000
EBP=0x00e1f1ec is pointing into the stack for thread: 0x00d5e000
ESI=0x3462c6e8 is an oop
{method}
- klass: {other class}
EDI=0x00d5e000 is a thread
Stack: [0x00dd0000,0x00e20000], sp=0x00e1f1dc, free space=316k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C 0x3462c9e8
j jnitest.JNIClass.<init>()V+27
j jnitest.JNIClass.getInstance()Ljnitest/JNIClass;+22
j jnitest.Program.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
V [jvm.dll+0x140e6a]
V [jvm.dll+0x20529e]
V [jvm.dll+0x140eed]
V [jvm.dll+0x14d2ee]
V [jvm.dll+0x14d515]
V [jvm.dll+0xf1f99]
C [java.dll+0x7d82]
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j com.intellij.rt.execution.application.AppMain.main([Ljava/lang/String;)V+163
v ~StubRoutines::call_stub
V [jvm.dll+0x140e6a]
V [jvm.dll+0x20529e]
V [jvm.dll+0x140eed]
V [jvm.dll+0xca5c5]
V [jvm.dll+0xd5267]
C [java.exe+0x2063]
C [java.exe+0xa5d1]
C [java.exe+0xa65b]
C [kernel32.dll+0x1338a]
C [ntdll.dll+0x39f72]
C [ntdll.dll+0x39f45]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j jnitest.JNIWrapper.createUuid(Ljnitest/JNIWrapper$sender_id_t;)I+25
j jnitest.JNIClass.<init>()V+27
j jnitest.JNIClass.getInstance()Ljnitest/JNIClass;+22
j jnitest.Program.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j com.intellij.rt.execution.application.AppMain.main([Ljava/lang/String;)V+163
v ~StubRoutines::call_stub
The Java class:
package jnitest;
public class JNIClass {
static final Object _mutex = new Object();
static JNIClass _instance = null;
public static JNIClass getInstance()
{
if (_instance == null)
{
synchronized (_mutex)
{
if (_instance == null)
_instance = new JNIClass();
}
}
return _instance;
}
JNIWrapper.sender_id_t sid = null;
JNIClass() {
//create uuid
sid = new JNIWrapper.sender_id_t();
System.out.print(JNIWrapper.createUuid(sid));
}
}
The JNI wrapper class:
package jnitest;
public final class JNIWrapper {
static {
System.loadLibrary("JNIWrapper");
}
public static class sender_id_t
{
public long phy_idx;
}
public static native int createUuid(JNIWrapper.sender_id_t id);
}
The application:
package jnitest;
public class Program
{
public static void main(String[] args)
{
JNIClass.getInstance();
System.exit(0);
}
}
The auto-generated JNI DLL header:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnitest_JNIWrapper */
#ifndef _Included_jnitest_JNIWrapper
#define _Included_jnitest_JNIWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnitest_JNIWrapper
* Method: createUuid
* Signature: (Ljnitest/JNIWrapper/sender_id_t;)I
*/
JNIEXPORT jint JNICALL Java_jnitest_JNIWrapper_createUuid
(JNIEnv *, jclass, jobject);
#ifdef __cplusplus
}
#endif
#endif
The JNI DLL implementation (updated to be able to use either C or C++ interface):
#include "jnitest_JNIWrapper.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define JNIFUNC(e,f) e->f()
#define JNIFUNCV(e,f,...) e->f(__VA_ARGS__)
#else
#define JNIFUNC(e,f) (*e)->f(e)
#define JNIFUNCV(e,f,...) (*e)->f(e,__VA_ARGS__)
#endif
JNIEXPORT jint JNICALL Java_jnitest_JNIWrapper_createUuid(JNIEnv *env, jclass clazz, jobject sid)
{
(void)clazz;
jclass cls = JNIFUNCV(env,GetObjectClass, sid);
jfieldID phyID = JNIFUNCV(env,GetFieldID, cls, "phy_idx", "J");
(void)phyID;
if (JNIFUNC(env,ExceptionCheck))
return 100;
return 0;
}
#ifdef __cplusplus
}
#endif
Update:
The compilation command:
i686-w64-mingw32-gcc -std=c99 -O3 -s -Wall -Wextra -Werror -o ../bin/JNIWrapper.dll -shared -Wl,--subsystem,windows dllmain.c JNIWrapper.c -I /usr/lib/jvm/java-7-openjdk-amd64/include
You are trying to get a field id of an inner class but your cls variable is the outer class JNIWrapper. You probably need to run something like (*env)->FindClass(env, "jnitest/JNIWrapper$sender_id_t") to get the correct cls to call get field id on. The javap -c tool can tell you what "jnitest/JNIWrapper$sender_id_t" should be.
I tried with
extern void __NSAutoreleaseNoPool(void* obj);
but that results in an unresolved symbol when linking (not sure what Framework it needs, though).
I also tried
dlsym(RTLD_DEFAULT, "__NSAutoreleaseNoPool")
but that just gives be NULL.
And I tried with _dyld_lookup_and_bind and NSLookupSymbolInImage but they also don't work.
dsymutil and nm both find the symbol, though:
$ dsymutil -s --arch=x86_64
----------------------------------------------------------------------
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
----------------------------------------------------------------------
Symbol table for: '/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' (x86_64)
----------------------------------------------------------------------
Index n_strx n_type n_sect n_desc n_value
======== -------- ------------------ ------ ------ ----------------
[ 0] 00010795 1e (PEXT SECT ) 01 0000 0000000000000000 '__mh_dylib_header'
[ 1] 000107a7 0e ( SECT ) 01 0000 0000000000001c20 '+[NSObject(NSObject) load]'
[ 2] 000107c2 0e ( SECT ) 01 0000 0000000000002630 '___exceptionInit'
[ 3] 000107d3 0e ( SECT ) 01 0000 00000000000029e0 '___CFgetenv'
[ 4] 000107df 0e ( SECT ) 01 0000 0000000000002a50 '___CFBaseInitialize'
...
[ 1923] 0001e820 0e ( SECT ) 01 0000 000000000010ad30 '___NSAutoreleaseNoPool'
...
$ nm -arch x86_64 /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
...
000000000010ad30 t ___NSAutoreleaseNoPool
...
(That is on MacOSX 10.6. On later MacOSX versions, the symbol really does not seem to exists, at least I cannot find any ref via grep in /usr/lib and /System/Library/Frameworks and also LLDB does not find it. Probably it was removed somehow with ARC.)
So, how can I get that address in my code?
(Related questions: here and here)
(My motivation to do this is here.)
This works:
#include <dlfcn.h>
#include <stdio.h>
#import <Foundation/Foundation.h>
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
#include <string.h>
#include <assert.h>
// Adapted from:
// https://github.com/0xced/iOS-Artwork-Extractor/blob/master/Classes/FindSymbol.c
// Adapted from MoreAddrToSym / GetFunctionName()
// http://www.opensource.apple.com/source/openmpi/openmpi-8/openmpi/opal/mca/backtrace/darwin/MoreBacktrace/MoreDebugging/MoreAddrToSym.c
void *FindSymbol(const struct mach_header *img, const char *symbol)
{
if ((img == NULL) || (symbol == NULL))
return NULL;
// only 64bit supported
#if defined (__LP64__)
if(img->magic != MH_MAGIC_64)
// we currently only support Intel 64bit
return NULL;
struct mach_header_64 *image = (struct mach_header_64*) img;
struct segment_command_64 *seg_linkedit = NULL;
struct segment_command_64 *seg_text = NULL;
struct symtab_command *symtab = NULL;
unsigned int index;
struct load_command *cmd = (struct load_command*)(image + 1);
for (index = 0; index < image->ncmds; index += 1, cmd = (struct load_command*)((char*)cmd + cmd->cmdsize))
{
switch(cmd->cmd)
{
case LC_SEGMENT_64: {
struct segment_command_64* segcmd = (struct segment_command_64*)cmd;
if (!strcmp(segcmd->segname, SEG_TEXT))
seg_text = segcmd;
else if (!strcmp(segcmd->segname, SEG_LINKEDIT))
seg_linkedit = segcmd;
break;
}
case LC_SYMTAB:
symtab = (struct symtab_command*)cmd;
break;
default:
break;
}
}
if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL))
return NULL;
unsigned long vm_slide = (unsigned long)image - (unsigned long)seg_text->vmaddr;
unsigned long file_slide = ((unsigned long)seg_linkedit->vmaddr - (unsigned long)seg_text->vmaddr) - seg_linkedit->fileoff;
struct nlist_64 *symbase = (struct nlist_64*)((unsigned long)image + (symtab->symoff + file_slide));
char *strings = (char*)((unsigned long)image + (symtab->stroff + file_slide));
struct nlist_64 *sym;
for (index = 0, sym = symbase; index < symtab->nsyms; index += 1, sym += 1)
{
if (sym->n_un.n_strx != 0 && !strcmp(symbol, strings + sym->n_un.n_strx))
{
unsigned long address = vm_slide + sym->n_value;
if (sym->n_desc & N_ARM_THUMB_DEF)
return (void*)(address | 1);
else
return (void*)(address);
}
}
#endif
return NULL;
}
typedef void (*NSAutoreleaseNoPoolFunc) (void* obj);
void getNSAutoreleaseNoPool() {
const struct mach_header* img = NSAddImage("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", NSADDIMAGE_OPTION_NONE);
NSAutoreleaseNoPoolFunc f = (NSAutoreleaseNoPoolFunc) FindSymbol((struct mach_header*)img, "___NSAutoreleaseNoPool");
printf("func: %p\n", f);
if(f) {
NSObject* foo = [[NSObject alloc] init];
f(foo);
}
}
It gets the same function pointer as within GDB.
Note that you wont see the common NSAutoreleaseNoPool log:
2014-02-18 14:46:26.583 a.out[24989:a0b] *** __NSAutoreleaseNoPool(): Object 0x7fff71154190 of class NSCFString autoreleased with no pool in place - just leaking
The standard backtrace, when that happens, is this:
(gdb) bt
#0 0x00007fff8724bd34 in __NSAutoreleaseNoPool ()
#1 0x00007fff87196e79 in _CFAutoreleasePoolAddObject ()
#2 0x00007fff87196be6 in -[NSObject(NSObject) autorelease] ()
The actual NSLog call is done in _CFAutoreleasePoolAddObject.
A note about __NSAutoreleaseNoPool, from Foundation/NSDebug.h:
/**************** Autorelease pool debugging ****************/
// Functions used as interesting breakpoints in a debugger
// void __NSAutoreleaseNoPool(void *object);
// Called to log the "Object X of class Y autoreleased with no
// pool in place - just leaking" message. If an environment
// variable named "NSAutoreleaseHaltOnNoPool" is set with string
// value "YES", the function will automatically break in the
// debugger (or terminate the process).
// void __NSAutoreleaseFreedObject(void *freedObject);
// Called when a previously freed object would be released
// by an autorelease pool. If an environment variable named
// "NSAutoreleaseHaltOnFreedObject" is set with string value
// "YES", the function will automatically break in the debugger
// (or terminate the process).
So, if you want to debug such cases, either start up GDB and issue b __NSAutoreleaseNoPool to setup the breakpoint on this function. Or do an export NSAutoreleaseHaltOnNoPool=1 in your shell.
__NSAutoreleaseNoPool is pretty simple:
(gdb) disassemble
Dump of assembler code for function __NSAutoreleaseNoPool:
0x00007fff8724bd30 <__NSAutoreleaseNoPool+0>: push %rbp
0x00007fff8724bd31 <__NSAutoreleaseNoPool+1>: mov %rsp,%rbp
0x00007fff8724bd34 <__NSAutoreleaseNoPool+4>: nop
0x00007fff8724bd35 <__NSAutoreleaseNoPool+5>: nopl 0x0(%rax)
0x00007fff8724bd39 <__NSAutoreleaseNoPool+9>: lea 0x2ced8(%rip),%rdi # 0x7fff87278c18 <__PRETTY_FUNCTION__.27904+480>
0x00007fff8724bd40 <__NSAutoreleaseNoPool+16>: callq 0x7fff871439e0 <__CFgetenv>
0x00007fff8724bd45 <__NSAutoreleaseNoPool+21>: test %rax,%rax
0x00007fff8724bd48 <__NSAutoreleaseNoPool+24>: je 0x7fff8724bd55 <__NSAutoreleaseNoPool+37>
0x00007fff8724bd4a <__NSAutoreleaseNoPool+26>: movzbl (%rax),%eax
0x00007fff8724bd4d <__NSAutoreleaseNoPool+29>: cmp $0x59,%al
0x00007fff8724bd4f <__NSAutoreleaseNoPool+31>: je 0x7fff8724bd60 <__NSAutoreleaseNoPool+48>
0x00007fff8724bd51 <__NSAutoreleaseNoPool+33>: cmp $0x79,%al
0x00007fff8724bd53 <__NSAutoreleaseNoPool+35>: je 0x7fff8724bd60 <__NSAutoreleaseNoPool+48>
0x00007fff8724bd55 <__NSAutoreleaseNoPool+37>: leaveq
0x00007fff8724bd56 <__NSAutoreleaseNoPool+38>: retq
0x00007fff8724bd57 <__NSAutoreleaseNoPool+39>: nopw 0x0(%rax,%rax,1)
0x00007fff8724bd60 <__NSAutoreleaseNoPool+48>: int3
0x00007fff8724bd61 <__NSAutoreleaseNoPool+49>: callq 0x7fff872609c2 <dyld_stub_getpid>
0x00007fff8724bd66 <__NSAutoreleaseNoPool+54>: mov %eax,%edi
0x00007fff8724bd68 <__NSAutoreleaseNoPool+56>: mov $0x9,%esi
0x00007fff8724bd6d <__NSAutoreleaseNoPool+61>: leaveq
0x00007fff8724bd6e <__NSAutoreleaseNoPool+62>: jmpq 0x7fff87260a16 <dyld_stub_kill>
0x00007fff8724bd73 <__NSAutoreleaseNoPool+67>: nopw 0x0(%rax,%rax,1)
0x00007fff8724bd79 <__NSAutoreleaseNoPool+73>: nopl 0x0(%rax)
End of assembler dump.
For a practical example, see demo_NSAutoreleaseNoPool.mm.