A New Location Notice

May 15, 2018

For readers who usually try to find the latest article in this blog, this is to inform you that new posts will be placed on https://excellentcorner.com.

The old articles starting from this one below will still be here.

Thank you for your attention.


A Plea for Donation

May 3, 2018

First, I’d like to say thank you to all personnel involved in WordPress web application, so that I can post articles that can be shared to the world.

I’ve started to create the article into this platform since 2008 and now it is already have more than 100 articles and still counting 🙂

This website is about problem and solution in the field of IT (Information Technology) ranging from Windows, Android to any other open sources platforms.

You can see that in the span of the year 2008 until now, the average article each month is about one or two articles.

It is because of the nature of the subject, which is about problem and its solution, sometimes, the solution will came about a month after the problem is clearly identified.

One example is my research on Android’s SIGILL problem in Android Native Debugging. Much of the time is to seek out information and provide necessary conditions to tackle the problem at hand.

The topic of SIGILL itself is requires about 5 articles starting from problem statement to its solution.

So, it takes time to create article with this kind of situation.

If you find this article is helpful, inspiring, saves your time and money, you can consider to provide donation to my Paypal account at surya_rakanta[at]yahoo.com.

But this doesn’t mean that you obliged to provide donation when reading at my website, it’s optional.

How to Check for HTTP 500 Error (Part 2)

April 24, 2018

Suppose I add an invalid tag inside web.config as follows:

Even if I already add httpErrors tag with error mode “Detailed” the page will only show as follows:

Internally there is a call to FormatMessage function with detailed callstack as follows:

By examining the stack register (rsp) inside the FormatMessageW function we have as follows:

Here’s the output buffer after the call:

Although this message is somehow do not get thorugh the browser, you can check the parsing error message using WinDBG to get more information to fix web.config error.

How to Check for HTTP 500 Error

April 21, 2018

So, when I created some testing web site and accidentally adding the impersonate tag as follows:

The browser will show the HTTP 500 Internal Server Error as follows:

To set for a detailed error, this can be done by adding tag as follows:

This will show detailed error as follows:

An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode.

Most likely causes:

system.web/identity@impersonate is set to true.

But in this article, I will discuss how the IIS handles this error internally.

The first indication of the error can be found at System.Web.Handlers.ScriptModule.EndRequestHandler method by checking the HttpContext object:

The IL dissasembled instruction as follows:

Here is the HttpContext object:

Inside this object resides the HttpResponse object in the _response variable, let’s examine it:

Inside HttpResponse object there is _statusCode already filled with 500 value. But when this status code is created ? This can be found by perform break point at HttpContext class construction, identify the HttpResponse object and perform write break point for the status code. In this fashion, I arrive at this call stack:

By examining the IL dissasemble of System.Web.Hosting.IIS7WorkerRequest.GetStatusChanges, the status code is originated from MgdGetStatusChanges export function of webengine4.dll.

Again by performing break point on write access of status code inside MgdGetStatusChanges, I arrive at:

By performing break point at iiscore!RESPONSE_CONTEXT::SetStatus, I arrive at:

Inside validcfg!CDetectISAPIConfigModule::OnBeginRequest:

At close examination, the value r15+8 of 1 below seems to determine whether it is in integrated or classic mode in the validation process:

The value of r15 itself is obtained from iiscore!CONTEXT_CONTAINER::GetModuleContext

The subsequent check on values relative to r15 seems to denote the detail of the error, for example, when I set value of r15+0Ch to 1. It will show this error on the browser in detailed mode:

Most likely causes:
This application defines configuration in the system.web/httpModules section.

The httpErrors tag inside system.webServer is supposed to give detailed web.config error, but if I accidentally placed it in wrong place, again the HTTP 500 error will show up. So, it is a paradox situation. The httpErrors tag that supposed to provide detailed error information is trapped into HTTP 500 error itself. This will surely caused confusion.

But at this time, there are no call into validcfg!CDetectISAPIConfigModule::OnBeginRequest, but on nativerd!PARSE_ERROR_INFO::SetErrorInfo instead.

Here’s the complete call stack:

The routine below will call iisutil!STRU::QueryStr three times, which actually extract information from nativerd!READ_ONLY_DOM_NODE::GetLineContents.

And by examining the rax register at each call, I can determine the line information of web.config error:

Here’s the rax register contents of each call. The first one is the line information that causes error, the second is before and after of this line:

There is also information about line number that causes error in the web.config in routine nativerd!CONFIG_DOM_ERROR::SetNodeError as follows:

Inside the above routine, there’s a call to nativerd!READ_ONLY_DOM_NODE::QueryLineNumber that gives the line number in rax register.

In the end this might provide some insights on the how the IIS 8.0 process the web page request.

Analysis of OsiSoft’s PI OPC DA Interface (OPCInt_ReadOnly)

March 28, 2018

For the purpose of analysis, the interface program is placed into D:\Projects\OsiSoft\PI03\Interfaces. Using WinDbg when try to activate the program, I have:

Copy the required piapi32.dll from source to C:\Windows\SysWOW64. Perform program re-load in WinDBG, this time no error, but the program exit without any error indication.

But when performing execution on Windows Command Prompt, I have:

The point source is required by PI OPC Interface to determine which group of tags that should be retrieved for OPC data. Let’s specify it as:

Now let’s try to use non-existent server for required host:

Now let’s try the server that does exist:

OPCpi> OPCDA_ABB 0> CRITICAL ERROR> Error -10400: pipt_nextptwsourcelong failed for point source UI_IF_INFO

The above error is caused by trust is not entered in PI SMT (System Management Tool). The trust should contains as follows:

Where the IP Address contains IP of client that tries to connect to the PI Data Archive server. Now, I have:

Try to run in help mode:

From the help description, the command line for defining scan class is using /f. Now I have:

OPCpi> OPCDA_ABB 0> No OPC server specified
OPCpi> OPCDA_ABB 0> CRITICAL ERROR> Interface initialization error, Intf halted

Specify the OPC Server as requested, now I have:

OPCpi> OPCDA_ABB 0> No such OPC Server; CLSID_OPCServerList returns : Access is denied (80070005)
OPCpi> OPCDA_ABB 0> Can’t get GUID for CLSID: Access is denied (80070005)

This is because the authorized user does not have administrative privilege on the OPC Server. So perform runas as follows:

runas /user:xxx “OPCInt_ReadOnly.exe /PS=OPCDA_ABB /host=yyyy:5450 /SERVER=172.xx.xx.xx::ABB.AfwOpcDaSurrogate.1 /f=00:00:30”

Where xxx is user that has administrative privilege on destination OPC Server and yyyy is PI Data Archive server name. But now I have:

OPCpi> OPCDA_ABB 0> No such OPC Server; CLSID_OPCServerList returns : 80040155(Interface not registered). This error is rather strange, because the interface should exist in destination ABB OPC Server.

After exhausting all other options, I decided to move the PI OPC Interface to another machine that acted as a server, and this time the error 0x80040155 is gone. It looks like that ABB OPC Server is rather picky about the type of machine that tries to connect to it, and it looks for a server class machine.

For option /vn=1, the PI OPC Interface will use IDataObject for registering callback, but strangely returns no error information when the remote OPC Server failed to call the callback function on PI OPC Client due to authentication error.

This is evident in the event viewer on the client as follows:

This will cause confusion on the user because they will think that interface is working but no data will be shown on PI Data Archive.

For option /vn=2, PI OPC Interface will show error as follows:

OPCpi> OPCDA_ABB 0> Advise returns E_ACCESSDENIED, check DCOM configuration: Access is denied (80070005)

This is because for v2.05a, PI OPC Interface will try to use IConnectionPointContainer and IOPCDataCallback interface for OPC connection, so that the OPC Server and Client should have proper user authentication as administrator.

This kind of two way communication will certainly not work if the OPC Server is already invoked with different user id and it is not registered in OPC client. Seems that PI OPC Interface is not implementing SyncRead that provide one way communication and doesn’t care about registering proper user id for both machine.

I think that’s enough for now.

Experiences in Using OsiSoft’s PI OPC DA Interface

March 24, 2018

So, the OPC DA Interface should be properly configured, but got this error message in pigetmsg:

I 22-Mar-18 07:34:43 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0 (4)
>> Advise returns E_ACCESSDENIED, check DCOM configuration: Access is denied (80070005)

Checking on DCOM Security is already configured properly:

Let’s try using OPC Protocol v1.0a instead of v2.05a:

Re-start the OPC Interface and this time there are no errors:

I 22-Mar-18 07:41:43 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0 (4)
>> Connected to OPC Server 172.xx.xx.xx::ABB.AfwOpcDaSurrogate.1 in thread ID
1908 (1908)…

Created a test PI Point using PI System Management Tools:

So, the Location4 is Scan Class number. What about other location description ? This can be obtained from:


Description copied here:

Location1, Location2, Location3, Location4, and Location5

There are five integer location codes. Their meanings depend on the interface. For many PI Interfaces, you use the Location1 attribute to specify the interface ID number and the Location4 attribute to assign scan class. For instrument interfaces, the location codes often describe a hardware or software address for reading or writing the value.
For example, in the PI Interface for OPC DA:

The Location1 attribute specifies the instance of the interface to which the PI point belongs.
Location2 configures the handling of data types.
Location3 specifies whether this PI point is a polled, advise, event, or output point.
Location4 configures the scan class for the PI point. The scan class determines the frequency at which input tags are scanned for new values.
Location5 specifies a deadband value for analog OPC items.

But the PI Data Archive Server still do not get incoming data:

Checking on the log message reveals:

I 22-Mar-18 07:41:45 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 |
>> Poll Group 1 named ‘Poll0’.

E 22-Mar-18 07:41:45 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0
>> xxx: cannot have polled tag in scan class 1

Seems this error has some relation to Location3 setting. Let’s perform some revision and save:

And wait for some minutes to get the status:

I 22-Mar-18 08:15:42 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0
>> Added point xxx

I 22-Mar-18 08:15:42 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0
>> tag xxx (13) is added to the Interface

But still, the PI Data Archive is not getting data. Hmm, interesting. Let’s re-start the OPC Interface and I have:

I 22-Mar-18 08:30:35 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0
>> Advise class 1, update rate 00:00:20.0 (20000), has 2 points

Trying to change the value of location3 to any value other than 0 (zero) has no effect, the OPC Interface will always treated the point as “Advice class” which seems do not getting any data.

So, let’s change back the Location3 value to 0 (zero) that will cause the error above. Then, I create 2 class, and assign each point scan class (Location4) to 2 and leave location3 intact (with 0 (zero)) value. (Some correction: Location3 should be the value other than 0 (zero) in order for PI OPC DA to retrieved the advised data from remote OPC server).

This time there are no errors but the PI Data Archive still not getting any data. Later on, by examining the Event Viewer, I notice that on security audit, there are lots of security failure for 800xAService user like this:

This identity is coming from the setting in DCOM Configuration for AfwDsOpcSurrogate executable as follows:

Each time when “Advise error” is shown in PI Message log:

I 23-Mar-18 13:56:28 OPCInt_ReadOnly:OPCpi:OPCDA_ABB | 1 | 0 (4)
>> Advise returns E_ACCESSDENIED, check DCOM configuration: Access is denied (80070005)

The security failure even is always shown up, regardless of whether the “Force v1.0a Protocol” is checked or not. Seems that PI OPC DA Interface is always using Advise callback interface that causes remote OPC Server to call a method in the PI OPC DA Interface program.

Seems that either v1.0a or v2.05a the PI OPC Client Interface always use the remote callback mechanism instead of one way using Synchronous read.

The callback ends in failure because 800xAService is not recognized by server that runs the PI OPC DA Interface. Some possible solutions as follows: create a different user and password that exist for both workstation, or ask the password for 800xAService and create it in PI OPC Interface server, using the given password.

To make sure that is really this is the case, I’ve created GrayBox OPC Server Simulator on another workstation, and try to perform OPC Client check using PI OPC DA Client tools. Upon connecting to the server, there’s an error as follows:

The error says “Unable to advise for shutdown notification”. Although I already login using user that has administrative privilege on both machine. Curiously, there’s also a COMRuntime error on event viewer:

Which says:

The machine-default permission settings do not grant Remote access permission to the COM Server application D:\Program Files (x86)\PIPC\PI OPC Tools\PI_OPCClient\OPCClient.exe with APPID Unavailable to the user NT AUTHORITY\ANONYMOUS LOGON SID (S-1-5-7) from address 172.xx.xx.xx running in the application container Unavailable SID (Unavailable). This security permission can be modified using the Component Services administrative tool.

Checking into GrayBox’s OPC Server DCOM Config on Component Services in the destination workstation I have as follows:

So, “The launching user” when it tries to perform callback to the client, the client will identify it as NT AUTHORITY\ANONYMOUS LOGON.

Checking into the task manager, it is indeed the user name that is logged on and executing the PI OPC Client, which then activates the gb_opcsim.exe remotely as this user:

If I change to “The interactive user” and the OPC Server is running as user A. If I logged on as user A in PI OPC Client, there are no errors in PI OPC DA Client tool, using Async read.

But, when no user logged in as administrator on the OPC Server, it will show the 0x8000401A The server process could not be started because the configured identity is incorrect error:

The task manager on OPC Server will show the “user A” as the process owner. What if I am now perform “Run as different user” on PI OPC Client as user B, the task manager always shown as “user A” in OPC Server.

Since “user A” has privilege as administrator on PI OPC Client, this condition has no problem of authentication on client side.

Let’s perform this revision using the “This user” option:

Since the above user has administrator privilege and also it is recognized as a domain user, there is no problem of authentication on client side. What if I change to use local user account ?

Then on PI OPC Client there’s Security Audit failure:

But when I try to create the same user ID and password:

The machine wide limit settings do not grant Remote access permission to the COM Server application D:\Program Files (x86)\PIPC\PI OPC Tools\PI_OPCClient\OPCClient.exe with APPID Unavailable to the user xxx\MyTesting SID (S-1-5-21-3767000330-646621135-450484541-1011) from address 172.xx.xx.xx running in the application container Unavailable SID (Unavailable). This security permission can be modified using the Component Services administrative tool.

But there are no security audit error. The above COMRuntime error persists, even when I already place the MyTesting local account to Administrators group. Hmm, fishy.

Later on, I’ve found that in Windows Server 2012 there are new security feature called UAC that treated local account as ordinary user even when it is already on adiministrators group.

The solution is to disable UAC by creating the registry key:


And set this value to 1. This will tell the Windows OS to treat local account as supposed to be. Remember also that this account should be added as administrator on OPC Server so that two way communication can be successfully authenticated and established.

Now the remote OPC Server can happily call the Advise function on PI OPC Client using local account.

After the successful OPC DA Client tool test, the next step is to escalate this into creating the actual OsiSoft PI OPC DA Interface, to connect to GrayBox’s OPS Server simulator, to verify that it actually can collect the data.

And indeed it is again a success 🙂

How to Check Memory Leak in ASP.NET Application

February 22, 2018

The indication of excessive usage of memory in ASP.NET application can be seen in the anomaly of memory size in w3wp.exe on Windows Server task manager.

Based on experiences, the unusual memory size is caused by using SELECT * criteria without any limiting parameter, and the retrieve records is in range of hundred thousand of records.

But the question is, which SELECT * statement ?

The first step is performing memory dump of the w3wp.exe in question by using WinDBG as follows:

In this case, the resulting file size is whooping 900 MB.

This file then is moved to the local computer for further analysis, and then this is opened using WinDBG. Activate SOS.DLL for appropriate .NET version as follows:

Then use the very popular command called dumpheap as follows:

Here it is shown that there is DataRow object that contains an unusually large rows of 294656. Surely this is very big for a normal and optimized application.

Let’s see more detail here:

WinDBG will show tons of object data, so you can use CTRL-BREAK and check just one item only because, each objects are the same.

Just take one of them to be examined:

One of interesting object to be examined is the column object:

Check the list object:

Then check the list item:

Take one to be examined:

Check the column name:

Based on the column name we can inferred the table name involved, but which select statement that retrieve the unusual amount of records ?

Let’s check again the result of dumpheap for SqlDataAdapter as follows:

There are 7 objects to be examined, this should be checked one by one:

The first one clearly do not cause memory leak, so the above step should be repeated for subsequent objects. Until it is found as follows:

The above query clearly will cause memory leak, especially when the data rows already reaching thousands of records. For the above case, it will gives about 300000 records and will eating up much of Windows OS memory in W3WP.EXE.

To check what kind of program that runs the above query, you can examine again the result of object dump as follows:

Perform the revision of the program’s routine and memory leak is now solved !

RBMware (*.RBM) File Format

January 17, 2018

RBMware is a data analysis software. And the RBM itself is Reliability Based Maintenance. But I don’t want to explain more regarding how to use this software.

This article will focus on how to access the database used in RBMware application, especially the data used by OilAnalysis.exe program.

The database is using *.RBM file extension and it is maintained by one of the sofware component called DAF42.DLL.

In user perspective, the RBM data contains hierarchical format as follows:

Here you can see that there are 5 (five) level of hierarchical data: Database, Area, Equipment, Point and the sample data.

Using low level perspective, the database consists of blocks of 512 bytes size called a Record, and it is usually access using DAF42.DLL’s ReadRecord by assigning the record number.

To access each block based on record number, you can use the (n-1) * 512 formula. For example, when the record number is 0x46 or 70, then (70 – 1) * 512 we get 35328 or 0x8A00 which is the block or physical sector number of the record being accessed.

The database can contain zero or many areas, but how to access the record that contains area information ? This is done by accessing the data at sector 0 which contains data as follows:

So the physical sector for the area records is located at record number 0x46, and when translated to physical sector using the above formula, we get 0x8A00:

You can see that it contains the fix sized long description and short description. Each block can contain maximum of 22 items of long description and short description. So what if there’s more than 22 records of area ? It is maintained using the dword data (green one) that will contain record number of the next area data to be retrieved.

Now each area can have zero or many equipment records. This is done by retrieving dword data marked with yellow color (see above). And it is a collection of intermediate record called stdg (or read as gdts at low level) for each corresponding area.

So, for example when the corresponding block has 3 areas, then the dword collection will has 3 items, each points to associated stdg for the related area.

Let’s traverse to first stdg record for first area which is the record number 0xB6 (182) which is (182 – 1) * 512 = 92672 or 0x16A00:

The dword data mark with red color denotes record number for the equipment block. Let’s perform some calculation on 0xC4 (196) which is (196 – 1) * 512 = 99840 or 0x18600:

It has the same characteristics as area record, so the dword denoted with blue one will point to next equipment record when there are more than certain limits.

To retrieve the point record related to each equipment, you can refer to collection of dwords denoted with yellow color called mcdg or gdcm at low level view. Let’s perform some calculation on 0xC6 (198) which is (198 – 1) * 512 = 100864 or 0x18A00:

The mcdg will contain record number to mpig or gipm (denoted with blue color). 0xE4 (228) which is (228 – 1) * 512 = 116224 or 0x1C600:

The mpig record contains collection of point description record (mpdo or opdm denoted by yellow color) and also point short description (code). For example at record number 0xE8 (232) which is (232 – 1) * 512 = 118272 or 0x1CE00 we have:

Each point has record number (blue color above) that points to dcod or odcd record that contains start (red color below) and end (blue color below) record number for point samples data. For 0xEB (235) which is (235 – 1) * 512 = 119808 or 0x1D400 we have:

From the above structure, the start record number for sample point is located at 0x121 which is (289 – 1) * 512 = 147456 or 0x24000 we have:

From the above point sample data or tddo (oddt) we can retrieve record number for next sample for corresponding point (green color), sample data time stamp (blue color), the sample description (red color) and array of dwords 32 bit float data in the IEEE-754 format that will show up on user perspective as follows:

The dword arrays will corresponds to Analyzing Parameter (AP) that can be customized by the user. For the above example, the AP Set is Aluminum, Antimony, Barium, etc. How each of the dword arrays corresponds to the AP Set ?

Let’s take sample 219 for example:

The floating point 0x40C00000 is floating point for 6 for AP Aluminum, the next one will corresponds to Barium, etc. This correlation can be retrieved using record called apdo or odpa.

The record number for apdo is retrieved from apig or gipa record which is a constant of 12 (0xC). So by passing 0xC to ReadRecord we arrive at apig record as follows:

From the above apig record, the apdo record number is denoted with red color so we have 0xA9 which is (169 – 1) * 512 = 86016 or 0x15000:

Based on these parameter strings, then I can provide corresponding relation between array of values from apdo record to the parameter description.

Based on the above information, it is possible to create an application to provide tree traversing and data access for some necessary purposes.

How to Get Property Name in Compiled Visual Basic Applications

December 19, 2017

Suppose I created a small program in Visual Basic form that contains one button, whose click event is coded as follows:

Then the compiled Visual Basic program for the form’s name assignment above will look like this:

Suppose then I revised the above coding to:

Then the generated program will revised to:

Clearly there’s some mapping between the call offset that is generated and the property name in the source program. So, if I can somehow perform the reverse operation between the call offset to the property name, then I can apply it to the unknown compiled program to determine what kind of the property that the program is accessed.

After performing some detailed analysis, it is revealed that the above offset has some relation to the Dispatch ID and it is processed using VB6.EXE’s unknown interface method.

When I tried to view the interface methods of VB6.EXE using OLEVIEW.exe it is failed to do so, but there’s file called VB6.OLB that can be successfully viewed by OLEVIEW.exe program.

And here is the part of the content that deals with form’s method:

What’s interesting is that it is missing the Dispatch ID that usually exist in regular application’s type library functions. As you can see from the above, the Name property is the first one declared in the method interface, and 0x48 is actually the lowest magic number value.

The next method name can be deduced by adding 0x8 offset to the starting number, so for example above, the value 0x50 will corresponds to Caption property etc.

Using the above fact, then I can create the mapping table between the property and it is calling offset, so that it can be used to deduce the property name that is used from certain unknown applications that is developed using Visual Basic compiler.

How to Install NopCommerce 3.90

November 15, 2017

Since the downloaded 3.90 source from:


Can be compiled using Visual Studio 2013 without any issues, this article will focused on deployment and installation of nopCommerce.

The target server machine is using Windows Server 2012 R2 64 bit and IIS 8.0.

First, create the virtual directory called NoC:

And assigned the proper Application Pool to it:

Then copy the entire compiled version of nopCommerce in D:\Projects\Mvc\NoC\3.90\src\Presentation\Nop.Web to the destination server folder.

Then fire up the browser using this link:

That’s all folks 🙂