Skip to content

Improve standalone mode#101

Merged
kevoreilly merged 1 commit into
kevoreilly:capemonfrom
GDATAAdvancedAnalytics:improve-standalone-mode
Jul 25, 2025
Merged

Improve standalone mode#101
kevoreilly merged 1 commit into
kevoreilly:capemonfrom
GDATAAdvancedAnalytics:improve-standalone-mode

Conversation

@meck-gd
Copy link
Copy Markdown
Contributor

@meck-gd meck-gd commented Jul 24, 2025

A while ago I submitted #79. When I debugged the hook issue I was having a couple days ago, I was confused because nothing was happening. I must have had additional changes back then that I didn't commit.

The changes in this MR make standalone mode more useful. Before, it bailed pretty early in initialization - there were no hooks set, no callbacks installed, and so on. The only issue with the code after the return was that it waited for input from CAPE via pipe2(). I changed the pipe2() function so that it won't contact CAPE in standalone mode.

Other than that, some more path shuffling was required so certain hooks don't behave weirdly. The strcpy call was using g_config.analyzer before it was initialized, so I moved it down so that g_config.results has the expected fallback value.

It's working quite well now and I think that this was the original intent for standalone mode - it neatly writes API logs etc. to disk. I also tested with CAPE to make sure the changes don't affect it in any way.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @heck-gd, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the functionality and usability of the standalone mode. The core changes allow the application to fully initialize, including setting up hooks and callbacks, without requiring communication with the CAPE backend. This enables standalone mode to properly write API logs and other data to disk, fulfilling its intended purpose as a self-contained analysis tool.

Highlights

  • Standalone Mode Behavior: Modified the DllMain function to prevent standalone mode from bailing out early. Previously, it would return immediately after CAPE_init(), but now it proceeds with further initialization, allowing hooks and callbacks to be installed.
  • Configuration Path Initialization: Added logic within the standalone mode initialization to ensure g_config.w_analyzer and g_config.w_results (wide character paths) are properly populated. This addresses issues where certain hooks might behave unexpectedly if these paths are empty.
  • Inter-process Communication Bypass: Updated the pipe2 function to conditionally bypass named pipe communication when running in standalone mode. This prevents the application from waiting for input from the CAPE backend, which is not present in standalone operation.
  • Corrected Path Assignment Order: Adjusted the timing of the strcpy call that sets g_config.results based on g_config.analyzer. This ensures g_config.analyzer is fully processed and set before g_config.results is initialized from it, preventing potential issues with incorrect path values.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

An excellent pull request that significantly improves the utility of the standalone mode. The changes are logical and well-explained in the description. I've identified a few areas for improvement, mainly concerning code safety and correctness. My most critical feedback is regarding the use of variadic arguments in pipe.c, which could lead to undefined behavior. I've also suggested improvements for string handling in capemon.c and config.c to enhance robustness and security.

Comment thread pipe.c
Comment on lines +231 to 244
else {
va_start(args, fmt);
len = _pipe_sprintf(NULL, fmt, args);
if(len > 0) {
char *buf = calloc(1, len + 1);
_pipe_sprintf(buf, fmt, args);
va_end(args);

if(CallNamedPipeW(g_config.pipe_name, buf, len, out, *outlen,
(DWORD *) outlen, NMPWAIT_WAIT_FOREVER) != 0)
ret = 0;
free(buf);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There are a couple of issues with the variadic argument handling here:

  1. The va_list variable args is used in _pipe_sprintf on line 233, and then reused on line 236 without re-initialization. This leads to undefined behavior as the state of args is indeterminate after the first call.
  2. va_end is only called if len > 0, but it should always be called if va_start was called to avoid resource leaks.

To fix this, you should call va_end immediately after the first use of args, and then re-initialize args with va_start before the second use.

va_start(args, fmt);
		len = _pipe_sprintf(NULL, fmt, args);
		va_end(args);

		if(len > 0) {
			char *buf = calloc(1, len + 1);
			va_start(args, fmt);
			_pipe_sprintf(buf, fmt, args);
			va_end(args);

			if(CallNamedPipeW(g_config.pipe_name, buf, len, out, *outlen,
					(DWORD *) outlen, NMPWAIT_WAIT_FOREVER) != 0)
				ret = 0;
			free(buf);

Comment thread config.c
PathRemoveFileSpec(g_config.analyzer); // remove filename
sprintf(config_fname, "%s\\%u.ini", g_config.analyzer, GetCurrentProcessId());

strcpy(g_config.results, g_config.analyzer);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using strcpy is risky as it doesn't perform bounds checking, which can lead to buffer overflows. It's better to use a safer alternative like snprintf to prevent potential security vulnerabilities. This also ensures null-termination.

snprintf(g_config.results, sizeof(g_config.results), "%s", g_config.analyzer);

Comment thread capemon.c
Comment on lines +630 to +635
for (i = 0; i < ARRAYSIZE(g_config.analyzer); i++)
g_config.w_analyzer[i] = (wchar_t)(unsigned short)g_config.analyzer[i];
}
if (!g_config.w_results[0]) {
for (i = 0; i < ARRAYSIZE(g_config.results); i++)
g_config.w_results[i] = (wchar_t)(unsigned short)g_config.results[i];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These manual char-by-char conversions to wchar_t are inefficient as they iterate over the entire MAX_PATH length buffer regardless of the actual string length. They also assume an ASCII-compatible character set, which might not hold true in all environments. A more robust and idiomatic way to perform this conversion on Windows is to use the MultiByteToWideChar function. It correctly handles null-terminated strings and different character sets.

MultiByteToWideChar(CP_ACP, 0, g_config.analyzer, -1, g_config.w_analyzer, ARRAYSIZE(g_config.w_analyzer));
			}
			if (!g_config.w_results[0]) {
				MultiByteToWideChar(CP_ACP, 0, g_config.results, -1, g_config.w_results, ARRAYSIZE(g_config.w_results));

@kevoreilly
Copy link
Copy Markdown
Owner

Always warms my heart to see standalone mode being used by someone. Thank you for helping to improve it.

I like the changes, although as soon as I saw pipe2 I recalled that it's only ever used once in the entire codebase:

		// obtain all protected pids
		pipe2(pids, &length, "GETPIDS:");
		for (i = 0; i < length / sizeof(pids[0]); i++) {
			add_protected_pid(pids[i]);
		}

(capemon.c)

So I think I would rather this:

		if (!g_config.standalone) {
			// obtain all protected pids
			pipe2(pids, &length, "GETPIDS:");
			for (i = 0; i < length / sizeof(pids[0]); i++) {
				add_protected_pid(pids[i]);
			}
		}

... and leave pipe.c unchanged. What do you think?

@meck-gd
Copy link
Copy Markdown
Contributor Author

meck-gd commented Jul 25, 2025

I did that initially, but then decided against it. Reason being if pipe2 is ever going to be used for anything else in the future, it would cause trouble if care is not taken and the standalone check is forgotten.

Plus the pipe() function in pipe.c also has special internal handling for standalone mode.

In the end I'm leaving it up to you though, I can change it if you prefer not touching pipe.c.

@kevoreilly
Copy link
Copy Markdown
Owner

I can't argue with that! Thanks again

@kevoreilly kevoreilly merged commit 3271f81 into kevoreilly:capemon Jul 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants