Hooking in BASIC+

OI 10 Logo Transparent2

A respected colleague recently asked for ideas on how to audit the OpenInsight Editor++ so he can track when a stored procedure is compiled. We proposed a wrapper around the Editor++ commuter module. While our colleague was familiar with this technique, it occurred to us that some developers might not know how to implement this form of hooking into BASIC+. Software hooking is a well known technique used by developers and tools within various platforms. The concept allows a custom process to intercept function calls in order to monitor the messages being generated or, in some cases, to change the behavior of the function. This article will document how to create a stored procedure wrapper that can be used to hook into both developer stored procedures as well as (most) system stored procedures.

Caution – Enter at Your Own Risk

We should begin with a healthy amount of disclaimers. While our experience with hooking has been generally positive, great care should be taken before introducing a BASIC+ hook. This is not meant to discourage people. We just want to avoid angry mobs in case something goes horribly wrong. 🙂 Therefore, proceed with caution. As in all situations where a change in the core environment is being made, make sure you have a current backup handy and always test this in a development environment before releasing into production (if applicable). It should also go without saying that there is no obligation by Revelation Software to support your system if you make changes to system stored procedures. Management is not responsible for loss or damage!

Hook, Line, and Sinker

The steps for creating a BASIC+ hook are really quite simple:

  1. Back-up the object code for the targeted stored procedure. Name it something like $ProcedureName_ORIG.
  2. Create a new stored procedure using the original name of the targeted stored procedure. Pass in the same arguments that the targeted stored procedure uses. Define it as a subroutine or a function based on the target’s type.
  3. In this new stored procedure, make a direct call to ProcedureName_ORIG.

When OpenInsight tries to call the targeted stored procedure, the call will go to your routine automatically. You can capture the incoming arguments before calling the renamed procedure. Outgoing arguments and a function’s return value can be also observed after the renamed procedure has finished executing. This is very similar to implementing pre and post event handlers with respect to the System Event Handler or a custom MFS with respect to the BFS.

Worth the Effort?

This is not all that difficult to do, especially if you have a sample routine to follow (see below) and there are many good reasons for introducing hooks. As mentioned at the beginning of the article, developers can use them to track activity in specific tools, perhaps to log the activity for auditing purposes. This technique can be quite useful for learning more about system stored procedures, especially if they have not been fully documented. Finally, developers can modify or even replace the functionality of the original stored procedure. Case in point, we used a hook wrapper for RUN_OECGI_REQUEST to better understand how it received OECGI HTTP Requests and how HTTP Responses were intended to be formatted. This helped us to write our own dispatcher routine for the SRP HTTP Framework product.

To test the suggestion we gave our colleague, we created a hook for the Editor++ commuter module. Here is what our routine looks like:

Function RTI_Editor_Events(Action, File, Record, Param1, Param2, Param3, Param4)

Declare function RTI_Editor_Events_Orig

Ans = RTI_Editor_Events_Orig(Action, File, Record, Param1, Param2, Param3, Param4)

Return Ans

Forging the Signature

Obviously in order for this to work the hooking stored procedure needs to emulate the same signature of the targeted stored procedure. That is, they both must be subroutines or they both must be functions. Likewise, they both must use the same argument list. This leads us to a natural question: how do we get access to the targeted stored procedure’s signature?

In an ideal situation, documentation is your friend. Hopefully the subroutine versus function distinction is spelled out or is easy to infer (e.g., event commuter modules are almost always functions). If all else fails, make the hooking stored procedure a subroutine. If the calling process generates a VNAV, then this is most likely due to it expecting a return value from a function call. Simply change the hooking stored procedure to a function and the problem should go away.

While many system stored procedures are documented, their arguments lists are not always complete. In these cases it might be necessary to examine the object code. Arguments are stored in an @VM delimited list located at the end of the object code and preceded by a Char(0). Unfortunately, none of the current OpenInsight record editors can display object code, so other tools must be used. One could write the object code to an OS file and use an external editor to view the content. However, AREV has always been able to view object code…which means ARev32 can do the same! In the case of $RTI_EDITOR_EVENTS, here is what it looks like:

ARev32ObjectCode

Likewise, developers who use the SRP Editor can also display object code. However, with the SRP Editor, this is unnecessary because the argument list is automatically parsed for you when you invoke Call Tips:

SRPEditorCallTips

It’s a Wrap

Hooking BASIC+ stored procedures is not something we do everyday, but the technique has been quite useful in many situations to better understand the inner workings of OpenInsight or to help troubleshoot problems. It you have additional ideas on this or questions, start a conversation in the SRP forums. We would be quite happy to explore this further.

10 Responses to Hooking in BASIC+

  • Barry Stevens says:

    One more thing to take into consideration, is the requirement to comment out the ssp in the list SYSENV->SYSPROCNAMES to allow it to compile.

    You might need to exit OI after the change for it to be recongise.

    and as mentioned before, caution is the keyword here!!!!

  • Mark Marsh says:

    An MFS on Sysprocs, Sysreposwins, sysreposevents would be another good way to track changes.

    We achieved configurable ‘Control level security’ with a combination of the two options. An MFS on Sysreposwinsexes to disable a control on read if the current user wasn’t in the right group. Then a wrapper around set_property to block any requests to enable it again.

    • Don Bakke says:

      All good ideas. We’ve created very similar solutions ourselves using an MFS on system tables. I’m not sure this technically qualifies as a BASIC+ hook, as the hooking is against the system table rather than a stored procedure. But this idea certainly extends the realm of developer control over the OpenInsight environment to achieve similar ends…which is one of the core features that makes OpenInsight such a great tool to work with. Another technique which I might document in a follow-up article is the use of MD pointers.

    • Don Bakke says:

      Yep, he’s one of the best. We do wonder, however, how he came along with his task. Hopefully he’ll give us an update on this. 🙂

    • Don Bakke says:

      I might not be understanding the full intent of your question, but the simple answer is that you would hook into the SRP_EDITOR_EVENTS commuter. Once you have done that, I expect that you would be able to intercept virtually any and all event handler activity in a way that you could identify the relevant Key IDs to the stored procedures or inserts. Let me know if that didn’t quite give you the answer you needed.

Leave a Reply